Преглед изворни кода

Merge pull request #7427 from Popov72/csm-inspector

Cascaded Shadow Maps: add support in the inspector
sebavan пре 5 година
родитељ
комит
96d66694e2

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

@@ -83,6 +83,7 @@
 - Added support for `Material.depthFunction` property ([Popov72](https://github.com/Popov72))
 - Added an optional config option `initialTab` ([ycw](https://github.com/ycw/))
 - Added support for ImportAnimations ([noalak](https://github.com/noalak/))
+- Added support for Cascaded Shadow Maps ([Popov72](https://github.com/Popov72))
 
 ### Tools
 

+ 1 - 1
inspector/src/components/actionTabs/lines/optionsLineComponent.tsx

@@ -79,7 +79,7 @@ export class OptionsLineComponent extends React.Component<IOptionsLineComponentP
 
                 </div>
                 <div className="options">
-                    <select onChange={evt => this.updateValue(evt.target.value)} value={this.state.value || ""}>
+                    <select onChange={evt => this.updateValue(evt.target.value)} value={this.state.value ?? ""}>
                         {
                             this.props.options.map(option => {
                                 return (

+ 98 - 25
inspector/src/components/actionTabs/tabs/propertyGrids/lights/commonShadowLightPropertyGridComponent.tsx

@@ -9,27 +9,37 @@ import { LockObject } from "../lockObject";
 import { GlobalState } from '../../../../globalState';
 import { OptionsLineComponent } from '../../../lines/optionsLineComponent';
 import { ShadowGenerator } from 'babylonjs/Lights/Shadows/shadowGenerator';
+import { CascadedShadowGenerator } from 'babylonjs/Lights/Shadows/cascadedShadowGenerator';
 import { SliderLineComponent } from '../../../lines/sliderLineComponent';
 import { ButtonLineComponent } from '../../../lines/buttonLineComponent';
+import { DirectionalLight } from 'babylonjs/Lights/directionalLight';
 
 interface ICommonShadowLightPropertyGridComponentProps {
-    globalState: GlobalState,
-    light: IShadowLight,
-    lockObject: LockObject,
-    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+    globalState: GlobalState;
+    light: IShadowLight;
+    lockObject: LockObject;
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
 }
 
 export class CommonShadowLightPropertyGridComponent extends React.Component<ICommonShadowLightPropertyGridComponentProps> {
+    private _internals : { generatorType: number, mapSize: number };
+
     constructor(props: ICommonShadowLightPropertyGridComponentProps) {
         super(props);
+
+        this._internals = {
+            generatorType: 0,
+            mapSize: 1024,
+        };
     }
 
     createShadowGenerator() {
         const light = this.props.light;
         const scene = light.getScene();
-        let generator = new ShadowGenerator(512, light);
+        const internals = this._internals;
+        let generator = internals.generatorType === 0 ? new ShadowGenerator(internals.mapSize, light) : new CascadedShadowGenerator(internals.mapSize, light as DirectionalLight);
 
-        scene.meshes.forEach(m => {
+        scene.meshes.forEach((m) => {
             generator.addShadowCaster(m);
             m.receiveShadows = true;
         });
@@ -37,45 +47,104 @@ export class CommonShadowLightPropertyGridComponent extends React.Component<ICom
         this.forceUpdate();
     }
 
+    disposeShadowGenerator() {
+        const light = this.props.light;
+
+        light.getShadowGenerator()?.dispose();
+
+        this.forceUpdate();
+    }
+
     render() {
         const light = this.props.light;
-        const generator = light.getShadowGenerator() as ShadowGenerator || null;
-
-        var blurModeOptions = [
-            { label: "None", value: ShadowGenerator.FILTER_NONE },
-            { label: "PCF", value: ShadowGenerator.FILTER_PCF },
-            { label: "PCSS", value: ShadowGenerator.FILTER_PCSS },
-            { label: "Poisson", value: ShadowGenerator.FILTER_POISSONSAMPLING },
-            { label: "Exponential", value: ShadowGenerator.FILTER_EXPONENTIALSHADOWMAP },
-            { label: "Blurred exponential", value: ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP },
-            { label: "Close exponential", value: ShadowGenerator.FILTER_CLOSEEXPONENTIALSHADOWMAP },
-            { label: "Blurred close exponential", value: ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP },
+        const internals = this._internals;
+        const generator = light.getShadowGenerator() as (ShadowGenerator | CascadedShadowGenerator) || null;
+        const csmGenerator = generator instanceof CascadedShadowGenerator;
+        const camera = light.getScene().activeCamera;
+
+        var typeGeneratorOptions = [
+            { label: "Shadow Generator", value: 0 }
         ];
 
+        if (light instanceof DirectionalLight) {
+            typeGeneratorOptions.push({ label: "Cascaded Shadow Generator", value: 1 });
+        }
+
+        var mapSizeOptions = [
+            { label: "2048x2048", value: 2048 },
+            { label: "1024x1024", value: 1024 },
+            { label: "512x512", value: 512 },
+            { label: "256x256", value: 256 },
+        ];
+
+        var blurModeOptions;
+
+        if (generator instanceof CascadedShadowGenerator) {
+            blurModeOptions = [
+                { label: "None", value: ShadowGenerator.FILTER_NONE },
+                { label: "PCF", value: ShadowGenerator.FILTER_PCF },
+                { label: "PCSS", value: ShadowGenerator.FILTER_PCSS },
+            ];
+        } else {
+            blurModeOptions = [
+                { label: "None", value: ShadowGenerator.FILTER_NONE },
+                { label: "PCF", value: ShadowGenerator.FILTER_PCF },
+                { label: "PCSS", value: ShadowGenerator.FILTER_PCSS },
+                { label: "Poisson", value: ShadowGenerator.FILTER_POISSONSAMPLING },
+                { label: "Exponential", value: ShadowGenerator.FILTER_EXPONENTIALSHADOWMAP },
+                { label: "Blurred exponential", value: ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP },
+                { label: "Close exponential", value: ShadowGenerator.FILTER_CLOSEEXPONENTIALSHADOWMAP },
+                { label: "Blurred close exponential", value: ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP },
+            ];
+        }
+
         var filteringQualityOptions = [
             { label: "Low", value: ShadowGenerator.QUALITY_LOW },
             { label: "Medium", value: ShadowGenerator.QUALITY_MEDIUM },
-            { label: "High", value: ShadowGenerator.QUALITY_HIGH }
+            { label: "High", value: ShadowGenerator.QUALITY_HIGH },
         ];
 
+        var numCascadesOptions = [
+            { label: "2", value: 2 },
+            { label: "3", value: 3 },
+            { label: "4", value: 4 },
+        ];
+
+        const near = camera ? camera.minZ : 0, far = camera ? camera.maxZ : 0;
+
         let filter = generator ? generator.filter : 0;
 
         return (
             <div>
                 <LineContainerComponent globalState={this.props.globalState} title="SHADOWS">
                     <CheckBoxLineComponent label="Shadows enabled" target={light} propertyName="shadowEnabled" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent lockObject={this.props.lockObject} label="Shadows near plane" target={light} propertyName="shadowMinZ" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent lockObject={this.props.lockObject} label="Shadows far plane" target={light} propertyName="shadowMaxZ" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    { !csmGenerator && <>
+                        <FloatLineComponent lockObject={this.props.lockObject} label="Shadows near plane" target={light} propertyName="shadowMinZ" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        <FloatLineComponent lockObject={this.props.lockObject} label="Shadows far plane" target={light} propertyName="shadowMaxZ" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    </> }
                 </LineContainerComponent>
                 {
                     generator == null &&
                     <LineContainerComponent globalState={this.props.globalState} title="SHADOW GENERATOR">
+                        <OptionsLineComponent label="Type" options={typeGeneratorOptions} target={internals} propertyName="generatorType" />
+                        <OptionsLineComponent label="Map size" options={mapSizeOptions} target={internals} propertyName="mapSize" />
                         <ButtonLineComponent label="Create generator" onClick={() => this.createShadowGenerator()} />
                     </LineContainerComponent>
                 }
                 {
                     generator !== null &&
                     <LineContainerComponent globalState={this.props.globalState} title="SHADOW GENERATOR">
+                        <ButtonLineComponent label="Dispose generator" onClick={() => this.disposeShadowGenerator()} />
+                        { csmGenerator && <>
+                            <OptionsLineComponent label="Num cascades" options={numCascadesOptions} target={generator} propertyName="numCascades" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <CheckBoxLineComponent label="Debug mode" target={generator} propertyName="debug" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <CheckBoxLineComponent label="Stabilize cascades" target={generator} propertyName="stabilizeCascades" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <SliderLineComponent label="Lambda" minimum={0} maximum={1.0} step={0.01} target={generator} propertyName="lambda" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <SliderLineComponent label="Cascade blend" minimum={0} maximum={1.0} step={0.01} target={generator} propertyName="cascadeBlendPercentage" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <CheckBoxLineComponent label="Depth clamp" target={generator} propertyName="depthClamp" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <CheckBoxLineComponent label="Auto-Calc depth bounds" target={generator} propertyName="autoCalcDepthBounds" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <SliderLineComponent label="Shadow MaxZ" minimum={near} maximum={far} step={0.5} target={generator} propertyName="shadowMaxZ" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        </> }
                         <FloatLineComponent lockObject={this.props.lockObject} digits={4} step="0.0001" label="Bias" target={generator} propertyName="bias" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                         <FloatLineComponent lockObject={this.props.lockObject} label="Normal bias" target={generator} propertyName="normalBias" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                         <SliderLineComponent label="Darkness" target={generator} minimum={0} maximum={1} step={0.01} propertyName="darkness" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
@@ -98,28 +167,32 @@ export class CommonShadowLightPropertyGridComponent extends React.Component<ICom
                             <SliderLineComponent label="Penumbra ratio" minimum={0} maximum={0.5} step={0.001} target={generator} propertyName="contactHardeningLightSizeUVRatio" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                         }
                         {
-                            (filter === ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP || filter === ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP) &&
+                            !csmGenerator && (filter === ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP || filter === ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP) &&
                             <CheckBoxLineComponent label="Use kernel blur" target={generator} propertyName="useKernelBlur"
                                 onValueChanged={() => this.forceUpdate()}
                                 onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                         }
                         {
-                            (filter === ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP || filter === ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP) &&
+                            (generator instanceof ShadowGenerator) && (filter === ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP || filter === ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP) &&
                             !generator.useKernelBlur &&
                             <SliderLineComponent label="Blur box offset" target={generator} propertyName="blurBoxOffset" minimum={1} maximum={64} step={1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />}
                         {
-                            (filter === ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP || filter === ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP) &&
+                            (generator instanceof ShadowGenerator) && (filter === ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP || filter === ShadowGenerator.FILTER_BLURCLOSEEXPONENTIALSHADOWMAP) &&
                             generator.useKernelBlur &&
                             <SliderLineComponent label="Blur kernel" target={generator} propertyName="blurKernel" minimum={1} maximum={64} step={1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                         }
                         {
-                            (filter === ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP || filter === ShadowGenerator.FILTER_EXPONENTIALSHADOWMAP) &&
+                            (generator instanceof ShadowGenerator) && (filter === ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP || filter === ShadowGenerator.FILTER_EXPONENTIALSHADOWMAP) &&
                             <FloatLineComponent lockObject={this.props.lockObject} label="Depth scale" target={generator} propertyName="depthScale" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                         }
                         {
-                            (filter === ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP || filter === ShadowGenerator.FILTER_EXPONENTIALSHADOWMAP) &&
+                            (generator instanceof ShadowGenerator) && (filter === ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP || filter === ShadowGenerator.FILTER_EXPONENTIALSHADOWMAP) &&
                             <SliderLineComponent label="Blur scale" target={generator} propertyName="blurScale" minimum={1} maximum={4} step={1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                         }
+                        {
+                            csmGenerator && (filter === ShadowGenerator.FILTER_PCSS) &&
+                            <SliderLineComponent label="Penumbra darkness" minimum={0} maximum={1.0} step={0.01} target={generator} propertyName="penumbraDarkness" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        }
                     </LineContainerComponent>
                 }
             </div>

+ 10 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/lights/directionalLightPropertyGridComponent.tsx

@@ -9,6 +9,9 @@ import { Vector3LineComponent } from "../../../lines/vector3LineComponent";
 import { CommonShadowLightPropertyGridComponent } from "./commonShadowLightPropertyGridComponent";
 import { LockObject } from "../lockObject";
 import { GlobalState } from '../../../../globalState';
+import { CheckBoxLineComponent } from '../../../lines/checkBoxLineComponent';
+import { ShadowGenerator } from 'babylonjs/Lights/Shadows/shadowGenerator';
+import { CascadedShadowGenerator } from 'babylonjs/Lights/Shadows/cascadedShadowGenerator';
 
 interface IDirectionalLightPropertyGridComponentProps {
     globalState: GlobalState,
@@ -25,6 +28,10 @@ export class DirectionalLightPropertyGridComponent extends React.Component<IDire
     render() {
         const light = this.props.light;
 
+        const generator = light.getShadowGenerator() as (ShadowGenerator | CascadedShadowGenerator) || null;
+
+        const hideAutoCalcShadowZBounds = generator instanceof CascadedShadowGenerator;
+
         return (
             <div className="pane">
                 <CommonLightPropertyGridComponent globalState={this.props.globalState} lockObject={this.props.lockObject} light={light} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
@@ -33,6 +40,9 @@ export class DirectionalLightPropertyGridComponent extends React.Component<IDire
                     <Color3LineComponent label="Specular" target={light} propertyName="specular" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <Vector3LineComponent label="Position" target={light} propertyName="position" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <Vector3LineComponent label="Direction" target={light} propertyName="direction" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    { !hideAutoCalcShadowZBounds &&
+                        <CheckBoxLineComponent label="Auto Calc Shadow ZBounds" target={light} propertyName="autoCalcShadowZBounds" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    }
                 </LineContainerComponent>
                 <CommonShadowLightPropertyGridComponent globalState={this.props.globalState} lockObject={this.props.lockObject} light={light} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
             </div>

+ 4 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/materials/commonMaterialPropertyGridComponent.tsx

@@ -32,6 +32,8 @@ export class CommonMaterialPropertyGridComponent extends React.Component<ICommon
     render() {
         const material = this.props.material;
 
+        material.depthFunction = material.depthFunction ?? 0;
+
         var orientationOptions = [
             { label: "Clockwise", value: Material.ClockWiseSideOrientation },
             { label: "Counterclockwise", value: Material.CounterClockWiseSideOrientation }
@@ -55,6 +57,7 @@ export class CommonMaterialPropertyGridComponent extends React.Component<ICommon
         ];
 
         var depthfunctionOptions = [
+            { label: "<Engine Default>", value: 0 },
             { label: "Never", value: Engine.NEVER },
             { label: "Always", value: Engine.ALWAYS },
             { label: "Equal", value: Engine.EQUAL },
@@ -87,7 +90,7 @@ export class CommonMaterialPropertyGridComponent extends React.Component<ICommon
                     <ButtonLineComponent label="Dispose" onClick={() => {
                         material.dispose();
                         this.props.globalState.onSelectionChangedObservable.notifyObservers(null);
-                    }} />                       
+                    }} />
                 </LineContainerComponent>
                 <LineContainerComponent globalState={this.props.globalState} title="TRANSPARENCY">
                     <SliderLineComponent label="Alpha" target={material} propertyName="alpha" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />

+ 12 - 0
src/Lights/Shadows/cascadedShadowGenerator.ts

@@ -492,6 +492,16 @@ export class CascadedShadowGenerator implements IShadowGenerator {
         this._breaksAreDirty = true;
     }
 
+    /** Gets the minimal distance used in the cascade break computation */
+    public get minDistance(): number {
+        return this._minDistance;
+    }
+
+    /** Gets the maximal distance used in the cascade break computation */
+    public get maxDistance(): number {
+        return this._maxDistance;
+    }
+
     /**
      * Gets the class name of that object
      * @returns "ShadowGenerator"
@@ -766,6 +776,8 @@ export class CascadedShadowGenerator implements IShadowGenerator {
             return;
         }
 
+        this._autoCalcDepthBounds = value;
+
         if (!value) {
             if (this._depthReducer) {
                 this._depthReducer.deactivate();