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.

332 lines (331 loc) 13.4 kB
import { Texture } from "../Materials/Textures/texture.js"; import { ProceduralTexture } from "../Materials/Textures/Procedurals/proceduralTexture.js"; import { PostProcess } from "../PostProcesses/postProcess.js"; import { Vector4 } from "../Maths/math.vector.js"; import { RawTexture } from "../Materials/Textures/rawTexture.js"; import { Observable } from "../Misc/observable.js"; import { Engine } from "../Engines/engine.js"; import { _WarnImport } from "../Misc/devTools.js"; import { EngineStore } from "../Engines/engineStore.js"; /** * Build cdf maps to be used for IBL importance sampling. */ export class IblCdfGenerator { /** * Gets the IBL source texture being used by the CDF renderer */ get iblSource() { return this._iblSource; } /** * Sets the IBL source texture to be used by the CDF renderer. * This will trigger recreation of the CDF assets. */ set iblSource(source) { if (this._iblSource === source) { return; } this._disposeTextures(); this._iblSource = source; if (!source) { return; } if (source.isCube) { if (source.isReadyOrNotBlocking()) { this._recreateAssetsFromNewIbl(); } else { source.onLoadObservable.addOnce(this._recreateAssetsFromNewIbl.bind(this, source)); } } else { if (source.isReadyOrNotBlocking()) { this._recreateAssetsFromNewIbl(); } else { source.onLoadObservable.addOnce(this._recreateAssetsFromNewIbl.bind(this, source)); } } } _recreateAssetsFromNewIbl() { if (this._debugPass) { this._debugPass.dispose(); } this._createTextures(); if (this._debugPass) { // Recreate the debug pass because of the new textures this._createDebugPass(); } } /** * Return the cumulative distribution function (CDF) texture * @returns Return the cumulative distribution function (CDF) texture */ getIcdfTexture() { return this._icdfPT ? this._icdfPT : this._dummyTexture; } /** * 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); } /** * The name of the debug pass post process */ get debugPassName() { return this._debugPassName; } /** * Gets the debug pass post process * @returns The post process */ getDebugPassPP() { if (!this._debugPass) { this._createDebugPass(); } return this._debugPass; } /** * Instanciates the CDF renderer * @param sceneOrEngine Scene to attach to * @returns The CDF renderer */ constructor(sceneOrEngine) { /** Enable the debug view for this pass */ this.debugEnabled = false; this._debugSizeParams = new Vector4(0.0, 0.0, 1.0, 1.0); this._debugPassName = "CDF Debug"; /** * Observable that triggers when the CDF renderer is ready */ this.onGeneratedObservable = new Observable(); if (sceneOrEngine) { if (IblCdfGenerator._IsScene(sceneOrEngine)) { this._scene = sceneOrEngine; } else { this._engine = sceneOrEngine; } } else { this._scene = EngineStore.LastCreatedScene; } if (this._scene) { this._engine = this._scene.getEngine(); } const blackPixels = new Uint16Array([0, 0, 0, 255]); this._dummyTexture = new RawTexture(blackPixels, 1, 1, Engine.TEXTUREFORMAT_RGBA, sceneOrEngine, false, false, undefined, 2); if (this._scene) { IblCdfGenerator._SceneComponentInitialization(this._scene); } } _createTextures() { const size = this._iblSource ? { width: this._iblSource.getSize().width, height: this._iblSource.getSize().height } : { width: 1, height: 1 }; if (!this._iblSource) { this._iblSource = RawTexture.CreateRTexture(new Uint8Array([255]), 1, 1, this._engine, false, false, 1, 0); this._iblSource.name = "Placeholder IBL Source"; } if (this._iblSource.isCube) { size.width *= 4; size.height *= 2; // Force the resolution to be a power of 2 because we rely on the // auto-mipmap generation for the scaled luminance texture to produce // a 1x1 mip that represents the true average pixel intensity of the IBL. size.width = 1 << Math.floor(Math.log2(size.width)); size.height = 1 << Math.floor(Math.log2(size.height)); } const isWebGPU = this._engine.isWebGPU; // Create CDF maps (Cumulative Distribution Function) to assist in importance sampling const cdfOptions = { generateDepthBuffer: false, generateMipMaps: false, format: 6, type: 1, samplingMode: 1, shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */, gammaSpace: false, extraInitializationsAsync: async () => { if (isWebGPU) { await Promise.all([import("../ShadersWGSL/iblCdfx.fragment.js"), import("../ShadersWGSL/iblCdfy.fragment.js"), import("../ShadersWGSL/iblScaledLuminance.fragment.js")]); } else { await Promise.all([import("../Shaders/iblCdfx.fragment.js"), import("../Shaders/iblCdfy.fragment.js"), import("../Shaders/iblScaledLuminance.fragment.js")]); } }, }; const icdfOptions = { generateDepthBuffer: false, generateMipMaps: false, format: 5, type: 2, samplingMode: 1, shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */, gammaSpace: false, extraInitializationsAsync: async () => { if (isWebGPU) { await Promise.all([import("../ShadersWGSL/iblIcdf.fragment.js")]); } else { await Promise.all([import("../Shaders/iblIcdf.fragment.js")]); } }, }; this._cdfyPT = new ProceduralTexture("cdfyTexture", { width: size.width, height: size.height + 1 }, "iblCdfy", this._scene, cdfOptions, false, false); this._cdfyPT.autoClear = false; this._cdfyPT.setTexture("iblSource", this._iblSource); this._cdfyPT.setInt("iblHeight", size.height); this._cdfyPT.wrapV = 0; this._cdfyPT.refreshRate = 0; if (this._iblSource.isCube) { this._cdfyPT.defines = "#define IBL_USE_CUBE_MAP\n"; } this._cdfxPT = new ProceduralTexture("cdfxTexture", { width: size.width + 1, height: 1 }, "iblCdfx", this._scene, cdfOptions, false, false); this._cdfxPT.autoClear = false; this._cdfxPT.setTexture("cdfy", this._cdfyPT); this._cdfxPT.refreshRate = 0; this._cdfxPT.wrapU = 0; this._scaledLuminancePT = new ProceduralTexture("iblScaledLuminance", { width: size.width, height: size.height }, "iblScaledLuminance", this._scene, { ...cdfOptions, samplingMode: 3, generateMipMaps: true }, true, false); this._scaledLuminancePT.autoClear = false; this._scaledLuminancePT.setTexture("iblSource", this._iblSource); this._scaledLuminancePT.setInt("iblHeight", size.height); this._scaledLuminancePT.setInt("iblWidth", size.width); this._scaledLuminancePT.refreshRate = 0; if (this._iblSource.isCube) { this._scaledLuminancePT.defines = "#define IBL_USE_CUBE_MAP\n"; } this._icdfPT = new ProceduralTexture("icdfTexture", { width: size.width, height: size.height }, "iblIcdf", this._scene, icdfOptions, false, false); this._icdfPT.autoClear = false; this._icdfPT.setTexture("cdfy", this._cdfyPT); this._icdfPT.setTexture("cdfx", this._cdfxPT); this._icdfPT.setTexture("iblSource", this._iblSource); this._icdfPT.setTexture("scaledLuminanceSampler", this._scaledLuminancePT); this._icdfPT.refreshRate = 0; this._icdfPT.wrapV = 0; this._icdfPT.wrapU = 0; if (this._iblSource.isCube) { this._icdfPT.defines = "#define IBL_USE_CUBE_MAP\n"; } // Once the textures are generated, notify that they are ready to use. this._icdfPT.onGeneratedObservable.addOnce(() => { this.onGeneratedObservable.notifyObservers(); }); } _disposeTextures() { this._cdfyPT?.dispose(); this._cdfxPT?.dispose(); this._icdfPT?.dispose(); this._scaledLuminancePT?.dispose(); } _createDebugPass() { if (this._debugPass) { this._debugPass.dispose(); } const isWebGPU = this._engine.isWebGPU; const debugOptions = { width: this._engine.getRenderWidth(), height: this._engine.getRenderHeight(), samplingMode: Texture.BILINEAR_SAMPLINGMODE, engine: this._engine, textureType: 0, uniforms: ["sizeParams"], samplers: ["cdfy", "icdf", "cdfx", "iblSource"], defines: this._iblSource?.isCube ? "#define IBL_USE_CUBE_MAP\n" : "", shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */, extraInitializations: (useWebGPU, list) => { if (useWebGPU) { list.push(import("../ShadersWGSL/iblCdfDebug.fragment.js")); } else { list.push(import("../Shaders/iblCdfDebug.fragment.js")); } }, }; this._debugPass = new PostProcess(this._debugPassName, "iblCdfDebug", debugOptions); const debugEffect = this._debugPass.getEffect(); if (debugEffect) { debugEffect.defines = this._iblSource?.isCube ? "#define IBL_USE_CUBE_MAP\n" : ""; } if (this._iblSource?.isCube) { this._debugPass.updateEffect("#define IBL_USE_CUBE_MAP\n"); } this._debugPass.onApplyObservable.add((effect) => { effect.setTexture("cdfy", this._cdfyPT); effect.setTexture("icdf", this._icdfPT); effect.setTexture("cdfx", this._cdfxPT); effect.setTexture("iblSource", this._iblSource); effect.setFloat4("sizeParams", this._debugSizeParams.x, this._debugSizeParams.y, this._debugSizeParams.z, this._debugSizeParams.w); }); } /** * Checks if the CDF renderer is ready * @returns true if the CDF renderer is ready */ isReady() { return (this._iblSource && this._iblSource.name !== "Placeholder IBL Source" && this._iblSource.isReady() && this._cdfyPT && this._cdfyPT.isReady() && this._icdfPT && this._icdfPT.isReady() && this._cdfxPT && this._cdfxPT.isReady() && this._scaledLuminancePT && this._scaledLuminancePT.isReady()); } /** * Explicitly trigger generation of CDF maps when they are ready to render. * @returns Promise that resolves when the CDF maps are rendered. */ renderWhenReady() { // Once the textures are generated, notify that they are ready to use. this._icdfPT.onGeneratedObservable.addOnce(() => { this.onGeneratedObservable.notifyObservers(); }); const promises = []; const renderTargets = [this._cdfyPT, this._cdfxPT, this._scaledLuminancePT, this._icdfPT]; for (const target of renderTargets) { promises.push(new Promise((resolve) => { if (target.isReady()) { resolve(); } else { target.getEffect().executeWhenCompiled(() => { resolve(); }); } })); } return Promise.all(promises).then(() => { for (const target of renderTargets) { target.render(); } }); } /** * Disposes the CDF renderer and associated resources */ dispose() { this._disposeTextures(); this._dummyTexture.dispose(); if (this._debugPass) { this._debugPass.dispose(); } this.onGeneratedObservable.clear(); } static _IsScene(sceneOrEngine) { return sceneOrEngine.getClassName() === "Scene"; } } /** * @internal */ IblCdfGenerator._SceneComponentInitialization = (_) => { throw _WarnImport("IblCdfGeneratorSceneComponentSceneComponent"); }; //# sourceMappingURL=iblCdfGenerator.js.map