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.

308 lines (307 loc) 13.5 kB
import { Vector4 } from "../../Maths/math.vector.js"; import { PostProcess } from "../../PostProcesses/postProcess.js"; import { GeometryBufferRenderer } from "../../Rendering/geometryBufferRenderer.js"; import { ProceduralTexture } from "../../Materials/Textures/Procedurals/proceduralTexture.js"; import { Observable } from "../../Misc/observable.js"; /** * This should not be instantiated directly, as it is part of a scene component * @internal */ export class _IblShadowsAccumulationPass { /** * Returns the output texture of the pass. * @returns The output texture. */ getOutputTexture() { return this._outputTexture; } /** * Gets the debug pass post process * @returns The post process */ getDebugPassPP() { if (!this._debugPassPP) { this._createDebugPass(); } return this._debugPassPP; } /** * Gets the name of the debug pass * @returns The name of the debug pass */ get debugPassName() { return this._debugPassName; } /** * A value that controls how much of the previous frame's accumulation to keep. * The higher the value, the faster the shadows accumulate but the more potential ghosting you'll see. */ get remanence() { return this._remanence; } /** * A value that controls how much of the previous frame's accumulation to keep. * The higher the value, the faster the shadows accumulate but the more potential ghosting you'll see. */ set remanence(value) { this._remanence = value; } /** * Reset the accumulation. */ get reset() { return this._reset; } /** * Reset the accumulation. */ set reset(value) { this._reset = value; } /** * Tell the pass that the camera is moving. This will cause the accumulation * rate to change. */ set isMoving(value) { this._isMoving = value; } /** * Sets params that control the position and scaling of the debug display on the screen. * @param x Screen X offset of the debug display (0-1) * @param y Screen Y offset of the debug display (0-1) * @param widthScale X scale of the debug display (0-1) * @param heightScale Y scale of the debug display (0-1) */ setDebugDisplayParams(x, y, widthScale, heightScale) { this._debugSizeParams.set(x, y, widthScale, heightScale); } /** * Creates the debug post process effect for this pass */ _createDebugPass() { if (!this._debugPassPP) { const isWebGPU = this._engine.isWebGPU; const debugOptions = { width: this._engine.getRenderWidth(), height: this._engine.getRenderHeight(), textureFormat: 5, textureType: 0, samplingMode: 1, uniforms: ["sizeParams"], samplers: ["debugSampler"], engine: this._engine, reusable: false, shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */, extraInitializations: (useWebGPU, list) => { if (useWebGPU) { list.push(import("../../ShadersWGSL/iblShadowDebug.fragment.js")); } else { list.push(import("../../Shaders/iblShadowDebug.fragment.js")); } }, }; this._debugPassPP = new PostProcess(this.debugPassName, "iblShadowDebug", debugOptions); this._debugPassPP.autoClear = false; this._debugPassPP.onApplyObservable.add((effect) => { // update the caustic texture with what we just rendered. effect.setTexture("debugSampler", this._outputTexture); effect.setVector4("sizeParams", this._debugSizeParams); }); } } /** * Instantiates the accumulation pass * @param scene Scene to attach to * @param iblShadowsRenderPipeline The IBL shadows render pipeline * @returns The accumulation pass */ constructor(scene, iblShadowsRenderPipeline) { this._accumulationParams = new Vector4(0.0, 0.0, 0.0, 0.0); /** Enable the debug view for this pass */ this.debugEnabled = false; /** * Is the effect enabled */ this.enabled = true; /** * Observable that triggers when the accumulation texture is ready */ this.onReadyObservable = new Observable(); this._debugPassName = "Shadow Accumulation Debug Pass"; this._remanence = 0.9; this._reset = true; this._isMoving = false; this._debugSizeParams = new Vector4(0.0, 0.0, 0.0, 0.0); this._renderWhenGBufferReady = null; this._scene = scene; this._engine = scene.getEngine(); this._renderPipeline = iblShadowsRenderPipeline; this._createTextures(); } _createTextures() { const isWebGPU = this._engine.isWebGPU; const outputTextureOptions = { type: 2, format: 5, samplingMode: 1, generateDepthBuffer: false, generateMipMaps: false, shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */, extraInitializationsAsync: async () => { if (isWebGPU) { await Promise.all([import("../../ShadersWGSL/iblShadowAccumulation.fragment.js")]); } else { await Promise.all([import("../../Shaders/iblShadowAccumulation.fragment.js")]); } }, }; this._outputTexture = new ProceduralTexture("shadowAccumulationPass", { width: this._engine.getRenderWidth(), height: this._engine.getRenderHeight(), }, "iblShadowAccumulation", this._scene, outputTextureOptions); this._outputTexture.refreshRate = 1; this._outputTexture.autoClear = false; this._outputTexture.onGeneratedObservable.addOnce(() => { this.onReadyObservable.notifyObservers(); }); // Need to set all the textures first so that the effect gets created with the proper uniforms. this._setOutputTextureBindings(); this._renderWhenGBufferReady = this._render.bind(this); // Don't start rendering until the first vozelization is done. this._renderPipeline.onVoxelizationCompleteObservable.addOnce(() => { this._scene.geometryBufferRenderer.getGBuffer().onAfterRenderObservable.add(this._renderWhenGBufferReady); }); // Create the accumulation texture for the previous frame. // We'll copy the output of the accumulation pass to this texture at the start of every frame. const accumulationOptions = { type: 2, format: 5, samplingMode: 1, generateDepthBuffer: false, generateMipMaps: false, shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */, extraInitializationsAsync: async () => { if (isWebGPU) { await Promise.all([import("../../ShadersWGSL/pass.fragment.js")]); } else { await Promise.all([import("../../Shaders/pass.fragment.js")]); } }, }; this._oldAccumulationCopy = new ProceduralTexture("oldAccumulationRT", { width: this._engine.getRenderWidth(), height: this._engine.getRenderHeight() }, "pass", this._scene, accumulationOptions, false); this._oldAccumulationCopy.autoClear = false; this._oldAccumulationCopy.refreshRate = 1; this._oldAccumulationCopy.onBeforeGenerationObservable.add(this._setAccumulationCopyBindings.bind(this)); this._setAccumulationCopyBindings(); // Create the local position texture for the previous frame. // We'll copy the previous local position texture to this texture at the start of every frame. const localPositionOptions = { type: 2, format: 5, samplingMode: 1, generateDepthBuffer: false, generateMipMaps: false, shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */, extraInitializationsAsync: async () => { if (isWebGPU) { await Promise.all([import("../../ShadersWGSL/pass.fragment.js")]); } else { await Promise.all([import("../../Shaders/pass.fragment.js")]); } }, }; this._oldPositionCopy = new ProceduralTexture("oldLocalPositionRT", { width: this._engine.getRenderWidth(), height: this._engine.getRenderHeight() }, "pass", this._scene, localPositionOptions, false); this._updatePositionCopy(); this._oldPositionCopy.autoClear = false; this._oldPositionCopy.refreshRate = 1; this._oldPositionCopy.onBeforeGenerationObservable.add(this._updatePositionCopy.bind(this)); } _setOutputTextureBindings() { const remanence = this._isMoving ? this.remanence : 0.99; this._accumulationParams.set(remanence, this.reset ? 1.0 : 0.0, this._renderPipeline.voxelGridSize, 0.0); this._outputTexture.setTexture("spatialBlurSampler", this._renderPipeline._getSpatialBlurTexture()); this._outputTexture.setVector4("accumulationParameters", this._accumulationParams); this._outputTexture.setTexture("oldAccumulationSampler", this._oldAccumulationCopy ? this._oldAccumulationCopy : this._renderPipeline._dummyTexture2d); this._outputTexture.setTexture("prevPositionSampler", this._oldPositionCopy ? this._oldPositionCopy : this._renderPipeline._dummyTexture2d); const geometryBufferRenderer = this._scene.geometryBufferRenderer; if (!geometryBufferRenderer) { return false; } const velocityIndex = geometryBufferRenderer.getTextureIndex(GeometryBufferRenderer.VELOCITY_LINEAR_TEXTURE_TYPE); this._outputTexture.setTexture("motionSampler", geometryBufferRenderer.getGBuffer().textures[velocityIndex]); const wPositionIndex = geometryBufferRenderer.getTextureIndex(GeometryBufferRenderer.POSITION_TEXTURE_TYPE); this._outputTexture.setTexture("positionSampler", geometryBufferRenderer.getGBuffer().textures[wPositionIndex]); this.reset = false; this._isMoving = false; return true; } _updatePositionCopy() { const geometryBufferRenderer = this._scene.geometryBufferRenderer; const index = geometryBufferRenderer.getTextureIndex(GeometryBufferRenderer.POSITION_TEXTURE_TYPE); this._oldPositionCopy.setTexture("textureSampler", geometryBufferRenderer.getGBuffer().textures[index]); } _setAccumulationCopyBindings() { this._oldAccumulationCopy.setTexture("textureSampler", this._outputTexture); } _render() { if (this.enabled && this._outputTexture.isReady() && this._outputTexture.getEffect()?.isReady()) { if (this._setOutputTextureBindings()) { this._outputTexture.render(); } } } /** * Called by render pipeline when canvas resized. * @param scaleFactor The factor by which to scale the canvas size. */ resize(scaleFactor = 1.0) { const newSize = { width: Math.max(1.0, Math.floor(this._engine.getRenderWidth() * scaleFactor)), height: Math.max(1.0, Math.floor(this._engine.getRenderHeight() * scaleFactor)), }; // Don't resize if the size is the same as the current size. if (this._outputTexture.getSize().width === newSize.width && this._outputTexture.getSize().height === newSize.height) { return; } this._outputTexture.resize(newSize, false); this._oldAccumulationCopy.resize(newSize, false); this._oldPositionCopy.resize({ width: this._engine.getRenderWidth(), height: this._engine.getRenderHeight() }, false); this.reset = true; } _disposeTextures() { this._oldAccumulationCopy.dispose(); this._oldPositionCopy.dispose(); this._outputTexture.dispose(); } /** * Checks if the pass is ready * @returns true if the pass is ready */ isReady() { return (this._oldAccumulationCopy && this._oldAccumulationCopy.isReady() && this._oldPositionCopy && this._oldPositionCopy.isReady() && this._outputTexture.isReady() && !(this._debugPassPP && !this._debugPassPP.isReady())); } /** * Disposes the associated resources */ dispose() { if (this._scene.geometryBufferRenderer && this._renderWhenGBufferReady) { const gBuffer = this._scene.geometryBufferRenderer.getGBuffer(); gBuffer.onAfterRenderObservable.removeCallback(this._renderWhenGBufferReady); } this._disposeTextures(); if (this._debugPassPP) { this._debugPassPP.dispose(); } this.onReadyObservable.clear(); } } //# sourceMappingURL=iblShadowsAccumulationPass.js.map