glowLayer.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. import { __decorate } from "../tslib.es6.js";
  2. /* eslint-disable @typescript-eslint/no-unused-vars */
  3. import { serialize } from "../Misc/decorators.js";
  4. import { Vector2 } from "../Maths/math.vector.js";
  5. import { VertexBuffer } from "../Buffers/buffer.js";
  6. import { Texture } from "../Materials/Textures/texture.js";
  7. import { RenderTargetTexture } from "../Materials/Textures/renderTargetTexture.js";
  8. import { Material } from "../Materials/material.js";
  9. import { BlurPostProcess } from "../PostProcesses/blurPostProcess.js";
  10. import { EffectLayer } from "./effectLayer.js";
  11. import { AbstractScene } from "../abstractScene.js";
  12. import { RegisterClass } from "../Misc/typeStore.js";
  13. import { Color4 } from "../Maths/math.color.js";
  14. import "../Shaders/glowMapMerge.fragment.js";
  15. import "../Shaders/glowMapMerge.vertex.js";
  16. import "../Layers/effectLayerSceneComponent.js";
  17. import { SerializationHelper } from "../Misc/decorators.serialization.js";
  18. import { GetExponentOfTwo } from "../Misc/tools.functions.js";
  19. AbstractScene.prototype.getGlowLayerByName = function (name) {
  20. for (let index = 0; index < this.effectLayers?.length; index++) {
  21. if (this.effectLayers[index].name === name && this.effectLayers[index].getEffectName() === GlowLayer.EffectName) {
  22. return this.effectLayers[index];
  23. }
  24. }
  25. return null;
  26. };
  27. /**
  28. * The glow layer Helps adding a glow effect around the emissive parts of a mesh.
  29. *
  30. * Once instantiated in a scene, by default, all the emissive meshes will glow.
  31. *
  32. * Documentation: https://doc.babylonjs.com/features/featuresDeepDive/mesh/glowLayer
  33. */
  34. export class GlowLayer extends EffectLayer {
  35. /**
  36. * Sets the kernel size of the blur.
  37. */
  38. set blurKernelSize(value) {
  39. if (value === this._options.blurKernelSize) {
  40. return;
  41. }
  42. this._options.blurKernelSize = value;
  43. const effectiveKernel = this._getEffectiveBlurKernelSize();
  44. this._horizontalBlurPostprocess1.kernel = effectiveKernel;
  45. this._verticalBlurPostprocess1.kernel = effectiveKernel;
  46. this._horizontalBlurPostprocess2.kernel = effectiveKernel;
  47. this._verticalBlurPostprocess2.kernel = effectiveKernel;
  48. }
  49. /**
  50. * Gets the kernel size of the blur.
  51. */
  52. get blurKernelSize() {
  53. return this._options.blurKernelSize;
  54. }
  55. /**
  56. * Sets the glow intensity.
  57. */
  58. set intensity(value) {
  59. this._intensity = value;
  60. }
  61. /**
  62. * Gets the glow intensity.
  63. */
  64. get intensity() {
  65. return this._intensity;
  66. }
  67. /**
  68. * Instantiates a new glow Layer and references it to the scene.
  69. * @param name The name of the layer
  70. * @param scene The scene to use the layer in
  71. * @param options Sets of none mandatory options to use with the layer (see IGlowLayerOptions for more information)
  72. */
  73. constructor(name, scene, options) {
  74. super(name, scene);
  75. this._intensity = 1.0;
  76. this._includedOnlyMeshes = [];
  77. this._excludedMeshes = [];
  78. this._meshesUsingTheirOwnMaterials = [];
  79. this.neutralColor = new Color4(0, 0, 0, 1);
  80. // Adapt options
  81. this._options = {
  82. mainTextureRatio: GlowLayer.DefaultTextureRatio,
  83. blurKernelSize: 32,
  84. mainTextureFixedSize: undefined,
  85. camera: null,
  86. mainTextureSamples: 1,
  87. renderingGroupId: -1,
  88. ldrMerge: false,
  89. alphaBlendingMode: 1,
  90. mainTextureType: 0,
  91. generateStencilBuffer: false,
  92. ...options,
  93. };
  94. // Initialize the layer
  95. this._init({
  96. alphaBlendingMode: this._options.alphaBlendingMode,
  97. camera: this._options.camera,
  98. mainTextureFixedSize: this._options.mainTextureFixedSize,
  99. mainTextureRatio: this._options.mainTextureRatio,
  100. renderingGroupId: this._options.renderingGroupId,
  101. mainTextureType: this._options.mainTextureType,
  102. generateStencilBuffer: this._options.generateStencilBuffer,
  103. });
  104. }
  105. /**
  106. * Get the effect name of the layer.
  107. * @returns The effect name
  108. */
  109. getEffectName() {
  110. return GlowLayer.EffectName;
  111. }
  112. /**
  113. * @internal
  114. * Create the merge effect. This is the shader use to blit the information back
  115. * to the main canvas at the end of the scene rendering.
  116. */
  117. _createMergeEffect() {
  118. let defines = "#define EMISSIVE \n";
  119. if (this._options.ldrMerge) {
  120. defines += "#define LDR \n";
  121. }
  122. // Effect
  123. return this._engine.createEffect("glowMapMerge", [VertexBuffer.PositionKind], ["offset"], ["textureSampler", "textureSampler2"], defines);
  124. }
  125. /**
  126. * Creates the render target textures and post processes used in the glow layer.
  127. */
  128. _createTextureAndPostProcesses() {
  129. let blurTextureWidth = this._mainTextureDesiredSize.width;
  130. let blurTextureHeight = this._mainTextureDesiredSize.height;
  131. blurTextureWidth = this._engine.needPOTTextures ? GetExponentOfTwo(blurTextureWidth, this._maxSize) : blurTextureWidth;
  132. blurTextureHeight = this._engine.needPOTTextures ? GetExponentOfTwo(blurTextureHeight, this._maxSize) : blurTextureHeight;
  133. let textureType = 0;
  134. if (this._engine.getCaps().textureHalfFloatRender) {
  135. textureType = 2;
  136. }
  137. else {
  138. textureType = 0;
  139. }
  140. this._blurTexture1 = new RenderTargetTexture("GlowLayerBlurRTT", {
  141. width: blurTextureWidth,
  142. height: blurTextureHeight,
  143. }, this._scene, false, true, textureType);
  144. this._blurTexture1.wrapU = Texture.CLAMP_ADDRESSMODE;
  145. this._blurTexture1.wrapV = Texture.CLAMP_ADDRESSMODE;
  146. this._blurTexture1.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
  147. this._blurTexture1.renderParticles = false;
  148. this._blurTexture1.ignoreCameraViewport = true;
  149. const blurTextureWidth2 = Math.floor(blurTextureWidth / 2);
  150. const blurTextureHeight2 = Math.floor(blurTextureHeight / 2);
  151. this._blurTexture2 = new RenderTargetTexture("GlowLayerBlurRTT2", {
  152. width: blurTextureWidth2,
  153. height: blurTextureHeight2,
  154. }, this._scene, false, true, textureType);
  155. this._blurTexture2.wrapU = Texture.CLAMP_ADDRESSMODE;
  156. this._blurTexture2.wrapV = Texture.CLAMP_ADDRESSMODE;
  157. this._blurTexture2.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
  158. this._blurTexture2.renderParticles = false;
  159. this._blurTexture2.ignoreCameraViewport = true;
  160. this._textures = [this._blurTexture1, this._blurTexture2];
  161. const effectiveKernel = this._getEffectiveBlurKernelSize();
  162. this._horizontalBlurPostprocess1 = new BlurPostProcess("GlowLayerHBP1", new Vector2(1.0, 0), effectiveKernel, {
  163. width: blurTextureWidth,
  164. height: blurTextureHeight,
  165. }, null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, textureType);
  166. this._horizontalBlurPostprocess1.width = blurTextureWidth;
  167. this._horizontalBlurPostprocess1.height = blurTextureHeight;
  168. this._horizontalBlurPostprocess1.externalTextureSamplerBinding = true;
  169. this._horizontalBlurPostprocess1.onApplyObservable.add((effect) => {
  170. effect.setTexture("textureSampler", this._mainTexture);
  171. });
  172. this._verticalBlurPostprocess1 = new BlurPostProcess("GlowLayerVBP1", new Vector2(0, 1.0), effectiveKernel, {
  173. width: blurTextureWidth,
  174. height: blurTextureHeight,
  175. }, null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, textureType);
  176. this._horizontalBlurPostprocess2 = new BlurPostProcess("GlowLayerHBP2", new Vector2(1.0, 0), effectiveKernel, {
  177. width: blurTextureWidth2,
  178. height: blurTextureHeight2,
  179. }, null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, textureType);
  180. this._horizontalBlurPostprocess2.width = blurTextureWidth2;
  181. this._horizontalBlurPostprocess2.height = blurTextureHeight2;
  182. this._horizontalBlurPostprocess2.externalTextureSamplerBinding = true;
  183. this._horizontalBlurPostprocess2.onApplyObservable.add((effect) => {
  184. effect.setTexture("textureSampler", this._blurTexture1);
  185. });
  186. this._verticalBlurPostprocess2 = new BlurPostProcess("GlowLayerVBP2", new Vector2(0, 1.0), effectiveKernel, {
  187. width: blurTextureWidth2,
  188. height: blurTextureHeight2,
  189. }, null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, textureType);
  190. this._postProcesses = [this._horizontalBlurPostprocess1, this._verticalBlurPostprocess1, this._horizontalBlurPostprocess2, this._verticalBlurPostprocess2];
  191. this._postProcesses1 = [this._horizontalBlurPostprocess1, this._verticalBlurPostprocess1];
  192. this._postProcesses2 = [this._horizontalBlurPostprocess2, this._verticalBlurPostprocess2];
  193. this._mainTexture.samples = this._options.mainTextureSamples;
  194. this._mainTexture.onAfterUnbindObservable.add(() => {
  195. const internalTexture = this._blurTexture1.renderTarget;
  196. if (internalTexture) {
  197. this._scene.postProcessManager.directRender(this._postProcesses1, internalTexture, true);
  198. const internalTexture2 = this._blurTexture2.renderTarget;
  199. if (internalTexture2) {
  200. this._scene.postProcessManager.directRender(this._postProcesses2, internalTexture2, true);
  201. }
  202. this._engine.unBindFramebuffer(internalTexture2 ?? internalTexture, true);
  203. }
  204. });
  205. // Prevent autoClear.
  206. this._postProcesses.map((pp) => {
  207. pp.autoClear = false;
  208. });
  209. }
  210. /**
  211. * @returns The blur kernel size used by the glow.
  212. * Note: The value passed in the options is divided by 2 for back compatibility.
  213. */
  214. _getEffectiveBlurKernelSize() {
  215. return this._options.blurKernelSize / 2;
  216. }
  217. /**
  218. * Checks for the readiness of the element composing the layer.
  219. * @param subMesh the mesh to check for
  220. * @param useInstances specify whether or not to use instances to render the mesh
  221. * @returns true if ready otherwise, false
  222. */
  223. isReady(subMesh, useInstances) {
  224. const material = subMesh.getMaterial();
  225. const mesh = subMesh.getRenderingMesh();
  226. if (!material || !mesh) {
  227. return false;
  228. }
  229. const emissiveTexture = material.emissiveTexture;
  230. return super._isReady(subMesh, useInstances, emissiveTexture);
  231. }
  232. /**
  233. * @returns whether or not the layer needs stencil enabled during the mesh rendering.
  234. */
  235. needStencil() {
  236. return false;
  237. }
  238. /**
  239. * Returns true if the mesh can be rendered, otherwise false.
  240. * @param mesh The mesh to render
  241. * @param material The material used on the mesh
  242. * @returns true if it can be rendered otherwise false
  243. */
  244. _canRenderMesh(mesh, material) {
  245. return true;
  246. }
  247. /**
  248. * Implementation specific of rendering the generating effect on the main canvas.
  249. * @param effect The effect used to render through
  250. */
  251. _internalRender(effect) {
  252. // Texture
  253. effect.setTexture("textureSampler", this._blurTexture1);
  254. effect.setTexture("textureSampler2", this._blurTexture2);
  255. effect.setFloat("offset", this._intensity);
  256. // Cache
  257. const engine = this._engine;
  258. const previousStencilBuffer = engine.getStencilBuffer();
  259. // Draw order
  260. engine.setStencilBuffer(false);
  261. engine.drawElementsType(Material.TriangleFillMode, 0, 6);
  262. // Draw order
  263. engine.setStencilBuffer(previousStencilBuffer);
  264. }
  265. /**
  266. * Sets the required values for both the emissive texture and and the main color.
  267. * @param mesh
  268. * @param subMesh
  269. * @param material
  270. */
  271. _setEmissiveTextureAndColor(mesh, subMesh, material) {
  272. let textureLevel = 1.0;
  273. if (this.customEmissiveTextureSelector) {
  274. this._emissiveTextureAndColor.texture = this.customEmissiveTextureSelector(mesh, subMesh, material);
  275. }
  276. else {
  277. if (material) {
  278. this._emissiveTextureAndColor.texture = material.emissiveTexture;
  279. if (this._emissiveTextureAndColor.texture) {
  280. textureLevel = this._emissiveTextureAndColor.texture.level;
  281. }
  282. }
  283. else {
  284. this._emissiveTextureAndColor.texture = null;
  285. }
  286. }
  287. if (this.customEmissiveColorSelector) {
  288. this.customEmissiveColorSelector(mesh, subMesh, material, this._emissiveTextureAndColor.color);
  289. }
  290. else {
  291. if (material.emissiveColor) {
  292. const emissiveIntensity = material.emissiveIntensity ?? 1;
  293. textureLevel *= emissiveIntensity;
  294. this._emissiveTextureAndColor.color.set(material.emissiveColor.r * textureLevel, material.emissiveColor.g * textureLevel, material.emissiveColor.b * textureLevel, material.alpha);
  295. }
  296. else {
  297. this._emissiveTextureAndColor.color.set(this.neutralColor.r, this.neutralColor.g, this.neutralColor.b, this.neutralColor.a);
  298. }
  299. }
  300. }
  301. /**
  302. * Returns true if the mesh should render, otherwise false.
  303. * @param mesh The mesh to render
  304. * @returns true if it should render otherwise false
  305. */
  306. _shouldRenderMesh(mesh) {
  307. return this.hasMesh(mesh);
  308. }
  309. /**
  310. * Adds specific effects defines.
  311. * @param defines The defines to add specifics to.
  312. */
  313. _addCustomEffectDefines(defines) {
  314. defines.push("#define GLOW");
  315. }
  316. /**
  317. * Add a mesh in the exclusion list to prevent it to impact or being impacted by the glow layer.
  318. * @param mesh The mesh to exclude from the glow layer
  319. */
  320. addExcludedMesh(mesh) {
  321. if (this._excludedMeshes.indexOf(mesh.uniqueId) === -1) {
  322. this._excludedMeshes.push(mesh.uniqueId);
  323. }
  324. }
  325. /**
  326. * Remove a mesh from the exclusion list to let it impact or being impacted by the glow layer.
  327. * @param mesh The mesh to remove
  328. */
  329. removeExcludedMesh(mesh) {
  330. const index = this._excludedMeshes.indexOf(mesh.uniqueId);
  331. if (index !== -1) {
  332. this._excludedMeshes.splice(index, 1);
  333. }
  334. }
  335. /**
  336. * Add a mesh in the inclusion list to impact or being impacted by the glow layer.
  337. * @param mesh The mesh to include in the glow layer
  338. */
  339. addIncludedOnlyMesh(mesh) {
  340. if (this._includedOnlyMeshes.indexOf(mesh.uniqueId) === -1) {
  341. this._includedOnlyMeshes.push(mesh.uniqueId);
  342. }
  343. }
  344. /**
  345. * Remove a mesh from the Inclusion list to prevent it to impact or being impacted by the glow layer.
  346. * @param mesh The mesh to remove
  347. */
  348. removeIncludedOnlyMesh(mesh) {
  349. const index = this._includedOnlyMeshes.indexOf(mesh.uniqueId);
  350. if (index !== -1) {
  351. this._includedOnlyMeshes.splice(index, 1);
  352. }
  353. }
  354. /**
  355. * Determine if a given mesh will be used in the glow layer
  356. * @param mesh The mesh to test
  357. * @returns true if the mesh will be highlighted by the current glow layer
  358. */
  359. hasMesh(mesh) {
  360. if (!super.hasMesh(mesh)) {
  361. return false;
  362. }
  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. * Defines whether the current material of the mesh should be use to render the effect.
  375. * @param mesh defines the current mesh to render
  376. * @returns true if the material of the mesh should be use to render the effect
  377. */
  378. _useMeshMaterial(mesh) {
  379. if (this._meshesUsingTheirOwnMaterials.length == 0) {
  380. return false;
  381. }
  382. return this._meshesUsingTheirOwnMaterials.indexOf(mesh.uniqueId) > -1;
  383. }
  384. /**
  385. * Add a mesh to be rendered through its own material and not with emissive only.
  386. * @param mesh The mesh for which we need to use its material
  387. */
  388. referenceMeshToUseItsOwnMaterial(mesh) {
  389. mesh.resetDrawCache(this._mainTexture.renderPassId);
  390. this._meshesUsingTheirOwnMaterials.push(mesh.uniqueId);
  391. mesh.onDisposeObservable.add(() => {
  392. this._disposeMesh(mesh);
  393. });
  394. }
  395. /**
  396. * Remove a mesh from being rendered through its own material and not with emissive only.
  397. * @param mesh The mesh for which we need to not use its material
  398. */
  399. unReferenceMeshFromUsingItsOwnMaterial(mesh) {
  400. let index = this._meshesUsingTheirOwnMaterials.indexOf(mesh.uniqueId);
  401. while (index >= 0) {
  402. this._meshesUsingTheirOwnMaterials.splice(index, 1);
  403. index = this._meshesUsingTheirOwnMaterials.indexOf(mesh.uniqueId);
  404. }
  405. mesh.resetDrawCache(this._mainTexture.renderPassId);
  406. }
  407. /**
  408. * Free any resources and references associated to a mesh.
  409. * Internal use
  410. * @param mesh The mesh to free.
  411. * @internal
  412. */
  413. _disposeMesh(mesh) {
  414. this.removeIncludedOnlyMesh(mesh);
  415. this.removeExcludedMesh(mesh);
  416. }
  417. /**
  418. * Gets the class name of the effect layer
  419. * @returns the string with the class name of the effect layer
  420. */
  421. getClassName() {
  422. return "GlowLayer";
  423. }
  424. /**
  425. * Serializes this glow layer
  426. * @returns a serialized glow layer object
  427. */
  428. serialize() {
  429. const serializationObject = SerializationHelper.Serialize(this);
  430. serializationObject.customType = "BABYLON.GlowLayer";
  431. let index;
  432. // Included meshes
  433. serializationObject.includedMeshes = [];
  434. if (this._includedOnlyMeshes.length) {
  435. for (index = 0; index < this._includedOnlyMeshes.length; index++) {
  436. const mesh = this._scene.getMeshByUniqueId(this._includedOnlyMeshes[index]);
  437. if (mesh) {
  438. serializationObject.includedMeshes.push(mesh.id);
  439. }
  440. }
  441. }
  442. // Excluded meshes
  443. serializationObject.excludedMeshes = [];
  444. if (this._excludedMeshes.length) {
  445. for (index = 0; index < this._excludedMeshes.length; index++) {
  446. const mesh = this._scene.getMeshByUniqueId(this._excludedMeshes[index]);
  447. if (mesh) {
  448. serializationObject.excludedMeshes.push(mesh.id);
  449. }
  450. }
  451. }
  452. return serializationObject;
  453. }
  454. /**
  455. * Creates a Glow Layer from parsed glow layer data
  456. * @param parsedGlowLayer defines glow layer data
  457. * @param scene defines the current scene
  458. * @param rootUrl defines the root URL containing the glow layer information
  459. * @returns a parsed Glow Layer
  460. */
  461. static Parse(parsedGlowLayer, scene, rootUrl) {
  462. const gl = SerializationHelper.Parse(() => new GlowLayer(parsedGlowLayer.name, scene, parsedGlowLayer.options), parsedGlowLayer, scene, rootUrl);
  463. let index;
  464. // Excluded meshes
  465. for (index = 0; index < parsedGlowLayer.excludedMeshes.length; index++) {
  466. const mesh = scene.getMeshById(parsedGlowLayer.excludedMeshes[index]);
  467. if (mesh) {
  468. gl.addExcludedMesh(mesh);
  469. }
  470. }
  471. // Included meshes
  472. for (index = 0; index < parsedGlowLayer.includedMeshes.length; index++) {
  473. const mesh = scene.getMeshById(parsedGlowLayer.includedMeshes[index]);
  474. if (mesh) {
  475. gl.addIncludedOnlyMesh(mesh);
  476. }
  477. }
  478. return gl;
  479. }
  480. }
  481. /**
  482. * Effect Name of the layer.
  483. */
  484. GlowLayer.EffectName = "GlowLayer";
  485. /**
  486. * The default blur kernel size used for the glow.
  487. */
  488. GlowLayer.DefaultBlurKernelSize = 32;
  489. /**
  490. * The default texture size ratio used for the glow.
  491. */
  492. GlowLayer.DefaultTextureRatio = 0.5;
  493. __decorate([
  494. serialize()
  495. ], GlowLayer.prototype, "blurKernelSize", null);
  496. __decorate([
  497. serialize()
  498. ], GlowLayer.prototype, "intensity", null);
  499. __decorate([
  500. serialize("options")
  501. ], GlowLayer.prototype, "_options", void 0);
  502. RegisterClass("BABYLON.GlowLayer", GlowLayer);
  503. //# sourceMappingURL=glowLayer.js.map