diff --git a/examples/src/examples/graphics/clustered-omni-shadows.controls.mjs b/examples/src/examples/graphics/clustered-omni-shadows.controls.mjs index febdc1d3173..de3381f9921 100644 --- a/examples/src/examples/graphics/clustered-omni-shadows.controls.mjs +++ b/examples/src/examples/graphics/clustered-omni-shadows.controls.mjs @@ -19,7 +19,10 @@ export const controls = ({ observer, ReactPCUI, React, jsx, fragment }) => { options: [ { v: pc.SHADOW_PCF1, t: 'PCF1' }, { v: pc.SHADOW_PCF3, t: 'PCF3' }, - { v: pc.SHADOW_PCF5, t: 'PCF5' } + { v: pc.SHADOW_PCF5, t: 'PCF5' }, + { v: pc.SHADOW_PCF1_FLOAT16, t: 'PCF1_FLOAT16' }, + { v: pc.SHADOW_PCF3_FLOAT16, t: 'PCF3_FLOAT16' }, + { v: pc.SHADOW_PCF5_FLOAT16, t: 'PCF5_FLOAT16' } ] }) ), diff --git a/examples/src/examples/graphics/clustered-spot-shadows.controls.mjs b/examples/src/examples/graphics/clustered-spot-shadows.controls.mjs index 91d9350df1f..be031d12ee9 100644 --- a/examples/src/examples/graphics/clustered-spot-shadows.controls.mjs +++ b/examples/src/examples/graphics/clustered-spot-shadows.controls.mjs @@ -46,7 +46,10 @@ export const controls = ({ observer, ReactPCUI, React, jsx, fragment }) => { options: [ { v: pc.SHADOW_PCF1, t: 'PCF1' }, { v: pc.SHADOW_PCF3, t: 'PCF3' }, - { v: pc.SHADOW_PCF5, t: 'PCF5' } + { v: pc.SHADOW_PCF5, t: 'PCF5' }, + { v: pc.SHADOW_PCF1_FLOAT16, t: 'PCF1_FLOAT16' }, + { v: pc.SHADOW_PCF3_FLOAT16, t: 'PCF3_FLOAT16' }, + { v: pc.SHADOW_PCF5_FLOAT16, t: 'PCF5_FLOAT16' } ] }) ) diff --git a/examples/src/examples/graphics/shadow-cascades.controls.mjs b/examples/src/examples/graphics/shadow-cascades.controls.mjs index 4af6a60798a..49cb5239a1d 100644 --- a/examples/src/examples/graphics/shadow-cascades.controls.mjs +++ b/examples/src/examples/graphics/shadow-cascades.controls.mjs @@ -21,6 +21,9 @@ export const controls = ({ observer, ReactPCUI, React, jsx, fragment }) => { { v: pc.SHADOW_PCF1, t: 'PCF1' }, { v: pc.SHADOW_PCF3, t: 'PCF3' }, { v: pc.SHADOW_PCF5, t: 'PCF5' }, + { v: pc.SHADOW_PCF1_FLOAT16, t: 'PCF1_FLOAT16' }, + { v: pc.SHADOW_PCF3_FLOAT16, t: 'PCF3_FLOAT16' }, + { v: pc.SHADOW_PCF5_FLOAT16, t: 'PCF5_FLOAT16' }, { v: pc.SHADOW_VSM8, t: 'VSM8' }, { v: pc.SHADOW_VSM16, t: 'VSM16' }, { v: pc.SHADOW_VSM32, t: 'VSM32' } diff --git a/src/framework/components/light/component.js b/src/framework/components/light/component.js index 6ff3c0897d7..62e021b350e 100644 --- a/src/framework/components/light/component.js +++ b/src/framework/components/light/component.js @@ -547,16 +547,16 @@ class LightComponent extends Component { /** * Sets the type of shadows being rendered by this light. Can be: * - * - {@link SHADOW_PCF3}: Render depth, can be used for PCF 3x3 sampling. - * - {@link SHADOW_VSM8}: Render packed variance shadow map. All shadow receivers must also cast - * shadows for this mode to work correctly. - * - {@link SHADOW_VSM16}: Render 16-bit exponential variance shadow map. Requires - * OES_texture_half_float extension. Falls back to {@link SHADOW_VSM8}, if not supported. - * - {@link SHADOW_VSM32}: Render 32-bit exponential variance shadow map. Requires - * OES_texture_float extension. Falls back to {@link SHADOW_VSM16}, if not supported. - * - {@link SHADOW_PCF5}: Render depth buffer only, can be used for hardware-accelerated PCF 5x5 - * sampling. - * - {@link SHADOW_PCSS}: Render depth as color, and use the software sampled PCSS method for shadows. + * - {@link SHADOW_PCF1} + * - {@link SHADOW_PCF3} + * - {@link SHADOW_PCF5} + * - {@link SHADOW_PCF1_FLOAT16} + * - {@link SHADOW_PCF3_FLOAT16} + * - {@link SHADOW_PCF5_FLOAT16} + * - {@link SHADOW_VSM8} + * - {@link SHADOW_VSM16} + * - {@link SHADOW_VSM32} + * - {@link SHADOW_PCSS} * * @type {number} */ diff --git a/src/platform/graphics/constants.js b/src/platform/graphics/constants.js index 6dc26b02449..5ad3432c578 100644 --- a/src/platform/graphics/constants.js +++ b/src/platform/graphics/constants.js @@ -1069,6 +1069,14 @@ export const PIXELFORMAT_BC7 = 67; */ export const PIXELFORMAT_BC7_SRGBA = 68; +/** + * A 16-bit depth buffer format. + * + * @type {number} + * @category Graphics + */ +export const PIXELFORMAT_DEPTH16 = 69; + /** * Information about pixel formats. * @@ -1100,12 +1108,13 @@ export const pixelFormatInfo = new Map([ [PIXELFORMAT_RGBA32F, { name: 'RGBA32F', size: 16 }], [PIXELFORMAT_R32F, { name: 'R32F', size: 4 }], [PIXELFORMAT_DEPTH, { name: 'DEPTH', size: 4 }], + [PIXELFORMAT_DEPTH16, { name: 'DEPTH16', size: 2 }], [PIXELFORMAT_DEPTHSTENCIL, { name: 'DEPTHSTENCIL', size: 4 }], [PIXELFORMAT_111110F, { name: '111110F', size: 4 }], [PIXELFORMAT_SRGB8, { name: 'SRGB8', size: 4, ldr: true, srgb: true }], [PIXELFORMAT_SRGBA8, { name: 'SRGBA8', size: 4, ldr: true, srgb: true }], [PIXELFORMAT_BGRA8, { name: 'BGRA8', size: 4, ldr: true }], - [PIXELFORMAT_SBGRA8, { name: 'SBGRA8', size: 4, ldr: true, srgb: true }], + [PIXELFORMAT_SBGRA8, { name: 'SBGRA8', size: 4, ldr: true, srgb: true }], // compressed formats [PIXELFORMAT_DXT1, { name: 'DXT1', blockSize: 8, ldr: true, srgbFormat: PIXELFORMAT_DXT1_SRGB }], diff --git a/src/platform/graphics/render-target.js b/src/platform/graphics/render-target.js index 8d52f50cc8f..0a4cfbb3cd6 100644 --- a/src/platform/graphics/render-target.js +++ b/src/platform/graphics/render-target.js @@ -1,6 +1,6 @@ import { Debug } from '../../core/debug.js'; import { TRACEID_RENDER_TARGET_ALLOC } from '../../core/constants.js'; -import { PIXELFORMAT_DEPTH, PIXELFORMAT_DEPTHSTENCIL, PIXELFORMAT_R32F, isSrgbPixelFormat } from './constants.js'; +import { PIXELFORMAT_DEPTH, PIXELFORMAT_DEPTH16, PIXELFORMAT_DEPTHSTENCIL, PIXELFORMAT_R32F, isSrgbPixelFormat } from './constants.js'; import { DebugGraphics } from './debug-graphics.js'; import { GraphicsDevice } from './graphics-device.js'; import { TextureUtils } from './texture-utils.js'; @@ -191,7 +191,7 @@ class RenderTarget { if (this._depthBuffer) { const format = this._depthBuffer._format; - if (format === PIXELFORMAT_DEPTH) { + if (format === PIXELFORMAT_DEPTH || format === PIXELFORMAT_DEPTH16) { this._depth = true; this._stencil = false; } else if (format === PIXELFORMAT_DEPTHSTENCIL) { diff --git a/src/platform/graphics/webgl/webgl-texture.js b/src/platform/graphics/webgl/webgl-texture.js index 027112dc187..391a69fa3cf 100644 --- a/src/platform/graphics/webgl/webgl-texture.js +++ b/src/platform/graphics/webgl/webgl-texture.js @@ -13,7 +13,8 @@ import { PIXELFORMAT_DXT1_SRGB, PIXELFORMAT_DXT3_SRGBA, PIXELFORMAT_DXT5_SRGBA, PIXELFORMAT_PVRTC_2BPP_SRGB_1, PIXELFORMAT_PVRTC_2BPP_SRGBA_1, PIXELFORMAT_PVRTC_4BPP_SRGB_1, PIXELFORMAT_PVRTC_4BPP_SRGBA_1, PIXELFORMAT_ETC2_SRGB, PIXELFORMAT_ETC2_SRGBA, PIXELFORMAT_ASTC_4x4_SRGB, PIXELFORMAT_SBGRA8, - PIXELFORMAT_BC6F, PIXELFORMAT_BC6UF, PIXELFORMAT_BC7, PIXELFORMAT_BC7_SRGBA + PIXELFORMAT_BC6F, PIXELFORMAT_BC6UF, PIXELFORMAT_BC7, PIXELFORMAT_BC7_SRGBA, + PIXELFORMAT_DEPTH16 } from '../constants.js'; /** @@ -323,9 +324,14 @@ class WebglTexture { break; case PIXELFORMAT_DEPTH: this._glFormat = gl.DEPTH_COMPONENT; - this._glInternalFormat = gl.DEPTH_COMPONENT32F; // should allow 16/24 bits? + this._glInternalFormat = gl.DEPTH_COMPONENT32F; this._glPixelType = gl.FLOAT; break; + case PIXELFORMAT_DEPTH16: + this._glFormat = gl.DEPTH_COMPONENT; + this._glInternalFormat = gl.DEPTH_COMPONENT16; + this._glPixelType = gl.UNSIGNED_SHORT; + break; case PIXELFORMAT_DEPTHSTENCIL: this._glFormat = gl.DEPTH_STENCIL; this._glInternalFormat = gl.DEPTH24_STENCIL8; diff --git a/src/platform/graphics/webgpu/constants.js b/src/platform/graphics/webgpu/constants.js index 9cf254dd782..faaef6b9341 100644 --- a/src/platform/graphics/webgpu/constants.js +++ b/src/platform/graphics/webgpu/constants.js @@ -13,7 +13,8 @@ import { PIXELFORMAT_PVRTC_2BPP_SRGBA_1, PIXELFORMAT_PVRTC_4BPP_SRGB_1, PIXELFORMAT_PVRTC_4BPP_SRGBA_1, PIXELFORMAT_ETC2_SRGB, PIXELFORMAT_ETC2_SRGBA, PIXELFORMAT_SBGRA8, PIXELFORMAT_BC6F, PIXELFORMAT_BC6UF, PIXELFORMAT_BC7, PIXELFORMAT_BC7_SRGBA, - PIXELFORMAT_ASTC_4x4_SRGB + PIXELFORMAT_ASTC_4x4_SRGB, + PIXELFORMAT_DEPTH16 } from '../constants.js'; // map of PIXELFORMAT_*** to GPUTextureFormat @@ -39,6 +40,7 @@ gpuTextureFormats[PIXELFORMAT_RGB32F] = ''; gpuTextureFormats[PIXELFORMAT_RGBA32F] = 'rgba32float'; gpuTextureFormats[PIXELFORMAT_R32F] = 'r32float'; gpuTextureFormats[PIXELFORMAT_DEPTH] = 'depth32float'; +gpuTextureFormats[PIXELFORMAT_DEPTH16] = 'depth16unorm'; gpuTextureFormats[PIXELFORMAT_DEPTHSTENCIL] = 'depth24plus-stencil8'; gpuTextureFormats[PIXELFORMAT_111110F] = 'rg11b10ufloat'; gpuTextureFormats[PIXELFORMAT_SRGB8] = ''; diff --git a/src/scene/constants.js b/src/scene/constants.js index 751dcab2e9e..53e41d37f66 100644 --- a/src/scene/constants.js +++ b/src/scene/constants.js @@ -1,3 +1,5 @@ +import { PIXELFORMAT_DEPTH, PIXELFORMAT_DEPTH16, PIXELFORMAT_R32F, PIXELFORMAT_RGBA16F, PIXELFORMAT_RGBA32F, PIXELFORMAT_RGBA8 } from '../platform/graphics/constants.js'; + /** * Subtract the color of the source fragment from the destination fragment and write the result to * the frame buffer. @@ -273,7 +275,8 @@ export const LIGHTFALLOFF_LINEAR = 0; export const LIGHTFALLOFF_INVERSESQUARED = 1; /** - * Render depth buffer only, can be used for PCF 3x3 sampling. + * A shadow sampling technique using 32bit shadow map that averages depth comparisons from a 3x3 + * grid of texels for softened shadow edges. * * @type {number} * @category Graphics @@ -282,8 +285,9 @@ export const SHADOW_PCF3 = 0; export const SHADOW_DEPTH = 0; // alias for SHADOW_PCF3 for backwards compatibility /** - * Render packed variance shadow map. All shadow receivers must also cast shadows for this mode to - * work correctly. + * A shadow sampling technique using a 16-bit exponential variance shadow map packed into + * {@link PIXELFORMAT_RGBA8} that leverages variance to approximate shadow boundaries, enabling soft + * shadows. All shadow receivers must also cast shadows for this mode to work correctly. * * @type {number} * @category Graphics @@ -291,8 +295,10 @@ export const SHADOW_DEPTH = 0; // alias for SHADOW_PCF3 for backwards compatibil export const SHADOW_VSM8 = 1; /** - * Render 16-bit exponential variance shadow map. Requires OES_texture_half_float extension. Falls - * back to {@link SHADOW_VSM8}, if not supported. + * A shadow sampling technique using a 16-bit exponential variance shadow map that leverages + * variance to approximate shadow boundaries, enabling soft shadows. Only supported when + * {@link GraphicsDevice#textureHalfFloatRenderable} is true. Falls back to {@link SHADOW_VSM8}, if not + * supported. * * @type {number} * @category Graphics @@ -300,8 +306,10 @@ export const SHADOW_VSM8 = 1; export const SHADOW_VSM16 = 2; /** - * Render 32-bit exponential variance shadow map. Requires OES_texture_float extension. Falls back - * to {@link SHADOW_VSM16}, if not supported. + * A shadow sampling technique using a 32-bit exponential variance shadow map that leverages + * variance to approximate shadow boundaries, enabling soft shadows. Only supported when + * {@link GraphicsDevice#textureFloatRenderable} is true. Falls back to {@link SHADOW_VSM16}, if not + * supported. * * @type {number} * @category Graphics @@ -309,7 +317,8 @@ export const SHADOW_VSM16 = 2; export const SHADOW_VSM32 = 3; /** - * Render depth buffer only, can be used for hardware-accelerated PCF 5x5 sampling. + * A shadow sampling technique using 32bit shadow map that averages depth comparisons from a 5x5 + * grid of texels for softened shadow edges. * * @type {number} * @category Graphics @@ -317,7 +326,8 @@ export const SHADOW_VSM32 = 3; export const SHADOW_PCF5 = 4; /** - * Render depth buffer only, can be used for PCF 1x1 sampling. + * A shadow sampling technique using a 32-bit shadow map that performs a single depth comparison for + * sharp shadow edges. * * @type {number} * @category Graphics @@ -325,7 +335,8 @@ export const SHADOW_PCF5 = 4; export const SHADOW_PCF1 = 5; /** - * Render depth as color for PCSS software filtering. + * A shadow sampling technique using a 32-bit shadow map that adjusts filter size based on blocker + * distance, producing realistic, soft shadow edges that vary with the light's occlusion. * * @type {number} * @category Graphics @@ -333,20 +344,50 @@ export const SHADOW_PCF1 = 5; export const SHADOW_PCSS = 6; /** - * map of engine SHADOW__*** to a string representation + * A shadow sampling technique using a 16-bit shadow map that performs a single depth comparison for + * sharp shadow edges. * - * @type {object} - * @ignore + * @type {number} + * @category Graphics + */ +export const SHADOW_PCF1_FLOAT16 = 7; + +/** + * A shadow sampling technique using 16-bit shadow map that averages depth comparisons from a 3x3 + * grid of texels for softened shadow edges. + * + * @type {number} + * @category Graphics + */ +export const SHADOW_PCF3_FLOAT16 = 8; + +/** + * A shadow sampling technique using 16-bit shadow map that averages depth comparisons from a 3x3 + * grid of texels for softened shadow edges. + * + * @type {number} * @category Graphics */ -export const shadowTypeToString = {}; -shadowTypeToString[SHADOW_PCF3] = 'PCF3'; -shadowTypeToString[SHADOW_VSM8] = 'VSM8'; -shadowTypeToString[SHADOW_VSM16] = 'VSM16'; -shadowTypeToString[SHADOW_VSM32] = 'VSM32'; -shadowTypeToString[SHADOW_PCF5] = 'PCF5'; -shadowTypeToString[SHADOW_PCF1] = 'PCF1'; -shadowTypeToString[SHADOW_PCSS] = 'PCSS'; +export const SHADOW_PCF5_FLOAT16 = 9; + +/** + * Information about shadow types. + * + * @type {Map} + * @ignore + */ +export const shadowTypeInfo = new Map([ + [SHADOW_PCF1, { name: 'PCF1', format: PIXELFORMAT_DEPTH, pcf: true }], + [SHADOW_PCF3, { name: 'PCF3', format: PIXELFORMAT_DEPTH, pcf: true }], + [SHADOW_PCF5, { name: 'PCF5', format: PIXELFORMAT_DEPTH, pcf: true }], + [SHADOW_PCF1_FLOAT16, { name: 'PCF1_FLOAT16', format: PIXELFORMAT_DEPTH16, pcf: true }], + [SHADOW_PCF3_FLOAT16, { name: 'PCF3_FLOAT16', format: PIXELFORMAT_DEPTH16, pcf: true }], + [SHADOW_PCF5_FLOAT16, { name: 'PCF5_FLOAT16', format: PIXELFORMAT_DEPTH16, pcf: true }], + [SHADOW_VSM8, { name: 'VSM8', format: PIXELFORMAT_RGBA8, vsm: true }], + [SHADOW_VSM16, { name: 'VSM16', format: PIXELFORMAT_RGBA16F, vsm: true }], + [SHADOW_VSM32, { name: 'VSM32', format: PIXELFORMAT_RGBA32F, vsm: true }], + [SHADOW_PCSS, { name: 'PCSS', format: PIXELFORMAT_R32F }] +]); /** * Box filter. diff --git a/src/scene/light.js b/src/scene/light.js index d089fb039bc..f727f1fe822 100644 --- a/src/scene/light.js +++ b/src/scene/light.js @@ -8,9 +8,12 @@ import { BLUR_GAUSSIAN, LIGHTTYPE_DIRECTIONAL, LIGHTTYPE_OMNI, LIGHTTYPE_SPOT, MASK_BAKE, MASK_AFFECT_DYNAMIC, - SHADOW_PCF1, SHADOW_PCF3, SHADOW_PCF5, SHADOW_VSM8, SHADOW_VSM16, SHADOW_VSM32, SHADOW_PCSS, + SHADOW_PCF1, SHADOW_PCF3, SHADOW_VSM8, SHADOW_VSM16, SHADOW_VSM32, SHADOW_PCSS, SHADOWUPDATE_NONE, SHADOWUPDATE_REALTIME, SHADOWUPDATE_THISFRAME, - LIGHTSHAPE_PUNCTUAL, LIGHTFALLOFF_LINEAR + LIGHTSHAPE_PUNCTUAL, LIGHTFALLOFF_LINEAR, + shadowTypeInfo, + SHADOW_PCF1_FLOAT16, + SHADOW_PCF3_FLOAT16 } from './constants.js'; import { ShadowRenderer } from './renderer/shadow-renderer.js'; import { DepthState } from '../platform/graphics/depth-state.js'; @@ -50,7 +53,7 @@ let id = 0; * Class storing shadow rendering related private information */ class LightRenderData { - constructor(device, camera, face, light) { + constructor(camera, face, light) { // light this data belongs to this.light = light; @@ -62,7 +65,7 @@ class LightRenderData { this.camera = camera; // camera used to cull / render the shadow map - this.shadowCamera = ShadowRenderer.createShadowCamera(device, light._shadowType, light._type, face); + this.shadowCamera = ShadowRenderer.createShadowCamera(light._shadowType, light._type, face); // shadow view-projection matrix this.shadowMatrix = new Mat4(); @@ -386,7 +389,8 @@ class Light { const device = this.device; // omni light supports PCF1, PCF3 and PCSS only - if (this._type === LIGHTTYPE_OMNI && value !== SHADOW_PCF1 && value !== SHADOW_PCF3 && value !== SHADOW_PCSS) { + if (this._type === LIGHTTYPE_OMNI && value !== SHADOW_PCF1 && value !== SHADOW_PCF3 && + value !== SHADOW_PCF1_FLOAT16 && value !== SHADOW_PCF3_FLOAT16 && value !== SHADOW_PCSS) { value = SHADOW_PCF3; } @@ -400,8 +404,9 @@ class Light { value = SHADOW_VSM8; } - this._isVsm = value === SHADOW_VSM8 || value === SHADOW_VSM16 || value === SHADOW_VSM32; - this._isPcf = value === SHADOW_PCF1 || value === SHADOW_PCF3 || value === SHADOW_PCF5; + const shadowInfo = shadowTypeInfo.get(value); + this._isVsm = shadowInfo?.vsm ?? false; + this._isPcf = shadowInfo?.pcf ?? false; this._shadowType = value; this._destroyShadowMap(); @@ -724,7 +729,7 @@ class Light { } // create new one - const rd = new LightRenderData(this.device, camera, face, this); + const rd = new LightRenderData(camera, face, this); this._renderData.push(rd); return rd; } @@ -963,8 +968,7 @@ class Light { // Bit // 31 : sign bit (leave) // 29 - 30 : type - // 28 : cast shadows - // 25 - 27 : shadow type + // 25 - 28 : shadow type // 23 - 24 : falloff mode // 22 : normal offset bias // 21 : cookie @@ -977,9 +981,9 @@ class Light { // 8 - 9 : light num cascades // 7 : disable specular // 6 - 4 : mask + // 3 : cast shadows let key = (this._type << 29) | - ((this._castShadows ? 1 : 0) << 28) | (this._shadowType << 25) | (this._falloffMode << 23) | ((this._normalOffsetBias !== 0.0 ? 1 : 0) << 22) | @@ -990,7 +994,8 @@ class Light { ((this._shape) << 10) | ((this.numCascades - 1) << 8) | ((this.affectSpecularity ? 1 : 0) << 7) | - ((this.mask) << 6); + ((this.mask) << 6) | + ((this._castShadows ? 1 : 0) << 3); if (this._cookieChannel.length === 3) { key |= (chanId[this._cookieChannel.charAt(1)] << 16); diff --git a/src/scene/lighting/light-texture-atlas.js b/src/scene/lighting/light-texture-atlas.js index c91f3f2b8de..9109723b36e 100644 --- a/src/scene/lighting/light-texture-atlas.js +++ b/src/scene/lighting/light-texture-atlas.js @@ -5,7 +5,7 @@ import { ADDRESS_CLAMP_TO_EDGE, FILTER_NEAREST, PIXELFORMAT_RGBA8 } from '../../ import { RenderTarget } from '../../platform/graphics/render-target.js'; import { Texture } from '../../platform/graphics/texture.js'; -import { LIGHTTYPE_OMNI, LIGHTTYPE_SPOT, SHADOW_PCF3 } from '../constants.js'; +import { LIGHTTYPE_OMNI, LIGHTTYPE_SPOT, SHADOW_PCF3, shadowTypeInfo } from '../constants.js'; import { ShadowMap } from '../renderer/shadow-map.js'; const _tempArray = []; @@ -98,15 +98,17 @@ class LightTextureAtlas { this.cookieRenderTarget = null; } - allocateShadowAtlas(resolution) { + allocateShadowAtlas(resolution, shadowType = SHADOW_PCF3) { - if (!this.shadowAtlas || this.shadowAtlas.texture.width !== resolution) { + const existingFormat = this.shadowAtlas?.texture.format; + const requiredFormat = shadowTypeInfo.get(shadowType).format; + if (!this.shadowAtlas || this.shadowAtlas.texture.width !== resolution || existingFormat !== requiredFormat) { // content of atlas is lost, force re-render of static shadows this.version++; this.destroyShadowAtlas(); - this.shadowAtlas = ShadowMap.createAtlas(this.device, resolution, SHADOW_PCF3); + this.shadowAtlas = ShadowMap.createAtlas(this.device, resolution, shadowType); // avoid it being destroyed by lights this.shadowAtlas.cached = true; @@ -254,7 +256,7 @@ class LightTextureAtlas { }); if (needsShadowAtlas) { - this.allocateShadowAtlas(this.shadowAtlasResolution); + this.allocateShadowAtlas(this.shadowAtlasResolution, lightingParams.shadowType); } if (needsCookieAtlas) { diff --git a/src/scene/lighting/lighting-params.js b/src/scene/lighting/lighting-params.js index 08a9a8b3e21..7c4a60acf91 100644 --- a/src/scene/lighting/lighting-params.js +++ b/src/scene/lighting/lighting-params.js @@ -154,9 +154,12 @@ class LightingParams { /** * Sets the type of shadow filtering used by all shadows. Can be: * - * - {@link SHADOW_PCF1}: PCF 1x1 sampling. - * - {@link SHADOW_PCF3}: PCF 3x3 sampling. - * - {@link SHADOW_PCF5}: PCF 5x5 sampling. + * - {@link SHADOW_PCF1} + * - {@link SHADOW_PCF3} + * - {@link SHADOW_PCF5} + * - {@link SHADOW_PCF1_FLOAT16} + * - {@link SHADOW_PCF3_FLOAT16} + * - {@link SHADOW_PCF5_FLOAT16} * * Defaults to {@link SHADOW_PCF3} * diff --git a/src/scene/renderer/shadow-map.js b/src/scene/renderer/shadow-map.js index f07f604a675..5324327446d 100644 --- a/src/scene/renderer/shadow-map.js +++ b/src/scene/renderer/shadow-map.js @@ -1,17 +1,18 @@ +import { Debug } from '../../core/debug.js'; import { ADDRESS_CLAMP_TO_EDGE, FILTER_LINEAR, FILTER_NEAREST, FUNC_LESS, - PIXELFORMAT_DEPTH, PIXELFORMAT_RGBA8, PIXELFORMAT_RGBA16F, PIXELFORMAT_RGBA32F, - TEXHINT_SHADOWMAP, - PIXELFORMAT_R32F + pixelFormatInfo, + TEXHINT_SHADOWMAP } from '../../platform/graphics/constants.js'; import { RenderTarget } from '../../platform/graphics/render-target.js'; import { Texture } from '../../platform/graphics/texture.js'; import { LIGHTTYPE_OMNI, - SHADOW_PCF1, SHADOW_PCF3, SHADOW_PCF5, SHADOW_VSM16, SHADOW_VSM32, SHADOW_PCSS + SHADOW_VSM32, SHADOW_PCSS, + shadowTypeInfo } from '../constants.js'; @@ -45,20 +46,6 @@ class ShadowMap { this.renderTargets.length = 0; } - static getShadowFormat(shadowType) { - if (shadowType === SHADOW_VSM32) { - return PIXELFORMAT_RGBA32F; - } else if (shadowType === SHADOW_VSM16) { - return PIXELFORMAT_RGBA16F; - } else if (shadowType === SHADOW_PCF1 || shadowType === SHADOW_PCF3 || shadowType === SHADOW_PCF5) { - return PIXELFORMAT_DEPTH; - } else if (shadowType === SHADOW_PCSS) { - return PIXELFORMAT_R32F; - } - - return PIXELFORMAT_RGBA8; - } - static getShadowFiltering(device, shadowType) { if (shadowType === SHADOW_VSM32) { return device.extTextureFloatLinear ? FILTER_LINEAR : FILTER_NEAREST; @@ -94,14 +81,16 @@ class ShadowMap { static create2dMap(device, size, shadowType) { - const format = this.getShadowFormat(shadowType); + const shadowInfo = shadowTypeInfo.get(shadowType); + Debug.assert(shadowInfo); const filter = this.getShadowFiltering(device, shadowType); + const formatName = pixelFormatInfo.get(shadowInfo.format)?.name; const texture = new Texture(device, { // #if _PROFILER profilerHint: TEXHINT_SHADOWMAP, // #endif - format: format, + format: shadowInfo?.format, width: size, height: size, mipmaps: false, @@ -109,11 +98,11 @@ class ShadowMap { magFilter: filter, addressU: ADDRESS_CLAMP_TO_EDGE, addressV: ADDRESS_CLAMP_TO_EDGE, - name: 'ShadowMap2D' + name: `ShadowMap2D_${formatName}` }); let target = null; - if (shadowType === SHADOW_PCF1 || shadowType === SHADOW_PCF3 || shadowType === SHADOW_PCF5) { + if (shadowInfo?.pcf) { // enable hardware PCF when sampling the depth texture texture.compareOnRead = true; @@ -131,7 +120,7 @@ class ShadowMap { }); } - // TODO: this is temporary, and will be handle on generic level for all render targets for WebGPU + // TODO: this is temporary, and will be handled on generic level for all render targets for WebGPU if (device.isWebGPU) { target.flipY = true; } @@ -141,15 +130,17 @@ class ShadowMap { static createCubemap(device, size, shadowType) { + const shadowInfo = shadowTypeInfo.get(shadowType); + Debug.assert(shadowInfo); + const formatName = pixelFormatInfo.get(shadowInfo.format)?.name; const isPcss = shadowType === SHADOW_PCSS; - const format = this.getShadowFormat(shadowType); const filter = isPcss ? FILTER_NEAREST : FILTER_LINEAR; const cubemap = new Texture(device, { // #if _PROFILER profilerHint: TEXHINT_SHADOWMAP, // #endif - format: format, + format: shadowInfo?.format, width: size, height: size, cubemap: true, @@ -158,7 +149,7 @@ class ShadowMap { magFilter: filter, addressU: ADDRESS_CLAMP_TO_EDGE, addressV: ADDRESS_CLAMP_TO_EDGE, - name: 'ShadowMapCube' + name: `ShadowMapCube_${formatName}` }); // enable hardware PCF when sampling the depth texture diff --git a/src/scene/renderer/shadow-renderer.js b/src/scene/renderer/shadow-renderer.js index 11303128fa8..160ec43002c 100644 --- a/src/scene/renderer/shadow-renderer.js +++ b/src/scene/renderer/shadow-renderer.js @@ -11,9 +11,10 @@ import { BLUR_GAUSSIAN, LIGHTTYPE_DIRECTIONAL, LIGHTTYPE_OMNI, SHADER_SHADOW, - SHADOW_PCF1, SHADOW_PCF3, SHADOW_PCF5, SHADOW_VSM8, SHADOW_VSM32, + SHADOW_VSM8, SHADOW_VSM32, SHADOWUPDATE_NONE, SHADOWUPDATE_THISFRAME, - SORTKEY_DEPTH + SORTKEY_DEPTH, + shadowTypeInfo } from '../constants.js'; import { ShaderPass } from '../shader-pass.js'; import { shaderChunks } from '../shader-lib/chunks/chunks.js'; @@ -113,7 +114,7 @@ class ShadowRenderer { } // creates shadow camera for a light and sets up its constant properties - static createShadowCamera(device, shadowType, type, face) { + static createShadowCamera(shadowType, type, face) { const shadowCam = LightCamera.create('ShadowCamera', type, face); @@ -127,8 +128,11 @@ class ShadowRenderer { shadowCam.clearDepthBuffer = true; shadowCam.clearStencilBuffer = false; + const shadowInfo = shadowTypeInfo.get(shadowType); + Debug.assert(shadowInfo); + // clear color buffer only when using it - const hwPcf = shadowType === SHADOW_PCF1 || shadowType === SHADOW_PCF3 || shadowType === SHADOW_PCF5; + const hwPcf = shadowInfo?.pcf ?? false; shadowCam.clearColorBuffer = !hwPcf; return shadowCam; @@ -468,7 +472,8 @@ class ShadowRenderer { getVsmBlurShader(isVsm8, blurMode, filterSize) { - let blurShader = (isVsm8 ? this.blurPackedVsmShader : this.blurVsmShader)[blurMode][filterSize]; + const cache = isVsm8 ? this.blurPackedVsmShader : this.blurVsmShader; + let blurShader = cache[blurMode][filterSize]; if (!blurShader) { this.blurVsmWeights[filterSize] = gaussWeights(filterSize); @@ -481,12 +486,7 @@ class ShadowRenderer { } const blurShaderName = `blurVsm${blurMode}${filterSize}${isVsm8}`; blurShader = createShaderFromCode(this.device, blurVS, blurFS, blurShaderName); - - if (isVsm8) { - this.blurPackedVsmShader[blurMode][filterSize] = blurShader; - } else { - this.blurVsmShader[blurMode][filterSize] = blurShader; - } + cache[blurMode][filterSize] = blurShader; } return blurShader; diff --git a/src/scene/shader-lib/programs/lit-shader.js b/src/scene/shader-lib/programs/lit-shader.js index f5ebb3907ee..1ee5ed5dea6 100644 --- a/src/scene/shader-lib/programs/lit-shader.js +++ b/src/scene/shader-lib/programs/lit-shader.js @@ -13,7 +13,10 @@ import { SHADER_DEPTH, SHADER_PICK, SHADOW_PCF1, SHADOW_PCF3, SHADOW_PCF5, SHADOW_VSM8, SHADOW_VSM16, SHADOW_VSM32, SHADOW_PCSS, SPECOCC_AO, SPECOCC_GLOSSDEPENDENT, - SPRITE_RENDERMODE_SLICED, SPRITE_RENDERMODE_TILED, shadowTypeToString, SHADER_PREPASS + SPRITE_RENDERMODE_SLICED, SPRITE_RENDERMODE_TILED, shadowTypeInfo, SHADER_PREPASS, + SHADOW_PCF1_FLOAT16, + SHADOW_PCF5_FLOAT16, + SHADOW_PCF3_FLOAT16 } from '../../constants.js'; import { shaderChunks } from '../chunks/chunks.js'; import { ChunkUtils } from '../chunk-utils.js'; @@ -23,6 +26,7 @@ import { validateUserChunks } from '../chunks/chunk-validation.js'; import { ShaderUtils } from '../../../platform/graphics/shader-utils.js'; import { ChunkBuilder } from '../chunk-builder.js'; import { ShaderGenerator } from './shader-generator.js'; +import { Debug } from '../../../core/debug.js'; /** * @import { GraphicsDevice } from '../../../platform/graphics/graphics-device.js' @@ -786,10 +790,10 @@ class LitShader { if (shadowedDirectionalLightUsed) { func.append(chunks.shadowCascadesPS); } - if (shadowTypeUsed[SHADOW_PCF1] || shadowTypeUsed[SHADOW_PCF3]) { + if (shadowTypeUsed[SHADOW_PCF1] || shadowTypeUsed[SHADOW_PCF3] || shadowTypeUsed[SHADOW_PCF1_FLOAT16] || shadowTypeUsed[SHADOW_PCF3_FLOAT16]) { func.append(chunks.shadowStandardPS); } - if (shadowTypeUsed[SHADOW_PCF5]) { + if (shadowTypeUsed[SHADOW_PCF5] || shadowTypeUsed[SHADOW_PCF5_FLOAT16]) { func.append(chunks.shadowStandardGL2PS); } if (useVsm) { @@ -898,8 +902,11 @@ class LitShader { decl.append('#define CLUSTER_COOKIES'); } if (options.clusteredLightingShadowsEnabled && !options.noShadow) { + const shadowTypeName = shadowTypeInfo.get(options.clusteredLightingShadowType)?.name; + Debug.assert(shadowTypeName); + const clusteredSampleType = shadowTypeName.substring(0, 4); // PCF1 from PCF1_FLOAT16 decl.append('#define CLUSTER_SHADOWS'); - decl.append(`#define CLUSTER_SHADOW_TYPE_${shadowTypeToString[options.clusteredLightingShadowType]}`); + decl.append(`#define CLUSTER_SHADOW_TYPE_${clusteredSampleType}`); } if (options.clusteredLightingAreaLightsEnabled) { @@ -1155,9 +1162,12 @@ class LitShader { } if (light.castShadows && !options.noShadow) { + const shadowInfo = shadowTypeInfo.get(light._shadowType); + Debug.assert(shadowInfo); + const pcssShadows = light._shadowType === SHADOW_PCSS; - const vsmShadows = light._shadowType === SHADOW_VSM8 || light._shadowType === SHADOW_VSM16 || light._shadowType === SHADOW_VSM32; - const pcfShadows = light._shadowType === SHADOW_PCF1 || light._shadowType === SHADOW_PCF3 || light._shadowType === SHADOW_PCF5; + const vsmShadows = shadowInfo?.vsm; + const pcfShadows = shadowInfo?.pcf; let shadowReadMode = null; let evsmExp; switch (light._shadowType) { @@ -1174,15 +1184,18 @@ class LitShader { evsmExp = '15.0'; break; case SHADOW_PCF1: + case SHADOW_PCF1_FLOAT16: shadowReadMode = 'PCF1x1'; break; case SHADOW_PCF5: + case SHADOW_PCF5_FLOAT16: shadowReadMode = 'PCF5x5'; break; case SHADOW_PCSS: shadowReadMode = 'PCSS'; break; case SHADOW_PCF3: + case SHADOW_PCF3_FLOAT16: default: shadowReadMode = 'PCF3x3'; break;