瀏覽代碼

Add support for CSM to webgpu

Popov72 4 年之前
父節點
當前提交
4e5ea4936a

+ 10 - 3
src/Engines/WebGPU/webgpuShaderProcessors.ts

@@ -27,6 +27,7 @@ const _samplerFunctionByWebGLSamplerType: { [key: string]: string } = {
     "texture2D": "sampler2D",
     "sampler2D": "sampler2D",
     "sampler2DShadow": "sampler2DShadow",
+    "sampler2DArrayShadow": "sampler2DArrayShadow",
     "samplerCube": "samplerCube"
 };
 
@@ -35,21 +36,27 @@ const _textureTypeByWebGLSamplerType: { [key: string]: string } = {
     "texture2D": "texture2D",
     "sampler2D": "texture2D",
     "sampler2DShadow": "texture2D",
-    "samplerCube": "textureCube"
+    "sampler2DArrayShadow": "texture2DArray",
+    "samplerCube": "textureCube",
+    "samplerCubeArray": "textureCubeArray"
 };
 
 const _gpuTextureViewDimensionByWebGPUTextureType: { [key: string]: GPUTextureViewDimension } = {
     "textureCube": WebGPUConstants.TextureViewDimension.Cube,
+    "textureCubeArray": WebGPUConstants.TextureViewDimension.CubeArray,
     "texture2D": WebGPUConstants.TextureViewDimension.E2d,
+    "texture2DArray": WebGPUConstants.TextureViewDimension.E2dArray,
 };
 
 // if the webgl sampler type is not listed in this array, "sampler" is taken by default
 const _samplerTypeByWebGLSamplerType: { [key: string]: string } = {
     "sampler2DShadow": "samplerShadow",
+    "sampler2DArrayShadow": "samplerShadow",
 };
 
