babylon.glowLayer.ts 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. module BABYLON {
  2. /**
  3. * Glow layer options. This helps customizing the behaviour
  4. * of the glow layer.
  5. */
  6. export interface IGlowLayerOptions {
  7. /**
  8. * Multiplication factor apply to the canvas size to compute the render target size
  9. * used to generated the glowing objects (the smaller the faster).
  10. */
  11. mainTextureRatio: number;
  12. /**
  13. * Enforces a fixed size texture to ensure resize independant blur.
  14. */
  15. mainTextureFixedSize?: number;
  16. /**
  17. * How big is the kernel of the blur texture.
  18. */
  19. blurKernelSize: number;
  20. /**
  21. * The camera attached to the layer.
  22. */
  23. camera: Nullable<Camera>;
  24. /**
  25. * Enable MSAA by chosing the number of samples.
  26. */
  27. mainTextureSamples?: number;
  28. }
  29. /**
  30. * The glow layer Helps adding a glow effect around the emissive parts of a mesh.
  31. *
  32. * Once instantiated in a scene, simply use the pushMesh or removeMesh method to add or remove
  33. * glowy meshes to your scene.
  34. *
  35. * Documentation: https://doc.babylonjs.com/how_to/glow_layer
  36. */
  37. export class GlowLayer extends EffectLayer {
  38. /**
  39. * Effect Name of the layer.
  40. */
  41. public static readonly EffectName = "GlowLayer";
  42. /**
  43. * The default blur kernel size used for the glow.
  44. */
  45. public static DefaultBlurKernelSize = 32;
  46. /**
  47. * The default texture size ratio used for the glow.
  48. */
  49. public static DefaultTextureRatio = 0.5;
  50. /**
  51. * Sets the kernel size of the blur.
  52. */
  53. public set blurKernelSize(value: number) {
  54. this._horizontalBlurPostprocess1.kernel = value;
  55. this._verticalBlurPostprocess1.kernel = value;
  56. this._horizontalBlurPostprocess2.kernel = value;
  57. this._verticalBlurPostprocess2.kernel = value;
  58. }
  59. /**
  60. * Gets the kernel size of the blur.
  61. */
  62. public get blurKernelSize(): number {
  63. return this._horizontalBlurPostprocess1.kernel;
  64. }
  65. /**
  66. * Sets the glow intensity.
  67. */
  68. public set intensity(value: number) {
  69. this._intensity = value;
  70. }
  71. /**
  72. * Gets the glow intensity.
  73. */
  74. public get intensity(): number {
  75. return this._intensity;
  76. }
  77. private _options: IGlowLayerOptions;
  78. private _intensity: number = 1.0;
  79. private _horizontalBlurPostprocess1: BlurPostProcess;
  80. private _verticalBlurPostprocess1: BlurPostProcess;
  81. private _horizontalBlurPostprocess2: BlurPostProcess;
  82. private _verticalBlurPostprocess2: BlurPostProcess;
  83. private _blurTexture1: RenderTargetTexture;
  84. private _blurTexture2: RenderTargetTexture;
  85. private _postProcesses1: PostProcess[];
  86. private _postProcesses2: PostProcess[];
  87. private _includedOnlyMeshes: number[] = [];
  88. private _excludedMeshes: number[] = [];
  89. /**
  90. * Callback used to let the user override the color selection on a per mesh basis
  91. */
  92. public customEmissiveColorSelector: (mesh: Mesh, subMesh: SubMesh, material: Material, result: Color4) => void;
  93. /**
  94. * Callback used to let the user override the texture selection on a per mesh basis
  95. */
  96. public customEmissiveTextureSelector: (mesh: Mesh, subMesh: SubMesh, material: Material) => Texture;
  97. /**
  98. * Instantiates a new glow Layer and references it to the scene.
  99. * @param name The name of the layer
  100. * @param scene The scene to use the layer in
  101. * @param options Sets of none mandatory options to use with the layer (see IGlowLayerOptions for more information)
  102. */
  103. constructor(public name: string, scene: Scene, options?: Partial<IGlowLayerOptions>) {
  104. super(name, scene);
  105. this.neutralColor = new Color4(0, 0, 0, 1);
  106. // Adapt options
  107. this._options = {
  108. mainTextureRatio: GlowLayer.DefaultTextureRatio,
  109. blurKernelSize: 32,
  110. mainTextureFixedSize: undefined,
  111. camera: null,
  112. mainTextureSamples: 1,
  113. ...options,
  114. };
  115. // Initialize the layer
  116. this._init({
  117. alphaBlendingMode: Engine.ALPHA_ADD,
  118. camera: this._options.camera,
  119. mainTextureFixedSize: this._options.mainTextureFixedSize,
  120. mainTextureRatio: this._options.mainTextureRatio
  121. });
  122. }
  123. /**
  124. * Get the effect name of the layer.
  125. * @return The effect name
  126. */
  127. public getEffectName(): string {
  128. return GlowLayer.EffectName;
  129. }
  130. /**
  131. * Create the merge effect. This is the shader use to blit the information back
  132. * to the main canvas at the end of the scene rendering.
  133. */
  134. protected _createMergeEffect(): Effect {
  135. // Effect
  136. return this._engine.createEffect("glowMapMerge",
  137. [VertexBuffer.PositionKind],
  138. ["offset"],
  139. ["textureSampler", "textureSampler2"],
  140. "#define EMISSIVE \n");
  141. }
  142. /**
  143. * Creates the render target textures and post processes used in the glow layer.
  144. */
  145. protected _createTextureAndPostProcesses(): void {
  146. var blurTextureWidth = this._mainTextureDesiredSize.width;
  147. var blurTextureHeight = this._mainTextureDesiredSize.height;
  148. blurTextureWidth = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(blurTextureWidth, this._maxSize) : blurTextureWidth;
  149. blurTextureHeight = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(blurTextureHeight, this._maxSize) : blurTextureHeight;
  150. this._blurTexture1 = new RenderTargetTexture("GlowLayerBlurRTT",
  151. {
  152. width: blurTextureWidth,
  153. height: blurTextureHeight
  154. },
  155. this._scene,
  156. false,
  157. true,
  158. Engine.TEXTURETYPE_HALF_FLOAT);
  159. this._blurTexture1.wrapU = Texture.CLAMP_ADDRESSMODE;
  160. this._blurTexture1.wrapV = Texture.CLAMP_ADDRESSMODE;
  161. this._blurTexture1.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
  162. this._blurTexture1.renderParticles = false;
  163. this._blurTexture1.ignoreCameraViewport = true;
  164. var blurTextureWidth2 = Math.floor(blurTextureWidth / 2);
  165. var blurTextureHeight2 = Math.floor(blurTextureHeight / 2);
  166. this._blurTexture2 = new RenderTargetTexture("GlowLayerBlurRTT2",
  167. {
  168. width: blurTextureWidth2,
  169. height: blurTextureHeight2
  170. },
  171. this._scene,
  172. false,
  173. true,
  174. Engine.TEXTURETYPE_HALF_FLOAT);
  175. this._blurTexture2.wrapU = Texture.CLAMP_ADDRESSMODE;
  176. this._blurTexture2.wrapV = Texture.CLAMP_ADDRESSMODE;
  177. this._blurTexture2.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
  178. this._blurTexture2.renderParticles = false;
  179. this._blurTexture2.ignoreCameraViewport = true;
  180. this._textures = [ this._blurTexture1, this._blurTexture2 ];
  181. this._horizontalBlurPostprocess1 = new BlurPostProcess("GlowLayerHBP1", new Vector2(1.0, 0), this._options.blurKernelSize / 2, {
  182. width: blurTextureWidth,
  183. height: blurTextureHeight
  184. },
  185. null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, Engine.TEXTURETYPE_HALF_FLOAT);
  186. this._horizontalBlurPostprocess1.width = blurTextureWidth;
  187. this._horizontalBlurPostprocess1.height = blurTextureHeight;
  188. this._horizontalBlurPostprocess1.onApplyObservable.add(effect => {
  189. effect.setTexture("textureSampler", this._mainTexture);
  190. });
  191. this._verticalBlurPostprocess1 = new BlurPostProcess("GlowLayerVBP1", new Vector2(0, 1.0), this._options.blurKernelSize / 2, {
  192. width: blurTextureWidth,
  193. height: blurTextureHeight
  194. },
  195. null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, Engine.TEXTURETYPE_HALF_FLOAT);
  196. this._horizontalBlurPostprocess2 = new BlurPostProcess("GlowLayerHBP2", new Vector2(1.0, 0), this._options.blurKernelSize / 2, {
  197. width: blurTextureWidth2,
  198. height: blurTextureHeight2
  199. },
  200. null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, Engine.TEXTURETYPE_HALF_FLOAT);
  201. this._horizontalBlurPostprocess2.width = blurTextureWidth2;
  202. this._horizontalBlurPostprocess2.height = blurTextureHeight2;
  203. this._horizontalBlurPostprocess2.onApplyObservable.add(effect => {
  204. effect.setTexture("textureSampler", this._blurTexture1);
  205. });
  206. this._verticalBlurPostprocess2 = new BlurPostProcess("GlowLayerVBP2", new Vector2(0, 1.0), this._options.blurKernelSize / 2, {
  207. width: blurTextureWidth2,
  208. height: blurTextureHeight2
  209. },
  210. null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, Engine.TEXTURETYPE_HALF_FLOAT);
  211. this._postProcesses = [ this._horizontalBlurPostprocess1, this._verticalBlurPostprocess1, this._horizontalBlurPostprocess2, this._verticalBlurPostprocess2 ];
  212. this._postProcesses1 = [ this._horizontalBlurPostprocess1, this._verticalBlurPostprocess1 ];
  213. this._postProcesses2 = [ this._horizontalBlurPostprocess2, this._verticalBlurPostprocess2 ];
  214. this._mainTexture.samples = this._options.mainTextureSamples!;
  215. this._mainTexture.onAfterUnbindObservable.add(() => {
  216. let internalTexture = this._blurTexture1.getInternalTexture();
  217. if (internalTexture) {
  218. this._scene.postProcessManager.directRender(
  219. this._postProcesses1,
  220. internalTexture,
  221. true);
  222. internalTexture = this._blurTexture2.getInternalTexture();
  223. if (internalTexture) {
  224. this._scene.postProcessManager.directRender(
  225. this._postProcesses2,
  226. internalTexture,
  227. true);
  228. }
  229. }
  230. });
  231. // Prevent autoClear.
  232. this._postProcesses.map(pp => { pp.autoClear = false; });
  233. }
  234. /**
  235. * Checks for the readiness of the element composing the layer.
  236. * @param subMesh the mesh to check for
  237. * @param useInstances specify wether or not to use instances to render the mesh
  238. * @param emissiveTexture the associated emissive texture used to generate the glow
  239. * @return true if ready otherwise, false
  240. */
  241. public isReady(subMesh: SubMesh, useInstances: boolean): boolean {
  242. let material = subMesh.getMaterial();
  243. let mesh = subMesh.getRenderingMesh();
  244. if (!material || !mesh) {
  245. return false;
  246. }
  247. let emissiveTexture = (<any>material).emissiveTexture;
  248. return super._isReady(subMesh, useInstances, emissiveTexture);
  249. }
  250. /**
  251. * Returns wether or nood the layer needs stencil enabled during the mesh rendering.
  252. */
  253. public needStencil(): boolean {
  254. return false;
  255. }
  256. /**
  257. * Implementation specific of rendering the generating effect on the main canvas.
  258. * @param effect The effect used to render through
  259. */
  260. protected _internalRender(effect: Effect): void {
  261. // Texture
  262. effect.setTexture("textureSampler", this._blurTexture1);
  263. effect.setTexture("textureSampler2", this._blurTexture2);
  264. effect.setFloat("offset", this._intensity);
  265. // Cache
  266. var engine = this._engine;
  267. var previousStencilBuffer = engine.getStencilBuffer();
  268. // Draw order
  269. engine.setStencilBuffer(false);
  270. engine.drawElementsType(Material.TriangleFillMode, 0, 6);
  271. // Draw order
  272. engine.setStencilBuffer(previousStencilBuffer);
  273. }
  274. /**
  275. * Sets the required values for both the emissive texture and and the main color.
  276. */
  277. protected _setEmissiveTextureAndColor(mesh: Mesh, subMesh: SubMesh, material: Material): void {
  278. var textureLevel = 1.0;
  279. if (this.customEmissiveTextureSelector) {
  280. this._emissiveTextureAndColor.texture = this.customEmissiveTextureSelector(mesh, subMesh, material);
  281. } else {
  282. if (material) {
  283. this._emissiveTextureAndColor.texture = (<any>material).emissiveTexture;
  284. if (this._emissiveTextureAndColor.texture) {
  285. textureLevel = this._emissiveTextureAndColor.texture.level;
  286. }
  287. }
  288. else {
  289. this._emissiveTextureAndColor.texture = null;
  290. }
  291. }
  292. if (this.customEmissiveColorSelector) {
  293. this.customEmissiveColorSelector(mesh, subMesh, material, this._emissiveTextureAndColor.color);
  294. } else {
  295. if ((<any>material).emissiveColor) {
  296. this._emissiveTextureAndColor.color.set(
  297. (<any>material).emissiveColor.r * textureLevel,
  298. (<any>material).emissiveColor.g * textureLevel,
  299. (<any>material).emissiveColor .b * textureLevel,
  300. 1.0);
  301. }
  302. else {
  303. this._emissiveTextureAndColor.color.set(
  304. this.neutralColor.r,
  305. this.neutralColor.g,
  306. this.neutralColor.b,
  307. this.neutralColor.a);
  308. }
  309. }
  310. }
  311. /**
  312. * Returns true if the mesh should render, otherwise false.
  313. * @param mesh The mesh to render
  314. * @returns true if it should render otherwise false
  315. */
  316. protected _shouldRenderMesh(mesh: Mesh): boolean {
  317. return this.hasMesh(mesh);
  318. }
  319. /**
  320. * Add a mesh in the exclusion list to prevent it to impact or being impacted by the glow layer.
  321. * @param mesh The mesh to exclude from the glow layer
  322. */
  323. public addExcludedMesh(mesh: Mesh): void {
  324. if (this._excludedMeshes.indexOf(mesh.uniqueId) === -1) {
  325. this._excludedMeshes.push(mesh.uniqueId);
  326. }
  327. }
  328. /**
  329. * Remove a mesh from the exclusion list to let it impact or being impacted by the glow layer.
  330. * @param mesh The mesh to remove
  331. */
  332. public removeExcludedMesh(mesh: Mesh): void {
  333. var index = this._excludedMeshes.indexOf(mesh.uniqueId);
  334. if (index !== -1) {
  335. this._excludedMeshes.splice(index, 1);
  336. }
  337. }
  338. /**
  339. * Add a mesh in the inclusion list to impact or being impacted by the glow layer.
  340. * @param mesh The mesh to include in the glow layer
  341. */
  342. public addIncludedOnlyMesh(mesh: Mesh): void {
  343. if (this._includedOnlyMeshes.indexOf(mesh.uniqueId) === -1) {
  344. this._includedOnlyMeshes.push(mesh.uniqueId);
  345. }
  346. }
  347. /**
  348. * Remove a mesh from the Inclusion list to prevent it to impact or being impacted by the glow layer.
  349. * @param mesh The mesh to remove
  350. */
  351. public removeIncludedOnlyMesh(mesh: Mesh): void {
  352. var index = this._includedOnlyMeshes.indexOf(mesh.uniqueId);
  353. if (index !== -1) {
  354. this._includedOnlyMeshes.splice(index, 1);
  355. }
  356. }
  357. /**
  358. * Determine if a given mesh will be used in the glow layer
  359. * @param mesh The mesh to test
  360. * @returns true if the mesh will be highlighted by the current glow layer
  361. */
  362. public hasMesh(mesh: AbstractMesh): boolean {
  363. // Included Mesh
  364. if (this._includedOnlyMeshes.length) {
  365. return this._includedOnlyMeshes.indexOf(mesh.uniqueId) !== -1;
  366. };
  367. // Excluded Mesh
  368. if (this._excludedMeshes.length) {
  369. return this._excludedMeshes.indexOf(mesh.uniqueId) === -1;
  370. };
  371. return true;
  372. }
  373. /**
  374. * Free any resources and references associated to a mesh.
  375. * Internal use
  376. * @param mesh The mesh to free.
  377. */
  378. public _disposeMesh(mesh: Mesh): void {
  379. this.removeIncludedOnlyMesh(mesh);
  380. this.removeExcludedMesh(mesh);
  381. }
  382. }
  383. }