UNPKG

@babylonjs/core

Version:

Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.

295 lines (294 loc) 11.6 kB
import { Vector2 } from "../Maths/math.vector.js"; import { VertexBuffer } from "../Buffers/buffer.js"; import { Material } from "../Materials/material.js"; import { ThinEffectLayer } from "./thinEffectLayer.js"; import { Color4 } from "../Maths/math.color.js"; import { ThinBlurPostProcess } from "../PostProcesses/thinBlurPostProcess.js"; /** * @internal */ export class ThinGlowLayer extends ThinEffectLayer { /** * Gets the ldrMerge option. */ get ldrMerge() { return this._options.ldrMerge; } /** * Sets the kernel size of the blur. */ set blurKernelSize(value) { if (value === this._options.blurKernelSize) { return; } this._options.blurKernelSize = value; const effectiveKernel = this._getEffectiveBlurKernelSize(); this._horizontalBlurPostprocess1.kernel = effectiveKernel; this._verticalBlurPostprocess1.kernel = effectiveKernel; this._horizontalBlurPostprocess2.kernel = effectiveKernel; this._verticalBlurPostprocess2.kernel = effectiveKernel; } /** * Gets the kernel size of the blur. */ get blurKernelSize() { return this._options.blurKernelSize; } /** * Sets the glow intensity. */ set intensity(value) { this._intensity = value; } /** * Gets the glow intensity. */ get intensity() { return this._intensity; } /** * Instantiates a new glow Layer and references it to the scene. * @param name The name of the layer * @param scene The scene to use the layer in * @param options Sets of none mandatory options to use with the layer (see IGlowLayerOptions for more information) * @param dontCheckIfReady Specifies if the layer should disable checking whether all the post processes are ready (default: false). To save performance, this should be set to true and you should call `isReady` manually before rendering to the layer. */ constructor(name, scene, options, dontCheckIfReady = false) { super(name, scene, false, dontCheckIfReady); this._intensity = 1.0; /** @internal */ this._includedOnlyMeshes = []; /** @internal */ this._excludedMeshes = []; this._meshesUsingTheirOwnMaterials = []; /** @internal */ this._renderPassId = 0; this.neutralColor = new Color4(0, 0, 0, 1); // Adapt options this._options = { mainTextureRatio: 0.5, mainTextureFixedSize: 0, mainTextureType: 0, blurKernelSize: 32, camera: null, renderingGroupId: -1, ldrMerge: false, alphaBlendingMode: 1, ...options, }; // Initialize the layer this._init(this._options); if (dontCheckIfReady) { // When dontCheckIfReady is true, we are in the new ThinXXX layer mode, so we must call _createTextureAndPostProcesses ourselves (it is called by EffectLayer otherwise) this._createTextureAndPostProcesses(); } } /** * Gets the class name of the thin glow layer * @returns the string with the class name of the glow layer */ getClassName() { return "GlowLayer"; } async _importShadersAsync() { if (this._shaderLanguage === 1 /* ShaderLanguage.WGSL */) { await Promise.all([ import("../ShadersWGSL/glowMapMerge.fragment.js"), import("../ShadersWGSL/glowMapMerge.vertex.js"), import("../ShadersWGSL/glowBlurPostProcess.fragment.js"), ]); } else { await Promise.all([import("../Shaders/glowMapMerge.fragment.js"), import("../Shaders/glowMapMerge.vertex.js"), import("../Shaders/glowBlurPostProcess.fragment.js")]); } await super._importShadersAsync(); } getEffectName() { return ThinGlowLayer.EffectName; } _createMergeEffect() { let defines = "#define EMISSIVE \n"; if (this._options.ldrMerge) { defines += "#define LDR \n"; } // Effect return this._engine.createEffect("glowMapMerge", [VertexBuffer.PositionKind], ["offset"], ["textureSampler", "textureSampler2"], defines, undefined, undefined, undefined, undefined, this.shaderLanguage, this._shadersLoaded ? undefined : async () => { await this._importShadersAsync(); this._shadersLoaded = true; }); } _createTextureAndPostProcesses() { const effectiveKernel = this._getEffectiveBlurKernelSize(); this._horizontalBlurPostprocess1 = new ThinBlurPostProcess("GlowLayerHBP1", this._scene.getEngine(), new Vector2(1.0, 0), effectiveKernel); this._verticalBlurPostprocess1 = new ThinBlurPostProcess("GlowLayerVBP1", this._scene.getEngine(), new Vector2(0, 1.0), effectiveKernel); this._horizontalBlurPostprocess2 = new ThinBlurPostProcess("GlowLayerHBP2", this._scene.getEngine(), new Vector2(1.0, 0), effectiveKernel); this._verticalBlurPostprocess2 = new ThinBlurPostProcess("GlowLayerVBP2", this._scene.getEngine(), new Vector2(0, 1.0), effectiveKernel); this._postProcesses = [this._horizontalBlurPostprocess1, this._verticalBlurPostprocess1, this._horizontalBlurPostprocess2, this._verticalBlurPostprocess2]; } _getEffectiveBlurKernelSize() { return this._options.blurKernelSize / 2; } isReady(subMesh, useInstances) { const material = subMesh.getMaterial(); const mesh = subMesh.getRenderingMesh(); if (!material || !mesh) { return false; } const emissiveTexture = material.emissiveTexture; return super._isSubMeshReady(subMesh, useInstances, emissiveTexture); } _canRenderMesh(_mesh, _material) { return true; } _internalCompose(effect) { // Texture this.bindTexturesForCompose(effect); effect.setFloat("offset", this._intensity); // Cache const engine = this._engine; const previousStencilBuffer = engine.getStencilBuffer(); // Draw order engine.setStencilBuffer(false); engine.drawElementsType(Material.TriangleFillMode, 0, 6); // Draw order engine.setStencilBuffer(previousStencilBuffer); } _setEmissiveTextureAndColor(mesh, subMesh, material) { let textureLevel = 1.0; if (this.customEmissiveTextureSelector) { this._emissiveTextureAndColor.texture = this.customEmissiveTextureSelector(mesh, subMesh, material); } else { if (material) { this._emissiveTextureAndColor.texture = material.emissiveTexture; if (this._emissiveTextureAndColor.texture) { textureLevel = this._emissiveTextureAndColor.texture.level; } } else { this._emissiveTextureAndColor.texture = null; } } if (this.customEmissiveColorSelector) { this.customEmissiveColorSelector(mesh, subMesh, material, this._emissiveTextureAndColor.color); } else { if (material.emissiveColor) { const emissiveIntensity = material.emissiveIntensity ?? 1; textureLevel *= emissiveIntensity; this._emissiveTextureAndColor.color.set(material.emissiveColor.r * textureLevel, material.emissiveColor.g * textureLevel, material.emissiveColor.b * textureLevel, material.alpha); } else { this._emissiveTextureAndColor.color.set(this.neutralColor.r, this.neutralColor.g, this.neutralColor.b, this.neutralColor.a); } } } _shouldRenderMesh(mesh) { return this.hasMesh(mesh); } _addCustomEffectDefines(defines) { defines.push("#define GLOW"); } /** * Add a mesh in the exclusion list to prevent it to impact or being impacted by the glow layer. * @param mesh The mesh to exclude from the glow layer */ addExcludedMesh(mesh) { if (this._excludedMeshes.indexOf(mesh.uniqueId) === -1) { this._excludedMeshes.push(mesh.uniqueId); } } /** * Remove a mesh from the exclusion list to let it impact or being impacted by the glow layer. * @param mesh The mesh to remove */ removeExcludedMesh(mesh) { const index = this._excludedMeshes.indexOf(mesh.uniqueId); if (index !== -1) { this._excludedMeshes.splice(index, 1); } } /** * Add a mesh in the inclusion list to impact or being impacted by the glow layer. * @param mesh The mesh to include in the glow layer */ addIncludedOnlyMesh(mesh) { if (this._includedOnlyMeshes.indexOf(mesh.uniqueId) === -1) { this._includedOnlyMeshes.push(mesh.uniqueId); } } /** * Remove a mesh from the Inclusion list to prevent it to impact or being impacted by the glow layer. * @param mesh The mesh to remove */ removeIncludedOnlyMesh(mesh) { const index = this._includedOnlyMeshes.indexOf(mesh.uniqueId); if (index !== -1) { this._includedOnlyMeshes.splice(index, 1); } } hasMesh(mesh) { if (!super.hasMesh(mesh)) { return false; } // Included Mesh if (this._includedOnlyMeshes.length) { return this._includedOnlyMeshes.indexOf(mesh.uniqueId) !== -1; } // Excluded Mesh if (this._excludedMeshes.length) { return this._excludedMeshes.indexOf(mesh.uniqueId) === -1; } return true; } _useMeshMaterial(mesh) { // Specific case of material supporting glow directly if (mesh.material?._supportGlowLayer) { return true; } if (this._meshesUsingTheirOwnMaterials.length == 0) { return false; } return this._meshesUsingTheirOwnMaterials.indexOf(mesh.uniqueId) > -1; } /** * Add a mesh to be rendered through its own material and not with emissive only. * @param mesh The mesh for which we need to use its material */ referenceMeshToUseItsOwnMaterial(mesh) { mesh.resetDrawCache(this._renderPassId); this._meshesUsingTheirOwnMaterials.push(mesh.uniqueId); mesh.onDisposeObservable.add(() => { this._disposeMesh(mesh); }); } /** * Remove a mesh from being rendered through its own material and not with emissive only. * @param mesh The mesh for which we need to not use its material * @param renderPassId The render pass id used when rendering the mesh */ unReferenceMeshFromUsingItsOwnMaterial(mesh, renderPassId) { let index = this._meshesUsingTheirOwnMaterials.indexOf(mesh.uniqueId); while (index >= 0) { this._meshesUsingTheirOwnMaterials.splice(index, 1); index = this._meshesUsingTheirOwnMaterials.indexOf(mesh.uniqueId); } mesh.resetDrawCache(renderPassId); } /** @internal */ _disposeMesh(mesh) { this.removeIncludedOnlyMesh(mesh); this.removeExcludedMesh(mesh); } } /** * Effect Name of the layer. */ ThinGlowLayer.EffectName = "GlowLayer"; /** * The default blur kernel size used for the glow. */ ThinGlowLayer.DefaultBlurKernelSize = 32; //# sourceMappingURL=thinGlowLayer.js.map