babylon.glowLayer.ts 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  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. @serialize()
  63. public get blurKernelSize(): number {
  64. return this._horizontalBlurPostprocess1.kernel;
  65. }
  66. /**
  67. * Sets the glow intensity.
  68. */
  69. public set intensity(value: number) {
  70. this._intensity = value;
  71. }
  72. /**
  73. * Gets the glow intensity.
  74. */
  75. @serialize()
  76. public get intensity(): number {
  77. return this._intensity;
  78. }
  79. @serialize('options')
  80. private _options: IGlowLayerOptions;
  81. private _intensity: number = 1.0;
  82. private _horizontalBlurPostprocess1: BlurPostProcess;
  83. private _verticalBlurPostprocess1: BlurPostProcess;
  84. private _horizontalBlurPostprocess2: BlurPostProcess;
  85. private _verticalBlurPostprocess2: BlurPostProcess;
  86. private _blurTexture1: RenderTargetTexture;
  87. private _blurTexture2: RenderTargetTexture;
  88. private _postProcesses1: PostProcess[];
  89. private _postProcesses2: PostProcess[];
  90. private _includedOnlyMeshes: number[] = [];
  91. private _excludedMeshes: number[] = [];
  92. /**
  93. * Callback used to let the user override the color selection on a per mesh basis
  94. */
  95. public customEmissiveColorSelector: (mesh: Mesh, subMesh: SubMesh, material: Material, result: Color4) => void;
  96. /**
  97. * Callback used to let the user override the texture selection on a per mesh basis
  98. */
  99. public customEmissiveTextureSelector: (mesh: Mesh, subMesh: SubMesh, material: Material) => Texture;
  100. /**
  101. * Instantiates a new glow Layer and references it to the scene.
  102. * @param name The name of the layer
  103. * @param scene The scene to use the layer in
  104. * @param options Sets of none mandatory options to use with the layer (see IGlowLayerOptions for more information)
  105. */
  106. constructor(name: string, scene: Scene, options?: Partial<IGlowLayerOptions>) {
  107. super(name, scene);
  108. this.neutralColor = new Color4(0, 0, 0, 1);
  109. // Adapt options
  110. this._options = {
  111. mainTextureRatio: GlowLayer.DefaultTextureRatio,
  112. blurKernelSize: 32,
  113. mainTextureFixedSize: undefined,
  114. camera: null,
  115. mainTextureSamples: 1,
  116. ...options,
  117. };
  118. // Initialize the layer
  119. this._init({
  120. alphaBlendingMode: Engine.ALPHA_ADD,
  121. camera: this._options.camera,
  122. mainTextureFixedSize: this._options.mainTextureFixedSize,
  123. mainTextureRatio: this._options.mainTextureRatio
  124. });
  125. }
  126. /**
  127. * Get the effect name of the layer.
  128. * @return The effect name
  129. */
  130. public getEffectName(): string {
  131. return GlowLayer.EffectName;
  132. }
  133. /**
  134. * Create the merge effect. This is the shader use to blit the information back
  135. * to the main canvas at the end of the scene rendering.
  136. */
  137. protected _createMergeEffect(): Effect {
  138. // Effect
  139. return this._engine.createEffect("glowMapMerge",
  140. [VertexBuffer.PositionKind],
  141. ["offset"],
  142. ["textureSampler", "textureSampler2"],
  143. "#define EMISSIVE \n");
  144. }
  145. /**
  146. * Creates the render target textures and post processes used in the glow layer.
  147. */
  148. protected _createTextureAndPostProcesses(): void {
  149. var blurTextureWidth = this._mainTextureDesiredSize.width;
  150. var blurTextureHeight = this._mainTextureDesiredSize.height;
  151. blurTextureWidth = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(blurTextureWidth, this._maxSize) : blurTextureWidth;
  152. blurTextureHeight = this._engine.needPOTTextures ? Tools.GetExponentOfTwo(blurTextureHeight, this._maxSize) : blurTextureHeight;
  153. var textureType = 0;
  154. if (this._engine.getCaps().textureHalfFloatRender) {
  155. textureType = Engine.TEXTURETYPE_HALF_FLOAT;
  156. }
  157. else {
  158. textureType = Engine.TEXTURETYPE_UNSIGNED_INT;
  159. }
  160. this._blurTexture1 = new RenderTargetTexture("GlowLayerBlurRTT",
  161. {
  162. width: blurTextureWidth,
  163. height: blurTextureHeight
  164. },
  165. this._scene,
  166. false,
  167. true,
  168. textureType);
  169. this._blurTexture1.wrapU = Texture.CLAMP_ADDRESSMODE;
  170. this._blurTexture1.wrapV = Texture.CLAMP_ADDRESSMODE;
  171. this._blurTexture1.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
  172. this._blurTexture1.renderParticles = false;
  173. this._blurTexture1.ignoreCameraViewport = true;
  174. var blurTextureWidth2 = Math.floor(blurTextureWidth / 2);
  175. var blurTextureHeight2 = Math.floor(blurTextureHeight / 2);
  176. this._blurTexture2 = new RenderTargetTexture("GlowLayerBlurRTT2",
  177. {
  178. width: blurTextureWidth2,
  179. height: blurTextureHeight2
  180. },
  181. this._scene,
  182. false,
  183. true,
  184. textureType);
  185. this._blurTexture2.wrapU = Texture.CLAMP_ADDRESSMODE;
  186. this._blurTexture2.wrapV = Texture.CLAMP_ADDRESSMODE;
  187. this._blurTexture2.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
  188. this._blurTexture2.renderParticles = false;
  189. this._blurTexture2.ignoreCameraViewport = true;
  190. this._textures = [ this._blurTexture1, this._blurTexture2 ];
  191. this._horizontalBlurPostprocess1 = new BlurPostProcess("GlowLayerHBP1", new Vector2(1.0, 0), this._options.blurKernelSize / 2, {
  192. width: blurTextureWidth,
  193. height: blurTextureHeight
  194. },
  195. null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, textureType);
  196. this._horizontalBlurPostprocess1.width = blurTextureWidth;
  197. this._horizontalBlurPostprocess1.height = blurTextureHeight;
  198. this._horizontalBlurPostprocess1.onApplyObservable.add(effect => {
  199. effect.setTexture("textureSampler", this._mainTexture);
  200. });
  201. this._verticalBlurPostprocess1 = new BlurPostProcess("GlowLayerVBP1", new Vector2(0, 1.0), this._options.blurKernelSize / 2, {
  202. width: blurTextureWidth,
  203. height: blurTextureHeight
  204. },
  205. null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, textureType);
  206. this._horizontalBlurPostprocess2 = new BlurPostProcess("GlowLayerHBP2", new Vector2(1.0, 0), this._options.blurKernelSize / 2, {
  207. width: blurTextureWidth2,
  208. height: blurTextureHeight2
  209. },
  210. null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, textureType);
  211. this._horizontalBlurPostprocess2.width = blurTextureWidth2;
  212. this._horizontalBlurPostprocess2.height = blurTextureHeight2;
  213. this._horizontalBlurPostprocess2.onApplyObservable.add(effect => {
  214. effect.setTexture("textureSampler", this._blurTexture1);
  215. });
  216. this._verticalBlurPostprocess2 = new BlurPostProcess("GlowLayerVBP2", new Vector2(0, 1.0), this._options.blurKernelSize / 2, {
  217. width: blurTextureWidth2,
  218. height: blurTextureHeight2
  219. },
  220. null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, textureType);
  221. this._postProcesses = [ this._horizontalBlurPostprocess1, this._verticalBlurPostprocess1, this._horizontalBlurPostprocess2, this._verticalBlurPostprocess2 ];
  222. this._postProcesses1 = [ this._horizontalBlurPostprocess1, this._verticalBlurPostprocess1 ];
  223. this._postProcesses2 = [ this._horizontalBlurPostprocess2, this._verticalBlurPostprocess2 ];
  224. this._mainTexture.samples = this._options.mainTextureSamples!;
  225. this._mainTexture.onAfterUnbindObservable.add(() => {
  226. let internalTexture = this._blurTexture1.getInternalTexture();
  227. if (internalTexture) {
  228. this._scene.postProcessManager.directRender(
  229. this._postProcesses1,
  230. internalTexture,
  231. true);
  232. internalTexture = this._blurTexture2.getInternalTexture();
  233. if (internalTexture) {
  234. this._scene.postProcessManager.directRender(
  235. this._postProcesses2,
  236. internalTexture,
  237. true);
  238. }
  239. }
  240. });
  241. // Prevent autoClear.
  242. this._postProcesses.map(pp => { pp.autoClear = false; });
  243. }
  244. /**
  245. * Checks for the readiness of the element composing the layer.
  246. * @param subMesh the mesh to check for
  247. * @param useInstances specify wether or not to use instances to render the mesh
  248. * @param emissiveTexture the associated emissive texture used to generate the glow
  249. * @return true if ready otherwise, false
  250. */
  251. public isReady(subMesh: SubMesh, useInstances: boolean): boolean {
  252. let material = subMesh.getMaterial();
  253. let mesh = subMesh.getRenderingMesh();
  254. if (!material || !mesh) {
  255. return false;
  256. }
  257. let emissiveTexture = (<any>material).emissiveTexture;
  258. return super._isReady(subMesh, useInstances, emissiveTexture);
  259. }
  260. /**
  261. * Returns wether or nood the layer needs stencil enabled during the mesh rendering.
  262. */
  263. public needStencil(): boolean {
  264. return false;
  265. }
  266. /**
  267. * Implementation specific of rendering the generating effect on the main canvas.
  268. * @param effect The effect used to render through
  269. */
  270. protected _internalRender(effect: Effect): void {
  271. // Texture
  272. effect.setTexture("textureSampler", this._blurTexture1);
  273. effect.setTexture("textureSampler2", this._blurTexture2);
  274. effect.setFloat("offset", this._intensity);
  275. // Cache
  276. var engine = this._engine;
  277. var previousStencilBuffer = engine.getStencilBuffer();
  278. // Draw order
  279. engine.setStencilBuffer(false);
  280. engine.drawElementsType(Material.TriangleFillMode, 0, 6);
  281. // Draw order
  282. engine.setStencilBuffer(previousStencilBuffer);
  283. }
  284. /**
  285. * Sets the required values for both the emissive texture and and the main color.
  286. */
  287. protected _setEmissiveTextureAndColor(mesh: Mesh, subMesh: SubMesh, material: Material): void {
  288. var textureLevel = 1.0;
  289. if (this.customEmissiveTextureSelector) {
  290. this._emissiveTextureAndColor.texture = this.customEmissiveTextureSelector(mesh, subMesh, material);
  291. } else {
  292. if (material) {
  293. this._emissiveTextureAndColor.texture = (<any>material).emissiveTexture;
  294. if (this._emissiveTextureAndColor.texture) {
  295. textureLevel = this._emissiveTextureAndColor.texture.level;
  296. }
  297. }
  298. else {
  299. this._emissiveTextureAndColor.texture = null;
  300. }
  301. }
  302. if (this.customEmissiveColorSelector) {
  303. this.customEmissiveColorSelector(mesh, subMesh, material, this._emissiveTextureAndColor.color);
  304. } else {
  305. if ((<any>material).emissiveColor) {
  306. this._emissiveTextureAndColor.color.set(
  307. (<any>material).emissiveColor.r * textureLevel,
  308. (<any>material).emissiveColor.g * textureLevel,
  309. (<any>material).emissiveColor .b * textureLevel,
  310. 1.0);
  311. }
  312. else {
  313. this._emissiveTextureAndColor.color.set(
  314. this.neutralColor.r,
  315. this.neutralColor.g,
  316. this.neutralColor.b,
  317. this.neutralColor.a);
  318. }
  319. }
  320. }
  321. /**
  322. * Returns true if the mesh should render, otherwise false.
  323. * @param mesh The mesh to render
  324. * @returns true if it should render otherwise false
  325. */
  326. protected _shouldRenderMesh(mesh: Mesh): boolean {
  327. return this.hasMesh(mesh);
  328. }
  329. /**
  330. * Add a mesh in the exclusion list to prevent it to impact or being impacted by the glow layer.
  331. * @param mesh The mesh to exclude from the glow layer
  332. */
  333. public addExcludedMesh(mesh: Mesh): void {
  334. if (this._excludedMeshes.indexOf(mesh.uniqueId) === -1) {
  335. this._excludedMeshes.push(mesh.uniqueId);
  336. }
  337. }
  338. /**
  339. * Remove a mesh from the exclusion list to let it impact or being impacted by the glow layer.
  340. * @param mesh The mesh to remove
  341. */
  342. public removeExcludedMesh(mesh: Mesh): void {
  343. var index = this._excludedMeshes.indexOf(mesh.uniqueId);
  344. if (index !== -1) {
  345. this._excludedMeshes.splice(index, 1);
  346. }
  347. }
  348. /**
  349. * Add a mesh in the inclusion list to impact or being impacted by the glow layer.
  350. * @param mesh The mesh to include in the glow layer
  351. */
  352. public addIncludedOnlyMesh(mesh: Mesh): void {
  353. if (this._includedOnlyMeshes.indexOf(mesh.uniqueId) === -1) {
  354. this._includedOnlyMeshes.push(mesh.uniqueId);
  355. }
  356. }
  357. /**
  358. * Remove a mesh from the Inclusion list to prevent it to impact or being impacted by the glow layer.
  359. * @param mesh The mesh to remove
  360. */
  361. public removeIncludedOnlyMesh(mesh: Mesh): void {
  362. var index = this._includedOnlyMeshes.indexOf(mesh.uniqueId);
  363. if (index !== -1) {
  364. this._includedOnlyMeshes.splice(index, 1);
  365. }
  366. }
  367. /**
  368. * Determine if a given mesh will be used in the glow layer
  369. * @param mesh The mesh to test
  370. * @returns true if the mesh will be highlighted by the current glow layer
  371. */
  372. public hasMesh(mesh: AbstractMesh): boolean {
  373. // Included Mesh
  374. if (this._includedOnlyMeshes.length) {
  375. return this._includedOnlyMeshes.indexOf(mesh.uniqueId) !== -1;
  376. };
  377. // Excluded Mesh
  378. if (this._excludedMeshes.length) {
  379. return this._excludedMeshes.indexOf(mesh.uniqueId) === -1;
  380. };
  381. return true;
  382. }
  383. /**
  384. * Free any resources and references associated to a mesh.
  385. * Internal use
  386. * @param mesh The mesh to free.
  387. */
  388. public _disposeMesh(mesh: Mesh): void {
  389. this.removeIncludedOnlyMesh(mesh);
  390. this.removeExcludedMesh(mesh);
  391. }
  392. /**
  393. * Gets the class name of the effect layer
  394. * @returns the string with the class name of the effect layer
  395. */
  396. public getClassName(): string {
  397. return "GlowLayer";
  398. }
  399. /**
  400. * Serializes this glow layer
  401. * @returns a serialized glow layer object
  402. */
  403. public serialize(): any {
  404. var serializationObject = SerializationHelper.Serialize(this);
  405. var index;
  406. // Included meshes
  407. serializationObject.includedMeshes = [];
  408. if (this._includedOnlyMeshes.length) {
  409. for (index = 0; index < this._includedOnlyMeshes.length; index++) {
  410. var mesh = this._scene.getMeshByUniqueID(this._includedOnlyMeshes[index]);
  411. if (mesh) {
  412. serializationObject.includedMeshes.push(mesh.id);
  413. }
  414. }
  415. }
  416. // Excluded meshes
  417. serializationObject.excludedMeshes = [];
  418. if (this._excludedMeshes.length) {
  419. for (index = 0; index < this._excludedMeshes.length; index++) {
  420. var mesh = this._scene.getMeshByUniqueID(this._excludedMeshes[index]);
  421. if (mesh) {
  422. serializationObject.excludedMeshes.push(mesh.id);
  423. }
  424. }
  425. }
  426. return serializationObject;
  427. }
  428. /**
  429. * Creates a Glow Layer from parsed glow layer data
  430. * @param parsedGlowLayer defines glow layer data
  431. * @param scene defines the current scene
  432. * @param rootUrl defines the root URL containing the glow layer information
  433. * @returns a parsed Glow Layer
  434. */
  435. public static Parse(parsedGlowLayer: any, scene: Scene, rootUrl: string): GlowLayer {
  436. var gl = SerializationHelper.Parse(() => new GlowLayer(parsedGlowLayer.name, scene, parsedGlowLayer.options), parsedGlowLayer, scene, rootUrl);
  437. var index;
  438. // Excluded meshes
  439. for (index = 0; index < parsedGlowLayer.excludedMeshes.length; index++) {
  440. var mesh = scene.getMeshByID(parsedGlowLayer.excludedMeshes[index]);
  441. if (mesh) {
  442. gl.addExcludedMesh(<Mesh>mesh);
  443. }
  444. }
  445. // Included meshes
  446. for (index = 0; index < parsedGlowLayer.includedMeshes.length; index++) {
  447. var mesh = scene.getMeshByID(parsedGlowLayer.includedMeshes[index]);
  448. if (mesh) {
  449. gl.addIncludedOnlyMesh(<Mesh>mesh);
  450. }
  451. }
  452. return gl;
  453. }
  454. }
  455. }