瀏覽代碼

Merge pull request #7429 from Popov72/csm-allow-culling

Add the RenderTargetTexture.getCustomRenderList user function
David Catuhe 5 年之前
父節點
當前提交
7b38dcabef

+ 1 - 0
dist/preview release/what's new.md

@@ -56,6 +56,7 @@
 - Added `CascadedShadowGenerator.autoCalcDepthBounds` to improve the shadow quality rendering ([Popov72](https://github.com/Popov72))
 - Improved cascade blending in CSM shadow technique ([Popov72](https://github.com/Popov72))
 - Speed optimization when cascade blending is not used in CSM shadow technique ([Popov72](https://github.com/Popov72))
+- Added `RenderTargetTexture.getCustomRenderList` to overload the render list at rendering time (and possibly for each layer (2DArray) / face (Cube)) ([Popov72](https://github.com/Popov72))
 
 ### Engine
 

+ 19 - 23
src/Lights/Shadows/cascadedShadowGenerator.ts

@@ -608,10 +608,8 @@ export class CascadedShadowGenerator implements IShadowGenerator {
     private _effect: Effect;
 
     private _cascades: Array<ICascade>;
-    private _cachedPosition: Vector3 = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
     private _cachedDirection: Vector3 = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
     private _cachedDefines: string;
-    private _currentRenderID: Array<number>;
     private _mapSize: number;
     private _currentLayer = 0;
     private _textureType: number;
@@ -735,6 +733,15 @@ export class CascadedShadowGenerator implements IShadowGenerator {
         return cascadeNum >= 0 && cascadeNum < this._numCascades ? this._viewMatrices[cascadeNum] : null;
     }
 
+    /**
+     * Gets the projection matrix corresponding to a given cascade
+     * @param cascadeNum cascade to retrieve the projection matrix from
+     * @returns the cascade projection matrix
+     */
+    public getCascadeProjectionMatrix(cascadeNum: number): Nullable<Matrix> {
+        return cascadeNum >= 0 && cascadeNum < this._numCascades ? this._projectionMatrices[cascadeNum] : null;
+    }
+
     private _depthRenderer: Nullable<DepthRenderer>;
     /**
      * Sets the depth renderer to use when autoCalcDepthBounds is enabled.
@@ -871,21 +878,15 @@ export class CascadedShadowGenerator implements IShadowGenerator {
      * @returns The transform matrix used to create the CSM shadow map
      */
     public getCSMTransformMatrix(cascadeIndex: number): Matrix {
+        return this._transformMatrices[cascadeIndex];
+    }
+
+    private _computeMatrices(): void {
         var scene = this._scene;
-        if (this._currentRenderID[cascadeIndex] === scene.getRenderId()) {
-            return this._transformMatrices[cascadeIndex];
-        }
 
         let camera = scene.activeCamera;
         if (!camera) {
-            return this._transformMatrices[cascadeIndex];
-        }
-
-        this._currentRenderID[cascadeIndex] = scene.getRenderId();
-
-        var lightPosition = this._light.position;
-        if (this._light.computeTransformedInformation()) {
-            lightPosition = this._light.transformedPosition;
+            return;
         }
 
         Vector3.NormalizeToRef(this._light.getShadowDirection(0), this._lightDirection);
@@ -893,10 +894,9 @@ export class CascadedShadowGenerator implements IShadowGenerator {
             this._lightDirection.z = 0.0000000000001; // Required to avoid perfectly perpendicular light
         }
 
-        if (this._light.needProjectionMatrixCompute() || !this._cachedPosition || !this._cachedDirection || !lightPosition.equals(this._cachedPosition) || !this._lightDirection.equals(this._cachedDirection)) {
-            this._cachedPosition.copyFrom(lightPosition);
-            this._cachedDirection.copyFrom(this._lightDirection);
+        this._cachedDirection.copyFrom(this._lightDirection);
 
+        for (let cascadeIndex = 0; cascadeIndex < this._numCascades; ++cascadeIndex) {
             this._computeFrustumInWorldSpace(cascadeIndex);
             this._computeCascadeFrustum(cascadeIndex);
 
@@ -948,11 +948,9 @@ export class CascadedShadowGenerator implements IShadowGenerator {
 
             this._projectionMatrices[cascadeIndex].multiplyToRef(matrix, this._projectionMatrices[cascadeIndex]);
             this._viewMatrices[cascadeIndex].multiplyToRef(this._projectionMatrices[cascadeIndex], this._transformMatrices[cascadeIndex]);
-        }
 
-        this._transformMatrices[cascadeIndex].copyToArray(this._transformMatricesAsArray, cascadeIndex * 16);
-
-        return this._transformMatrices[cascadeIndex];
+            this._transformMatrices[cascadeIndex].copyToArray(this._transformMatricesAsArray, cascadeIndex * 16);
+        }
     }
 
     // Get the 8 points of the view frustum in world space
@@ -1030,8 +1028,6 @@ export class CascadedShadowGenerator implements IShadowGenerator {
                 this._cascadeMaxExtents[cascadeIndex].maximizeInPlace(tmpv1);
             }
         }
-
-        return;
     }
 
     /** @hidden */
@@ -1102,7 +1098,6 @@ export class CascadedShadowGenerator implements IShadowGenerator {
         this._transformMatricesAsArray = new Float32Array(this._numCascades * 16);
         this._viewSpaceFrustumsZ = new Array(this._numCascades);
         this._frustumLengths = new Array(this._numCascades);
-        this._currentRenderID = new Array(this._numCascades);
         this._lightSizeUVCorrection = new Array(this._numCascades * 2);
         this._depthCorrection = new Array(this._numCascades);
 
@@ -1157,6 +1152,7 @@ export class CascadedShadowGenerator implements IShadowGenerator {
             if (this._breaksAreDirty) {
                 this._splitFrustum();
             }
+            this._computeMatrices();
         });
 
         // Record Face Index before render.

+ 84 - 44
src/Materials/Textures/renderTargetTexture.ts

@@ -1,7 +1,7 @@
 import { Observer, Observable } from "../../Misc/observable";
 import { Tools } from "../../Misc/tools";
 import { SmartArray } from "../../Misc/smartArray";
-import { Nullable } from "../../types";
+import { Nullable, Immutable } from "../../types";
 import { Camera } from "../../Cameras/camera";
 import { Scene } from "../../scene";
 import { Matrix, Vector3 } from "../../Maths/math.vector";
@@ -63,6 +63,15 @@ export class RenderTargetTexture extends Texture {
         }
     }
 
+    /**
+     * Use this function to overload the renderList array at rendering time.
+     * Return null to render with the curent renderList, else return the list of meshes to use for rendering.
+     * For 2DArray RTT, layerOrFace is the index of the layer that is going to be rendered, else it is the faceIndex of
+     * the cube (if the RTT is a cube, else layerOrFace=0).
+     * The renderList passed to the function is the current render list (the one that will be used if the function returns null)
+    */
+    public getCustomRenderList: (layerOrFace: number, renderList: Nullable<Immutable<Array<AbstractMesh>>>) => Nullable<Array<AbstractMesh>>;
+
     private _hookArray(array: AbstractMesh[]): void {
         var oldPush = array.push;
         array.push = (...items: AbstractMesh[]) => {
@@ -602,6 +611,8 @@ export class RenderTargetTexture extends Texture {
         }
     }
 
+    private _defaultRenderListPrepared: boolean;
+
     /**
      * Renders all the objects from the render list into the texture.
      * @param useCameraPostProcess Define if camera post processes should be used during the rendering
@@ -677,11 +688,57 @@ export class RenderTargetTexture extends Texture {
             }
         }
 
-        // Prepare renderingManager
+        this._defaultRenderListPrepared = false;
+
+        if (this.is2DArray) {
+            for (let layer = 0; layer < this.getRenderLayers(); layer++) {
+                this.renderToTarget(0, useCameraPostProcess, dumpForDebug, layer, camera);
+                scene.incrementRenderId();
+                scene.resetCachedMaterial();
+            }
+        }
+        else if (this.isCube) {
+            for (var face = 0; face < 6; face++) {
+                this.renderToTarget(face, useCameraPostProcess, dumpForDebug, undefined, camera);
+                scene.incrementRenderId();
+                scene.resetCachedMaterial();
+            }
+        } else {
+            this.renderToTarget(0, useCameraPostProcess, dumpForDebug, undefined, camera);
+        }
+
+        this.onAfterUnbindObservable.notifyObservers(this);
+
+        if (scene.activeCamera) {
+            // Do not avoid setting uniforms when multiple scenes are active as another camera may have overwrite these
+            if (scene.getEngine().scenes.length > 1 || (this.activeCamera && this.activeCamera !== scene.activeCamera)) {
+                scene.setTransformMatrix(scene.activeCamera.getViewMatrix(), scene.activeCamera.getProjectionMatrix(true));
+            }
+            engine.setViewport(scene.activeCamera.viewport);
+        }
+
+        scene.resetCachedMaterial();
+    }
+
+    private _bestReflectionRenderTargetDimension(renderDimension: number, scale: number): number {
+        let minimum = 128;
+        let x = renderDimension * scale;
+        let curved = Engine.NearestPOT(x + (minimum * minimum / (minimum + x)));
+
+        // Ensure we don't exceed the render dimension (while staying POT)
+        return Math.min(Engine.FloorPOT(renderDimension), curved);
+    }
+
+    private _prepareRenderingManager(currentRenderList: Array<AbstractMesh>, camera: Nullable<Camera>, checkLayerMask: boolean): void {
+        var scene = this.getScene();
+
+        if (!scene) {
+            return;
+        }
+
         this._renderingManager.reset();
 
-        var currentRenderList = this.renderList ? this.renderList : scene.getActiveMeshes().data;
-        var currentRenderListLength = this.renderList ? this.renderList.length : scene.getActiveMeshes().length;
+        var currentRenderListLength = currentRenderList.length;
         var sceneRenderId = scene.getRenderId();
         for (var meshIndex = 0; meshIndex < currentRenderListLength; meshIndex++) {
             var mesh = currentRenderList[meshIndex];
@@ -695,7 +752,7 @@ export class RenderTargetTexture extends Texture {
                 mesh._preActivateForIntermediateRendering(sceneRenderId);
 
                 let isMasked;
-                if (!this.renderList && camera) {
+                if (checkLayerMask && camera) {
                     isMasked = ((mesh.layerMask & camera.layerMask) === 0);
                 } else {
                     isMasked = false;
@@ -731,44 +788,6 @@ export class RenderTargetTexture extends Texture {
                 this._renderingManager.dispatchParticles(particleSystem);
             }
         }
-
-        if (this.is2DArray) {
-            for (let layer = 0; layer < this.getRenderLayers(); layer++) {
-                this.renderToTarget(0, currentRenderList, useCameraPostProcess, dumpForDebug, layer);
-                scene.incrementRenderId();
-                scene.resetCachedMaterial();
-            }
-        }
-        else if (this.isCube) {
-            for (var face = 0; face < 6; face++) {
-                this.renderToTarget(face, currentRenderList, useCameraPostProcess, dumpForDebug);
-                scene.incrementRenderId();
-                scene.resetCachedMaterial();
-            }
-        } else {
-            this.renderToTarget(0, currentRenderList, useCameraPostProcess, dumpForDebug);
-        }
-
-        this.onAfterUnbindObservable.notifyObservers(this);
-
-        if (scene.activeCamera) {
-            // Do not avoid setting uniforms when multiple scenes are active as another camera may have overwrite these
-            if (scene.getEngine().scenes.length > 1 || (this.activeCamera && this.activeCamera !== scene.activeCamera)) {
-                scene.setTransformMatrix(scene.activeCamera.getViewMatrix(), scene.activeCamera.getProjectionMatrix(true));
-            }
-            engine.setViewport(scene.activeCamera.viewport);
-        }
-
-        scene.resetCachedMaterial();
-    }
-
-    private _bestReflectionRenderTargetDimension(renderDimension: number, scale: number): number {
-        let minimum = 128;
-        let x = renderDimension * scale;
-        let curved = Engine.NearestPOT(x + (minimum * minimum / (minimum + x)));
-
-        // Ensure we don't exceed the render dimension (while staying POT)
-        return Math.min(Engine.FloorPOT(renderDimension), curved);
     }
 
     /**
@@ -797,7 +816,7 @@ export class RenderTargetTexture extends Texture {
         });
     }
 
-    private renderToTarget(faceIndex: number, currentRenderList: AbstractMesh[], useCameraPostProcess: boolean, dumpForDebug: boolean, layer = 0): void {
+    private renderToTarget(faceIndex: number, useCameraPostProcess: boolean, dumpForDebug: boolean, layer = 0, camera: Nullable<Camera> = null): void {
         var scene = this.getScene();
 
         if (!scene) {
@@ -825,6 +844,27 @@ export class RenderTargetTexture extends Texture {
             this.onBeforeRenderObservable.notifyObservers(faceIndex);
         }
 
+        // Get the list of meshes to render
+        let currentRenderList: Nullable<Array<AbstractMesh>> = null;
+        let defaultRenderList = this.renderList ? this.renderList : scene.getActiveMeshes().data;
+
+        if (this.getCustomRenderList) {
+            currentRenderList = this.getCustomRenderList(this.is2DArray ? layer : faceIndex, defaultRenderList);
+        }
+
+        if (!currentRenderList) {
+            // No custom render list provided, we prepare the rendering for the default list, but check
+            // first if we did not already performed the preparation before so as to avoid re-doing it several times
+            if (!this._defaultRenderListPrepared) {
+                this._prepareRenderingManager(defaultRenderList, camera, !this.renderList);
+                this._defaultRenderListPrepared = true;
+            }
+            currentRenderList = defaultRenderList;
+        } else {
+            // Prepare the rendering for the custom render list provided
+            this._prepareRenderingManager(currentRenderList, camera, false);
+        }
+
         // Clear
         if (this.onClearObservable.hasObservers()) {
             this.onClearObservable.notifyObservers(engine);