-const _comparisonSamplerByWebGPUSamplerType: { [key: string]: boolean } = {
+const _isComparisonSamplerByWebGPUSamplerType: { [key: string]: boolean } = {
     "samplerShadow": true,
+    "samplerArrayShadow": true,
     "sampler": false,
 };
 
@@ -157,7 +164,7 @@ export class WebGPUShaderProcessor implements IShaderProcessor {
                 }
                 webgpuProcessingContext.orderedUBOsAndSamplers[setIndex][textureBindingIndex] = {
                     isSampler: true,
-                    isComparisonSampler: _comparisonSamplerByWebGPUSamplerType[samplerType] ?? false,
+                    isComparisonSampler: _isComparisonSamplerByWebGPUSamplerType[samplerType] ?? false,
                     textureDimension,
                     name,
                 };

+ 2 - 2
src/Engines/WebGPU/webgpuTextureHelper.ts

@@ -134,13 +134,13 @@ export class WebGPUTextureHelper {
         return false;
     }
 
-    public createTexture(imageBitmap: ImageBitmap | { width: number, height: number }, hasMipmaps = false, generateMipmaps = false, invertY = false, premultiplyAlpha = false, format: GPUTextureFormat = WebGPUConstants.TextureFormat.RGBA8Unorm,
+    public createTexture(imageBitmap: ImageBitmap | { width: number, height: number, layers: number }, hasMipmaps = false, generateMipmaps = false, invertY = false, premultiplyAlpha = false, format: GPUTextureFormat = WebGPUConstants.TextureFormat.RGBA8Unorm,
         sampleCount = 1, commandEncoder?: GPUCommandEncoder, usage = -1): GPUTexture
     {
         let textureSize = {
             width: imageBitmap.width,
             height: imageBitmap.height,
-            depth: 1,
+            depth: (imageBitmap as any).layers || 1,
         };
 
         const mipLevelCount = hasMipmaps ? WebGPUTextureHelper.computeNumMipmapLevels(imageBitmap.width, imageBitmap.height) : 1;

+ 3 - 0
src/Engines/engineFeatures.ts

@@ -23,6 +23,9 @@ export interface EngineFeatures {
     /** Indicates to track the usage of ubos and to create new ones as necessary during a frame duration */
     trackUbosInFrame: boolean;
 
+    /** Indicates that the Cascaded Shadow Map technic is supported */
+    supportCSM: boolean;
+
     /** @hidden */
     _collectUbosUpdatedInFrame: boolean;
 

+ 2 - 0
src/Engines/thinEngine.ts

@@ -183,6 +183,7 @@ export class ThinEngine {
         uniformBufferHardCheckMatrix: false,
         allowTexturePrefiltering: false,
         trackUbosInFrame: false,
+        supportCSM: false,
         _collectUbosUpdatedInFrame: false,
         _warnWhenTooManyBuffersInUniformBufferClass: 0,
     };
@@ -733,6 +734,7 @@ export class ThinEngine {
         ThinEngine.Features.supportDepthStencilTexture = this._webGLVersion !== 1;
         ThinEngine.Features.supportShadowSamplers = this._webGLVersion !== 1;
         ThinEngine.Features.allowTexturePrefiltering = this._webGLVersion !== 1;
+        ThinEngine.Features.supportCSM = this._webGLVersion !== 1;
 
         // Ensures a consistent color space unpacking of textures cross browser.
         this._gl.pixelStorei(this._gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, this._gl.NONE);

+ 9 - 6
src/Engines/webgpuEngine.ts

@@ -237,6 +237,7 @@ export class WebGPUEngine extends Engine {
         ThinEngine.Features.uniformBufferHardCheckMatrix = true;
         ThinEngine.Features.allowTexturePrefiltering = true;
         ThinEngine.Features.trackUbosInFrame = true;
+        ThinEngine.Features.supportCSM = true;
         ThinEngine.Features._collectUbosUpdatedInFrame = true;
         ThinEngine.Features._warnWhenTooManyBuffersInUniformBufferClass = 30;
 
@@ -1804,6 +1805,7 @@ export class WebGPUEngine extends Engine {
             texture._source === InternalTextureSource.Depth ? WebGPUConstants.TextureUsage.Sampled | WebGPUConstants.TextureUsage.OutputAttachment : -1;
 
         const generateMipMaps = texture._source === InternalTextureSource.RenderTarget ? false : texture.generateMipMaps;
+        const layerCount = texture.depth || 1;
 
         if (texture.isCube) {
             const gpuTexture = this._textureHelper.createCubeTexture({ width, height }, texture.generateMipMaps, texture.generateMipMaps, texture.invertY, false, gpuTextureWrapper.format, texture.samples || 1, this._uploadEncoder, textureUsages);
@@ -1817,14 +1819,15 @@ export class WebGPUEngine extends Engine {
                 aspect: WebGPUConstants.TextureAspect.All
             });
         } else {
-            const gpuTexture = this._textureHelper.createTexture({ width, height }, texture.generateMipMaps, texture.generateMipMaps, texture.invertY, false, gpuTextureWrapper.format, texture.samples || 1, this._uploadEncoder, textureUsages);
+            const gpuTexture = this._textureHelper.createTexture({ width, height, layers: layerCount }, texture.generateMipMaps, texture.generateMipMaps, texture.invertY, false, gpuTextureWrapper.format, texture.samples || 1, this._uploadEncoder, textureUsages);
 
             gpuTextureWrapper.set(gpuTexture);
             gpuTextureWrapper.createView({
-                dimension: WebGPUConstants.TextureViewDimension.E2d,
+                dimension: texture.is2DArray ? WebGPUConstants.TextureViewDimension.E2dArray : WebGPUConstants.TextureViewDimension.E2d,
                 mipLevelCount: generateMipMaps ? WebGPUTextureHelper.computeNumMipmapLevels(width!, height!) : 1,
                 baseArrayLayer: 0,
                 baseMipLevel: 0,
+                arrayLayerCount: layerCount,
                 aspect: WebGPUConstants.TextureAspect.All
             });
         }
@@ -2106,6 +2109,7 @@ export class WebGPUEngine extends Engine {
         texture.format = fullOptions.format;
         texture._generateDepthBuffer = fullOptions.generateDepthBuffer;
         texture._generateStencilBuffer = fullOptions.generateStencilBuffer ? true : false;
+        texture.is2DArray = layers > 0;
 
         this._internalTexturesCache.push(texture);
 
@@ -2475,12 +2479,11 @@ export class WebGPUEngine extends Engine {
         this._setDepthTextureFormat(this._currentRenderTarget._depthStencilTexture ? this._getWebGPUTextureFormat(-1, this._currentRenderTarget._depthStencilTexture.format) : undefined);
         this._setColorFormat(this._getWebGPUTextureFormat(this._currentRenderTarget.type, this._currentRenderTarget.format));
 
-        // TODO WEBGPU handle array layer
         this._currentRenderTargetColorAttachmentViewDescriptor = {
             format: this._colorFormat,
             dimension: WebGPUConstants.TextureViewDimension.E2d,
             mipLevelCount: 1,
-            baseArrayLayer: faceIndex,
+            baseArrayLayer: texture.isCube ? layer * 6 + faceIndex : layer,
             baseMipLevel: lodLevel,
             arrayLayerCount: 1,
             aspect: WebGPUConstants.TextureAspect.All
@@ -2490,7 +2493,7 @@ export class WebGPUEngine extends Engine {
             format: this._depthTextureFormat,
             dimension: WebGPUConstants.TextureViewDimension.E2d,
             mipLevelCount: 1,
-            baseArrayLayer: 0,
+            baseArrayLayer: texture.isCube ? layer * 6 + faceIndex : layer,
             baseMipLevel: 0,
             arrayLayerCount: 1,
             aspect: WebGPUConstants.TextureAspect.All
@@ -2498,7 +2501,7 @@ export class WebGPUEngine extends Engine {
 
         if (dbgVerboseLogsForFirstFrames) {
             if (!(this as any)._count || (this as any)._count < dbgVerboseLogsNumFrames) {
-                console.log("frame #" + (this as any)._count + " - bindFramebuffer called - face=", faceIndex, "lodLevel=", lodLevel, this._currentRenderTargetColorAttachmentViewDescriptor, this._currentRenderTargetDepthAttachmentViewDescriptor);
+                console.log("frame #" + (this as any)._count + " - bindFramebuffer called - face=", faceIndex, "lodLevel=", lodLevel, "layer=", layer, this._currentRenderTargetColorAttachmentViewDescriptor, this._currentRenderTargetDepthAttachmentViewDescriptor);
             }
         }
 

+ 10 - 11
src/Lights/Shadows/cascadedShadowGenerator.ts

@@ -23,7 +23,7 @@ import { DepthRenderer } from '../../Rendering/depthRenderer';
 import { DepthReducer } from '../../Misc/depthReducer';
 
 import { Logger } from "../../Misc/logger";
-import { EngineStore } from '../../Engines/engineStore';
+import { ThinEngine } from '../../Engines/thinEngine';
 
 interface ICascade {
     prevBreakDistance: number;
@@ -693,11 +693,7 @@ export class CascadedShadowGenerator extends ShadowGenerator {
     *  Support test.
     */
     public static get IsSupported(): boolean {
-        var engine = EngineStore.LastCreatedEngine;
-        if (!engine) {
-            return false;
-        }
-        return engine.webGLVersion != 1;
+        return ThinEngine.Features.supportCSM;
     }
 
     /** @hidden */
@@ -716,7 +712,7 @@ export class CascadedShadowGenerator extends ShadowGenerator {
      */
     constructor(mapSize: number, light: DirectionalLight, usefulFloatFirst?: boolean) {
         if (!CascadedShadowGenerator.IsSupported) {
-            Logger.Error("CascadedShadowMap needs WebGL 2 support.");
+            Logger.Error("CascadedShadowMap is not supported by the current engine.");
             return;
         }
 
@@ -797,13 +793,16 @@ export class CascadedShadowGenerator extends ShadowGenerator {
             }
         }
 
+        let engine = this._scene.getEngine();
+
+        this._shadowMap.onBeforeRenderObservable.clear();
+
         this._shadowMap.onBeforeRenderObservable.add((layer: number) => {
             this._currentLayer = layer;
-            if (this._scene.getSceneUniformBuffer().useUbo) {
-                const sceneUBO = this._scene.getSceneUniformBuffer();
-                sceneUBO.updateMatrix("viewProjection", this.getCascadeTransformMatrix(layer)!);
-                sceneUBO.updateMatrix("view", this.getCascadeViewMatrix(layer)!);
+            if (this._filter === ShadowGenerator.FILTER_PCF) {
+                engine.setColorWrite(false);
             }
+            this._scene.setTransformMatrix(this.getCascadeViewMatrix(layer)!, this.getCascadeProjectionMatrix(layer)!);
         });
 
         this._shadowMap.onBeforeBindObservable.add(() => {

+ 5 - 9
src/Lights/Shadows/shadowGenerator.ts

@@ -144,6 +144,8 @@ export interface IShadowGenerator {
  */
 export class ShadowGenerator implements IShadowGenerator {
 
+    private static _Counter = 0;
+
     /**
      * Name of the shadow generator class
      */
@@ -221,16 +223,8 @@ export class ShadowGenerator implements IShadowGenerator {
      */
     public static readonly QUALITY_LOW = 2;
 
-    private _id: string;
     /** Gets or set the id of the shadow generator. It will be the one from the light if not defined */
-    public get id(): string {
-        return this._id;
-    }
-
-    public set id(id: string) {
-        this._id = id;
-        this._nameForCustomEffect = Constants.CUSTOMEFFECT_PREFIX_SHADOWGENERATOR + id;
-    }
+    public id: string;
 
     /** Gets or sets the custom shader name to use */
     public customShaderOptions: ICustomShaderOptions;
@@ -831,6 +825,8 @@ export class ShadowGenerator implements IShadowGenerator {
         light._shadowGenerator = this;
         this.id = light.id;
 
+        this._nameForCustomEffect = Constants.CUSTOMEFFECT_PREFIX_SHADOWGENERATOR + ShadowGenerator._Counter++;
+
         ShadowGenerator._SceneComponentInitialization(this._scene);
 
         // Texture type fallback from float to int if not supported.

二進制
tests/validation/ReferenceImages/webgpu/cascaded shadow maps and flying saucers.png


+ 5 - 0
tests/validation/webgpu.json

@@ -100,6 +100,11 @@
         {
             "title": "dissolve effect with node material and glow layer",
             "playgroundId": "#F6PGMC"
+        },
+        {
+            "title": "cascaded shadow maps and flying saucers",
+            "renderCount": 10,
+            "playgroundId": "#IIZ9UU#40"
         }
     ]
 }