From 1062fff2c664adbe06964d5201d7ba3744b241b3 Mon Sep 17 00:00:00 2001 From: Mike Bond Date: Tue, 17 Dec 2024 08:56:54 -0800 Subject: [PATCH 01/11] First pass using CDF for irradiance prefiltering --- .../core/src/Materials/PBR/pbrBaseMaterial.ts | 1 + .../Filtering/hdrIrradianceFiltering.ts | 241 ++++++++++++++++++ .../src/Materials/Textures/hdrCubeTexture.ts | 44 +++- .../dev/core/src/Materials/Textures/index.ts | 4 + .../ShadersInclude/pbrBlockReflection.fx | 39 +-- .../hdrIrradianceFiltering.fragment.fx | 23 ++ .../Shaders/hdrIrradianceFiltering.vertex.fx | 24 ++ .../ShadersInclude/pbrBlockReflection.fx | 39 +-- .../hdrIrradianceFiltering.fragment.fx | 26 ++ .../hdrIrradianceFiltering.vertex.fx | 24 ++ 10 files changed, 426 insertions(+), 39 deletions(-) create mode 100644 packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts create mode 100644 packages/dev/core/src/Shaders/hdrIrradianceFiltering.fragment.fx create mode 100644 packages/dev/core/src/Shaders/hdrIrradianceFiltering.vertex.fx create mode 100644 packages/dev/core/src/ShadersWGSL/hdrIrradianceFiltering.fragment.fx create mode 100644 packages/dev/core/src/ShadersWGSL/hdrIrradianceFiltering.vertex.fx diff --git a/packages/dev/core/src/Materials/PBR/pbrBaseMaterial.ts b/packages/dev/core/src/Materials/PBR/pbrBaseMaterial.ts index 5e11e2d3710..ae3d9e551da 100644 --- a/packages/dev/core/src/Materials/PBR/pbrBaseMaterial.ts +++ b/packages/dev/core/src/Materials/PBR/pbrBaseMaterial.ts @@ -1720,6 +1720,7 @@ export abstract class PBRBaseMaterial extends PushMaterial { if (reflectionTexture.irradianceTexture) { defines.USEIRRADIANCEMAP = true; defines.USESPHERICALFROMREFLECTIONMAP = false; + defines.USESPHERICALINVERTEX = false; } // Assume using spherical polynomial if the reflection texture is a cube map else if (reflectionTexture.isCube) { diff --git a/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts b/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts new file mode 100644 index 00000000000..3a692495f2b --- /dev/null +++ b/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts @@ -0,0 +1,241 @@ +import { Vector3 } from "../../../Maths/math"; +import { ILog2 } from "../../../Maths/math.scalar.functions"; +import { BaseTexture } from "../baseTexture"; +import type { AbstractEngine } from "../../../Engines/abstractEngine"; +import type { Effect } from "../../../Materials/effect"; +import { Constants } from "../../../Engines/constants"; +import { EffectWrapper, EffectRenderer } from "../../../Materials/effectRenderer"; +import type { Nullable } from "../../../types"; +import type { RenderTargetWrapper } from "../../../Engines/renderTargetWrapper"; +import { Logger } from "../../../Misc/logger"; + +import { ShaderLanguage } from "core/Materials/shaderLanguage"; + +/** + * Options for texture filtering + */ +interface IHDRIrradianceFilteringOptions { + /** + * Scales pixel intensity for the input HDR map. + */ + hdrScale?: number; + + /** + * Quality of the filter. Should be `Constants.TEXTURE_FILTERING_QUALITY_OFFLINE` for prefiltering + */ + quality?: number; +} + +/** + * Filters HDR maps to get correct renderings of PBR reflections + */ +export class HDRIrradianceFiltering { + private _engine: AbstractEngine; + private _effectRenderer: EffectRenderer; + private _effectWrapper: EffectWrapper; + + /** + * Quality switch for prefiltering. Should be set to `Constants.TEXTURE_FILTERING_QUALITY_OFFLINE` unless + * you care about baking speed. + */ + public quality: number = Constants.TEXTURE_FILTERING_QUALITY_OFFLINE; + + /** + * Scales pixel intensity for the input HDR map. + */ + public hdrScale: number = 1; + + /** + * Instantiates HDR filter for irradiance map + * + * @param engine Thin engine + * @param options Options + */ + constructor(engine: AbstractEngine, options: IHDRIrradianceFilteringOptions = {}) { + // pass + this._engine = engine; + this.hdrScale = options.hdrScale || this.hdrScale; + this.quality = options.quality || this.quality; + } + + private _createRenderTarget(size: number): RenderTargetWrapper { + let textureType = Constants.TEXTURETYPE_UNSIGNED_BYTE; + if (this._engine.getCaps().textureHalfFloatRender) { + textureType = Constants.TEXTURETYPE_HALF_FLOAT; + } else if (this._engine.getCaps().textureFloatRender) { + textureType = Constants.TEXTURETYPE_FLOAT; + } + + const rtWrapper = this._engine.createRenderTargetCubeTexture(size, { + format: Constants.TEXTUREFORMAT_RGBA, + type: textureType, + createMipMaps: false, + generateMipMaps: false, + generateDepthBuffer: false, + generateStencilBuffer: false, + samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE, + }); + this._engine.updateTextureWrappingMode(rtWrapper.texture!, Constants.TEXTURE_CLAMP_ADDRESSMODE, Constants.TEXTURE_CLAMP_ADDRESSMODE, Constants.TEXTURE_CLAMP_ADDRESSMODE); + + this._engine.updateTextureSamplingMode(Constants.TEXTURE_BILINEAR_SAMPLINGMODE, rtWrapper.texture!, false); + + return rtWrapper; + } + + private _prefilterInternal(texture: BaseTexture): BaseTexture { + const width = texture.getSize().width; + const mipmapsCount = ILog2(width); + + const effect = this._effectWrapper.effect; + const outputTexture = this._createRenderTarget(width); + this._effectRenderer.saveStates(); + this._effectRenderer.setViewport(); + + // const intTexture = texture.getInternalTexture(); + // if (intTexture) { + // // Just in case generate fresh clean mips. + // this._engine.updateTextureSamplingMode(Constants.TEXTURE_TRILINEAR_SAMPLINGMODE, intTexture, true); + // } + + this._effectRenderer.applyEffectWrapper(this._effectWrapper); + + const directions = [ + [new Vector3(0, 0, -1), new Vector3(0, -1, 0), new Vector3(1, 0, 0)], // PositiveX + [new Vector3(0, 0, 1), new Vector3(0, -1, 0), new Vector3(-1, 0, 0)], // NegativeX + [new Vector3(1, 0, 0), new Vector3(0, 0, 1), new Vector3(0, 1, 0)], // PositiveY + [new Vector3(1, 0, 0), new Vector3(0, 0, -1), new Vector3(0, -1, 0)], // NegativeY + [new Vector3(1, 0, 0), new Vector3(0, -1, 0), new Vector3(0, 0, 1)], // PositiveZ + [new Vector3(-1, 0, 0), new Vector3(0, -1, 0), new Vector3(0, 0, -1)], // NegativeZ + ]; + + effect.setFloat("hdrScale", this.hdrScale); + effect.setFloat2("vFilteringInfo", texture.getSize().width, mipmapsCount); + effect.setTexture("inputTexture", texture); + const cdfGenerator = texture.getScene()?.iblCdfGenerator; + if (cdfGenerator) { + effect.setTexture("icdfTexture", cdfGenerator.getIcdfTexture()); + } + + for (let face = 0; face < 6; face++) { + effect.setVector3("up", directions[face][0]); + effect.setVector3("right", directions[face][1]); + effect.setVector3("front", directions[face][2]); + + // for (let lod = 0; lod < mipmapsCount; lod++) { + this._engine.bindFramebuffer(outputTexture, face, outputTexture.width, outputTexture.height, true, 0); + this._effectRenderer.applyEffectWrapper(this._effectWrapper); + + // let alpha = Math.pow(2, (lod - this._lodGenerationOffset) / this._lodGenerationScale) / width; + // if (lod === 0) { + // alpha = 0; + // } + + // effect.setFloat("alphaG", alpha); + + this._effectRenderer.draw(); + // } + } + + // Cleanup + this._effectRenderer.restoreStates(); + this._engine.restoreDefaultFramebuffer(); + // this._engine._releaseTexture(texture._texture!); + // texture.irradianceTexture = new BaseTexture(this._engine, outputTexture.texture!); + + // Internal Swap + // const type = outputTexture.texture!.type; + // const format = outputTexture.texture!.format; + + // outputTexture._swapAndDie(texture._texture!); + + // texture._texture!.type = type; + // texture._texture!.format = format; + + // New settings + // texture.gammaSpace = false; + // texture.lodGenerationOffset = this._lodGenerationOffset; + // texture.lodGenerationScale = this._lodGenerationScale; + // texture._prefiltered = true; + + const irradianceTexture = new BaseTexture(texture.getScene(), outputTexture.texture!); + irradianceTexture.displayName = texture.name + "_irradiance"; + irradianceTexture.gammaSpace = false; + return irradianceTexture; + } + + private _createEffect(texture: BaseTexture, onCompiled?: Nullable<(effect: Effect) => void>): EffectWrapper { + const defines = []; + if (texture.gammaSpace) { + defines.push("#define GAMMA_INPUT"); + } + + defines.push("#define NUM_SAMPLES " + this.quality + "u"); // unsigned int + + const isWebGPU = this._engine.isWebGPU; + const samplers = ["inputTexture"]; + if (texture.getScene()?.iblCdfGenerator) { + samplers.push("icdfTexture"); + defines.push("#define IBL_CDF_FILTERING"); + } + const effectWrapper = new EffectWrapper({ + engine: this._engine, + name: "HDRIrradianceFiltering", + vertexShader: "hdrIrradianceFiltering", + fragmentShader: "hdrIrradianceFiltering", + samplerNames: samplers, + uniformNames: ["vSampleDirections", "vWeights", "up", "right", "front", "vFilteringInfo", "hdrScale"], + useShaderStore: true, + defines, + onCompiled: onCompiled, + shaderLanguage: isWebGPU ? ShaderLanguage.WGSL : ShaderLanguage.GLSL, + extraInitializationsAsync: async () => { + if (isWebGPU) { + await Promise.all([import("../../../ShadersWGSL/hdrIrradianceFiltering.vertex"), import("../../../ShadersWGSL/hdrIrradianceFiltering.fragment")]); + } else { + await Promise.all([import("../../../Shaders/hdrIrradianceFiltering.vertex"), import("../../../Shaders/hdrIrradianceFiltering.fragment")]); + } + }, + }); + + return effectWrapper; + } + + /** + * Get a value indicating if the filter is ready to be used + * @param texture Texture to filter + * @returns true if the filter is ready + */ + public isReady(texture: BaseTexture) { + return texture.isReady() && this._effectWrapper.effect.isReady(); + } + + /** + * Prefilters a cube texture to have mipmap levels representing roughness values. + * Prefiltering will be invoked at the end of next rendering pass. + * This has to be done once the map is loaded, and has not been prefiltered by a third party software. + * See http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf for more information + * @param texture Texture to filter + * @param onFinished Callback when filtering is done + * @returns Promise called when prefiltering is done + */ + public prefilter(texture: BaseTexture, onFinished: Nullable<() => void> = null): Promise { + if (!this._engine._features.allowTexturePrefiltering) { + Logger.Warn("HDR prefiltering is not available in WebGL 1., you can use real time filtering instead."); + return Promise.reject("HDR prefiltering is not available in WebGL 1., you can use real time filtering instead."); + } + + return new Promise((resolve) => { + this._effectRenderer = new EffectRenderer(this._engine); + this._effectWrapper = this._createEffect(texture); + this._effectWrapper.effect.executeWhenCompiled(() => { + const irradianceTexture = this._prefilterInternal(texture); + this._effectRenderer.dispose(); + this._effectWrapper.dispose(); + resolve(irradianceTexture); + if (onFinished) { + onFinished(); + } + }); + }); + } +} diff --git a/packages/dev/core/src/Materials/Textures/hdrCubeTexture.ts b/packages/dev/core/src/Materials/Textures/hdrCubeTexture.ts index 200e077c56d..cd6cc0c0b89 100644 --- a/packages/dev/core/src/Materials/Textures/hdrCubeTexture.ts +++ b/packages/dev/core/src/Materials/Textures/hdrCubeTexture.ts @@ -12,6 +12,7 @@ import { Tools } from "../../Misc/tools"; import { ToGammaSpace } from "../../Maths/math.constants"; import type { AbstractEngine } from "../../Engines/abstractEngine"; import { HDRFiltering } from "../../Materials/Textures/Filtering/hdrFiltering"; +import { HDRIrradianceFiltering } from "../../Materials/Textures/Filtering/hdrIrradianceFiltering"; import { ToHalfFloat } from "../../Misc/textureTools"; import "../../Materials/Textures/baseTexture.polynomial"; @@ -278,7 +279,48 @@ export class HDRCubeTexture extends BaseTexture { const previousOnLoad = this._onLoad; const hdrFiltering = new HDRFiltering(engine); this._onLoad = () => { - hdrFiltering.prefilter(this, previousOnLoad); + let irradiancePromise: Promise>; + let cdfGeneratedPromise: Promise; + const cdfGenerator = this.getScene()?.iblCdfGenerator; + if (!this._generateHarmonics) { + if (cdfGenerator) { + // If we're using CDF maps, the importanceSamplingRenderer needs this texture to be + // ready before it can generate the CDF maps and that won't happen until prefiltering + // is done. So, lets make a new texture with the non-prefiltered data and set it as the + // iblSource for the importanceSamplingRenderer. Then, we'll wait for that to be ready + // before we continue with the prefiltering. + cdfGenerator.iblSource = new BaseTexture(this._engine, this.getInternalTexture()); + cdfGeneratedPromise = new Promise((resolve) => { + cdfGenerator.onGeneratedObservable.addOnce(() => { + resolve(); + }); + }); + } else { + cdfGeneratedPromise = Promise.resolve(); + } + const hdrIrradianceFiltering = new HDRIrradianceFiltering(engine); + irradiancePromise = cdfGeneratedPromise.then(() => hdrIrradianceFiltering.prefilter(this)); + } else { + irradiancePromise = Promise.resolve(null); + } + irradiancePromise.then((irradianceTexture) => { + return hdrFiltering.prefilter(this).then(() => { + if (!this._generateHarmonics && irradianceTexture) { + this.irradianceTexture = irradianceTexture; + } + if (cdfGenerator) { + cdfGenerator.iblSource = this; + } + const scene = this.getScene(); + if (scene) { + scene.markAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag); + } + Promise.resolve(); + if (previousOnLoad) { + previousOnLoad(); + } + }); + }); }; } diff --git a/packages/dev/core/src/Materials/Textures/index.ts b/packages/dev/core/src/Materials/Textures/index.ts index 669606b0412..fd767e0bf3e 100644 --- a/packages/dev/core/src/Materials/Textures/index.ts +++ b/packages/dev/core/src/Materials/Textures/index.ts @@ -39,3 +39,7 @@ export * from "../../Shaders/hdrFiltering.vertex"; export * from "../../Shaders/hdrFiltering.fragment"; export * from "../../ShadersWGSL/hdrFiltering.vertex"; export * from "../../ShadersWGSL/hdrFiltering.fragment"; +export * from "../../Shaders/hdrIrradianceFiltering.vertex"; +export * from "../../Shaders/hdrIrradianceFiltering.fragment"; +export * from "../../ShadersWGSL/hdrIrradianceFiltering.vertex"; +export * from "../../ShadersWGSL/hdrIrradianceFiltering.fragment"; diff --git a/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockReflection.fx b/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockReflection.fx index fed612a09c5..859a3527872 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockReflection.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockReflection.fx @@ -260,24 +260,29 @@ // _____________________________ Irradiance ________________________________ vec3 environmentIrradiance = vec3(0., 0., 0.); - #ifdef USESPHERICALFROMREFLECTIONMAP - #if defined(NORMAL) && defined(USESPHERICALINVERTEX) - environmentIrradiance = vEnvironmentIrradiance; + #if defined(USESPHERICALFROMREFLECTIONMAP) || defined(USEIRRADIANCEMAP) + #ifdef ANISOTROPIC + vec3 irradianceVector = vec3(reflectionMatrix * vec4(anisotropicOut.anisotropicNormal, 0)).xyz; #else - #ifdef ANISOTROPIC - vec3 irradianceVector = vec3(reflectionMatrix * vec4(anisotropicOut.anisotropicNormal, 0)).xyz; - #else - vec3 irradianceVector = vec3(reflectionMatrix * vec4(normalW, 0)).xyz; - #endif + vec3 irradianceVector = vec3(reflectionMatrix * vec4(normalW, 0)).xyz; + #endif - #ifdef REFLECTIONMAP_OPPOSITEZ - irradianceVector.z *= -1.0; - #endif + #ifdef REFLECTIONMAP_OPPOSITEZ + irradianceVector.z *= -1.0; + #endif - #ifdef INVERTCUBICMAP - irradianceVector.y *= -1.0; - #endif + #ifdef INVERTCUBICMAP + irradianceVector.y *= -1.0; + #endif + #ifdef SS_TRANSLUCENCY + outParams.irradianceVector = irradianceVector; + #endif + #endif + #ifdef USESPHERICALFROMREFLECTIONMAP + #if defined(NORMAL) && defined(USESPHERICALINVERTEX) + environmentIrradiance = vEnvironmentIrradiance; + #else #if defined(REALTIME_FILTERING) environmentIrradiance = irradiance(reflectionSampler, irradianceVector, vReflectionFilteringInfo #ifdef IBL_CDF_FILTERING @@ -287,13 +292,9 @@ #else environmentIrradiance = computeEnvironmentIrradiance(irradianceVector); #endif - - #ifdef SS_TRANSLUCENCY - outParams.irradianceVector = irradianceVector; - #endif #endif #elif defined(USEIRRADIANCEMAP) - vec4 environmentIrradiance4 = sampleReflection(irradianceSampler, reflectionCoords); + vec4 environmentIrradiance4 = sampleReflection(irradianceSampler, irradianceVector); environmentIrradiance = environmentIrradiance4.rgb; #ifdef RGBDREFLECTION environmentIrradiance.rgb = fromRGBD(environmentIrradiance4); diff --git a/packages/dev/core/src/Shaders/hdrIrradianceFiltering.fragment.fx b/packages/dev/core/src/Shaders/hdrIrradianceFiltering.fragment.fx new file mode 100644 index 00000000000..49a44d9d44b --- /dev/null +++ b/packages/dev/core/src/Shaders/hdrIrradianceFiltering.fragment.fx @@ -0,0 +1,23 @@ +#include +#include +#include +#include + +uniform samplerCube inputTexture; +#ifdef IBL_CDF_FILTERING +uniform sampler2D icdfTexture; +#endif +uniform vec2 vFilteringInfo; +uniform float hdrScale; + +varying vec3 direction; + +void main() { + vec3 color = irradiance(inputTexture, direction, vFilteringInfo + #ifdef IBL_CDF_FILTERING + , icdfTexture + #endif + ); + + gl_FragColor = vec4(color * hdrScale, 1.0); +} \ No newline at end of file diff --git a/packages/dev/core/src/Shaders/hdrIrradianceFiltering.vertex.fx b/packages/dev/core/src/Shaders/hdrIrradianceFiltering.vertex.fx new file mode 100644 index 00000000000..038d8e15b6a --- /dev/null +++ b/packages/dev/core/src/Shaders/hdrIrradianceFiltering.vertex.fx @@ -0,0 +1,24 @@ +// Attributes +attribute vec2 position; + +// Output +varying vec3 direction; + +// Uniforms +uniform vec3 up; +uniform vec3 right; +uniform vec3 front; + + +#define CUSTOM_VERTEX_DEFINITIONS + +void main(void) { + +#define CUSTOM_VERTEX_MAIN_BEGIN + + mat3 view = mat3(up, right, front); + direction = view * vec3(position, 1.0); + gl_Position = vec4(position, 0.0, 1.0); + +#define CUSTOM_VERTEX_MAIN_END +} \ No newline at end of file diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockReflection.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockReflection.fx index 3ee08eef2cd..79957429f19 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockReflection.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockReflection.fx @@ -289,24 +289,29 @@ // _____________________________ Irradiance ________________________________ var environmentIrradiance: vec3f = vec3f(0., 0., 0.); - #ifdef USESPHERICALFROMREFLECTIONMAP - #if defined(NORMAL) && defined(USESPHERICALINVERTEX) - environmentIrradiance = vEnvironmentIrradiance; +#if defined(USESPHERICALFROMREFLECTIONMAP) || defined(USEIRRADIANCEMAP) + #ifdef ANISOTROPIC + var irradianceVector: vec3f = (reflectionMatrix * vec4f(anisotropicOut.anisotropicNormal, 0)).xyz; #else - #ifdef ANISOTROPIC - var irradianceVector: vec3f = (reflectionMatrix * vec4f(anisotropicOut.anisotropicNormal, 0)).xyz; - #else - var irradianceVector: vec3f = (reflectionMatrix * vec4f(normalW, 0)).xyz; - #endif + var irradianceVector: vec3f = (reflectionMatrix * vec4f(normalW, 0)).xyz; + #endif - #ifdef REFLECTIONMAP_OPPOSITEZ - irradianceVector.z *= -1.0; - #endif + #ifdef REFLECTIONMAP_OPPOSITEZ + irradianceVector.z *= -1.0; + #endif - #ifdef INVERTCUBICMAP - irradianceVector.y *= -1.0; - #endif + #ifdef INVERTCUBICMAP + irradianceVector.y *= -1.0; + #endif + #ifdef SS_TRANSLUCENCY + outParams.irradianceVector = irradianceVector; + #endif + #endif + #ifdef USESPHERICALFROMREFLECTIONMAP + #if defined(NORMAL) && defined(USESPHERICALINVERTEX) + environmentIrradiance = vEnvironmentIrradiance; + #else #if defined(REALTIME_FILTERING) environmentIrradiance = irradiance(reflectionSampler, reflectionSamplerSampler, irradianceVector, vReflectionFilteringInfo #ifdef IBL_CDF_FILTERING @@ -317,13 +322,9 @@ #else environmentIrradiance = computeEnvironmentIrradiance(irradianceVector); #endif - - #ifdef SS_TRANSLUCENCY - outParams.irradianceVector = irradianceVector; - #endif #endif #elif defined(USEIRRADIANCEMAP) - var environmentIrradiance4: vec4f = textureSample(irradianceSampler, irradianceSamplerSampler, reflectionCoords); + var environmentIrradiance4: vec4f = textureSample(irradianceSampler, irradianceSamplerSampler, irradianceVector); environmentIrradiance = environmentIrradiance4.rgb; #ifdef RGBDREFLECTION environmentIrradiance = fromRGBD(environmentIrradiance4); diff --git a/packages/dev/core/src/ShadersWGSL/hdrIrradianceFiltering.fragment.fx b/packages/dev/core/src/ShadersWGSL/hdrIrradianceFiltering.fragment.fx new file mode 100644 index 00000000000..6a1ba090fca --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/hdrIrradianceFiltering.fragment.fx @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +var inputTextureSampler: sampler; +var inputTexture: texture_cube; +#ifdef IBL_CDF_FILTERING + var icdfTextureSampler: sampler; + var icdfTexture: texture_2d; +#endif +uniform vFilteringInfo: vec2f; +uniform hdrScale: f32; + +varying direction: vec3f; + +@fragment +fn main(input: FragmentInputs) -> FragmentOutputs { + var color: vec3f = irradiance(inputTexture, inputTextureSampler, input.direction, uniforms.vFilteringInfo + #ifdef IBL_CDF_FILTERING + , icdfTexture, icdfTextureSampler + #endif + ); + + fragmentOutputs.color = vec4f(color * uniforms.hdrScale, 1.0); +} \ No newline at end of file diff --git a/packages/dev/core/src/ShadersWGSL/hdrIrradianceFiltering.vertex.fx b/packages/dev/core/src/ShadersWGSL/hdrIrradianceFiltering.vertex.fx new file mode 100644 index 00000000000..54cb489a93e --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/hdrIrradianceFiltering.vertex.fx @@ -0,0 +1,24 @@ +// Attributes +attribute position: vec2f; + +// Output +varying direction: vec3f; + +// Uniforms +uniform up: vec3f; +uniform right: vec3f; +uniform front: vec3f; + + +#define CUSTOM_VERTEX_DEFINITIONS + +@vertex +fn main(input : VertexInputs) -> FragmentInputs { +#define CUSTOM_VERTEX_MAIN_BEGIN + + var view: mat3x3f = mat3x3f(uniforms.up, uniforms.right, uniforms.front); + vertexOutputs.direction = view * vec3f(input.position, 1.0); + vertexOutputs.position = vec4f(input.position, 0.0, 1.0); + +#define CUSTOM_VERTEX_MAIN_END +} \ No newline at end of file From 42dca94718d822c96229685d11b4f8ab6daa9891 Mon Sep 17 00:00:00 2001 From: Mike Bond Date: Tue, 17 Dec 2024 11:19:20 -0800 Subject: [PATCH 02/11] Cleanup --- .../Filtering/hdrIrradianceFiltering.ts | 39 +++---------------- .../dev/core/src/Rendering/iblCdfGenerator.ts | 6 +++ 2 files changed, 11 insertions(+), 34 deletions(-) diff --git a/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts b/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts index 3a692495f2b..12d1ec74a3d 100644 --- a/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts +++ b/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts @@ -87,16 +87,13 @@ export class HDRIrradianceFiltering { const mipmapsCount = ILog2(width); const effect = this._effectWrapper.effect; - const outputTexture = this._createRenderTarget(width); + // Choose a power of 2 size for the irradiance map. + // It can be much smaller than the original texture. + const irradianceSize = Math.max(32, 1 << Math.floor(Math.log2(width >> 3))); + const outputTexture = this._createRenderTarget(irradianceSize); this._effectRenderer.saveStates(); this._effectRenderer.setViewport(); - // const intTexture = texture.getInternalTexture(); - // if (intTexture) { - // // Just in case generate fresh clean mips. - // this._engine.updateTextureSamplingMode(Constants.TEXTURE_TRILINEAR_SAMPLINGMODE, intTexture, true); - // } - this._effectRenderer.applyEffectWrapper(this._effectWrapper); const directions = [ @@ -121,41 +118,15 @@ export class HDRIrradianceFiltering { effect.setVector3("right", directions[face][1]); effect.setVector3("front", directions[face][2]); - // for (let lod = 0; lod < mipmapsCount; lod++) { this._engine.bindFramebuffer(outputTexture, face, outputTexture.width, outputTexture.height, true, 0); this._effectRenderer.applyEffectWrapper(this._effectWrapper); - // let alpha = Math.pow(2, (lod - this._lodGenerationOffset) / this._lodGenerationScale) / width; - // if (lod === 0) { - // alpha = 0; - // } - - // effect.setFloat("alphaG", alpha); - this._effectRenderer.draw(); - // } } // Cleanup this._effectRenderer.restoreStates(); this._engine.restoreDefaultFramebuffer(); - // this._engine._releaseTexture(texture._texture!); - // texture.irradianceTexture = new BaseTexture(this._engine, outputTexture.texture!); - - // Internal Swap - // const type = outputTexture.texture!.type; - // const format = outputTexture.texture!.format; - - // outputTexture._swapAndDie(texture._texture!); - - // texture._texture!.type = type; - // texture._texture!.format = format; - - // New settings - // texture.gammaSpace = false; - // texture.lodGenerationOffset = this._lodGenerationOffset; - // texture.lodGenerationScale = this._lodGenerationScale; - // texture._prefiltered = true; const irradianceTexture = new BaseTexture(texture.getScene(), outputTexture.texture!); irradianceTexture.displayName = texture.name + "_irradiance"; @@ -210,7 +181,7 @@ export class HDRIrradianceFiltering { } /** - * Prefilters a cube texture to have mipmap levels representing roughness values. + * Prefilters a cube texture to contain IBL irradiance. * Prefiltering will be invoked at the end of next rendering pass. * This has to be done once the map is loaded, and has not been prefiltered by a third party software. * See http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf for more information diff --git a/packages/dev/core/src/Rendering/iblCdfGenerator.ts b/packages/dev/core/src/Rendering/iblCdfGenerator.ts index aa28eebb688..08f27863c75 100644 --- a/packages/dev/core/src/Rendering/iblCdfGenerator.ts +++ b/packages/dev/core/src/Rendering/iblCdfGenerator.ts @@ -162,6 +162,10 @@ export class IblCdfGenerator { 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. + size.width = 1 << Math.floor(Math.log2(size.width)); + size.height = 1 << Math.floor(Math.log2(size.height)); } const isWebGPU = this._engine.isWebGPU; @@ -173,6 +177,7 @@ export class IblCdfGenerator { type: Constants.TEXTURETYPE_FLOAT, samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE, shaderLanguage: isWebGPU ? ShaderLanguage.WGSL : ShaderLanguage.GLSL, + gammaSpace: false, extraInitializationsAsync: async () => { if (isWebGPU) { await Promise.all([import("../ShadersWGSL/iblCdfx.fragment"), import("../ShadersWGSL/iblCdfy.fragment"), import("../ShadersWGSL/iblScaledLuminance.fragment")]); @@ -188,6 +193,7 @@ export class IblCdfGenerator { type: Constants.TEXTURETYPE_HALF_FLOAT, samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE, shaderLanguage: isWebGPU ? ShaderLanguage.WGSL : ShaderLanguage.GLSL, + gammaSpace: false, extraInitializationsAsync: async () => { if (isWebGPU) { await Promise.all([import("../ShadersWGSL/iblIcdf.fragment")]); From 5f0fc91b6519135184959465c00b004be7a86ca2 Mon Sep 17 00:00:00 2001 From: Mike Bond Date: Tue, 17 Dec 2024 17:06:30 -0800 Subject: [PATCH 03/11] Shader fixes for irradianceTexture --- .../Materials/Node/Blocks/PBR/reflectionBlock.ts | 2 +- .../core/src/Materials/Textures/hdrCubeTexture.ts | 12 ++++++++---- packages/dev/core/src/Rendering/iblCdfGenerator.ts | 10 +++++++--- .../Shaders/ShadersInclude/pbrBlockReflection.fx | 12 ++++++++---- packages/dev/core/src/Shaders/pbr.fragment.fx | 2 +- .../ShadersInclude/pbrBlockReflection.fx | 14 +++++++++----- packages/dev/core/src/ShadersWGSL/pbr.fragment.fx | 2 +- 7 files changed, 35 insertions(+), 19 deletions(-) diff --git a/packages/dev/core/src/Materials/Node/Blocks/PBR/reflectionBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/PBR/reflectionBlock.ts index 464042036cb..c142f9c43ca 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/PBR/reflectionBlock.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/PBR/reflectionBlock.ts @@ -432,7 +432,7 @@ export class ReflectionBlock extends ReflectionTextureBaseBlock { #if defined(NORMAL) && defined(USESPHERICALINVERTEX) , ${isWebGPU ? "input." : ""}${this._vEnvironmentIrradianceName} #endif - #ifdef USESPHERICALFROMREFLECTIONMAP + #if defined(USESPHERICALFROMREFLECTIONMAP) || defined(USEIRRADIANCEMAP) #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX) , ${this._reflectionMatrixName} #endif diff --git a/packages/dev/core/src/Materials/Textures/hdrCubeTexture.ts b/packages/dev/core/src/Materials/Textures/hdrCubeTexture.ts index cd6cc0c0b89..db1955e18fd 100644 --- a/packages/dev/core/src/Materials/Textures/hdrCubeTexture.ts +++ b/packages/dev/core/src/Materials/Textures/hdrCubeTexture.ts @@ -292,6 +292,9 @@ export class HDRCubeTexture extends BaseTexture { cdfGenerator.iblSource = new BaseTexture(this._engine, this.getInternalTexture()); cdfGeneratedPromise = new Promise((resolve) => { cdfGenerator.onGeneratedObservable.addOnce(() => { + const oldTexture = cdfGenerator.iblSource; + oldTexture?.dispose(); + cdfGenerator.iblSource = null; resolve(); }); }); @@ -307,14 +310,15 @@ export class HDRCubeTexture extends BaseTexture { return hdrFiltering.prefilter(this).then(() => { if (!this._generateHarmonics && irradianceTexture) { this.irradianceTexture = irradianceTexture; + const scene = this.getScene(); + if (scene) { + scene.markAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag); + } } if (cdfGenerator) { cdfGenerator.iblSource = this; } - const scene = this.getScene(); - if (scene) { - scene.markAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag); - } + Promise.resolve(); if (previousOnLoad) { previousOnLoad(); diff --git a/packages/dev/core/src/Rendering/iblCdfGenerator.ts b/packages/dev/core/src/Rendering/iblCdfGenerator.ts index 08f27863c75..c7306041009 100644 --- a/packages/dev/core/src/Rendering/iblCdfGenerator.ts +++ b/packages/dev/core/src/Rendering/iblCdfGenerator.ts @@ -16,6 +16,7 @@ import type { CubeTexture } from "../Materials/Textures/cubeTexture"; import { ShaderLanguage } from "core/Materials/shaderLanguage"; import { Engine } from "../Engines/engine"; import { _WarnImport } from "../Misc/devTools"; +import type { Nullable } from "../types"; /** * Build cdf maps to be used for IBL importance sampling. @@ -28,12 +29,12 @@ export class IblCdfGenerator { private _cdfxPT: ProceduralTexture; private _icdfPT: ProceduralTexture; private _scaledLuminancePT: ProceduralTexture; - private _iblSource: BaseTexture; + private _iblSource: Nullable; private _dummyTexture: RawTexture; /** * Gets the IBL source texture being used by the CDF renderer */ - public get iblSource(): BaseTexture { + public get iblSource(): Nullable { return this._iblSource; } @@ -41,12 +42,15 @@ export class IblCdfGenerator { * Sets the IBL source texture to be used by the CDF renderer. * This will trigger recreation of the CDF assets. */ - public set iblSource(source: BaseTexture) { + public set iblSource(source: Nullable) { if (this._iblSource === source) { return; } this._disposeTextures(); this._iblSource = source; + if (!source) { + return; + } if (source.isCube) { if (source.isReadyOrNotBlocking()) { this._recreateAssetsFromNewIbl(); diff --git a/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockReflection.fx b/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockReflection.fx index 859a3527872..d60fd521635 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockReflection.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockReflection.fx @@ -181,7 +181,7 @@ #if defined(NORMAL) && defined(USESPHERICALINVERTEX) , in vec3 vEnvironmentIrradiance #endif - #ifdef USESPHERICALFROMREFLECTIONMAP + #if defined(USESPHERICALFROMREFLECTIONMAP) || defined(USEIRRADIANCEMAP) #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX) , in mat4 reflectionMatrix #endif @@ -261,10 +261,14 @@ vec3 environmentIrradiance = vec3(0., 0., 0.); #if defined(USESPHERICALFROMREFLECTIONMAP) || defined(USEIRRADIANCEMAP) - #ifdef ANISOTROPIC - vec3 irradianceVector = vec3(reflectionMatrix * vec4(anisotropicOut.anisotropicNormal, 0)).xyz; + #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX) + #ifdef ANISOTROPIC + vec3 irradianceVector = vec3(reflectionMatrix * vec4(anisotropicOut.anisotropicNormal, 0)).xyz; + #else + vec3 irradianceVector = vec3(reflectionMatrix * vec4(normalW, 0)).xyz; + #endif #else - vec3 irradianceVector = vec3(reflectionMatrix * vec4(normalW, 0)).xyz; + vec3 irradianceVector = normalW; #endif #ifdef REFLECTIONMAP_OPPOSITEZ diff --git a/packages/dev/core/src/Shaders/pbr.fragment.fx b/packages/dev/core/src/Shaders/pbr.fragment.fx index 675554c58fb..396eb85f81a 100644 --- a/packages/dev/core/src/Shaders/pbr.fragment.fx +++ b/packages/dev/core/src/Shaders/pbr.fragment.fx @@ -286,7 +286,7 @@ void main(void) { #if defined(NORMAL) && defined(USESPHERICALINVERTEX) , vEnvironmentIrradiance #endif - #ifdef USESPHERICALFROMREFLECTIONMAP + #if defined(USESPHERICALFROMREFLECTIONMAP) || defined(USEIRRADIANCEMAP) #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX) , reflectionMatrix #endif diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockReflection.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockReflection.fx index 79957429f19..e769de6ef30 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockReflection.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockReflection.fx @@ -201,7 +201,7 @@ #if defined(NORMAL) && defined(USESPHERICALINVERTEX) , vEnvironmentIrradiance: vec3f #endif - #ifdef USESPHERICALFROMREFLECTIONMAP + #if defined(USESPHERICALFROMREFLECTIONMAP) || defined(USEIRRADIANCEMAP) #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX) , reflectionMatrix: mat4x4f #endif @@ -289,11 +289,15 @@ // _____________________________ Irradiance ________________________________ var environmentIrradiance: vec3f = vec3f(0., 0., 0.); -#if defined(USESPHERICALFROMREFLECTIONMAP) || defined(USEIRRADIANCEMAP) - #ifdef ANISOTROPIC - var irradianceVector: vec3f = (reflectionMatrix * vec4f(anisotropicOut.anisotropicNormal, 0)).xyz; + #if defined(USESPHERICALFROMREFLECTIONMAP) || defined(USEIRRADIANCEMAP) + #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX) + #ifdef ANISOTROPIC + var irradianceVector: vec3f = (reflectionMatrix * vec4f(anisotropicOut.anisotropicNormal, 0)).xyz; + #else + var irradianceVector: vec3f = (reflectionMatrix * vec4f(normalW, 0)).xyz; + #endif #else - var irradianceVector: vec3f = (reflectionMatrix * vec4f(normalW, 0)).xyz; + var irradianceVector: vec3f = normalW; #endif #ifdef REFLECTIONMAP_OPPOSITEZ diff --git a/packages/dev/core/src/ShadersWGSL/pbr.fragment.fx b/packages/dev/core/src/ShadersWGSL/pbr.fragment.fx index 071cb853732..b8a31cabb24 100644 --- a/packages/dev/core/src/ShadersWGSL/pbr.fragment.fx +++ b/packages/dev/core/src/ShadersWGSL/pbr.fragment.fx @@ -272,7 +272,7 @@ fn main(input: FragmentInputs) -> FragmentOutputs { #if defined(NORMAL) && defined(USESPHERICALINVERTEX) , fragmentInputs.vEnvironmentIrradiance #endif - #ifdef USESPHERICALFROMREFLECTIONMAP + #if defined(USESPHERICALFROMREFLECTIONMAP) || defined(USEIRRADIANCEMAP) #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX) , uniforms.reflectionMatrix #endif From e6f699e78c30ca78c4ec94d1c4689d03f3bf4fa0 Mon Sep 17 00:00:00 2001 From: Mike Bond Date: Wed, 18 Dec 2024 14:52:32 -0800 Subject: [PATCH 04/11] Cleanup irradiance prefiltering --- .../Textures/Filtering/hdrFiltering.ts | 1 + .../Filtering/hdrIrradianceFiltering.ts | 72 +++++++++++++------ .../src/Materials/Textures/hdrCubeTexture.ts | 47 +++++------- .../dev/core/src/Rendering/iblCdfGenerator.ts | 37 +++++++--- 4 files changed, 96 insertions(+), 61 deletions(-) diff --git a/packages/dev/core/src/Materials/Textures/Filtering/hdrFiltering.ts b/packages/dev/core/src/Materials/Textures/Filtering/hdrFiltering.ts index 473715828fa..15cc53ad4f4 100644 --- a/packages/dev/core/src/Materials/Textures/Filtering/hdrFiltering.ts +++ b/packages/dev/core/src/Materials/Textures/Filtering/hdrFiltering.ts @@ -77,6 +77,7 @@ export class HDRFiltering { generateDepthBuffer: false, generateStencilBuffer: false, samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE, + label: "HDR_Radiance_Filtering_Target", }); this._engine.updateTextureWrappingMode(rtWrapper.texture!, Constants.TEXTURE_CLAMP_ADDRESSMODE, Constants.TEXTURE_CLAMP_ADDRESSMODE, Constants.TEXTURE_CLAMP_ADDRESSMODE); diff --git a/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts b/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts index 12d1ec74a3d..5acfe95fc17 100644 --- a/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts +++ b/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts @@ -10,6 +10,7 @@ import type { RenderTargetWrapper } from "../../../Engines/renderTargetWrapper"; import { Logger } from "../../../Misc/logger"; import { ShaderLanguage } from "core/Materials/shaderLanguage"; +import { IblCdfGenerator } from "core/Rendering"; /** * Options for texture filtering @@ -24,6 +25,11 @@ interface IHDRIrradianceFilteringOptions { * Quality of the filter. Should be `Constants.TEXTURE_FILTERING_QUALITY_OFFLINE` for prefiltering */ quality?: number; + + /** + * Use the Cumulative Distribution Function (CDF) for filtering + */ + useCdf?: boolean; } /** @@ -33,6 +39,7 @@ export class HDRIrradianceFiltering { private _engine: AbstractEngine; private _effectRenderer: EffectRenderer; private _effectWrapper: EffectWrapper; + private _cdfGenerator: IblCdfGenerator; /** * Quality switch for prefiltering. Should be set to `Constants.TEXTURE_FILTERING_QUALITY_OFFLINE` unless @@ -45,6 +52,11 @@ export class HDRIrradianceFiltering { */ public hdrScale: number = 1; + /** + * Use the Cumulative Distribution Function (CDF) for filtering + */ + public useCdf: boolean = false; + /** * Instantiates HDR filter for irradiance map * @@ -56,6 +68,7 @@ export class HDRIrradianceFiltering { this._engine = engine; this.hdrScale = options.hdrScale || this.hdrScale; this.quality = options.quality || this.quality; + this.useCdf = options.useCdf || this.useCdf; } private _createRenderTarget(size: number): RenderTargetWrapper { @@ -73,12 +86,11 @@ export class HDRIrradianceFiltering { generateMipMaps: false, generateDepthBuffer: false, generateStencilBuffer: false, - samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE, + samplingMode: Constants.TEXTURE_BILINEAR_SAMPLINGMODE, + label: "HDR_Irradiance_Filtering_Target", }); this._engine.updateTextureWrappingMode(rtWrapper.texture!, Constants.TEXTURE_CLAMP_ADDRESSMODE, Constants.TEXTURE_CLAMP_ADDRESSMODE, Constants.TEXTURE_CLAMP_ADDRESSMODE); - this._engine.updateTextureSamplingMode(Constants.TEXTURE_BILINEAR_SAMPLINGMODE, rtWrapper.texture!, false); - return rtWrapper; } @@ -89,7 +101,7 @@ export class HDRIrradianceFiltering { const effect = this._effectWrapper.effect; // Choose a power of 2 size for the irradiance map. // It can be much smaller than the original texture. - const irradianceSize = Math.max(32, 1 << Math.floor(Math.log2(width >> 3))); + const irradianceSize = Math.max(32, 1 << ILog2(width >> 3)); const outputTexture = this._createRenderTarget(irradianceSize); this._effectRenderer.saveStates(); this._effectRenderer.setViewport(); @@ -108,9 +120,8 @@ export class HDRIrradianceFiltering { effect.setFloat("hdrScale", this.hdrScale); effect.setFloat2("vFilteringInfo", texture.getSize().width, mipmapsCount); effect.setTexture("inputTexture", texture); - const cdfGenerator = texture.getScene()?.iblCdfGenerator; - if (cdfGenerator) { - effect.setTexture("icdfTexture", cdfGenerator.getIcdfTexture()); + if (this._cdfGenerator) { + effect.setTexture("icdfTexture", this._cdfGenerator.getIcdfTexture()); } for (let face = 0; face < 6; face++) { @@ -118,7 +129,7 @@ export class HDRIrradianceFiltering { effect.setVector3("right", directions[face][1]); effect.setVector3("front", directions[face][2]); - this._engine.bindFramebuffer(outputTexture, face, outputTexture.width, outputTexture.height, true, 0); + this._engine.bindFramebuffer(outputTexture, face, undefined, undefined, true); this._effectRenderer.applyEffectWrapper(this._effectWrapper); this._effectRenderer.draw(); @@ -127,7 +138,8 @@ export class HDRIrradianceFiltering { // Cleanup this._effectRenderer.restoreStates(); this._engine.restoreDefaultFramebuffer(); - + effect.setTexture("inputTexture", null); + effect.setTexture("icdfTexture", null); const irradianceTexture = new BaseTexture(texture.getScene(), outputTexture.texture!); irradianceTexture.displayName = texture.name + "_irradiance"; irradianceTexture.gammaSpace = false; @@ -144,7 +156,7 @@ export class HDRIrradianceFiltering { const isWebGPU = this._engine.isWebGPU; const samplers = ["inputTexture"]; - if (texture.getScene()?.iblCdfGenerator) { + if (this._cdfGenerator) { samplers.push("icdfTexture"); defines.push("#define IBL_CDF_FILTERING"); } @@ -194,18 +206,38 @@ export class HDRIrradianceFiltering { Logger.Warn("HDR prefiltering is not available in WebGL 1., you can use real time filtering instead."); return Promise.reject("HDR prefiltering is not available in WebGL 1., you can use real time filtering instead."); } + let cdfGeneratedPromise = Promise.resolve(); + if (this.useCdf) { + this._cdfGenerator = new IblCdfGenerator(texture.getScene()!); + const internalTexture = texture.getInternalTexture(); + if (internalTexture) { + this._cdfGenerator.iblSource = new BaseTexture(this._engine, internalTexture); + internalTexture.label = "HDR_IBL_Source"; + cdfGeneratedPromise = new Promise((resolve) => { + this._cdfGenerator.onGeneratedObservable.addOnce(() => { + resolve(); + }); + }); + } + } return new Promise((resolve) => { - this._effectRenderer = new EffectRenderer(this._engine); - this._effectWrapper = this._createEffect(texture); - this._effectWrapper.effect.executeWhenCompiled(() => { - const irradianceTexture = this._prefilterInternal(texture); - this._effectRenderer.dispose(); - this._effectWrapper.dispose(); - resolve(irradianceTexture); - if (onFinished) { - onFinished(); - } + return cdfGeneratedPromise.then(() => { + this._effectRenderer = new EffectRenderer(this._engine); + this._effectWrapper = this._createEffect(texture); + this._effectWrapper.effect.executeWhenCompiled(() => { + const irradianceTexture = this._prefilterInternal(texture); + this._effectRenderer.dispose(); + this._effectWrapper.dispose(); + if (this._cdfGenerator) { + // this._cdfGenerator.iblSource!._swapAndDie(texture._texture!); + } + this._cdfGenerator?.dispose(); + resolve(irradianceTexture); + if (onFinished) { + onFinished(); + } + }); }); }); } diff --git a/packages/dev/core/src/Materials/Textures/hdrCubeTexture.ts b/packages/dev/core/src/Materials/Textures/hdrCubeTexture.ts index db1955e18fd..27071803c86 100644 --- a/packages/dev/core/src/Materials/Textures/hdrCubeTexture.ts +++ b/packages/dev/core/src/Materials/Textures/hdrCubeTexture.ts @@ -15,6 +15,7 @@ import { HDRFiltering } from "../../Materials/Textures/Filtering/hdrFiltering"; import { HDRIrradianceFiltering } from "../../Materials/Textures/Filtering/hdrIrradianceFiltering"; import { ToHalfFloat } from "../../Misc/textureTools"; import "../../Materials/Textures/baseTexture.polynomial"; +import { IblCdfGenerator } from "core/Rendering"; /** * This represents a texture coming from an HDR input. @@ -28,6 +29,7 @@ export class HDRCubeTexture extends BaseTexture { private _generateHarmonics = true; private _noMipmap: boolean; private _prefilterOnLoad: boolean; + private _prefilterIrradianceOnLoad: boolean; private _textureMatrix: Matrix; private _size: number; private _supersample: boolean; @@ -114,6 +116,7 @@ export class HDRCubeTexture extends BaseTexture { * @param onLoad on success callback function * @param onError on error callback function * @param supersample Defines if texture must be supersampled (default: false) + * @param prefilterIrradianceOnLoad Prefilters HDR texture to allow use of this texture for irradiance lighting. */ constructor( url: string, @@ -125,7 +128,8 @@ export class HDRCubeTexture extends BaseTexture { prefilterOnLoad = false, onLoad: Nullable<() => void> = null, onError: Nullable<(message?: string, exception?: any) => void> = null, - supersample = false + supersample = false, + prefilterIrradianceOnLoad = false ) { super(sceneOrEngine); @@ -140,6 +144,7 @@ export class HDRCubeTexture extends BaseTexture { this.isCube = true; this._textureMatrix = Matrix.Identity(); this._prefilterOnLoad = prefilterOnLoad; + this._prefilterIrradianceOnLoad = prefilterIrradianceOnLoad; this._onLoad = () => { this.onLoadObservable.notifyObservers(this); if (onLoad) { @@ -275,50 +280,30 @@ export class HDRCubeTexture extends BaseTexture { return results; }; - if (engine._features.allowTexturePrefiltering && this._prefilterOnLoad) { + if (engine._features.allowTexturePrefiltering && (this._prefilterOnLoad || this._prefilterIrradianceOnLoad)) { const previousOnLoad = this._onLoad; const hdrFiltering = new HDRFiltering(engine); this._onLoad = () => { let irradiancePromise: Promise>; - let cdfGeneratedPromise: Promise; - const cdfGenerator = this.getScene()?.iblCdfGenerator; - if (!this._generateHarmonics) { - if (cdfGenerator) { - // If we're using CDF maps, the importanceSamplingRenderer needs this texture to be - // ready before it can generate the CDF maps and that won't happen until prefiltering - // is done. So, lets make a new texture with the non-prefiltered data and set it as the - // iblSource for the importanceSamplingRenderer. Then, we'll wait for that to be ready - // before we continue with the prefiltering. - cdfGenerator.iblSource = new BaseTexture(this._engine, this.getInternalTexture()); - cdfGeneratedPromise = new Promise((resolve) => { - cdfGenerator.onGeneratedObservable.addOnce(() => { - const oldTexture = cdfGenerator.iblSource; - oldTexture?.dispose(); - cdfGenerator.iblSource = null; - resolve(); - }); - }); - } else { - cdfGeneratedPromise = Promise.resolve(); - } - const hdrIrradianceFiltering = new HDRIrradianceFiltering(engine); - irradiancePromise = cdfGeneratedPromise.then(() => hdrIrradianceFiltering.prefilter(this)); + if (this._prefilterIrradianceOnLoad) { + const hdrIrradianceFiltering = new HDRIrradianceFiltering(engine, { useCdf: true }); + irradiancePromise = hdrIrradianceFiltering.prefilter(this); } else { irradiancePromise = Promise.resolve(null); } irradiancePromise.then((irradianceTexture) => { - return hdrFiltering.prefilter(this).then(() => { - if (!this._generateHarmonics && irradianceTexture) { + let radiancePromise = Promise.resolve(); + if (this._prefilterOnLoad) { + radiancePromise = hdrFiltering.prefilter(this); + } + return radiancePromise.then(() => { + if (this._prefilterIrradianceOnLoad && irradianceTexture) { this.irradianceTexture = irradianceTexture; const scene = this.getScene(); if (scene) { scene.markAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag); } } - if (cdfGenerator) { - cdfGenerator.iblSource = this; - } - Promise.resolve(); if (previousOnLoad) { previousOnLoad(); diff --git a/packages/dev/core/src/Rendering/iblCdfGenerator.ts b/packages/dev/core/src/Rendering/iblCdfGenerator.ts index c7306041009..607261e0fc7 100644 --- a/packages/dev/core/src/Rendering/iblCdfGenerator.ts +++ b/packages/dev/core/src/Rendering/iblCdfGenerator.ts @@ -17,12 +17,13 @@ import { ShaderLanguage } from "core/Materials/shaderLanguage"; import { Engine } from "../Engines/engine"; import { _WarnImport } from "../Misc/devTools"; import type { Nullable } from "../types"; +import { EngineStore } from "../Engines/engineStore"; /** * Build cdf maps to be used for IBL importance sampling. */ export class IblCdfGenerator { - private _scene: Scene; + private _scene: Nullable; private _engine: AbstractEngine; private _cdfyPT: ProceduralTexture; @@ -131,15 +132,27 @@ export class IblCdfGenerator { /** * Instanciates the CDF renderer - * @param scene Scene to attach to + * @param sceneOrEngine Scene to attach to * @returns The CDF renderer */ - constructor(scene: Scene) { - this._scene = scene; - this._engine = scene.getEngine(); + constructor(sceneOrEngine: Nullable) { + 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, scene, false, false, undefined, Constants.TEXTURETYPE_HALF_FLOAT); - IblCdfGenerator._SceneComponentInitialization(this._scene); + this._dummyTexture = new RawTexture(blackPixels, 1, 1, Engine.TEXTUREFORMAT_RGBA, sceneOrEngine, false, false, undefined, Constants.TEXTURETYPE_HALF_FLOAT); + if (this._scene) { + IblCdfGenerator._SceneComponentInitialization(this._scene); + } } /** @@ -154,7 +167,7 @@ export class IblCdfGenerator { new Uint8Array([255]), 1, 1, - this._scene, + this._engine, false, false, Constants.TEXTURE_NEAREST_SAMPLINGMODE, @@ -270,8 +283,8 @@ export class IblCdfGenerator { } const isWebGPU = this._engine.isWebGPU; const debugOptions: PostProcessOptions = { - width: this._scene.getEngine().getRenderWidth(), - height: this._scene.getEngine().getRenderHeight(), + width: this._engine.getRenderWidth(), + height: this._engine.getRenderHeight(), samplingMode: Texture.BILINEAR_SAMPLINGMODE, engine: this._engine, textureType: Constants.TEXTURETYPE_UNSIGNED_BYTE, @@ -335,4 +348,8 @@ export class IblCdfGenerator { } this.onGeneratedObservable.clear(); } + + private static _IsScene(sceneOrEngine: Scene | AbstractEngine): sceneOrEngine is Scene { + return sceneOrEngine.getClassName() === "Scene"; + } } From ec24446e2f2e22de4b5d181f09f5087b135ce94a Mon Sep 17 00:00:00 2001 From: Mike Bond Date: Wed, 18 Dec 2024 15:06:39 -0800 Subject: [PATCH 05/11] Don't create intermediate texture --- .../src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts b/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts index 5acfe95fc17..6ac79c39397 100644 --- a/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts +++ b/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts @@ -211,7 +211,7 @@ export class HDRIrradianceFiltering { this._cdfGenerator = new IblCdfGenerator(texture.getScene()!); const internalTexture = texture.getInternalTexture(); if (internalTexture) { - this._cdfGenerator.iblSource = new BaseTexture(this._engine, internalTexture); + this._cdfGenerator.iblSource = texture; internalTexture.label = "HDR_IBL_Source"; cdfGeneratedPromise = new Promise((resolve) => { this._cdfGenerator.onGeneratedObservable.addOnce(() => { From d382e41250b0560aba2a261dfb296a1bd10367a1 Mon Sep 17 00:00:00 2001 From: Mike Bond Date: Thu, 19 Dec 2024 21:54:28 -0800 Subject: [PATCH 06/11] Refactor filtering promises --- .../Filtering/hdrIrradianceFiltering.ts | 26 +++++-------- .../src/Materials/Textures/hdrCubeTexture.ts | 37 ++++++++----------- 2 files changed, 26 insertions(+), 37 deletions(-) diff --git a/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts b/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts index 6ac79c39397..e6835728db9 100644 --- a/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts +++ b/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts @@ -141,6 +141,7 @@ export class HDRIrradianceFiltering { effect.setTexture("inputTexture", null); effect.setTexture("icdfTexture", null); const irradianceTexture = new BaseTexture(texture.getScene(), outputTexture.texture!); + irradianceTexture.name = texture.name + "_irradiance"; irradianceTexture.displayName = texture.name + "_irradiance"; irradianceTexture.gammaSpace = false; return irradianceTexture; @@ -206,32 +207,25 @@ export class HDRIrradianceFiltering { Logger.Warn("HDR prefiltering is not available in WebGL 1., you can use real time filtering instead."); return Promise.reject("HDR prefiltering is not available in WebGL 1., you can use real time filtering instead."); } - let cdfGeneratedPromise = Promise.resolve(); + let cdfGeneratedPromise: Promise> = Promise.resolve(null); if (this.useCdf) { - this._cdfGenerator = new IblCdfGenerator(texture.getScene()!); - const internalTexture = texture.getInternalTexture(); - if (internalTexture) { - this._cdfGenerator.iblSource = texture; - internalTexture.label = "HDR_IBL_Source"; - cdfGeneratedPromise = new Promise((resolve) => { - this._cdfGenerator.onGeneratedObservable.addOnce(() => { - resolve(); - }); + this._cdfGenerator = new IblCdfGenerator(this._engine); + this._cdfGenerator.iblSource = texture; + cdfGeneratedPromise = new Promise((resolve) => { + this._cdfGenerator.onGeneratedObservable.addOnce(() => { + resolve(null); }); - } + }); } - return new Promise((resolve) => { - return cdfGeneratedPromise.then(() => { + return cdfGeneratedPromise.then(() => { + return new Promise((resolve) => { this._effectRenderer = new EffectRenderer(this._engine); this._effectWrapper = this._createEffect(texture); this._effectWrapper.effect.executeWhenCompiled(() => { const irradianceTexture = this._prefilterInternal(texture); this._effectRenderer.dispose(); this._effectWrapper.dispose(); - if (this._cdfGenerator) { - // this._cdfGenerator.iblSource!._swapAndDie(texture._texture!); - } this._cdfGenerator?.dispose(); resolve(irradianceTexture); if (onFinished) { diff --git a/packages/dev/core/src/Materials/Textures/hdrCubeTexture.ts b/packages/dev/core/src/Materials/Textures/hdrCubeTexture.ts index 27071803c86..e321c49c87d 100644 --- a/packages/dev/core/src/Materials/Textures/hdrCubeTexture.ts +++ b/packages/dev/core/src/Materials/Textures/hdrCubeTexture.ts @@ -15,7 +15,6 @@ import { HDRFiltering } from "../../Materials/Textures/Filtering/hdrFiltering"; import { HDRIrradianceFiltering } from "../../Materials/Textures/Filtering/hdrIrradianceFiltering"; import { ToHalfFloat } from "../../Misc/textureTools"; import "../../Materials/Textures/baseTexture.polynomial"; -import { IblCdfGenerator } from "core/Rendering"; /** * This represents a texture coming from an HDR input. @@ -284,31 +283,27 @@ export class HDRCubeTexture extends BaseTexture { const previousOnLoad = this._onLoad; const hdrFiltering = new HDRFiltering(engine); this._onLoad = () => { - let irradiancePromise: Promise>; + let irradiancePromise: Promise> = Promise.resolve(null); + let radiancePromise: Promise = Promise.resolve(); if (this._prefilterIrradianceOnLoad) { const hdrIrradianceFiltering = new HDRIrradianceFiltering(engine, { useCdf: true }); irradiancePromise = hdrIrradianceFiltering.prefilter(this); - } else { - irradiancePromise = Promise.resolve(null); } - irradiancePromise.then((irradianceTexture) => { - let radiancePromise = Promise.resolve(); - if (this._prefilterOnLoad) { - radiancePromise = hdrFiltering.prefilter(this); - } - return radiancePromise.then(() => { - if (this._prefilterIrradianceOnLoad && irradianceTexture) { - this.irradianceTexture = irradianceTexture; - const scene = this.getScene(); - if (scene) { - scene.markAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag); - } - } - Promise.resolve(); - if (previousOnLoad) { - previousOnLoad(); + if (this._prefilterOnLoad) { + radiancePromise = hdrFiltering.prefilter(this); + } + Promise.all([irradiancePromise, radiancePromise]).then((results) => { + const irradianceTexture = results[0] as Nullable; + if (this._prefilterIrradianceOnLoad && irradianceTexture) { + this.irradianceTexture = irradianceTexture; + const scene = this.getScene(); + if (scene) { + scene.markAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag); } - }); + } + if (previousOnLoad) { + previousOnLoad(); + } }); }; } From 1586355563a41cf2c3e115f9950fdfbd449453bd Mon Sep 17 00:00:00 2001 From: Mike Bond Date: Thu, 19 Dec 2024 22:44:17 -0800 Subject: [PATCH 07/11] Add vis tests for irradiance prefiltering --- .../Filtering/hdrIrradianceFiltering.ts | 2 +- .../src/Materials/Textures/hdrCubeTexture.ts | 8 ++++++-- .../ReferenceImages/prefilteringIrradiance.png | Bin 0 -> 12240 bytes .../prefilteringIrradianceCDF.png | Bin 0 -> 12354 bytes .../tools/tests/test/visualization/config.json | 14 ++++++++++++++ 5 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 packages/tools/tests/test/visualization/ReferenceImages/prefilteringIrradiance.png create mode 100644 packages/tools/tests/test/visualization/ReferenceImages/prefilteringIrradianceCDF.png diff --git a/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts b/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts index e6835728db9..b23fb86c68f 100644 --- a/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts +++ b/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts @@ -10,7 +10,7 @@ import type { RenderTargetWrapper } from "../../../Engines/renderTargetWrapper"; import { Logger } from "../../../Misc/logger"; import { ShaderLanguage } from "core/Materials/shaderLanguage"; -import { IblCdfGenerator } from "core/Rendering"; +import { IblCdfGenerator } from "../../../Rendering/iblCdfGenerator"; /** * Options for texture filtering diff --git a/packages/dev/core/src/Materials/Textures/hdrCubeTexture.ts b/packages/dev/core/src/Materials/Textures/hdrCubeTexture.ts index e321c49c87d..b7179b92796 100644 --- a/packages/dev/core/src/Materials/Textures/hdrCubeTexture.ts +++ b/packages/dev/core/src/Materials/Textures/hdrCubeTexture.ts @@ -29,6 +29,7 @@ export class HDRCubeTexture extends BaseTexture { private _noMipmap: boolean; private _prefilterOnLoad: boolean; private _prefilterIrradianceOnLoad: boolean; + private _prefilterUsingCdf: boolean; private _textureMatrix: Matrix; private _size: number; private _supersample: boolean; @@ -116,6 +117,7 @@ export class HDRCubeTexture extends BaseTexture { * @param onError on error callback function * @param supersample Defines if texture must be supersampled (default: false) * @param prefilterIrradianceOnLoad Prefilters HDR texture to allow use of this texture for irradiance lighting. + * @param prefilterUsingCdf Defines if the prefiltering should be done using a CDF instead of the default approach. */ constructor( url: string, @@ -128,7 +130,8 @@ export class HDRCubeTexture extends BaseTexture { onLoad: Nullable<() => void> = null, onError: Nullable<(message?: string, exception?: any) => void> = null, supersample = false, - prefilterIrradianceOnLoad = false + prefilterIrradianceOnLoad = false, + prefilterUsingCdf = false ) { super(sceneOrEngine); @@ -144,6 +147,7 @@ export class HDRCubeTexture extends BaseTexture { this._textureMatrix = Matrix.Identity(); this._prefilterOnLoad = prefilterOnLoad; this._prefilterIrradianceOnLoad = prefilterIrradianceOnLoad; + this._prefilterUsingCdf = prefilterUsingCdf; this._onLoad = () => { this.onLoadObservable.notifyObservers(this); if (onLoad) { @@ -286,7 +290,7 @@ export class HDRCubeTexture extends BaseTexture { let irradiancePromise: Promise> = Promise.resolve(null); let radiancePromise: Promise = Promise.resolve(); if (this._prefilterIrradianceOnLoad) { - const hdrIrradianceFiltering = new HDRIrradianceFiltering(engine, { useCdf: true }); + const hdrIrradianceFiltering = new HDRIrradianceFiltering(engine, { useCdf: this._prefilterUsingCdf }); irradiancePromise = hdrIrradianceFiltering.prefilter(this); } if (this._prefilterOnLoad) { diff --git a/packages/tools/tests/test/visualization/ReferenceImages/prefilteringIrradiance.png b/packages/tools/tests/test/visualization/ReferenceImages/prefilteringIrradiance.png new file mode 100644 index 0000000000000000000000000000000000000000..741dbaf4ea58544dff82e60859e4ea95453c9c27 GIT binary patch literal 12240 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 z�^N65A*j$LXjazA7PSwz{Hihf?RKSG{8Ds*$H;e;q$*b2HRzJzq9uSCb8KI2_hhw3Du>uCvgi zjZV&s5PKsCb6s-$UXHfu!+EC@JV-Q4O6q-e^Je+9jp&2}>(q)YLNP{m$UwKmM^Pi>VHbykKr22{sR@SH{4(8!eS{~`5+M6e)$u9}|G)M?fN2AUJy zT4gW;e&P-BN?`5zJU4^~9@qgs7siXS%4Wmnu`zh{Yl z=G*FsQg&wvt0_9@$Y`B`w(wONT@`S=Y}39B!=1r4q_yV;Y3Syv6knI%G@AuM&APx+ z^d6v7VpjSJqu9o5Mv&wlJ8o1x(IROzPx(*{Yg-i+dymNL924;h@}Ypk<-K-)O)diQ`f51YQSppEzY+ZO*f4X&CHpSrd$ z9y%WwNWjBZDcR-;u%T6ZF5v0qYzkbeFC#`Sio-%DxOiA!G<;5{@sXHn>=nH$U4V>t z9k`z-2CH!;AJ6miSTxBLQ|W%iDny}t4K;EX3au~-v$xVBi)mt&2V@azcJ>vB&rYT| zVH8M80sTWPvMpEdD@aB5w?utkP1;(&I#(X@o6;)JtuhE*4{vv*^r0J@Ba1cfQNH}D z%fWbODUYIol3MunwrPRB>VA~Q@t3feUhI?4G{d|)zV-jAyWz z8VQXPfalOe6>E|Tc{U*)+GNz{+GONo!Ho9sIc2%RQ%i`)zEY+{E+cTaKC`iAF@9da zxY!D~x1nyG?w$}1xDU-Q9^Fg`|0dcTGQedui`l!&w62JmJ!YWTL5egJ5?HGLp-e_b zB;odo7$9ZwW+fsa6`w;KHhDX1I|}*^0bw#N`ksWpYy>u}vieJbrNYOh=2oRBpa=S{nm;!cZJq%90f^an}M;d0Uo;s@+h-dqN(%_p3~y)?QLWsLCmQbuNuvT1K46b`u9HyoV<}=iX$qb<5OqeW4;YLZXNq# z09u<*<#>3h3$_a{Pk%XNS2?7qMvq3HT0@8FkaE7)er?X0SzJU~G ztO`bfP5Z#MZDw{}W$r%q(Ce&6Y;KJzSB zaFRh%%}0dEO=tX8o=x!oA#+US-_+u&_k1MePQbD$i&*SVU z`%w|RBB?0yMEhIn_J(`FAFEGxZmg(jyip%a05RljQgI9|GLFvTmUI=&GlpuWv?s9f zM=g%KoBtHaMUh!u-h={6K^?fWJw~x0s;w(y<8xo!NUl2gRCA_DA>bGUaJ(Fa(?^2MajIN;jRDR}_0VnS+fBGV zC<=(V1miMSQAi~hKP0IpeVNGL{^s+j5+$+&%T{;NxBl+cbuD)Eip{HOlQZqF!-moJ z;md4Fn*A6~1~_m7`e(61`VV{{XcT=HdtcB;K{1~50V>T%eM8MiJ2==Oa6d{Lj``s7 ntr4jIcljR%|9?DqqwL@+4LG7OZ-u=S04F1 Date: Fri, 20 Dec 2024 09:35:04 -0800 Subject: [PATCH 08/11] Shader compile fixes --- .../Shaders/ShadersInclude/pbrBlockReflection.fx | 14 +++++++++----- .../ShadersInclude/pbrBlockReflection.fx | 14 +++++++++----- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockReflection.fx b/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockReflection.fx index d60fd521635..091144754c3 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockReflection.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockReflection.fx @@ -278,10 +278,6 @@ #ifdef INVERTCUBICMAP irradianceVector.y *= -1.0; #endif - - #ifdef SS_TRANSLUCENCY - outParams.irradianceVector = irradianceVector; - #endif #endif #ifdef USESPHERICALFROMREFLECTIONMAP #if defined(NORMAL) && defined(USESPHERICALINVERTEX) @@ -296,9 +292,17 @@ #else environmentIrradiance = computeEnvironmentIrradiance(irradianceVector); #endif + + #ifdef SS_TRANSLUCENCY + outParams.irradianceVector = irradianceVector; + #endif #endif #elif defined(USEIRRADIANCEMAP) - vec4 environmentIrradiance4 = sampleReflection(irradianceSampler, irradianceVector); + #ifdef REFLECTIONMAP_3D + vec4 environmentIrradiance4 = sampleReflection(irradianceSampler, irradianceVector); + #else + vec4 environmentIrradiance4 = sampleReflection(irradianceSampler, reflectionCoords); + #endif environmentIrradiance = environmentIrradiance4.rgb; #ifdef RGBDREFLECTION environmentIrradiance.rgb = fromRGBD(environmentIrradiance4); diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockReflection.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockReflection.fx index e769de6ef30..56c2b0f6d71 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockReflection.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockReflection.fx @@ -307,10 +307,6 @@ #ifdef INVERTCUBICMAP irradianceVector.y *= -1.0; #endif - - #ifdef SS_TRANSLUCENCY - outParams.irradianceVector = irradianceVector; - #endif #endif #ifdef USESPHERICALFROMREFLECTIONMAP #if defined(NORMAL) && defined(USESPHERICALINVERTEX) @@ -326,9 +322,17 @@ #else environmentIrradiance = computeEnvironmentIrradiance(irradianceVector); #endif + + #ifdef SS_TRANSLUCENCY + outParams.irradianceVector = irradianceVector; + #endif #endif #elif defined(USEIRRADIANCEMAP) - var environmentIrradiance4: vec4f = textureSample(irradianceSampler, irradianceSamplerSampler, irradianceVector); + #ifdef REFLECTIONMAP_3D + var environmentIrradiance4: vec4f = textureSample(irradianceSampler, irradianceSamplerSampler, irradianceVector); + #else + var environmentIrradiance4: vec4f = textureSample(irradianceSampler, irradianceSamplerSampler, reflectionCoords); + #endif environmentIrradiance = environmentIrradiance4.rgb; #ifdef RGBDREFLECTION environmentIrradiance = fromRGBD(environmentIrradiance4); From cc028048e16862b3e0f6212f7fadfd4581e7aab3 Mon Sep 17 00:00:00 2001 From: Mike Bond Date: Wed, 8 Jan 2025 12:04:32 -0800 Subject: [PATCH 09/11] Address PR comments --- .../dev/core/src/Rendering/iblCdfGenerator.ts | 3 ++- .../ShadersInclude/pbrBlockReflection.fx | 20 +++++++------------ .../ShadersInclude/pbrBlockReflection.fx | 18 ++++++----------- 3 files changed, 15 insertions(+), 26 deletions(-) diff --git a/packages/dev/core/src/Rendering/iblCdfGenerator.ts b/packages/dev/core/src/Rendering/iblCdfGenerator.ts index 607261e0fc7..30e51381b31 100644 --- a/packages/dev/core/src/Rendering/iblCdfGenerator.ts +++ b/packages/dev/core/src/Rendering/iblCdfGenerator.ts @@ -180,7 +180,8 @@ export class IblCdfGenerator { 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. + // 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)); } diff --git a/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockReflection.fx b/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockReflection.fx index 091144754c3..ba1ecbee4d7 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockReflection.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockReflection.fx @@ -181,10 +181,8 @@ #if defined(NORMAL) && defined(USESPHERICALINVERTEX) , in vec3 vEnvironmentIrradiance #endif - #if defined(USESPHERICALFROMREFLECTIONMAP) || defined(USEIRRADIANCEMAP) - #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX) - , in mat4 reflectionMatrix - #endif + #if (defined(USESPHERICALFROMREFLECTIONMAP) && (!defined(NORMAL) || !defined(USESPHERICALINVERTEX))) || (defined(USEIRRADIANCEMAP) && defined(REFLECTIONMAP_3D)) + , in mat4 reflectionMatrix #endif #ifdef USEIRRADIANCEMAP #ifdef REFLECTIONMAP_3D @@ -260,17 +258,13 @@ // _____________________________ Irradiance ________________________________ vec3 environmentIrradiance = vec3(0., 0., 0.); - #if defined(USESPHERICALFROMREFLECTIONMAP) || defined(USEIRRADIANCEMAP) - #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX) - #ifdef ANISOTROPIC - vec3 irradianceVector = vec3(reflectionMatrix * vec4(anisotropicOut.anisotropicNormal, 0)).xyz; - #else - vec3 irradianceVector = vec3(reflectionMatrix * vec4(normalW, 0)).xyz; - #endif + #if (defined(USESPHERICALFROMREFLECTIONMAP) && (!defined(NORMAL) || !defined(USESPHERICALINVERTEX))) || (defined(USEIRRADIANCEMAP) && defined(REFLECTIONMAP_3D)) + #ifdef ANISOTROPIC + vec3 irradianceVector = vec3(reflectionMatrix * vec4(anisotropicOut.anisotropicNormal, 0)).xyz; #else - vec3 irradianceVector = normalW; + vec3 irradianceVector = vec3(reflectionMatrix * vec4(normalW, 0)).xyz; #endif - + #ifdef REFLECTIONMAP_OPPOSITEZ irradianceVector.z *= -1.0; #endif diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockReflection.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockReflection.fx index 56c2b0f6d71..1d254e8f82e 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockReflection.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockReflection.fx @@ -201,10 +201,8 @@ #if defined(NORMAL) && defined(USESPHERICALINVERTEX) , vEnvironmentIrradiance: vec3f #endif - #if defined(USESPHERICALFROMREFLECTIONMAP) || defined(USEIRRADIANCEMAP) - #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX) - , reflectionMatrix: mat4x4f - #endif + #if (defined(USESPHERICALFROMREFLECTIONMAP) && (!defined(NORMAL) || !defined(USESPHERICALINVERTEX))) || (defined(USEIRRADIANCEMAP) && defined(REFLECTIONMAP_3D)) + , reflectionMatrix: mat4x4f #endif #ifdef USEIRRADIANCEMAP #ifdef REFLECTIONMAP_3D @@ -289,15 +287,11 @@ // _____________________________ Irradiance ________________________________ var environmentIrradiance: vec3f = vec3f(0., 0., 0.); - #if defined(USESPHERICALFROMREFLECTIONMAP) || defined(USEIRRADIANCEMAP) - #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX) - #ifdef ANISOTROPIC - var irradianceVector: vec3f = (reflectionMatrix * vec4f(anisotropicOut.anisotropicNormal, 0)).xyz; - #else - var irradianceVector: vec3f = (reflectionMatrix * vec4f(normalW, 0)).xyz; - #endif + #if (defined(USESPHERICALFROMREFLECTIONMAP) && (!defined(NORMAL) || !defined(USESPHERICALINVERTEX))) || (defined(USEIRRADIANCEMAP) && defined(REFLECTIONMAP_3D)) + #ifdef ANISOTROPIC + var irradianceVector: vec3f = (reflectionMatrix * vec4f(anisotropicOut.anisotropicNormal, 0)).xyz; #else - var irradianceVector: vec3f = normalW; + var irradianceVector: vec3f = (reflectionMatrix * vec4f(normalW, 0)).xyz; #endif #ifdef REFLECTIONMAP_OPPOSITEZ From ce48119d5261c7137a7a8936019fb31960a7fd75 Mon Sep 17 00:00:00 2001 From: Mike Bond Date: Wed, 8 Jan 2025 13:27:39 -0800 Subject: [PATCH 10/11] Fix shader compile errors --- packages/dev/core/src/Shaders/pbr.fragment.fx | 6 ++---- packages/dev/core/src/ShadersWGSL/pbr.fragment.fx | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/dev/core/src/Shaders/pbr.fragment.fx b/packages/dev/core/src/Shaders/pbr.fragment.fx index 396eb85f81a..a2daa5c1479 100644 --- a/packages/dev/core/src/Shaders/pbr.fragment.fx +++ b/packages/dev/core/src/Shaders/pbr.fragment.fx @@ -286,10 +286,8 @@ void main(void) { #if defined(NORMAL) && defined(USESPHERICALINVERTEX) , vEnvironmentIrradiance #endif - #if defined(USESPHERICALFROMREFLECTIONMAP) || defined(USEIRRADIANCEMAP) - #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX) - , reflectionMatrix - #endif + #if (defined(USESPHERICALFROMREFLECTIONMAP) && (!defined(NORMAL) || !defined(USESPHERICALINVERTEX))) || (defined(USEIRRADIANCEMAP) && defined(REFLECTIONMAP_3D)) + , reflectionMatrix #endif #ifdef USEIRRADIANCEMAP , irradianceSampler diff --git a/packages/dev/core/src/ShadersWGSL/pbr.fragment.fx b/packages/dev/core/src/ShadersWGSL/pbr.fragment.fx index b8a31cabb24..b5d109e0945 100644 --- a/packages/dev/core/src/ShadersWGSL/pbr.fragment.fx +++ b/packages/dev/core/src/ShadersWGSL/pbr.fragment.fx @@ -272,10 +272,8 @@ fn main(input: FragmentInputs) -> FragmentOutputs { #if defined(NORMAL) && defined(USESPHERICALINVERTEX) , fragmentInputs.vEnvironmentIrradiance #endif - #if defined(USESPHERICALFROMREFLECTIONMAP) || defined(USEIRRADIANCEMAP) - #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX) - , uniforms.reflectionMatrix - #endif + #if (defined(USESPHERICALFROMREFLECTIONMAP) && (!defined(NORMAL) || !defined(USESPHERICALINVERTEX))) || (defined(USEIRRADIANCEMAP) && defined(REFLECTIONMAP_3D)) + , uniforms.reflectionMatrix #endif #ifdef USEIRRADIANCEMAP , irradianceSampler From 5d574c95e992e1e58f8b733e239259dd888f855c Mon Sep 17 00:00:00 2001 From: Michael Bond Date: Sat, 11 Jan 2025 10:26:32 -0800 Subject: [PATCH 11/11] Address PR comments --- .../dev/core/src/Materials/Node/Blocks/PBR/reflectionBlock.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/dev/core/src/Materials/Node/Blocks/PBR/reflectionBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/PBR/reflectionBlock.ts index c142f9c43ca..3a79a31c990 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/PBR/reflectionBlock.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/PBR/reflectionBlock.ts @@ -432,10 +432,8 @@ export class ReflectionBlock extends ReflectionTextureBaseBlock { #if defined(NORMAL) && defined(USESPHERICALINVERTEX) , ${isWebGPU ? "input." : ""}${this._vEnvironmentIrradianceName} #endif - #if defined(USESPHERICALFROMREFLECTIONMAP) || defined(USEIRRADIANCEMAP) - #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX) + #if (defined(USESPHERICALFROMREFLECTIONMAP) && (!defined(NORMAL) || !defined(USESPHERICALINVERTEX))) || (defined(USEIRRADIANCEMAP) && defined(REFLECTIONMAP_3D)) , ${this._reflectionMatrixName} - #endif #endif #ifdef USEIRRADIANCEMAP , irradianceSampler // ** not handled **