From d798bd24002cc170881dd6daf1f3691ba112a3d2 Mon Sep 17 00:00:00 2001 From: hellmor Date: Wed, 16 Aug 2023 22:23:33 +0800 Subject: [PATCH] feat(CSM) : add feature of Cascaded Shadow Map (#286) Add feature of Cascaded Shadow Map Rename boudingBox to boundingBox --- public | 2 +- samples/animation/Sample_CurveAnimation.ts | 13 +- samples/animation/Sample_MorphTarget.ts | 2 +- samples/animation/Sample_PropertyAnimation.ts | 3 +- samples/animation/Sample_Skeleton.ts | 1 - samples/animation/Sample_Skeleton2.ts | 4 +- samples/animation/Sample_Skeleton3.ts | 1 - samples/ext/Sample_Grass.ts | 3 +- samples/ext/Sample_Terrain.ts | 4 +- samples/geometry/Sample_ConduitGeometry2.ts | 5 +- samples/geometry/Sample_ConduitGeometry3.ts | 1 - samples/geometry/Sample_CustomGeometry.ts | 1 - samples/geometry/Sample_InternalGeometry.ts | 1 - samples/gi/Sample_GI.ts | 2 +- samples/gi/Sample_GICornellBox.ts | 9 +- samples/gui/Sample_POI.ts | 3 +- samples/gui/Sample_UIButton.ts | 1 - samples/gui/Sample_UIChangeParent.ts | 1 - samples/gui/Sample_UIImageColor.ts | 1 - samples/gui/Sample_UIImageGroup.ts | 1 - samples/gui/Sample_UIImageShadow.ts | 1 - samples/gui/Sample_UIMultiCanvas.ts | 1 - samples/gui/Sample_UIMultiPanel.ts | 2 - samples/gui/Sample_UIPanelOrder.ts | 1 - samples/gui/Sample_UIPanelScissor.ts | 1 - samples/gui/Sample_UIPerformance.ts | 1 - samples/gui/Sample_UIPerformance2.ts | 1 - samples/gui/Sample_UISingleImage.ts | 1 - samples/gui/Sample_UISpriteSheet.ts | 1 - samples/gui/Sample_UITextField.ts | 1 - samples/gui/Sample_UIVideo.ts | 1 - samples/gui/Sample_UIVisible.ts | 1 - samples/lights/Sample_CSM.ts | 146 ++++++++++++++ samples/lights/Sample_DirectLightShadow.ts | 22 ++- samples/loader/Sample_FlightHelmet.ts | 1 - samples/loader/Sample_LoadGLB.ts | 2 +- samples/loader/Sample_LoadGLB2.ts | 1 - samples/loader/Sample_LoadGLB3.ts | 1 - samples/loader/Sample_LoadGLTF.ts | 1 - samples/loader/Sample_LoadGLTF2.ts | 1 - samples/material/Sample_ChangeMaterial.ts | 1 - samples/material/Sample_ClearCoat.ts | 1 - samples/material/Sample_PBR.ts | 1 - samples/material/Sample_PBRMaterial.ts | 1 - samples/material/Sample_UVMove.ts | 1 - samples/octree/Sample_OctTreeBox.ts | 5 +- samples/octree/Sample_OctTreeFrustum.ts | 21 +- samples/octree/Sample_OctTreeRay.ts | 4 +- samples/physics/Sample_Physics01.ts | 1 - samples/physics/Sample_PhysicsBox.ts | 1 - samples/physics/Sample_PhysicsCar.ts | 1 - samples/post/Sample_Bloom.ts | 1 - samples/post/Sample_Fog.ts | 1 - samples/post/Sample_GTAO.ts | 1 - samples/post/Sample_GodRay.ts | 8 +- samples/post/Sample_TAA.ts | 1 - samples/render/Sample_BlendMode2.ts | 1 - src/Engine3D.ts | 10 +- .../shader/compute/DDGILighting_CSShader.ts | 86 ++++++--- src/assets/shader/compute/GodRay_cs.ts | 71 ++++++- src/assets/shader/compute/Picker_cs.ts | 39 +--- .../shader/core/common/GlobalUniform.ts | 23 ++- .../shader/core/pass/FrustumCulling_cs.ts | 40 +--- src/assets/shader/lighting/BxDF_frag.ts | 10 + .../materials/program/ShadowMapping_frag.ts | 126 +++++++++--- src/core/Camera3D.ts | 179 +++++++++++------- src/core/bound/Frustum.ts | 42 ++-- src/core/csm/CSM.ts | 3 + src/core/csm/FrustumCSM.ts | 138 ++++++++++++++ .../core/bindGroups/GlobalUniformGroup.ts | 87 +++++---- .../graphics/webGpu/shader/RenderShader.ts | 2 + .../renderJob/collect/ShadowLightsCollect.ts | 18 +- .../ddgi/DDGIIrradianceComputePass.ts | 2 +- .../passRenderer/ddgi/DDGILightingPass.ts | 8 +- .../passRenderer/ddgi/DDGIMultiBouncePass.ts | 2 +- .../passRenderer/ddgi/DDGIProbeRenderer.ts | 10 +- .../shadow/ShadowMapPassRenderer.ts | 165 ++++++++-------- src/index.ts | 2 + src/io/picker/PickCompute.ts | 3 +- src/setting/ShadowSetting.ts | 33 ++-- src/textures/Depth2DTextureArray.ts | 4 +- 81 files changed, 911 insertions(+), 490 deletions(-) create mode 100644 samples/lights/Sample_CSM.ts create mode 100644 src/core/csm/CSM.ts create mode 100644 src/core/csm/FrustumCSM.ts diff --git a/public b/public index fcd386df..1e9b664b 160000 --- a/public +++ b/public @@ -1 +1 @@ -Subproject commit fcd386dff4f93f6e4d5de69ac6d0160a06dae799 +Subproject commit 1e9b664bf6b6fc2985cfa5608463b57eed1324e5 diff --git a/samples/animation/Sample_CurveAnimation.ts b/samples/animation/Sample_CurveAnimation.ts index 008de852..c02ead0e 100644 --- a/samples/animation/Sample_CurveAnimation.ts +++ b/samples/animation/Sample_CurveAnimation.ts @@ -1,4 +1,5 @@ import { Object3D, Scene3D, AnimationCurve, Engine3D, AtmosphericComponent, CameraUtil, HoverCameraController, View3D, DirectLight, KelvinUtil, Keyframe, Object3DUtil, Time } from "@orillusion/core"; +import { GUIHelp } from "@orillusion/debug/GUIHelp"; class Sample_AnimCurve { lightObj3D: Object3D; @@ -12,22 +13,20 @@ class Sample_AnimCurve { async run() { await Engine3D.init({ beforeRender: () => this.renderUpdate() }); - Engine3D.setting.shadow.shadowBound = 100; - Engine3D.setting.shadow.shadowBias = 0.0001; - Engine3D.setting.shadow.pointShadowBias = 0.6; - Engine3D.setting.shadow.debug = true; - Engine3D.setting.shadow.autoUpdate = true; Engine3D.setting.shadow.updateFrameRate = 1; Engine3D.setting.shadow.type = `HARD`; + Engine3D.setting.shadow.csmScatteringExp = 0.5; + GUIHelp.init(); this.scene = new Scene3D(); let sky = this.scene.addComponent(AtmosphericComponent); let camera = CameraUtil.createCamera3DObject(this.scene); + camera.enableCSM = true; camera.perspective(60, Engine3D.aspect, 0.01, 5000.0); - camera.object3D.addComponent(HoverCameraController).setCamera(-30, -25, 400); + camera.object3D.addComponent(HoverCameraController).setCamera(-30, -45, 200); let view = new View3D(); view.scene = this.scene; @@ -43,7 +42,7 @@ class Sample_AnimCurve { /******** light *******/ { this.lightObj3D = new Object3D(); - this.lightObj3D.rotationX = 15; + this.lightObj3D.rotationX = 35; this.lightObj3D.rotationY = 110; this.lightObj3D.rotationZ = 0; let directLight = this.lightObj3D.addComponent(DirectLight); diff --git a/samples/animation/Sample_MorphTarget.ts b/samples/animation/Sample_MorphTarget.ts index 22af1b70..3f876564 100644 --- a/samples/animation/Sample_MorphTarget.ts +++ b/samples/animation/Sample_MorphTarget.ts @@ -9,7 +9,7 @@ class Sample_MorphTarget { influenceData: { [key: string]: number } = {}; async run() { - Engine3D.setting.shadow.shadowBias = 0.0001; + Engine3D.setting.shadow.shadowBound = 100; await Engine3D.init(); diff --git a/samples/animation/Sample_PropertyAnimation.ts b/samples/animation/Sample_PropertyAnimation.ts index 560a79e6..87277b02 100644 --- a/samples/animation/Sample_PropertyAnimation.ts +++ b/samples/animation/Sample_PropertyAnimation.ts @@ -11,7 +11,7 @@ class Sample_PropertyAnimation { Engine3D.setting.shadow.autoUpdate = true; Engine3D.setting.shadow.updateFrameRate = 1; Engine3D.setting.shadow.shadowBound = 20; - Engine3D.setting.shadow.shadowBias = 0.0001; + Engine3D.setting.shadow.shadowSize = 2048; await Engine3D.init(); let param = createSceneParam(); @@ -22,6 +22,7 @@ class Sample_PropertyAnimation { GUIUtil.renderDirLight(exampleScene.light, false); this.scene = exampleScene.scene; + exampleScene.camera.enableCSM = true; await this.initScene(this.scene); Engine3D.startRenderView(exampleScene.view); diff --git a/samples/animation/Sample_Skeleton.ts b/samples/animation/Sample_Skeleton.ts index ff26b72c..44051760 100644 --- a/samples/animation/Sample_Skeleton.ts +++ b/samples/animation/Sample_Skeleton.ts @@ -10,7 +10,6 @@ class Sample_Skeleton { Engine3D.setting.shadow.autoUpdate = true; Engine3D.setting.shadow.updateFrameRate = 1; Engine3D.setting.shadow.shadowBound = 100; - Engine3D.setting.shadow.shadowBias = 0.0001; await Engine3D.init(); diff --git a/samples/animation/Sample_Skeleton2.ts b/samples/animation/Sample_Skeleton2.ts index 7656a560..8442a5da 100644 --- a/samples/animation/Sample_Skeleton2.ts +++ b/samples/animation/Sample_Skeleton2.ts @@ -10,8 +10,7 @@ class Sample_Skeleton2 { Engine3D.setting.shadow.autoUpdate = true; Engine3D.setting.shadow.updateFrameRate = 1; - Engine3D.setting.shadow.shadowBound = 500; - Engine3D.setting.shadow.shadowBias = 0.0002; + Engine3D.setting.shadow.shadowSize = 2048; await Engine3D.init(); @@ -20,6 +19,7 @@ class Sample_Skeleton2 { this.scene.exposure = 1; let mainCamera = CameraUtil.createCamera3DObject(this.scene); + mainCamera.enableCSM = true; mainCamera.perspective(60, webGPUContext.aspect, 1, 3000.0); let hoverCameraController = mainCamera.object3D.addComponent(HoverCameraController); diff --git a/samples/animation/Sample_Skeleton3.ts b/samples/animation/Sample_Skeleton3.ts index e2df05b2..6646a5ec 100644 --- a/samples/animation/Sample_Skeleton3.ts +++ b/samples/animation/Sample_Skeleton3.ts @@ -13,7 +13,6 @@ class Sample_Skeleton3 { Engine3D.setting.shadow.autoUpdate = true; Engine3D.setting.shadow.updateFrameRate = 1; Engine3D.setting.shadow.shadowBound = 100; - Engine3D.setting.shadow.shadowBias = 0.0002; await Engine3D.init({ renderLoop: () => this.onRenderLoop(), }); diff --git a/samples/ext/Sample_Grass.ts b/samples/ext/Sample_Grass.ts index e14988d2..9adec569 100644 --- a/samples/ext/Sample_Grass.ts +++ b/samples/ext/Sample_Grass.ts @@ -10,7 +10,6 @@ class Sample_Grass { async run() { Engine3D.setting.shadow.autoUpdate = true; Engine3D.setting.shadow.updateFrameRate = 1; - Engine3D.setting.shadow.shadowBias = 0.0003; Engine3D.setting.shadow.shadowBound = 500; Engine3D.setting.shadow.shadowSize = 1024; // Engine3D.setting.render.zPrePass = true; @@ -23,6 +22,7 @@ class Sample_Grass { this.view.scene.addComponent(AtmosphericComponent); this.view.camera = CameraUtil.createCamera3DObject(this.view.scene); + this.view.camera.enableCSM = true; this.view.camera.perspective(60, webGPUContext.aspect, 1, 5000.0); this.view.camera.object3D.z = -15; this.view.camera.object3D.addComponent(HoverCameraController).setCamera(35, -20, 500); @@ -165,7 +165,6 @@ class Sample_Grass { GUIHelp.addFolder("shadow"); GUIHelp.add(Engine3D.setting.shadow, "shadowBound", 0.0, 3000, 0.0001); - GUIHelp.add(Engine3D.setting.shadow, "shadowBias", 0.0, 1, 0.0001); GUIHelp.endFolder(); let globalFog = this.post.getPost(GlobalFog); diff --git a/samples/ext/Sample_Terrain.ts b/samples/ext/Sample_Terrain.ts index 233ed02b..3a22b079 100644 --- a/samples/ext/Sample_Terrain.ts +++ b/samples/ext/Sample_Terrain.ts @@ -10,9 +10,8 @@ class Sample_Terrain { async run() { Engine3D.setting.shadow.autoUpdate = true; Engine3D.setting.shadow.updateFrameRate = 1; - Engine3D.setting.shadow.shadowBias = 0.0003; Engine3D.setting.shadow.shadowBound = 500; - Engine3D.setting.shadow.shadowSize = 1024; + Engine3D.setting.shadow.shadowSize = 2048; // Engine3D.setting.render.zPrePass = true; GUIHelp.init(); @@ -81,7 +80,6 @@ class Sample_Terrain { GUIHelp.addFolder("shadow"); GUIHelp.add(Engine3D.setting.shadow, "shadowBound", 0.0, 3000, 0.0001); - GUIHelp.add(Engine3D.setting.shadow, "shadowBias", 0.0, 1, 0.0001); GUIHelp.endFolder(); let globalFog = this.post.getPost(GlobalFog); diff --git a/samples/geometry/Sample_ConduitGeometry2.ts b/samples/geometry/Sample_ConduitGeometry2.ts index 883350ad..ca4d07f2 100644 --- a/samples/geometry/Sample_ConduitGeometry2.ts +++ b/samples/geometry/Sample_ConduitGeometry2.ts @@ -18,11 +18,12 @@ class Sample_ConduitGeometry2 { async run() { GUIHelp.init(); Engine3D.setting.shadow.shadowBound = 50; - Engine3D.setting.shadow.shadowBias = 0.0001; + Engine3D.setting.shadow.shadowSize = 1024; let param = createSceneParam(); - param.camera.distance = 50; + param.camera.distance = 60; await Engine3D.init(); let exampleScene = createExampleScene(param); + exampleScene.camera.enableCSM = true; this.scene = exampleScene.scene; Engine3D.startRenderView(exampleScene.view); await this.createMaterial(); diff --git a/samples/geometry/Sample_ConduitGeometry3.ts b/samples/geometry/Sample_ConduitGeometry3.ts index 13ea7e86..04fd2e8c 100644 --- a/samples/geometry/Sample_ConduitGeometry3.ts +++ b/samples/geometry/Sample_ConduitGeometry3.ts @@ -14,7 +14,6 @@ class Sample_ConduitGeometry3 { async run() { GUIHelp.init(); Engine3D.setting.shadow.shadowBound = 50; - Engine3D.setting.shadow.shadowBias = 0.0001; let param = createSceneParam(); param.camera.distance = 50; await Engine3D.init(); diff --git a/samples/geometry/Sample_CustomGeometry.ts b/samples/geometry/Sample_CustomGeometry.ts index ce14ce92..ada85a13 100644 --- a/samples/geometry/Sample_CustomGeometry.ts +++ b/samples/geometry/Sample_CustomGeometry.ts @@ -7,7 +7,6 @@ class Sample_CustomGeometry { dirLight: DirectLight; async run() { Engine3D.setting.shadow.autoUpdate = true; - Engine3D.setting.shadow.shadowBias = 0.002; GUIHelp.init(); diff --git a/samples/geometry/Sample_InternalGeometry.ts b/samples/geometry/Sample_InternalGeometry.ts index 58a5b418..56ddbc42 100644 --- a/samples/geometry/Sample_InternalGeometry.ts +++ b/samples/geometry/Sample_InternalGeometry.ts @@ -7,7 +7,6 @@ class Sample_InternalGeometry { lightObj: Object3D; async run() { Engine3D.setting.shadow.autoUpdate = true; - Engine3D.setting.shadow.shadowBias = 0.002; Engine3D.setting.shadow.shadowBound = 200; GUIHelp.init(); diff --git a/samples/gi/Sample_GI.ts b/samples/gi/Sample_GI.ts index a93fe075..96730343 100644 --- a/samples/gi/Sample_GI.ts +++ b/samples/gi/Sample_GI.ts @@ -28,7 +28,6 @@ class Sample_GI { Engine3D.setting.gi.autoRenderProbe = true; Engine3D.setting.shadow.shadowBound = 200; - Engine3D.setting.shadow.shadowBias = 0.001; Engine3D.setting.shadow.debug = true; Engine3D.setting.shadow.autoUpdate = true; @@ -58,6 +57,7 @@ class Sample_GI { let exampleScene = createExampleScene(param); exampleScene.atmosphericSky.exposure = 0.5; this.scene = exampleScene.scene; + exampleScene.camera.enableCSM = true; Engine3D.startRenderViews([exampleScene.view]); let job = Engine3D.getRenderJob(exampleScene.view); await this.initScene(); diff --git a/samples/gi/Sample_GICornellBox.ts b/samples/gi/Sample_GICornellBox.ts index fbabe0e9..6286e26b 100644 --- a/samples/gi/Sample_GICornellBox.ts +++ b/samples/gi/Sample_GICornellBox.ts @@ -30,10 +30,8 @@ class Sample_GICornellBox { Engine3D.setting.gi.depthSharpness = 1; Engine3D.setting.gi.autoRenderProbe = true; - Engine3D.setting.shadow.shadowBound = 80; - Engine3D.setting.shadow.shadowBias = 0.000035; Engine3D.setting.shadow.debug = true; - + Engine3D.setting.shadow.shadowSize = 1024; Engine3D.setting.shadow.autoUpdate = true; Engine3D.setting.shadow.updateFrameRate = 1; @@ -57,10 +55,11 @@ class Sample_GICornellBox { } }); let param = createSceneParam(); - param.camera.distance = 40; + param.camera.distance = 100; let exampleScene = createExampleScene(param); exampleScene.hoverCtrl.setCamera(0, 0, 20); + exampleScene.camera.enableCSM = true; this.scene = exampleScene.scene; this.addGIProbes(); Engine3D.startRenderViews([exampleScene.view]); @@ -69,6 +68,8 @@ class Sample_GICornellBox { postProcessing.addPost(GTAOPost); postProcessing.addPost(HDRBloomPost); + Engine3D.setting.shadow.csmScatteringExp = 0.8; + GUIHelp.add(Engine3D.setting.shadow, 'csmScatteringExp', 0.5, 1, 0.001); await this.initScene(); } diff --git a/samples/gui/Sample_POI.ts b/samples/gui/Sample_POI.ts index 50d5cbf4..7526ffe3 100644 --- a/samples/gui/Sample_POI.ts +++ b/samples/gui/Sample_POI.ts @@ -12,7 +12,7 @@ class Sample_POI { Engine3D.setting.shadow.autoUpdate = true; Engine3D.setting.shadow.updateFrameRate = 1; Engine3D.setting.shadow.shadowBound = 20; - Engine3D.setting.shadow.shadowBias = 0.0001; + Engine3D.setting.shadow.csmScatteringExp = 1; await Engine3D.init({ renderLoop: () => { this.loop(); } }); let param = createSceneParam(); @@ -22,6 +22,7 @@ class Sample_POI { GUIHelp.init(); this.scene = exampleScene.scene; + exampleScene.camera.enableCSM = true; Engine3D.startRenderView(exampleScene.view); diff --git a/samples/gui/Sample_UIButton.ts b/samples/gui/Sample_UIButton.ts index 0f8f9452..9531db39 100644 --- a/samples/gui/Sample_UIButton.ts +++ b/samples/gui/Sample_UIButton.ts @@ -9,7 +9,6 @@ export class Sample_UIButton { async run() { Engine3D.setting.shadow.autoUpdate = true; - Engine3D.setting.shadow.shadowBias = 0.002; GUIHelp.init(); diff --git a/samples/gui/Sample_UIChangeParent.ts b/samples/gui/Sample_UIChangeParent.ts index 1f3f5fd0..93989f75 100644 --- a/samples/gui/Sample_UIChangeParent.ts +++ b/samples/gui/Sample_UIChangeParent.ts @@ -5,7 +5,6 @@ import { Engine3D, Object3DUtil, Object3D, ViewPanel, UIImage, ImageType, UIPane export class Sample_UIChangeParent { async run() { Engine3D.setting.shadow.autoUpdate = true; - Engine3D.setting.shadow.shadowBias = 0.002; GUIHelp.init(); diff --git a/samples/gui/Sample_UIImageColor.ts b/samples/gui/Sample_UIImageColor.ts index 74876117..f00f3c21 100644 --- a/samples/gui/Sample_UIImageColor.ts +++ b/samples/gui/Sample_UIImageColor.ts @@ -10,7 +10,6 @@ export class Sample_UIImageColor { async run() { Engine3D.setting.shadow.autoUpdate = true; - Engine3D.setting.shadow.shadowBias = 0.002; GUIHelp.init(); diff --git a/samples/gui/Sample_UIImageGroup.ts b/samples/gui/Sample_UIImageGroup.ts index 09e04675..25b1bb65 100644 --- a/samples/gui/Sample_UIImageGroup.ts +++ b/samples/gui/Sample_UIImageGroup.ts @@ -10,7 +10,6 @@ export class Sample_UIImageGroup { async run() { Engine3D.setting.shadow.autoUpdate = true; - Engine3D.setting.shadow.shadowBias = 0.002; GUIHelp.init(); diff --git a/samples/gui/Sample_UIImageShadow.ts b/samples/gui/Sample_UIImageShadow.ts index 90872a98..a6300c36 100644 --- a/samples/gui/Sample_UIImageShadow.ts +++ b/samples/gui/Sample_UIImageShadow.ts @@ -8,7 +8,6 @@ class Sample_UIImageShadow { async run() { Engine3D.setting.shadow.autoUpdate = true; - Engine3D.setting.shadow.shadowBias = 0.002; GUIHelp.init(); diff --git a/samples/gui/Sample_UIMultiCanvas.ts b/samples/gui/Sample_UIMultiCanvas.ts index f1dce363..2bb59adf 100644 --- a/samples/gui/Sample_UIMultiCanvas.ts +++ b/samples/gui/Sample_UIMultiCanvas.ts @@ -4,7 +4,6 @@ import { Engine3D, Object3DUtil, Object3D, UIImage, ImageType, Color, UIPanel, V class Sample_UIMultiCanvas { async run() { Engine3D.setting.shadow.autoUpdate = true; - Engine3D.setting.shadow.shadowBias = 0.002; await Engine3D.init(); await Engine3D.res.loadFont('fnt/0.fnt'); diff --git a/samples/gui/Sample_UIMultiPanel.ts b/samples/gui/Sample_UIMultiPanel.ts index b92c3806..32d70270 100644 --- a/samples/gui/Sample_UIMultiPanel.ts +++ b/samples/gui/Sample_UIMultiPanel.ts @@ -10,8 +10,6 @@ export class Sample_UIMultiPanel { async run() { Engine3D.setting.shadow.autoUpdate = true; - Engine3D.setting.shadow.shadowBias = 0.002; - Engine3D.setting.shadow.shadowBound = 100; GUIHelp.init(); diff --git a/samples/gui/Sample_UIPanelOrder.ts b/samples/gui/Sample_UIPanelOrder.ts index db3c4d02..e411f1b0 100644 --- a/samples/gui/Sample_UIPanelOrder.ts +++ b/samples/gui/Sample_UIPanelOrder.ts @@ -6,7 +6,6 @@ export class Sample_UIPanelOrder { async run() { Engine3D.setting.shadow.autoUpdate = true; - Engine3D.setting.shadow.shadowBias = 0.002; GUIHelp.init(); diff --git a/samples/gui/Sample_UIPanelScissor.ts b/samples/gui/Sample_UIPanelScissor.ts index 5f27351b..5ba143ec 100644 --- a/samples/gui/Sample_UIPanelScissor.ts +++ b/samples/gui/Sample_UIPanelScissor.ts @@ -8,7 +8,6 @@ class Sample_UIPanelScissor { async run() { Engine3D.setting.shadow.autoUpdate = true; - Engine3D.setting.shadow.shadowBias = 0.002; GUIHelp.init(); diff --git a/samples/gui/Sample_UIPerformance.ts b/samples/gui/Sample_UIPerformance.ts index 368d0ab5..629fa267 100644 --- a/samples/gui/Sample_UIPerformance.ts +++ b/samples/gui/Sample_UIPerformance.ts @@ -57,7 +57,6 @@ export class Sample_UISpriteSheet { async run() { Engine3D.setting.shadow.autoUpdate = true; - Engine3D.setting.shadow.shadowBias = 0.002; GUIConfig.quadMaxCountForView = 5001; diff --git a/samples/gui/Sample_UIPerformance2.ts b/samples/gui/Sample_UIPerformance2.ts index a705a0b9..95315eed 100644 --- a/samples/gui/Sample_UIPerformance2.ts +++ b/samples/gui/Sample_UIPerformance2.ts @@ -61,7 +61,6 @@ export class Sample_UIPerformance2 { async run() { Engine3D.setting.shadow.autoUpdate = true; - Engine3D.setting.shadow.shadowBias = 0.002; GUIConfig.quadMaxCountForView = 5001; diff --git a/samples/gui/Sample_UISingleImage.ts b/samples/gui/Sample_UISingleImage.ts index 9723b17f..189ff005 100644 --- a/samples/gui/Sample_UISingleImage.ts +++ b/samples/gui/Sample_UISingleImage.ts @@ -6,7 +6,6 @@ import { GUIUtil } from "@samples/utils/GUIUtil"; export class Sample_UISingleImage { async run() { Engine3D.setting.shadow.autoUpdate = true; - Engine3D.setting.shadow.shadowBias = 0.002; GUIHelp.init(); diff --git a/samples/gui/Sample_UISpriteSheet.ts b/samples/gui/Sample_UISpriteSheet.ts index 045667d1..f1d7e596 100644 --- a/samples/gui/Sample_UISpriteSheet.ts +++ b/samples/gui/Sample_UISpriteSheet.ts @@ -9,7 +9,6 @@ export class Sample_UISpriteSheet { async run() { Engine3D.setting.shadow.autoUpdate = true; - Engine3D.setting.shadow.shadowBias = 0.002; GUIHelp.init(); diff --git a/samples/gui/Sample_UITextField.ts b/samples/gui/Sample_UITextField.ts index 4f7eadaa..b3f9c262 100644 --- a/samples/gui/Sample_UITextField.ts +++ b/samples/gui/Sample_UITextField.ts @@ -7,7 +7,6 @@ export class Sample_UITextField { async run() { Engine3D.setting.shadow.autoUpdate = true; - Engine3D.setting.shadow.shadowBias = 0.002; GUIHelp.init(); diff --git a/samples/gui/Sample_UIVideo.ts b/samples/gui/Sample_UIVideo.ts index f3d724a3..177deb88 100644 --- a/samples/gui/Sample_UIVideo.ts +++ b/samples/gui/Sample_UIVideo.ts @@ -7,7 +7,6 @@ export class Sample_UIVideo { async run() { Engine3D.setting.shadow.autoUpdate = true; - Engine3D.setting.shadow.shadowBias = 0.002; GUIHelp.init(); diff --git a/samples/gui/Sample_UIVisible.ts b/samples/gui/Sample_UIVisible.ts index e2ce5123..cbdd200d 100644 --- a/samples/gui/Sample_UIVisible.ts +++ b/samples/gui/Sample_UIVisible.ts @@ -9,7 +9,6 @@ export class Sample_UIVisible { spriteCount = 10; async run() { Engine3D.setting.shadow.autoUpdate = true; - Engine3D.setting.shadow.shadowBias = 0.002; GUIHelp.init(); diff --git a/samples/lights/Sample_CSM.ts b/samples/lights/Sample_CSM.ts new file mode 100644 index 00000000..c4e76cdb --- /dev/null +++ b/samples/lights/Sample_CSM.ts @@ -0,0 +1,146 @@ +import { GUIHelp } from "@orillusion/debug/GUIHelp"; +import { Scene3D, HoverCameraController, Engine3D, AtmosphericComponent, Object3D, Camera3D, Vector3, View3D, DirectLight, KelvinUtil, LitMaterial, MeshRenderer, BoxGeometry, CameraUtil, SphereGeometry, Color, Object3DUtil, BlendMode } from "@orillusion/core"; +import { GUIUtil } from "@samples/utils/GUIUtil"; + +//sample of csm +class Sample_CSM { + scene: Scene3D; + view: View3D; + light: DirectLight; + boxRenderer: MeshRenderer; + viewCamera: Camera3D; + async run() { + Engine3D.setting.shadow.autoUpdate = true; + Engine3D.setting.shadow.shadowSize = 1024; + await Engine3D.init({ renderLoop: () => { this.loop(); } }); + + GUIHelp.init(); + + this.scene = new Scene3D(); + let sky = this.scene.addComponent(AtmosphericComponent); + + // init camera3D + let mainCamera = CameraUtil.createCamera3D(null, this.scene); + mainCamera.perspective(60, Engine3D.aspect, 1, 5000.0); + //set camera data + mainCamera.object3D.z = -15; + mainCamera.object3D.addComponent(HoverCameraController).setCamera(-15, -35, 200); + + sky.relativeTransform = this.initLight('mainLight', 30, 45); + this.initLight('subLight', 10, 10); + this.initScene(); + + let view = new View3D(); + view.scene = this.scene; + view.camera = mainCamera; + this.view = view; + this.viewCamera = mainCamera; + + mainCamera.enableCSM = true; + GUIHelp.addFolder('CSM') + GUIHelp.add(mainCamera, 'enableCSM'); + GUIHelp.add(Engine3D.setting.shadow, 'csmScatteringExp', 0.5, 1.0, 0.01); + GUIHelp.add(Engine3D.setting.shadow, 'csmMargin', 0.01, 0.5, 0.01); + GUIHelp.open(); + GUIHelp.endFolder(); + Engine3D.startRenderView(view); + } + + // create direction light + private initLight(name: string, intensity: number, rotY: number) { + let lightObj3D = new Object3D(); + lightObj3D.name = name; + lightObj3D.rotationX = 46; + lightObj3D.rotationY = 62 + rotY; + lightObj3D.rotationZ = 0; + let sunLight = lightObj3D.addComponent(DirectLight); + sunLight.intensity = intensity; + sunLight.lightColor = KelvinUtil.color_temperature_to_rgb(6553); + sunLight.castShadow = true; + + GUIUtil.renderDirLight(sunLight); + this.scene.addChild(lightObj3D); + this.light = sunLight; + return sunLight.transform; + } + + initScene() { + { + let obj = new Object3D(); + let mr = obj.addComponent(MeshRenderer); + mr.geometry = new BoxGeometry(20, 100, 20); + mr.material = new LitMaterial(); + this.scene.addChild(obj); + } + + this.createBox(); + { + let mat = new LitMaterial(); + mat.baseMap = Engine3D.res.grayTexture; + let floor = new Object3D(); + let mr = floor.addComponent(MeshRenderer); + mr.geometry = new BoxGeometry(10000, 1, 10000); + mr.material = mat; + this.scene.addChild(floor); + } + + for (let i = 0; i < 1000; i++) { + let item = Object3DUtil.GetSingleSphere(4, 0.6, 0.4, 0.2); + let angle = Math.PI * 4 * i / 50; + item.x = Math.sin(angle) * (50 + i ** 1.4); + item.z = Math.cos(angle) * (50 + i ** 1.4); + item.y = 4; + let scale = ((i ** 1.4) * 5 + 1000) / 1000; + item.scaleX = item.scaleZ = scale; + item.scaleY = scale * 5; + this.scene.addChild(item); + } + } + + createBox() { + let box = new Object3D(); + let geom = new BoxGeometry(1, 1, 1); + let material = new LitMaterial(); + material.transparent = true; + material.shaderState.depthWriteEnabled = false + material.blendMode = BlendMode.NORMAL; + material.cullMode = "front"; + material.baseColor = new Color(0.2, 0.2, 0, 0.1); + let renderer = box.addComponent(MeshRenderer); + renderer.material = material; + renderer.geometry = geom; + // this.scene.addChild(box); + this.boxRenderer = renderer; + } + + private _shadowPos: Vector3 = new Vector3(); + private _shadowCameraTarget: Vector3 = new Vector3(); + loop() { + let viewCamera = this.viewCamera; + let light = this.light; + let view = this.view; + if (!this.boxRenderer || !this.viewCamera.csm) + return; + + + let csmBound = this.viewCamera.csm.children[0].bound; + //update box + let size = this.viewCamera.getCSMShadowWorldExtents(0) * 2; + this.boxRenderer.object3D.scaleX = size; + this.boxRenderer.object3D.scaleY = size; + this.boxRenderer.object3D.scaleZ = this.viewCamera.csm.children[0].shadowCamera.far; + + this.boxRenderer.object3D.localRotation = light.transform.localRotation; + this.boxRenderer.object3D.localPosition = csmBound.center; + + // light direction + this._shadowPos.copy(light.direction).normalize(viewCamera.far); + csmBound.center.add(this._shadowPos, this._shadowCameraTarget); + csmBound.center.subtract(this._shadowPos, this._shadowPos); + view.graphic3D.drawLines('shadowLine', [this._shadowPos, this._shadowCameraTarget], new Color(1, 1, 0, 1)); + } + +} + + +new Sample_CSM().run(); diff --git a/samples/lights/Sample_DirectLightShadow.ts b/samples/lights/Sample_DirectLightShadow.ts index 6a8de19e..486883ec 100644 --- a/samples/lights/Sample_DirectLightShadow.ts +++ b/samples/lights/Sample_DirectLightShadow.ts @@ -1,5 +1,5 @@ import { GUIHelp } from "@orillusion/debug/GUIHelp"; -import { Scene3D, HoverCameraController, Engine3D, AtmosphericComponent, Object3D, Camera3D, Vector3, View3D, DirectLight, KelvinUtil, LitMaterial, MeshRenderer, BoxGeometry, CameraUtil } from "@orillusion/core"; +import { Scene3D, HoverCameraController, Engine3D, AtmosphericComponent, Object3D, Camera3D, Vector3, View3D, DirectLight, KelvinUtil, LitMaterial, MeshRenderer, BoxGeometry, CameraUtil, SphereGeometry, Color, Object3DUtil } from "@orillusion/core"; import { GUIUtil } from "@samples/utils/GUIUtil"; //sample of direction light @@ -7,8 +7,7 @@ class Sample_DirectLightShadow { scene: Scene3D; async run() { Engine3D.setting.shadow.autoUpdate = true; - Engine3D.setting.shadow.shadowBias = 0.0001; - Engine3D.setting.shadow.shadowBound = 100; + Engine3D.setting.shadow.shadowBound = 400; await Engine3D.init({}); @@ -22,9 +21,7 @@ class Sample_DirectLightShadow { mainCamera.perspective(60, Engine3D.aspect, 1, 5000.0); //set camera data mainCamera.object3D.z = -15; - mainCamera.object3D - .addComponent(HoverCameraController) - .setCamera(-15, -35, 150); + mainCamera.object3D.addComponent(HoverCameraController).setCamera(-15, -35, 200); sky.relativeTransform = this.initLight(); this.initScene(); @@ -68,10 +65,21 @@ class Sample_DirectLightShadow { // mat.metallic = 0.6; let floor = new Object3D(); let mr = floor.addComponent(MeshRenderer); - mr.geometry = new BoxGeometry(200, 1, 200); + mr.geometry = new BoxGeometry(10000, 1, 10000); mr.material = mat; this.scene.addChild(floor); } + + { + for (let i = 0; i < 100; i++) { + let item = Object3DUtil.GetSingleSphere(4, 0.6, 0.4, 0.2); + let angle = Math.PI * 4 * i / 50; + item.x = Math.sin(angle) * (50 + i); + item.z = Math.cos(angle) * (50 + i); + // item.y = 4; + this.scene.addChild(item); + } + } } } diff --git a/samples/loader/Sample_FlightHelmet.ts b/samples/loader/Sample_FlightHelmet.ts index e7e4413d..f2a67da9 100644 --- a/samples/loader/Sample_FlightHelmet.ts +++ b/samples/loader/Sample_FlightHelmet.ts @@ -20,7 +20,6 @@ class Sample_FlightHelmet { Engine3D.setting.shadow.autoUpdate = true; Engine3D.setting.shadow.shadowBound = 10; - Engine3D.setting.shadow.shadowBias = 0.0001; Engine3D.setting.render.postProcessing.ssao.radius = 0.018; Engine3D.setting.render.postProcessing.ssao.aoPower = 1; Engine3D.setting.render.postProcessing.bloom = { diff --git a/samples/loader/Sample_LoadGLB.ts b/samples/loader/Sample_LoadGLB.ts index 0283c286..62e44ce6 100644 --- a/samples/loader/Sample_LoadGLB.ts +++ b/samples/loader/Sample_LoadGLB.ts @@ -6,9 +6,9 @@ export class Sample_LoadGLB { scene: Scene3D; async run() { + Engine3D.setting.shadow.shadowBound = 100; await Engine3D.init(); Engine3D.setting.shadow.autoUpdate = true; - Engine3D.setting.shadow.shadowBias = 0.0001; let exampleScene = createExampleScene(); this.scene = exampleScene.scene; Engine3D.startRenderView(exampleScene.view); diff --git a/samples/loader/Sample_LoadGLB2.ts b/samples/loader/Sample_LoadGLB2.ts index d513c20d..8a951fe0 100644 --- a/samples/loader/Sample_LoadGLB2.ts +++ b/samples/loader/Sample_LoadGLB2.ts @@ -11,7 +11,6 @@ export class Sample_LoadGLB2 { async run() { await Engine3D.init(); Engine3D.setting.shadow.autoUpdate = true; - Engine3D.setting.shadow.shadowBias = 0.0001; let exampleScene = createExampleScene(); exampleScene.atmosphericSky.displaySun = false; exampleScene.atmosphericSky.sunRadiance = 1; diff --git a/samples/loader/Sample_LoadGLB3.ts b/samples/loader/Sample_LoadGLB3.ts index 24298cde..de8c9783 100644 --- a/samples/loader/Sample_LoadGLB3.ts +++ b/samples/loader/Sample_LoadGLB3.ts @@ -8,7 +8,6 @@ export class Sample_LoadGLB3 { async run() { await Engine3D.init(); Engine3D.setting.shadow.autoUpdate = true; - Engine3D.setting.shadow.shadowBias = 0.0001; let exampleScene = createExampleScene(); this.scene = exampleScene.scene; diff --git a/samples/loader/Sample_LoadGLTF.ts b/samples/loader/Sample_LoadGLTF.ts index 35f79ed1..10364f32 100644 --- a/samples/loader/Sample_LoadGLTF.ts +++ b/samples/loader/Sample_LoadGLTF.ts @@ -10,7 +10,6 @@ class Sample_LoadGLTF { //config settings Engine3D.setting.material.materialChannelDebug = true; Engine3D.setting.shadow.shadowBound = 5; - Engine3D.setting.shadow.shadowBias = 0.0001; Engine3D.setting.render.postProcessing.bloom = { enable: true, blurX: 4, diff --git a/samples/loader/Sample_LoadGLTF2.ts b/samples/loader/Sample_LoadGLTF2.ts index cf5dec4a..7a8a43cf 100644 --- a/samples/loader/Sample_LoadGLTF2.ts +++ b/samples/loader/Sample_LoadGLTF2.ts @@ -9,7 +9,6 @@ class Sample_LoadGLTF2 { //config settings Engine3D.setting.material.materialChannelDebug = true; Engine3D.setting.shadow.shadowBound = 80; - Engine3D.setting.shadow.shadowBias = 0.0001; //init engine await Engine3D.init(); diff --git a/samples/material/Sample_ChangeMaterial.ts b/samples/material/Sample_ChangeMaterial.ts index d5c7da5b..0c37b0ec 100644 --- a/samples/material/Sample_ChangeMaterial.ts +++ b/samples/material/Sample_ChangeMaterial.ts @@ -12,7 +12,6 @@ class Sample_ChangeMaterial { Engine3D.setting.material.materialChannelDebug = true; Engine3D.setting.shadow.shadowBound = 5; - Engine3D.setting.shadow.shadowBias = 0.002; Engine3D.setting.render.postProcessing.bloom = { enable: true, blurX: 4, diff --git a/samples/material/Sample_ClearCoat.ts b/samples/material/Sample_ClearCoat.ts index 7a5f0d7a..6a733dd8 100644 --- a/samples/material/Sample_ClearCoat.ts +++ b/samples/material/Sample_ClearCoat.ts @@ -16,7 +16,6 @@ class Sample_ClearCoat { //config settings Engine3D.setting.shadow.shadowBound = 300; - Engine3D.setting.shadow.shadowBias = 0.0004; Engine3D.setting.render.postProcessing.bloom = { enable: true, blurX: 4, diff --git a/samples/material/Sample_PBR.ts b/samples/material/Sample_PBR.ts index 9391d502..0901950d 100644 --- a/samples/material/Sample_PBR.ts +++ b/samples/material/Sample_PBR.ts @@ -12,7 +12,6 @@ class Sample_PBR { await Engine3D.init({}); Engine3D.setting.shadow.shadowBound = 5; - Engine3D.setting.shadow.shadowBias = 0.002; GUIHelp.init(); diff --git a/samples/material/Sample_PBRMaterial.ts b/samples/material/Sample_PBRMaterial.ts index e7f4d91d..4f8d1ef7 100644 --- a/samples/material/Sample_PBRMaterial.ts +++ b/samples/material/Sample_PBRMaterial.ts @@ -11,7 +11,6 @@ class Sample_PBRMaterial { //config settings Engine3D.setting.shadow.shadowBound = 50; - Engine3D.setting.shadow.shadowBias = 0.0001; Engine3D.setting.render.postProcessing.bloom = { enable: true, blurX: 4, diff --git a/samples/material/Sample_UVMove.ts b/samples/material/Sample_UVMove.ts index de9a7888..82820e68 100644 --- a/samples/material/Sample_UVMove.ts +++ b/samples/material/Sample_UVMove.ts @@ -12,7 +12,6 @@ class Sample_UVMove { Engine3D.setting.material.materialChannelDebug = true; Engine3D.setting.shadow.shadowBound = 5; - Engine3D.setting.shadow.shadowBias = 0.002; Engine3D.setting.render.postProcessing.bloom = { enable: true, blurX: 4, diff --git a/samples/octree/Sample_OctTreeBox.ts b/samples/octree/Sample_OctTreeBox.ts index bc055253..68a95af0 100644 --- a/samples/octree/Sample_OctTreeBox.ts +++ b/samples/octree/Sample_OctTreeBox.ts @@ -10,7 +10,7 @@ export class Sample_OctTreeBox { entities: OctreeEntity[] = []; tree: Octree; red = new Color(1, 0, 0, 1); - gree = new Color(0, 1, 0, 1); + green = new Color(0, 1, 0, 1); yellow = new Color(1, 1, 0, 1) blue = new Color(0, 0, 1, 1) white = new Color(1, 1, 1, 1) @@ -54,7 +54,6 @@ export class Sample_OctTreeBox { GUIHelp.add(this.movingBox.size, 'x', 1, 200, 1).onChange(() => { updateBox(); }); GUIHelp.add(this.movingBox.size, 'y', 1, 200, 1).onChange(() => { updateBox(); }); GUIHelp.add(this.movingBox.size, 'z', 1, 200, 1).onChange(() => { updateBox(); }); - GUIHelp.open(); GUIHelp.endFolder(); } @@ -105,7 +104,7 @@ export class Sample_OctTreeBox { for (let item of this.entities) { item.renderer.enable = retBoolean[item.uuid]; } - this.view.graphic3D.drawBoundingBox('pick', this.movingBox, this.gree); + this.view.graphic3D.drawBoundingBox('pick', this.movingBox, this.green); } loop() { diff --git a/samples/octree/Sample_OctTreeFrustum.ts b/samples/octree/Sample_OctTreeFrustum.ts index d6b7b110..781fe823 100644 --- a/samples/octree/Sample_OctTreeFrustum.ts +++ b/samples/octree/Sample_OctTreeFrustum.ts @@ -12,13 +12,12 @@ export class Sample_OctTreeFrustum { entities: OctreeEntity[] = []; tree: Octree; red = new Color(1, 0, 0, 1); - gree = new Color(0, 1, 0, 1); + green = new Color(0, 1, 0, 1); yellow = new Color(1, 1, 0, 1) blue = new Color(0, 0, 1, 1) white = new Color(1, 1, 1, 1) camera: Camera3D; - frustumBound: BoundingBox; async run() { Engine3D.setting.occlusionQuery.octree = { width: 1000, height: 1000, depth: 1000, x: 0, y: 0, z: 0 } @@ -46,8 +45,6 @@ export class Sample_OctTreeFrustum { this.camera.perspective(60, Engine3D.aspect, 1, 1000); this.view.scene.addChild(this.camera.object3D); - this.frustumBound = new BoundingBox(); - GUIUtil.renderTransform(this.camera.transform); } @@ -61,7 +58,7 @@ export class Sample_OctTreeFrustum { let geometry = new BoxGeometry(); for (let i = 0; i < 100; i++) { for (let j = 0; j < 100; j++) { - for (let k = 0; k < 5; k++) { + for (let k = 0; k < 2; k++) { object3D = new Object3D(); let renderer = object3D.addComponent(MeshRenderer); renderer.geometry = geometry; @@ -85,11 +82,13 @@ export class Sample_OctTreeFrustum { private queryResult: OctreeEntity[] = []; private octreeTest() { - let range = this.camera.frustum.genBox(this.camera.pvMatrixInv); - this.frustumBound.setFromMinMax(new Vector3(range.minX, range.minY, range.minZ), new Vector3(range.maxX, range.maxY, range.maxZ)); - this.view.graphic3D.drawCameraFrustum(this.camera, this.gree); - this.view.graphic3D.drawBoundingBox('box', this.frustumBound, this.red); - + this.view.graphic3D.ClearAll(); + this.view.graphic3D.drawCameraFrustum(this.camera, this.green); + // this.view.graphic3D.drawBoundingBox('box', this.camera.frustum.boudingBox, this.red); + // this.camera.frustum.csm.name = 'sdfdf'; + // for (let block of this.camera.frustum.csm.children) { + // this.view.graphic3D.drawBoundingBox(block.name, block.bound, block.color); + // } let frustum = this.camera.frustum; //__________exec octree query @@ -98,7 +97,7 @@ export class Sample_OctTreeFrustum { // this.tree.boxCasts(this.frustumBound, this.queryResult); this.tree.frustumCasts(frustum, this.queryResult); - console.log('exec octree: ', performance.now() - now, 'count', this.queryResult.length); + // console.log('exec octree: ', performance.now() - now, 'count', this.queryResult.length); //end—————————————— let retBoolean = {}; diff --git a/samples/octree/Sample_OctTreeRay.ts b/samples/octree/Sample_OctTreeRay.ts index 4fa98aeb..68feddcd 100644 --- a/samples/octree/Sample_OctTreeRay.ts +++ b/samples/octree/Sample_OctTreeRay.ts @@ -10,7 +10,7 @@ export class Sample_OctTreeRay { entities: OctreeEntity[] = []; tree: Octree; red = new Color(1, 0, 0, 1); - gree = new Color(0, 1, 0, 1); + green = new Color(0, 1, 0, 1); yellow = new Color(1, 1, 0, 1) blue = new Color(0, 0, 1, 1) white = new Color(1, 1, 1, 1) @@ -94,7 +94,7 @@ export class Sample_OctTreeRay { //show box for (let key in boundList) { let tree = boundList[key]; - this.view.graphic3D.drawBoundingBox(key, tree.box, this.gree); + this.view.graphic3D.drawBoundingBox(key, tree.box, this.green); } } diff --git a/samples/physics/Sample_Physics01.ts b/samples/physics/Sample_Physics01.ts index 07e02bd3..c8d23945 100644 --- a/samples/physics/Sample_Physics01.ts +++ b/samples/physics/Sample_Physics01.ts @@ -14,7 +14,6 @@ class SamplePhysics01 { Engine3D.setting.shadow.updateFrameRate = 1; Engine3D.setting.shadow.shadowSize = 2048; Engine3D.setting.shadow.shadowBound = 150; - Engine3D.setting.shadow.shadowBias = 0.0001; await Physics.init(); await Engine3D.init({ renderLoop: () => this.loop() }); diff --git a/samples/physics/Sample_PhysicsBox.ts b/samples/physics/Sample_PhysicsBox.ts index 596b4ed8..247016cb 100644 --- a/samples/physics/Sample_PhysicsBox.ts +++ b/samples/physics/Sample_PhysicsBox.ts @@ -13,7 +13,6 @@ class Sample_PhysicsBox { Engine3D.setting.shadow.updateFrameRate = 1; Engine3D.setting.shadow.shadowSize = 2048; Engine3D.setting.shadow.shadowBound = 150; - Engine3D.setting.shadow.shadowBias = 0.0001; await Physics.init(); await Engine3D.init({ renderLoop: () => this.loop() }); diff --git a/samples/physics/Sample_PhysicsCar.ts b/samples/physics/Sample_PhysicsCar.ts index 2e7ddf96..abd75aaf 100644 --- a/samples/physics/Sample_PhysicsCar.ts +++ b/samples/physics/Sample_PhysicsCar.ts @@ -14,7 +14,6 @@ class Sample_PhysicsCar { Engine3D.setting.shadow.updateFrameRate = 1; Engine3D.setting.shadow.shadowSize = 2048; Engine3D.setting.shadow.shadowBound = 150; - Engine3D.setting.shadow.shadowBias = 0.0001; await Physics.init(); await Engine3D.init({ renderLoop: () => this.loop() }); diff --git a/samples/post/Sample_Bloom.ts b/samples/post/Sample_Bloom.ts index db1cae34..e9e1982e 100644 --- a/samples/post/Sample_Bloom.ts +++ b/samples/post/Sample_Bloom.ts @@ -13,7 +13,6 @@ class Sample_Bloom { async run() { Engine3D.setting.shadow.shadowSize = 2048 Engine3D.setting.shadow.shadowBound = 500; - Engine3D.setting.shadow.shadowBias = 0.0002; await Engine3D.init(); diff --git a/samples/post/Sample_Fog.ts b/samples/post/Sample_Fog.ts index efdae216..3285c043 100644 --- a/samples/post/Sample_Fog.ts +++ b/samples/post/Sample_Fog.ts @@ -10,7 +10,6 @@ class Sample_Fog { async run() { Engine3D.setting.shadow.shadowSize = 2048 Engine3D.setting.shadow.shadowBound = 1000; - Engine3D.setting.shadow.shadowBias = 0.0005; await Engine3D.init(); diff --git a/samples/post/Sample_GTAO.ts b/samples/post/Sample_GTAO.ts index d206c01a..72c8a50f 100644 --- a/samples/post/Sample_GTAO.ts +++ b/samples/post/Sample_GTAO.ts @@ -13,7 +13,6 @@ class Sample_GTAO { async run() { Engine3D.setting.shadow.shadowSize = 2048 Engine3D.setting.shadow.shadowBound = 500; - Engine3D.setting.shadow.shadowBias = 0.0002; await Engine3D.init(); diff --git a/samples/post/Sample_GodRay.ts b/samples/post/Sample_GodRay.ts index 00f5819d..ad6e5b2c 100644 --- a/samples/post/Sample_GodRay.ts +++ b/samples/post/Sample_GodRay.ts @@ -2,7 +2,7 @@ import { View3D, DirectLight, Engine3D, PostProcessingComponent, LitMaterial, HoverCameraController, KelvinUtil, MeshRenderer, Object3D, PlaneGeometry, Scene3D, SphereGeometry, - CameraUtil, webGPUContext, BoxGeometry, AtmosphericComponent, Time, + CameraUtil, webGPUContext, BoxGeometry, AtmosphericComponent, Time, HDRBloomPost, GodRayPost } from '@orillusion/core'; import { GUIHelp } from '@orillusion/debug/GUIHelp'; @@ -13,9 +13,8 @@ class Sample_GodRay { scene: Scene3D; async run() { - Engine3D.setting.shadow.shadowSize = 2048 + Engine3D.setting.shadow.shadowSize = 1024; Engine3D.setting.shadow.shadowBound = 400; - Engine3D.setting.shadow.shadowBias = 0.0005; await Engine3D.init({ renderLoop: () => { this.loop() } }); @@ -26,7 +25,7 @@ class Sample_GodRay { let mainCamera = CameraUtil.createCamera3DObject(this.scene, 'camera'); mainCamera.perspective(60, webGPUContext.aspect, 1, 5000.0); let ctrl = mainCamera.object3D.addComponent(HoverCameraController); - ctrl.setCamera(0, -15, 200); + ctrl.setCamera(110, -10, 300); await this.initScene(); sky.relativeTransform = this.lightObj.transform; @@ -34,6 +33,7 @@ class Sample_GodRay { let view = new View3D(); view.scene = this.scene; view.camera = mainCamera; + mainCamera.enableCSM = true; Engine3D.startRenderView(view); let postProcessing = this.scene.addComponent(PostProcessingComponent); diff --git a/samples/post/Sample_TAA.ts b/samples/post/Sample_TAA.ts index 0293f95f..66df356b 100644 --- a/samples/post/Sample_TAA.ts +++ b/samples/post/Sample_TAA.ts @@ -14,7 +14,6 @@ class Sample_TAA { // Engine3D.setting.shadow.debug = true; Engine3D.setting.shadow.shadowSize = 2048 Engine3D.setting.shadow.shadowBound = 50; - Engine3D.setting.shadow.shadowBias = 0.0002; await Engine3D.init(); diff --git a/samples/render/Sample_BlendMode2.ts b/samples/render/Sample_BlendMode2.ts index 5f5998ec..5f273e88 100644 --- a/samples/render/Sample_BlendMode2.ts +++ b/samples/render/Sample_BlendMode2.ts @@ -9,7 +9,6 @@ class Sample_BlendMode2 { Engine3D.setting.material.materialChannelDebug = true; Engine3D.setting.shadow.shadowBound = 5; - Engine3D.setting.shadow.shadowBias = 0.002; Engine3D.setting.render.postProcessing.bloom = { enable: true, blurX: 4, diff --git a/src/Engine3D.ts b/src/Engine3D.ts index 9f4216d3..4dadd429 100644 --- a/src/Engine3D.ts +++ b/src/Engine3D.ts @@ -223,18 +223,18 @@ export class Engine3D { shadow: { enable: true, type: 'HARD', - shadowBias: 0.00204, pointShadowBias: 0.002, - shadowQuality: 2.5, - shadowBound: 50, + // shadowQuality: 2.5, shadowSize: 1024, pointShadowSize: 1024, shadowSoft: 0.005, - shadowNear: 1, - shadowFar: 2000, + // shadowNear: 1, + // shadowFar: 2000, needUpdate: true, autoUpdate: true, updateFrameRate: 2, + csmMargin: 0.1, + csmScatteringExp: 0.7, debug: false, }, gi: { diff --git a/src/assets/shader/compute/DDGILighting_CSShader.ts b/src/assets/shader/compute/DDGILighting_CSShader.ts index d0e89180..98fe584d 100644 --- a/src/assets/shader/compute/DDGILighting_CSShader.ts +++ b/src/assets/shader/compute/DDGILighting_CSShader.ts @@ -1,3 +1,5 @@ +import { CSM } from "../../../core/csm/CSM"; + export let DDGILighting_shader = /*wgsl*/` #include "GlobalUniform" @@ -71,12 +73,11 @@ struct ShadowStruct{ directShadowVisibility:f32, pointShadows:array, } -var shadowStrut: ShadowStruct ; -var wsn:vec3; +var shadowStrut: ShadowStruct ; var ulitColor:vec3; - -var shadow:f32 = 1.0; +var wPosition:vec3; +var wNormal:vec3; const LUMEN = 10.764; @@ -102,24 +103,57 @@ fn sampleColor(uv:vec2) -> vec4 return oc; } -fn directionShadowMapping(worldPos:vec3,shadowBias:f32) { - var shadowPos = globalUniform.shadowMatrix[0] * vec4(worldPos.xyz, 1.0); - var shadowUV = shadowPos.xy * vec2(0.5, -0.5) + vec2(0.5, 0.5) ; - let texelSize = 1.0 / vec2(globalUniform.shadowMapSize); - - var visibility = 0.0 ; - let oneOverShadowDepthTextureSize = texelSize ; - for (var y = -1; y <= 1; y++) { - for (var x = -1; x <= 1; x++) { - let offset = shadowUV * oneOverShadowDepthTextureSize; - visibility += textureSampleCompareLevel( shadowMap, shadowMapSampler, shadowUV + offset , 0 , shadowPos.z - shadowBias ); - } - } - visibility /= 9.0; - shadowStrut.directShadowVisibility = visibility; +const csmCount:i32 = ${CSM.Cascades} ; +fn directShadowMaping(P:vec3, N:vec3, shadowBias: f32) { + let enableCSM:bool = globalUniform.enableCSM > 0.5; + var light = lightBuffer[0]; + var visibility = 1.0; + var shadowIndex = i32(light.castShadow); + if (shadowIndex >= 0 ) { + var shadowMatrix:mat4x4; + if(enableCSM && csmCount > 1){ + for(var csm:i32 = 0; csm < csmCount; csm ++){ + var csmShadowBias = globalUniform.csmShadowBias[csm]; + shadowMatrix = globalUniform.csmMatrix[csm]; + let csmShadowResult = directShadowMapingIndex(light, shadowMatrix, P, N, csm, csmShadowBias); + if(csmShadowResult.y < 0.5){ + visibility = csmShadowResult.x; + break; + } + } + }else{ + shadowMatrix = globalUniform.shadowMatrix[shadowIndex]; + visibility = directShadowMapingIndex(light, shadowMatrix, P, N, shadowIndex, shadowBias).x; + } + } + shadowStrut.directShadowVisibility = visibility; } -fn pointShadowMapCompare(worldPos:vec3,shadowBias:f32){ +fn directShadowMapingIndex(light:LightData, matrix:mat4x4, P:vec3, N:vec3, depthTexIndex:i32, shadowBias:f32) -> vec2 +{ + var visibility = 1.0; + var isOutSideArea:f32 = 1.0; + var shadowPosTmp = matrix * vec4(P.xyz, 1.0); + var shadowPos = shadowPosTmp.xyz / shadowPosTmp.w; + var varying_shadowUV = shadowPos.xy * vec2(0.5, -0.5) + vec2(0.5, 0.5); + if (varying_shadowUV.x <= 1.0 + && varying_shadowUV.x >= 0.0 + && varying_shadowUV.y <= 1.0 + && varying_shadowUV.y >= 0.0 + && shadowPosTmp.z <= 1.0 + && shadowPosTmp.z >= 0.0) + { + isOutSideArea = 0.0; + var uvOnePixel = 1.0 / vec2(globalUniform.shadowMapSize); + var NoL = abs(dot(N, normalize(light.direction))); + var bias = shadowBias / max(NoL, 0.000001); + visibility = textureSampleCompareLevel(shadowMap, shadowMapSampler, varying_shadowUV, depthTexIndex, shadowPos.z - bias); + visibility += 0.001; + } + return vec2(visibility, isOutSideArea); +} + +fn pointShadowMapCompare(shadowBias:f32){ for(var i:i32 = i32(0) ; i < i32(8); i = i + 1 ) { var v = 1.0 ; @@ -129,7 +163,7 @@ fn pointShadowMapCompare(worldPos:vec3,shadowBias:f32){ continue ; } - let frgToLight = worldPos - light.position.xyz; + let frgToLight = wPosition - light.position.xyz; var dir:vec3 = normalize(frgToLight) ; var len = length(frgToLight) ; @@ -215,8 +249,7 @@ fn coordFun(fragCoord:vec2)-> vec4{ var pos = samplePosition(uv); var normalMap = sampleNormal(uv); - wsn = normalMap.xyz * 2.0 - 1.0; - var normal = normalize( wsn ); + var normal = normalize( normalMap.xyz * 2.0 - 1.0 ); var color = sampleColor(uv); var emissive = vec4(pos.a,normalMap.a,color.a,0.0) * 1.0 ; @@ -226,8 +259,11 @@ fn coordFun(fragCoord:vec2)-> vec4{ var V = normalize(pos.xyz - globalUniform.cameraWorldMatrix[3].xyz); var N = normal.xyz ; - directionShadowMapping(pos.xyz,globalUniform.shadowBias); - pointShadowMapCompare(pos.xyz,globalUniform.shadowBias); + wPosition = pos.xyz; + wNormal = N; + + directShadowMaping(wPosition, wNormal, globalUniform.shadowBias); + pointShadowMapCompare(globalUniform.shadowBias); var lighting = vec3(0.0); let lightCount = 32 ; diff --git a/src/assets/shader/compute/GodRay_cs.ts b/src/assets/shader/compute/GodRay_cs.ts index 0006b25b..487fc66b 100644 --- a/src/assets/shader/compute/GodRay_cs.ts +++ b/src/assets/shader/compute/GodRay_cs.ts @@ -1,3 +1,5 @@ +import { CSM } from "../../../core/csm/CSM"; + export let GodRay_cs: string = /*wgsl*/ ` #include "GlobalUniform" @@ -58,6 +60,11 @@ export let GodRay_cs: string = /*wgsl*/ ` var models : Uniforms; @group(2) @binding(0) var historyGodRayData: array; + + struct ShadowStruct{ + directShadowVisibility:f32, + pointShadows:array, + } var viewDirection: vec3 ; var texSize: vec2; @@ -65,13 +72,57 @@ export let GodRay_cs: string = /*wgsl*/ ` var wPosition: vec3; var wNormal: vec4; var directLight: LightData; - - fn directionShadowMapping(worldPos:vec3, shadowBias:f32) -> f32 { - var shadowPos = globalUniform.shadowMatrix[0] * vec4(worldPos.xyz, 1.0); - var shadowUV = shadowPos.xy * vec2(0.5, -0.5) + vec2(0.5, 0.5) ; - var visibility = textureSampleCompareLevel( shadowMap, shadowMapSampler, shadowUV, 0, shadowPos.z - shadowBias ); - return visibility; - } + var shadowStrut: ShadowStruct ; + + const csmCount:i32 = ${CSM.Cascades} ; + fn directShadowMaping(P:vec3, N:vec3, shadowBias: f32) { + let enableCSM:bool = globalUniform.enableCSM > 0.5; + var light = lightBuffer[0]; + var visibility = 1.0; + var shadowIndex = i32(light.castShadow); + if (shadowIndex >= 0 ) { + var shadowMatrix:mat4x4; + if(enableCSM && csmCount > 1){ + for(var csm:i32 = 0; csm < csmCount; csm ++){ + var csmShadowBias = globalUniform.csmShadowBias[csm]; + shadowMatrix = globalUniform.csmMatrix[csm]; + let csmShadowResult = directShadowMapingIndex(light, shadowMatrix, P, N, csm, csmShadowBias); + if(csmShadowResult.y < 0.5){ + visibility = csmShadowResult.x; + break; + } + } + }else{ + shadowMatrix = globalUniform.shadowMatrix[shadowIndex]; + visibility = directShadowMapingIndex(light, shadowMatrix, P, N, shadowIndex, shadowBias).x; + } + } + shadowStrut.directShadowVisibility = visibility; + } + + fn directShadowMapingIndex(light:LightData, matrix:mat4x4, P:vec3, N:vec3, depthTexIndex:i32, shadowBias:f32) -> vec2 + { + var visibility = 1.0; + var isOutSideArea:f32 = 1.0; + var shadowPosTmp = matrix * vec4(P.xyz, 1.0); + var shadowPos = shadowPosTmp.xyz / shadowPosTmp.w; + var varying_shadowUV = shadowPos.xy * vec2(0.5, -0.5) + vec2(0.5, 0.5); + if (varying_shadowUV.x <= 1.0 + && varying_shadowUV.x >= 0.0 + && varying_shadowUV.y <= 1.0 + && varying_shadowUV.y >= 0.0 + && shadowPosTmp.z <= 1.0 + && shadowPosTmp.z >= 0.0) + { + isOutSideArea = 0.0; + var uvOnePixel = 1.0 / vec2(globalUniform.shadowMapSize); + var NoL = abs(dot(N, normalize(light.direction))); + var bias = shadowBias / max(NoL, 0.000001); + visibility = textureSampleCompareLevel(shadowMap, shadowMapSampler, varying_shadowUV, depthTexIndex, shadowPos.z - bias); + visibility += 0.001; + } + return vec2(visibility, isOutSideArea); + } @compute @workgroup_size( 8 , 8 , 1 ) fn CsMain( @builtin(workgroup_id) workgroup_id : vec3 , @builtin(global_invocation_id) globalInvocation_id : vec3) @@ -150,13 +201,15 @@ export let GodRay_cs: string = /*wgsl*/ ` var frameOffset = f32(i32(globalUniform.frame) % 4); frameOffset *= 0.25; - + var N = normalize(wNormal.xyz); for(var i:f32 = 1.0; i < rayMarchCount; i += 1.0){ var t = (i + frameOffset) / rayMarchCount; lastSamplePosition = samplePosition; samplePosition = mix(eyePosition, wPosition, t * t); - var shadowVisibility = directionShadowMapping(samplePosition, globalUniform.shadowBias); + // var shadowVisibility = directionShadowMapping(samplePosition, globalUniform.shadowBias); + directShadowMaping(samplePosition.xyz, N, globalUniform.shadowBias); + var shadowVisibility = shadowStrut.directShadowVisibility; if(shadowVisibility > 0.5){ var stepFactor = calcGodRayValue(samplePosition, L, viewDirection); stepFactor *= length(lastSamplePosition - samplePosition); diff --git a/src/assets/shader/compute/Picker_cs.ts b/src/assets/shader/compute/Picker_cs.ts index 61c7696d..cc502e70 100644 --- a/src/assets/shader/compute/Picker_cs.ts +++ b/src/assets/shader/compute/Picker_cs.ts @@ -1,38 +1,7 @@ export let Picker_cs: string = /*wgsl*/ ` - struct GlobalUniform { - projMat: mat4x4, - viewMat: mat4x4, - cameraWorldMatrix: mat4x4, - pvMatrixInv : mat4x4, - shadowMatrix: array,8>, - CameraPos: vec3, - - frame: f32, - time: f32, - delta: f32, - shadowBias: f32, - skyExposure: f32, - renderPassState:f32, - quadScale: f32, - hdrExposure: f32, - - renderState_left: i32, - renderState_right: i32, - renderState_split: f32, - mouseX: f32, - mouseY: f32, - windowWidth: f32, - windowHeight: f32, - - near: f32, - far: f32, - - pointShadowBias: f32, - shadowMapSize: f32, - shadowSoft: f32, - }; + #include "GlobalUniform" struct PickResult{ pick_meshID:f32, @@ -43,7 +12,7 @@ export let Picker_cs: string = /*wgsl*/ ` pick_Tangent:vec4, } - @group(0) @binding(0) var standUniform: GlobalUniform; + //@group(0) @binding(0) var globalUniform: GlobalUniform; @group(0) @binding(1) var outBuffer: PickResult; @group(0) @binding(2) var visibleMap : texture_2d; @@ -53,7 +22,7 @@ export let Picker_cs: string = /*wgsl*/ ` var result:PickResult ; // result.pick_meshID let texSize = textureDimensions(visibleMap).xy; - let screenPoint = vec2(standUniform.mouseX/standUniform.windowWidth,standUniform.mouseY/standUniform.windowHeight); + let screenPoint = vec2(globalUniform.mouseX/globalUniform.windowWidth,globalUniform.mouseY/globalUniform.windowHeight); let mouseUV = screenPoint * vec2(texSize.xy); let info = textureLoad(visibleMap, vec2(mouseUV) , 0); @@ -61,7 +30,7 @@ export let Picker_cs: string = /*wgsl*/ ` outBuffer.pick_meshID = f32(info.w) ; outBuffer.pick_meshID2 = f32(info.w) ; outBuffer.pick_Tangent = vec4(2.0,2.0,2.0,2.0) ; - outBuffer.pick_UV = vec2(standUniform.mouseX,standUniform.mouseY) ; + outBuffer.pick_UV = vec2(globalUniform.mouseX,globalUniform.mouseY) ; outBuffer.pick_Position = vec4(info.xyzw) ; outBuffer.pick_Normal = vec4(info.xyzw) ; } diff --git a/src/assets/shader/core/common/GlobalUniform.ts b/src/assets/shader/core/common/GlobalUniform.ts index ab6026b7..954e679e 100644 --- a/src/assets/shader/core/common/GlobalUniform.ts +++ b/src/assets/shader/core/common/GlobalUniform.ts @@ -1,36 +1,49 @@ +import { CSM } from "../../../../core/csm/CSM"; + export let GlobalUniform: string = /*wgsl*/ ` + struct GlobalUniform { projMat: mat4x4, viewMat: mat4x4, cameraWorldMatrix: mat4x4, pvMatrixInv : mat4x4, - shadowMatrix: array,8>, + shadowMatrix: array, 8u>, + csmShadowBias: vec4, + csmMatrix: array,${CSM.Cascades}>, + CameraPos: vec3, - frame: f32, + time: f32, delta: f32, shadowBias: f32, skyExposure: f32, + renderPassState:f32, quadScale: f32, hdrExposure: f32, - renderState_left: i32, + renderState_right: i32, renderState_split: f32, - mouseX: f32, mouseY: f32, + windowWidth: f32, windowHeight: f32, - near: f32, far: f32, pointShadowBias: f32, shadowMapSize: f32, shadowSoft: f32, + enableCSM:f32, + + csmMargin:f32, + notUsed1:f32, + notUsed2:f32, + notUsed3:f32 + }; @group(0) @binding(0) diff --git a/src/assets/shader/core/pass/FrustumCulling_cs.ts b/src/assets/shader/core/pass/FrustumCulling_cs.ts index c69488a8..dd32c4df 100644 --- a/src/assets/shader/core/pass/FrustumCulling_cs.ts +++ b/src/assets/shader/core/pass/FrustumCulling_cs.ts @@ -1,37 +1,5 @@ export let FrustumCulling_cs: string = /*wgsl*/ ` -struct ConstUniform { - projMat: mat4x4, - viewMat: mat4x4, - cameraWorldMatrix: mat4x4, - pvMatrixInv : mat4x4, - shadowMatrix: array,8>, - CameraPos: vec3, - - frame: f32, - time: f32, - delta: f32, - shadowBias: f32, - skyExposure: f32, - renderPassState:f32, - quadScale: f32, - hdrExposure: f32, - - renderState_left: i32, - renderState_right: i32, - renderState_split: f32, - - mouseX: f32, - mouseY: f32, - windowWidth: f32, - windowHeight: f32, - - near: f32, - far: f32, - - pointShadowBias: f32, - shadowMapSize: f32, - shadowSoft: f32, -} +#include "GlobalUniform" struct RenderBound{ index:f32, @@ -41,8 +9,8 @@ struct Uniforms { matrix : array> }; -@group(0) @binding(0) var models : Uniforms; -@group(0) @binding(1) var standUniform: ConstUniform; +//@group(0) @binding(0) var globalUniform: GlobalUniform; +@group(0) @binding(1) var models : Uniforms; @group(0) @binding(2) var planes: array,7>; @group(0) @binding(3) var cullingList: array; @group(0) @binding(4) var outBuffer: array; @@ -105,7 +73,7 @@ fn CsMain( @builtin(workgroup_id) workgroup_id : vec3 , @builtin(global_inv var plane = planes[0]; let worldMatrix = models.matrix[boundID]; - let projMat = standUniform.projMat ; + let projMat = globalUniform.projMat ; let const_boundMin : vec3 = vec3(-0.5,-0.5,-0.5) ; let const_boundMax : vec3 = vec3(0.5,0.5,0.5) ; diff --git a/src/assets/shader/lighting/BxDF_frag.ts b/src/assets/shader/lighting/BxDF_frag.ts index 4ce362f9..76a0274c 100644 --- a/src/assets/shader/lighting/BxDF_frag.ts +++ b/src/assets/shader/lighting/BxDF_frag.ts @@ -142,6 +142,16 @@ export let BxDF_frag: string = /*wgsl*/ ` color = vec3(clearCoatLayer.rgb/fragData.Albedo.a) ; #endif + // if(csmLevel == 0){ + // color += vec3(0.1, 0.0, 0.0); + // }else if(csmLevel == 1){ + // color += vec3(0.0, 0.1, 0.0); + // }else if(csmLevel == 2){ + // color += vec3(0.0, 0.0, 0.1); + // }else if(csmLevel == 3){ + // color += vec3(0.0, 0.1, 0.1); + // } + ORI_FragmentOutput.color = vec4(LinearToGammaSpace(color.rgb),fragData.Albedo.a) ; // ORI_FragmentOutput.color = vec4(irradiance.rgb,fragData.Albedo.a) ; } diff --git a/src/assets/shader/materials/program/ShadowMapping_frag.ts b/src/assets/shader/materials/program/ShadowMapping_frag.ts index 857671af..69f77f61 100644 --- a/src/assets/shader/materials/program/ShadowMapping_frag.ts +++ b/src/assets/shader/materials/program/ShadowMapping_frag.ts @@ -1,3 +1,5 @@ +import { CSM } from "../../../../core/csm/CSM"; + export let ShadowMapping_frag: string = /*wgsl*/ ` #if USE_SHADOWMAPING @group(1) @binding(auto) var shadowMapSampler: sampler_comparison; @@ -31,45 +33,109 @@ export let ShadowMapping_frag: string = /*wgsl*/ ` pointShadowMapCompare(globalUniform.pointShadowBias); } + fn calcBasicBias(shadowWorldSize:f32, shadowDepthTexSize:f32, near:f32, far:f32) -> f32{ + var bias = shadowWorldSize / shadowDepthTexSize; + bias = bias / (far - near); + return bias * 2.0; + } + const dirCount:i32 = 8 ; const pointCount:i32 = 8 ; + const csmCount:i32 = ${CSM.Cascades} ; + var csmLevel:i32 = -1; fn directShadowMaping(shadowBias: f32) { - + #if USE_SHADOWMAPING + let enableCSM:bool = globalUniform.enableCSM > 0.5; for (var i: i32 = 0; i < dirCount ; i = i + 1) { - if( i >= shadowBuffer.nDirShadowStart && i <= shadowBuffer.nDirShadowEnd ){ + if( i >= shadowBuffer.nDirShadowStart && i < shadowBuffer.nDirShadowEnd ){ let ldx = shadowBuffer.shadowLights[i]; var light = lightBuffer[ldx]; var shadowIndex = i32(light.castShadow); - #if USE_SHADOWMAPING - var shadowPosTmp = globalUniform.shadowMatrix[shadowIndex] * vec4(ORI_VertexVarying.vWorldPos.xyz, 1.0); - var shadowPos = shadowPosTmp.xyz / shadowPosTmp.w; - var varying_shadowUV = shadowPos.xy * vec2(0.5, -0.5) + vec2(0.5, 0.5); - var bias = min(shadowBias * (1.0 - dot(ORI_ShadingInput.Normal, light.direction)), 0.0005); - if (varying_shadowUV.x <= 1.0 && varying_shadowUV.x >= 0.0 && varying_shadowUV.y <= 1.0 && varying_shadowUV.y >= 0.0 && shadowPosTmp.z <= 1.0) { - var texelSize = 1.0 / vec2(globalUniform.shadowMapSize); - var oneOverShadowDepthTextureSize = texelSize; - var size = 1; - var sizeBlock = size * 2 + 1; - var sizeBlockA = sizeBlock * sizeBlock; - var visibility = 0.0; - for (var y = -size; y <= size; y++) { - for (var x = -size; x <= size; x++) { - var offset = vec2(f32(x), f32(y)) * oneOverShadowDepthTextureSize / f32(sizeBlock); - visibility += textureSampleCompare( - shadowMap, - shadowMapSampler, - varying_shadowUV + offset, - shadowIndex, - shadowPos.z - bias - ); + var visibility = 1.0; + var shadowMatrix:mat4x4; + #if USE_CSM + if(enableCSM && shadowIndex == 0){ + var totalWeight = 0.0; + visibility = 0.0; + var validCount = 0; + for(var csm:i32 = 0; csm < csmCount; csm ++){ + var csmShadowBias = globalUniform.csmShadowBias[csm]; + shadowMatrix = globalUniform.csmMatrix[csm]; + let csmShadowResult = directShadowMapingIndex(light, shadowMatrix, csm, csmShadowBias); + if(csmShadowResult.y < 0.5){ + validCount ++; + var uv = 2.0 * csmShadowResult.zw - vec2(1.0); + uv = saturate(vec2(1.0) - abs(uv)); + uv /= clamp(globalUniform.csmMargin, 0.01, 0.5); + + var weight:f32 = min(uv.x, 1.0); + weight = min(weight, uv.y); + // if(weight < 1.0){ + // visibility += 0.1; + // } + weight *= 1.0 - totalWeight; + visibility += csmShadowResult.x * weight; + totalWeight += weight; + if(validCount >= 2 || totalWeight >= 0.99){ + csmLevel = csm; + break; + } } } - visibility /= f32(sizeBlockA); - shadowStrut.directShadowVisibility[i] = visibility + 0.001; + totalWeight += 0.0001; + visibility = visibility / totalWeight; + }else{ + shadowMatrix = globalUniform.shadowMatrix[shadowIndex]; + if(enableCSM) { + shadowIndex += csmCount - 1; + } + visibility = directShadowMapingIndex(light, shadowMatrix, shadowIndex, shadowBias).x; } - #endif + #else + shadowMatrix = globalUniform.shadowMatrix[shadowIndex]; + visibility = directShadowMapingIndex(light, shadowMatrix, shadowIndex, shadowBias).x; + #endif + shadowStrut.directShadowVisibility[i] = visibility; } - } + } + #endif + } + + fn directShadowMapingIndex(light:LightData, matrix:mat4x4, depthTexIndex:i32, shadowBias:f32) -> vec4 + { + var visibility = 1.0; + var isOutSideArea:f32 = 1.0; + var varying_shadowUV:vec2 = vec2(0.0); + #if USE_SHADOWMAPING + var shadowPosTmp = matrix * vec4(ORI_VertexVarying.vWorldPos.xyz, 1.0); + var shadowPos = shadowPosTmp.xyz / shadowPosTmp.w; + varying_shadowUV = shadowPos.xy * vec2(0.5, -0.5) + vec2(0.5, 0.5); + if (varying_shadowUV.x <= 1.0 + && varying_shadowUV.x >= 0.0 + && varying_shadowUV.y <= 1.0 + && varying_shadowUV.y >= 0.0 + && shadowPosTmp.z <= 1.0 + && shadowPosTmp.z >= 0.0) + { + visibility = 0.0; + isOutSideArea = 0.0; + var uvOnePixel = 1.0 / vec2(globalUniform.shadowMapSize); + var totalWeight = 0.0; + var NoL = abs(dot(normalize(ORI_VertexVarying.vWorldNormal), normalize(light.direction))); + var bias = shadowBias / max(NoL, 0.000001); + for (var y = -1; y <= 1; y++) { + for (var x = -1; x <= 1; x++) { + var offset = vec2(f32(x), f32(y)) * uvOnePixel; + + visibility += textureSampleCompare(shadowMap, shadowMapSampler, varying_shadowUV + offset, depthTexIndex, shadowPos.z - bias); + totalWeight += 1.0; + } + } + visibility /= totalWeight; + visibility += 0.001; + } + #endif + return vec4(visibility, isOutSideArea, varying_shadowUV); } fn pointShadowMapCompare(shadowBias: f32){ @@ -111,7 +177,7 @@ export let ShadowMapping_frag: string = /*wgsl*/ ` let sampleRadies = globalUniform.shadowSoft; let samples = 20; for (var j: i32 = 0; j < samples; j += 1) { - let offsetDir = normalize(dir.xyz + sampleOffetDir[j] * sampleRadies); + let offsetDir = normalize(dir.xyz + sampleOffsetDir[j] * sampleRadies); var depth = textureSampleLevel(pointShadowMap, pointShadowMapSampler, offsetDir, light.castShadow, 0); depth *= globalUniform.far; if ((len - bias) > depth) { @@ -140,7 +206,7 @@ export let ShadowMapping_frag: string = /*wgsl*/ ` } #if USE_SOFT_SHADOW - varsampleOffetDir : array, 20> = array, 20>( + varsampleOffsetDir : array, 20> = array, 20>( vec3(1.0, 1.0, 1.0), vec3(1.0, -1.0, 1.0), vec3(-1.0, -1.0, 1.0), vec3(-1.0, 1.0, 1.0), vec3(1.0, 1.0, -1.0), vec3(1.0, -1.0, -1.0), vec3(-1.0, -1.0, -1.0), vec3(-1.0, 1.0, -1.0), vec3(1.0, 1.0, 0.0), vec3(1.0, -1.0, 0.0), vec3(-1.0, -1.0, 0.0), vec3(-1.0, 1.0, 0.0), diff --git a/src/core/Camera3D.ts b/src/core/Camera3D.ts index ecd5d7e2..f4a790f3 100644 --- a/src/core/Camera3D.ts +++ b/src/core/Camera3D.ts @@ -12,6 +12,8 @@ import { Frustum } from './bound/Frustum'; import { CameraType } from './CameraType'; import { CubeCamera } from './CubeCamera'; import { webGPUContext } from '../gfx/graphics/webGpu/Context3D'; +import { FrustumCSM } from './csm/FrustumCSM'; +import { CSM } from './csm/CSM'; /** * Camera components @@ -70,6 +72,7 @@ export class Camera3D extends ComponentBase { private _halfw: number; private _halfh: number; private _ray: Ray; + private _enableCSM: boolean = false; /** * @internal @@ -89,12 +92,22 @@ export class Camera3D extends ComponentBase { */ public type: CameraType = CameraType.perspective; + public csm: FrustumCSM; + /** * @internal */ public cubeShadowCameras: CubeCamera[] = []; - + public get enableCSM(): boolean { + return this._enableCSM; + } + public set enableCSM(value: boolean) { + if (value && !this.csm) { + this.csm = new FrustumCSM(CSM.Cascades); + } + this._enableCSM = value; + } constructor() { super(); } @@ -111,6 +124,32 @@ export class Camera3D extends ComponentBase { this.lookTarget = new Vector3(0, 0, 0); } + public getShadowBias(depthTexSize: number): number { + let sizeOnePixel = 2.0 * this.getShadowWorldExtents() / depthTexSize; + let depth = this.far - this.near; + return sizeOnePixel / depth; + } + + public getShadowWorldExtents(): number { + let shadowBound = Engine3D.setting.shadow.shadowBound; + if (!shadowBound) { + shadowBound = Math.round(0.05 * this.frustum.boundingBox.extents.length); + } else { + shadowBound *= 0.5; + } + return shadowBound; + } + + public getCSMShadowBias(index: number, depthTexSize: number): number { + let sizeOnePixel = 2.0 * this.getCSMShadowWorldExtents(index) / depthTexSize; + let depth = this.far - this.near; + return sizeOnePixel / depth; + } + + public getCSMShadowWorldExtents(index: number): number { + return Math.round(this.csm.children[index].bound.extents.length); + } + /** * Create a perspective camera * @param fov @@ -220,13 +259,10 @@ export class Camera3D extends ComponentBase { let fScreenPtX = n.x; let fScreenPtY = n.y; - let m_fRadius = 1; target.x = fScreenPtX / this.viewPort.width - 0.25; target.y = fScreenPtY / this.viewPort.height - 0.25; this.unProject(target.x, target.y, n.z, target); - // this.transform.localMatrix.transformVector4(target, target); - return target; } @@ -247,9 +283,9 @@ export class Camera3D extends ComponentBase { * get (project * view) invert matrix */ public get pvMatrixInv(): Matrix4 { - let pvMatrixInv = this._pvMatrixInv.copyFrom(this.pvMatrix); - pvMatrixInv.invert(); - return pvMatrixInv; + let matrix = this._pvMatrixInv.copyFrom(this.pvMatrix); + matrix.invert(); + return matrix; } /** @@ -376,6 +412,9 @@ export class Camera3D extends ComponentBase { this.getJitteredProjectionMatrix(); } this.frustum.update(this.pvMatrix); + this.frustum.updateBoundBox(this.pvMatrixInv); + let shadow = Engine3D.setting.shadow; + this.enableCSM && this.csm?.update(this._projectionMatrix, this._pvMatrixInv, this.near, this.far, shadow); } private _haltonSeq: HaltonSeq; @@ -439,69 +478,69 @@ export class Camera3D extends ComponentBase { this._jitterFrameIndex++; } - /** - * - * @param shadowCamera - * @param lightDir - */ - public getCastShadowLightSpaceMatrix(shadowCamera: Camera3D, lightDir: Vector3) { - let frustum: Frustum = this.frustum; - - let proMat = this.projectionMatrixInv; - let wMat = this.transform.worldMatrix; - Matrix4.helpMatrix.copyFrom(proMat); - Matrix4.helpMatrix.multiply(wMat); - - frustum.setFrustumCorners(Matrix4.helpMatrix); - - let corners = frustum.corners; - let center = Vector3.HELP_6; - center.set(0, 0, 0); - - for (const iterator of corners) { - center.add(iterator, center); - } - - center.div(corners.length, center); - - let lookTarget = Vector3.HELP_5; - lookTarget.copyFrom(center); - Vector3.HELP_0.copyFrom(lightDir); - lookTarget.add(Vector3.HELP_0, lookTarget); - shadowCamera.lookAt(lookTarget, center, Vector3.UP); - - let minX = Number.MAX_VALUE; - let maxX = -Number.MAX_VALUE; - let minY = Number.MAX_VALUE; - let maxY = -Number.MAX_VALUE; - let minZ = Number.MAX_VALUE; - let maxZ = -Number.MAX_VALUE; - - for (const iterator of corners) { - minX = Math.min(minX, iterator.x); - maxX = Math.max(maxX, iterator.x); - minY = Math.min(minY, iterator.y); - maxY = Math.max(maxY, iterator.y); - minZ = Math.min(minZ, iterator.z); - maxZ = Math.max(maxZ, iterator.z); - } - - // Tune this parameter according to the scene - let zMult = Engine3D.setting.shadow.shadowQuality; - - if (minZ < 0) { - minZ *= zMult; - } else { - minZ /= zMult; - } - if (maxZ < 0) { - maxZ /= zMult; - } else { - maxZ *= zMult; - } - - shadowCamera.orthoOffCenter(minX, maxX, minY, maxY, minZ, maxZ); - } + // /** + // * + // * @param shadowCamera + // * @param lightDir + // */ + // public getCastShadowLightSpaceMatrix(shadowCamera: Camera3D, lightDir: Vector3) { + // let frustum: Frustum = this.frustum; + + // let proMat = this.projectionMatrixInv; + // let wMat = this.transform.worldMatrix; + // Matrix4.helpMatrix.copyFrom(proMat); + // Matrix4.helpMatrix.multiply(wMat); + + // frustum.setFrustumCorners(Matrix4.helpMatrix); + + // let corners = frustum.corners; + // let center = Vector3.HELP_6; + // center.set(0, 0, 0); + + // for (const iterator of corners) { + // center.add(iterator, center); + // } + + // center.div(corners.length, center); + + // let lookTarget = Vector3.HELP_5; + // lookTarget.copyFrom(center); + // Vector3.HELP_0.copyFrom(lightDir); + // lookTarget.add(Vector3.HELP_0, lookTarget); + // shadowCamera.lookAt(lookTarget, center, Vector3.UP); + + // let minX = Number.MAX_VALUE; + // let maxX = -Number.MAX_VALUE; + // let minY = Number.MAX_VALUE; + // let maxY = -Number.MAX_VALUE; + // let minZ = Number.MAX_VALUE; + // let maxZ = -Number.MAX_VALUE; + + // for (const iterator of corners) { + // minX = Math.min(minX, iterator.x); + // maxX = Math.max(maxX, iterator.x); + // minY = Math.min(minY, iterator.y); + // maxY = Math.max(maxY, iterator.y); + // minZ = Math.min(minZ, iterator.z); + // maxZ = Math.max(maxZ, iterator.z); + // } + + // // Tune this parameter according to the scene + // let zMult = Engine3D.setting.shadow.shadowQuality; + + // if (minZ < 0) { + // minZ *= zMult; + // } else { + // minZ /= zMult; + // } + // if (maxZ < 0) { + // maxZ /= zMult; + // } else { + // maxZ *= zMult; + // } + + // shadowCamera.orthoOffCenter(minX, maxX, minY, maxY, minZ, maxZ); + // } public getWorldDirection(target?: Vector3) { target ||= new Vector3(); diff --git a/src/core/bound/Frustum.ts b/src/core/bound/Frustum.ts index bc7c1f27..78113b69 100644 --- a/src/core/bound/Frustum.ts +++ b/src/core/bound/Frustum.ts @@ -2,61 +2,49 @@ import { BoundingSphere } from './BoundingSphere'; import { Matrix4 } from '../../math/Matrix4'; import { Object3D } from '../entities/Object3D'; import { Vector3 } from '../../math/Vector3'; +import { BoundingBox } from './BoundingBox'; /** * @internal * @group Core */ export class Frustum { - public viewProj = new Matrix4(); public planes: Vector3[]; public corners: Vector3[]; + public boundingBox: BoundingBox = new BoundingBox(); - private _centerSize: Vector3; constructor() { - this._centerSize = new Vector3(); this.planes = []; this.corners = []; for (var i = 0; i < 6; i++) this.planes[i] = new Vector3(); for (var i = 0; i < 2 * 2 * 2; i++) this.corners[i] = new Vector3(); } - genBox(pvInv: Matrix4) { + public updateBoundBox(pvInv: Matrix4): this { + this.boundingBox.makeEmpty(); + let min = this.boundingBox.min; + let max = this.boundingBox.max; let i = 0; - let minX = 9999999; - let minY = 9999999; - let minZ = 9999999; - - let maxX = -9999999; - let maxY = -9999999; - let maxZ = -9999999; for (let x = 0; x < 2; ++x) { for (let y = 0; y < 2; ++y) { for (let z = 0; z < 2; ++z) { let pt = this.corners[i]; + i++; pt.set(2.0 * x - 1.0, 2.0 * y - 1.0, z, 1.0); pvInv.transformVector4(pt, pt); pt.div(pt.w, pt); - i++; - minX = Math.min(pt.x, minX); - minY = Math.min(pt.y, minY); - minZ = Math.min(pt.z, minZ); + min.x = Math.min(pt.x, min.x); + min.y = Math.min(pt.y, min.y); + min.z = Math.min(pt.z, min.z); - maxX = Math.max(pt.x, maxX); - maxY = Math.max(pt.y, maxY); - maxZ = Math.max(pt.z, maxZ); + max.x = Math.max(pt.x, max.x); + max.y = Math.max(pt.y, max.y); + max.z = Math.max(pt.z, max.z); } } } - - this._centerSize.x = maxX - minX; - this._centerSize.y = maxY - minY; - this._centerSize.x = maxZ - minZ; - - return { - minX, minY, minZ, - maxX, maxY, maxZ - }; + this.boundingBox.setFromMinMax(min, max); + return this; } setFrustumCorners(pvInv: Matrix4) { diff --git a/src/core/csm/CSM.ts b/src/core/csm/CSM.ts new file mode 100644 index 00000000..44aaecec --- /dev/null +++ b/src/core/csm/CSM.ts @@ -0,0 +1,3 @@ +export class CSM { + public static readonly Cascades = 4; +} \ No newline at end of file diff --git a/src/core/csm/FrustumCSM.ts b/src/core/csm/FrustumCSM.ts new file mode 100644 index 00000000..5e6ab755 --- /dev/null +++ b/src/core/csm/FrustumCSM.ts @@ -0,0 +1,138 @@ +import { Color } from "../../math/Color"; +import { Matrix4 } from "../../math/Matrix4"; +import { Vector3 } from "../../math/Vector3"; +import { ShadowSetting } from "../../setting/ShadowSetting"; +import { CameraUtil } from "../../util/CameraUtil"; +import { Camera3D } from "../Camera3D"; +import { BoundingBox } from "../bound/BoundingBox"; + +class FrustumSection { + public corners: Vector3[]; + public index: number; + constructor(index: number) { + this.index = index; + this.corners = []; + for (let i = 0; i < 4; i++) { + this.corners.push(new Vector3()); + } + } +} + +class FrustumChild { + public bound: BoundingBox; + public twoSections: FrustumSection[]; + public name: string; + public color: Color; + public shadowCamera: Camera3D; + public readonly index: number; + + + constructor(near: FrustumSection, far: FrustumSection, index: number) { + this.bound = new BoundingBox(); + this.shadowCamera = CameraUtil.createCamera3DObject(null, 'csmShadowCamera_' + index); + this.shadowCamera.isShadowCamera = true; + this.shadowCamera.orthoOffCenter(100, -100, 100, -100, 1, 10000); + + this.twoSections = [near, far]; + this.index = index; + if (index == 0) { + this.color = new Color(1, 0, 0, 1); + } else if (index == 1) { + this.color = new Color(0, 1, 0, 1); + } + else if (index == 2) { + this.color = new Color(0, 0, 1, 1); + } + else if (index == 3) { + this.color = new Color(0, 1, 1, 1); + } + this.name = 'child_' + index; + } + + public updateBound(): this { + this.bound.makeEmpty(); + + let min = this.bound.min; + let max = this.bound.max; + for (let section of this.twoSections) { + for (let pt of section.corners) { + min.x = Math.min(pt.x, min.x); + min.y = Math.min(pt.y, min.y); + min.z = Math.min(pt.z, min.z); + + max.x = Math.max(pt.x, max.x); + max.y = Math.max(pt.y, max.y); + max.z = Math.max(pt.z, max.z); + } + } + + this.bound.setFromMinMax(min, max); + return this; + } +} + +export class FrustumCSM { + public sections: FrustumSection[]; + public children: FrustumChild[]; + public name: string; + + constructor(blockCount: number) { + this.sections = []; + let sectionCount = blockCount + 1; + for (let i = 0; i < sectionCount; i++) { + this.sections.push(new FrustumSection(i)); + } + + this.children = []; + for (let i = 0; i < blockCount; i++) { + this.children.push(new FrustumChild(this.sections[i], this.sections[i + 1], i)); + } + } + + update(p: Matrix4, pvInv: Matrix4, near: number, far: number, shadowSetting: ShadowSetting): this { + let blockCount = this.sections.length - 1; + for (let z = 0; z <= blockCount; ++z) { + let section = this.sections[z]; + let cornerIndex = 0; + // let worldZ = this.squareSplit(near, far, z, this.sections.length); + let worldZ = this.logSplit(near, far, z, this.sections.length); + { + let scale = (worldZ - near) / far; + scale = scale ** shadowSetting.csmScatteringExp; + worldZ = (far - near) * scale + near; + } + + let depth = (p.rawData[10] * worldZ + p.rawData[14]) / worldZ; + for (let x = 0; x < 2; ++x) { + for (let y = 0; y < 2; ++y) { + let pt = section.corners[cornerIndex]; + cornerIndex++; + pt.set(2.0 * x - 1.0, 2.0 * y - 1.0, depth, 1.0); + pvInv.transformVector4(pt, pt); + pt.div(pt.w, pt); + } + } + } + + for (let miniFrustum of this.children) { + miniFrustum.updateBound(); + } + return this; + } + + private squareSplit(near: number, far: number, index: number, max: number): number { + let ratio = index / (max - 1); + return (ratio ** 4) * (far - near) + near; + } + + private uniformSplit(near: number, far: number, index: number, max: number): number { + let ratio = index / (max - 1); + return ratio * (far - near) + near; + } + + private logSplit(near: number, far: number, index: number, max: number): number { + let ratio = near * (far / near) ** (index / (max - 1)); + return ratio; + } + +} \ No newline at end of file diff --git a/src/gfx/graphics/webGpu/core/bindGroups/GlobalUniformGroup.ts b/src/gfx/graphics/webGpu/core/bindGroups/GlobalUniformGroup.ts index 338086cb..56ecb1d0 100644 --- a/src/gfx/graphics/webGpu/core/bindGroups/GlobalUniformGroup.ts +++ b/src/gfx/graphics/webGpu/core/bindGroups/GlobalUniformGroup.ts @@ -1,5 +1,6 @@ import { Engine3D } from "../../../../../Engine3D"; import { Camera3D } from "../../../../../core/Camera3D"; +import { CSM } from "../../../../../core/csm/CSM"; import { Matrix4 } from "../../../../../math/Matrix4"; import { UUID } from "../../../../../util/Global"; import { Time } from "../../../../../util/Time"; @@ -9,7 +10,6 @@ import { UniformGPUBuffer } from "../buffer/UniformGPUBuffer"; import { GlobalBindGroupLayout } from "./GlobalBindGroupLayout"; import { MatrixBindGroup } from "./MatrixBindGroup"; - /** * @internal * @author sirxu @@ -31,7 +31,8 @@ export class GlobalUniformGroup { constructor(matrixBindGroup: MatrixBindGroup) { this.uuid = UUID(); this.usage = GPUBufferUsage.UNIFORM | GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST; - this.uniformGPUBuffer = new UniformGPUBuffer(32 * 4 * 4 + (3 * 4 * 4) + 8 * 16); + // ... + 8(shadow matrix) + 8(csm matrix) + 4(csm bias) + 4(csm scattering exp...) + this.uniformGPUBuffer = new UniformGPUBuffer(32 * 4 * 4 + (3 * 4 * 4) + 8 * 16 + CSM.Cascades * 16 + 4 + 4); this.uniformGPUBuffer.visibility = GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT | GPUShaderStage.COMPUTE this.matrixBindGroup = matrixBindGroup; @@ -73,33 +74,51 @@ export class GlobalUniformGroup { // } // } + private shadowMatrixRaw = new Float32Array(8 * 16); + private csmMatrixRaw = new Float32Array(CSM.Cascades * 16); + private csmShadowBias = new Float32Array(4); + public setCamera(camera: Camera3D) { - // camera.transform.updateWorldMatrix(true); this.uniformGPUBuffer.setMatrix(`_projectionMatrix`, camera.projectionMatrix); this.uniformGPUBuffer.setMatrix(`_viewMatrix`, camera.viewMatrix); this.uniformGPUBuffer.setMatrix(`_cameraWorldMatrix`, camera.transform.worldMatrix); this.uniformGPUBuffer.setMatrix(`_projectionMatrixInv`, camera.projectionMatrixInv); - let raw = new Float32Array(8 * 16); + + let shadowLightList = ShadowLightsCollect.getDirectShadowLightWhichScene(camera.transform.scene3D); + + this.csmShadowBias.fill(0.0001); + this.shadowMatrixRaw.fill(0); + this.csmMatrixRaw.fill(0); for (let i = 0; i < 8; i++) { - let shadowLightList = ShadowLightsCollect.getDirectShadowLightWhichScene(camera.transform.scene3D); if (i < shadowLightList.length) { - let mat = shadowLightList[i].shadowCamera.pvMatrix.rawData; - raw.set(mat, i * 16); + let shadowCamera = shadowLightList[i].shadowCamera; + this.shadowMatrixRaw.set(shadowCamera.pvMatrix.rawData, i * 16); } else { - raw.set(camera.transform.worldMatrix.rawData, i * 16); + this.shadowMatrixRaw.set(camera.transform.worldMatrix.rawData, i * 16); } } - this.uniformGPUBuffer.setFloat32Array(`_shadowCamera`, raw); - this.uniformGPUBuffer.setVector3(`CameraPos`, camera.transform.worldPosition); + this.uniformGPUBuffer.setFloat32Array(`shadowMatrix`, this.shadowMatrixRaw); + + let shadowMapSize = Engine3D.setting.shadow.shadowSize; + if (CSM.Cascades > 1 && camera.enableCSM && shadowLightList[0]) { + for (let i = 0; i < CSM.Cascades; i++) { + let shadowCamera: Camera3D = camera.csm.children[i].shadowCamera; + this.csmMatrixRaw.set(shadowCamera.pvMatrix.rawData, i * 16); + this.csmShadowBias[i] = camera.getCSMShadowBias(i, shadowMapSize); + } + } + this.uniformGPUBuffer.setFloat32Array(`csmShadowBias`, this.csmShadowBias); + this.uniformGPUBuffer.setFloat32Array(`csmMatrix`, this.csmMatrixRaw); + this.uniformGPUBuffer.setVector3(`CameraPos`, camera.transform.worldPosition); this.uniformGPUBuffer.setFloat(`frame`, Time.frame); this.uniformGPUBuffer.setFloat(`time`, Time.frame); this.uniformGPUBuffer.setFloat(`delta`, Time.delta); - this.uniformGPUBuffer.setFloat(`EngineSetting.Shadow.shadowBias`, Engine3D.setting.shadow.shadowBias); + this.uniformGPUBuffer.setFloat(`shadowBias`, camera.getShadowBias(shadowMapSize)); this.uniformGPUBuffer.setFloat(`skyExposure`, Engine3D.setting.sky.skyExposure); - this.uniformGPUBuffer.setFloat(`EngineSetting.Render.renderPassState`, Engine3D.setting.render.renderPassState); - this.uniformGPUBuffer.setFloat(`EngineSetting.Render.quadScale`, Engine3D.setting.render.quadScale); - this.uniformGPUBuffer.setFloat(`EngineSetting.Render.hdrExposure`, Engine3D.setting.render.hdrExposure); + this.uniformGPUBuffer.setFloat(`renderPassState`, Engine3D.setting.render.renderPassState); + this.uniformGPUBuffer.setFloat(`quadScale`, Engine3D.setting.render.quadScale); + this.uniformGPUBuffer.setFloat(`hdrExposure`, Engine3D.setting.render.hdrExposure); this.uniformGPUBuffer.setInt32(`renderState_left`, Engine3D.setting.render.renderState_left); this.uniformGPUBuffer.setInt32(`renderState_right`, Engine3D.setting.render.renderState_right); @@ -114,9 +133,13 @@ export class GlobalUniformGroup { this.uniformGPUBuffer.setFloat(`near`, camera.near); this.uniformGPUBuffer.setFloat(`far`, camera.far); - this.uniformGPUBuffer.setFloat(`EngineSetting.Shadow.pointShadowBias`, Engine3D.setting.shadow.pointShadowBias); - this.uniformGPUBuffer.setFloat(`shadowMapSize`, Engine3D.setting.shadow.shadowSize); + this.uniformGPUBuffer.setFloat(`pointShadowBias`, Engine3D.setting.shadow.pointShadowBias); + this.uniformGPUBuffer.setFloat(`shadowMapSize`, shadowMapSize); this.uniformGPUBuffer.setFloat(`shadowSoft`, Engine3D.setting.shadow.shadowSoft); + this.uniformGPUBuffer.setFloat(`enableCSM`, camera.enableCSM ? 1 : 0); + + this.uniformGPUBuffer.setFloat(`csmMargin`, Engine3D.setting.shadow.csmMargin); + this.uniformGPUBuffer.apply(); } @@ -126,27 +149,22 @@ export class GlobalUniformGroup { this.uniformGPUBuffer.setMatrix(`_viewMatrix`, camera.viewMatrix); this.uniformGPUBuffer.setMatrix(`_pvMatrix`, camera.pvMatrix); this.uniformGPUBuffer.setMatrix(`_projectionMatrixInv`, camera.projectionMatrixInv); - let raw = new Float32Array(8 * 16); - for (let i = 0; i < 8; i++) { - let shadowLightList = ShadowLightsCollect.getDirectShadowLightWhichScene(camera.transform.scene3D); - if (i < shadowLightList.length) { - let mat = shadowLightList[i].shadowCamera.pvMatrix.rawData; - raw.set(mat, i * 16); - } else { - raw.set(camera.transform.worldMatrix.rawData, i * 16); - } - } - this.uniformGPUBuffer.setFloat32Array(`_shadowCamera`, raw); + this.csmShadowBias.fill(0.0001); + this.shadowMatrixRaw.fill(0); + this.csmMatrixRaw.fill(0); + this.uniformGPUBuffer.setFloat32Array(`shadowCamera`, this.shadowMatrixRaw); + this.uniformGPUBuffer.setFloat32Array(`csmShadowBias`, this.csmShadowBias); + this.uniformGPUBuffer.setFloat32Array(`csmMatrix`, this.csmMatrixRaw); this.uniformGPUBuffer.setVector3(`CameraPos`, camera.transform.worldPosition); this.uniformGPUBuffer.setFloat(`frame`, Time.frame); this.uniformGPUBuffer.setFloat(`time`, Time.frame); this.uniformGPUBuffer.setFloat(`delta`, Time.delta); - this.uniformGPUBuffer.setFloat(`EngineSetting.Shadow.shadowBias`, Engine3D.setting.shadow.shadowBias); + this.uniformGPUBuffer.setFloat(`shadowBias`, 0.0001); this.uniformGPUBuffer.setFloat(`skyExposure`, Engine3D.setting.sky.skyExposure); - this.uniformGPUBuffer.setFloat(`EngineSetting.Render.renderPassState`, Engine3D.setting.render.renderPassState); - this.uniformGPUBuffer.setFloat(`EngineSetting.Render.quadScale`, Engine3D.setting.render.quadScale); - this.uniformGPUBuffer.setFloat(`EngineSetting.Render.hdrExposure`, Engine3D.setting.render.hdrExposure); + this.uniformGPUBuffer.setFloat(`renderPassState`, Engine3D.setting.render.renderPassState); + this.uniformGPUBuffer.setFloat(`quadScale`, Engine3D.setting.render.quadScale); + this.uniformGPUBuffer.setFloat(`hdrExposure`, Engine3D.setting.render.hdrExposure); this.uniformGPUBuffer.setInt32(`renderState_left`, Engine3D.setting.render.renderState_left); this.uniformGPUBuffer.setInt32(`renderState_right`, Engine3D.setting.render.renderState_right); @@ -161,9 +179,14 @@ export class GlobalUniformGroup { this.uniformGPUBuffer.setFloat(`near`, camera.near); this.uniformGPUBuffer.setFloat(`far`, camera.far); - this.uniformGPUBuffer.setFloat(`EngineSetting.Shadow.pointShadowBias`, Engine3D.setting.shadow.pointShadowBias); + this.uniformGPUBuffer.setFloat(`pointShadowBias`, Engine3D.setting.shadow.pointShadowBias); this.uniformGPUBuffer.setFloat(`shadowMapSize`, Engine3D.setting.shadow.shadowSize); this.uniformGPUBuffer.setFloat(`shadowSoft`, Engine3D.setting.shadow.shadowSoft); + this.uniformGPUBuffer.setFloat(`enableCSM`, 0); + + this.uniformGPUBuffer.setFloat(`csmMargin`, Engine3D.setting.shadow.csmMargin); + + this.uniformGPUBuffer.apply(); } diff --git a/src/gfx/graphics/webGpu/shader/RenderShader.ts b/src/gfx/graphics/webGpu/shader/RenderShader.ts index c13fe1ea..d45bad9e 100644 --- a/src/gfx/graphics/webGpu/shader/RenderShader.ts +++ b/src/gfx/graphics/webGpu/shader/RenderShader.ts @@ -28,6 +28,7 @@ import { GPUBufferType } from "../core/buffer/GPUBufferType"; import { MaterialDataUniformGPUBuffer } from "../core/buffer/MaterialDataUniformGPUBuffer"; import { ShaderUtil } from "./util/ShaderUtil"; import { Reference } from "../../../../util/Reference"; +import { CSM } from "../../../../core/csm/CSM"; export class RenderShader extends ShaderBase { public useRz: boolean = false; @@ -932,6 +933,7 @@ export class RenderShader extends ShaderBase { this.defineValue[`USE_PCF_SHADOW`] = Engine3D.setting.shadow.type == `PCF`; this.defineValue[`USE_HARD_SHADOW`] = Engine3D.setting.shadow.type == `HARD`; this.defineValue[`USE_SOFT_SHADOW`] = Engine3D.setting.shadow.type == `SOFT`; + this.defineValue[`USE_CSM`] = CSM.Cascades > 1; this.defineValue[`USE_IES_PROFILE`] = IESProfiles.use; } diff --git a/src/gfx/renderJob/collect/ShadowLightsCollect.ts b/src/gfx/renderJob/collect/ShadowLightsCollect.ts index 4e544d6e..823f3b44 100644 --- a/src/gfx/renderJob/collect/ShadowLightsCollect.ts +++ b/src/gfx/renderJob/collect/ShadowLightsCollect.ts @@ -120,18 +120,11 @@ export class ShadowLightsCollect { list = []; this.directionLightList.set(scene, list); } - if (!light['shadowCamera']) { - light['shadowCamera'] = CameraUtil.createCamera3DObject(null, 'shadowCamera'); - light['shadowCamera'].name = UUID(); - light['shadowCamera'].isShadowCamera = true; - light['shadowCamera'].orthoOffCenter( - Engine3D.setting.shadow.shadowBound, - -Engine3D.setting.shadow.shadowBound, - Engine3D.setting.shadow.shadowBound, - -Engine3D.setting.shadow.shadowBound, - 1, - 50000, - ); + if (!light.shadowCamera) { + light.shadowCamera = CameraUtil.createCamera3DObject(null, 'shadowCamera'); + light.shadowCamera.isShadowCamera = true; + let shadowBound = -1000; + light.shadowCamera.orthoOffCenter(shadowBound, -shadowBound, shadowBound, -shadowBound, 1, 10000); } if (list.indexOf(light) == -1) { list.push(light); @@ -156,6 +149,7 @@ export class ShadowLightsCollect { } public static removeShadowLight(light: ILight) { + light.lightData.castShadowIndex = -1; if (!light.transform.view3D) return null; if (light.lightData.lightType == LightType.DirectionLight) { let list = this.directionLightList.get(light.transform.view3D.scene); diff --git a/src/gfx/renderJob/passRenderer/ddgi/DDGIIrradianceComputePass.ts b/src/gfx/renderJob/passRenderer/ddgi/DDGIIrradianceComputePass.ts index d405707a..cc58cd42 100644 --- a/src/gfx/renderJob/passRenderer/ddgi/DDGIIrradianceComputePass.ts +++ b/src/gfx/renderJob/passRenderer/ddgi/DDGIIrradianceComputePass.ts @@ -67,7 +67,7 @@ export class DDGIIrradianceComputePass { return this.depthRaysBuffer.readBuffer(); } - public computer(view: View3D, renderPassState: RendererPassState) { + public compute(view: View3D, renderPassState: RendererPassState) { let setting = this.volume.setting; let command = GPUContext.beginCommandEncoder(); let probes = EntityCollect.instance.getProbes(view.scene); diff --git a/src/gfx/renderJob/passRenderer/ddgi/DDGILightingPass.ts b/src/gfx/renderJob/passRenderer/ddgi/DDGILightingPass.ts index 9ce39cef..eb22bf49 100644 --- a/src/gfx/renderJob/passRenderer/ddgi/DDGILightingPass.ts +++ b/src/gfx/renderJob/passRenderer/ddgi/DDGILightingPass.ts @@ -30,6 +30,9 @@ export class DDGILightingPass { let lightUniformEntries = GlobalBindGroup.getLightEntries(view.scene); this.computeShader = new ComputeShader(DDGILighting_shader); + let cameraBindGroup = GlobalBindGroup.getCameraGroup(view.camera); + this.computeShader.setUniformBuffer("globalUniform", cameraBindGroup.uniformGPUBuffer); + this.computeShader.setStorageTexture("outputBuffer", this.lightingTexture); this.computeShader.setStorageBuffer("lightBuffer", lightUniformEntries.storageGPUBuffer); this.computeShader.setStorageBuffer("models", GlobalBindGroup.modelMatrixBindGroup.matrixBufferDst); @@ -50,12 +53,9 @@ export class DDGILightingPass { this.pointShadowMap = inputs[4]; } - public computer(view: View3D, renderPassState: RendererPassState) { + public compute(view: View3D, renderPassState: RendererPassState) { if (!this.computeShader) { this.create(view); - - let cameraBindGroup = GlobalBindGroup.getCameraGroup(view.camera); - this.computeShader.setUniformBuffer("globalUniform", cameraBindGroup.uniformGPUBuffer); } // EntityCollect.instance.sky ? EntityCollect.instance.sky.materials : defaultRes.defaultSky let command = GPUContext.beginCommandEncoder(); diff --git a/src/gfx/renderJob/passRenderer/ddgi/DDGIMultiBouncePass.ts b/src/gfx/renderJob/passRenderer/ddgi/DDGIMultiBouncePass.ts index 48d436f5..eb6bf5c6 100644 --- a/src/gfx/renderJob/passRenderer/ddgi/DDGIMultiBouncePass.ts +++ b/src/gfx/renderJob/passRenderer/ddgi/DDGIMultiBouncePass.ts @@ -43,7 +43,7 @@ export class DDGIMultiBouncePass { this.computerShader.setSamplerTexture("irradianceMap", irradianceMap); } - public computer(view: View3D, renderPassState: RendererPassState) { + public compute(view: View3D, renderPassState: RendererPassState) { let command = GPUContext.beginCommandEncoder(); let setting = this.volume.setting; let probesCount: number = setting.probeXCount * setting.probeYCount * setting.probeZCount; diff --git a/src/gfx/renderJob/passRenderer/ddgi/DDGIProbeRenderer.ts b/src/gfx/renderJob/passRenderer/ddgi/DDGIProbeRenderer.ts index b9036f13..6a96d8d2 100644 --- a/src/gfx/renderJob/passRenderer/ddgi/DDGIProbeRenderer.ts +++ b/src/gfx/renderJob/passRenderer/ddgi/DDGIProbeRenderer.ts @@ -184,7 +184,7 @@ export class DDGIProbeRenderer extends RendererBase { //back***********************/ } - public renderSceneOnce(view: View3D, probeCamera: Camera3D, encoder: GPURenderPassEncoder, lights: ILight[]) { + private renderSceneOnce(view: View3D, probeCamera: Camera3D, encoder: GPURenderPassEncoder, lights: ILight[]) { this.volume.uploadBuffer(); let collectInfo = EntityCollect.instance.getRenderNodes(view.scene); @@ -238,8 +238,6 @@ export class DDGIProbeRenderer extends RendererBase { public render(view: View3D, occlusionSystem: OcclusionSystem) { if (!Engine3D.setting.gi.enable) return; - let camera = view.camera; - let scene = view.scene; this.volume.updateOrientation(); this.volume.isVolumeFrameChange = false; @@ -255,9 +253,9 @@ export class DDGIProbeRenderer extends RendererBase { if (EntityCollect.instance.state.giLightingChange || probeBeRendered || Engine3D.setting.gi.realTimeGI) { EntityCollect.instance.state.giLightingChange = false; - this.lightingPass.computer(view, this.rendererPassState); - this.bouncePass.computer(view, this.rendererPassState); - this.irradianceComputePass.computer(view, this.rendererPassState); + this.lightingPass.compute(view, this.rendererPassState); + this.bouncePass.compute(view, this.rendererPassState); + this.irradianceComputePass.compute(view, this.rendererPassState); } if (this.probeRenderResult.complete) { diff --git a/src/gfx/renderJob/passRenderer/shadow/ShadowMapPassRenderer.ts b/src/gfx/renderJob/passRenderer/shadow/ShadowMapPassRenderer.ts index dcbf88aa..3359260f 100644 --- a/src/gfx/renderJob/passRenderer/shadow/ShadowMapPassRenderer.ts +++ b/src/gfx/renderJob/passRenderer/shadow/ShadowMapPassRenderer.ts @@ -1,10 +1,8 @@ import { Engine3D } from "../../../../Engine3D"; import { DirectLight } from "../../../../components/lights/DirectLight"; -import { LightType } from "../../../../components/lights/LightData"; import { RenderNode } from "../../../../components/renderer/RenderNode"; import { Camera3D } from "../../../../core/Camera3D"; import { View3D } from "../../../../core/View3D"; -import { clamp } from "../../../../math/MathUtil"; import { Vector3 } from "../../../../math/Vector3"; import { Depth2DTextureArray } from "../../../../textures/Depth2DTextureArray"; import { VirtualTexture } from "../../../../textures/VirtualTexture"; @@ -21,32 +19,28 @@ import { RendererType } from "../state/RendererType"; import { RendererBase } from "../RendererBase"; import { ClusterLightingBuffer } from "../cluster/ClusterLightingBuffer"; import { Reference } from "../../../../util/Reference"; +import { Texture } from "../../../graphics/webGpu/core/texture/Texture"; +import { CSM } from "../../../../core/csm/CSM"; /** * @internal * @group Post */ export class ShadowMapPassRenderer extends RendererBase { - // public shadowCamera: Camera3D; public shadowPassCount: number; public depth2DArrayTexture: Depth2DTextureArray; public rendererPassStates: RendererPassState[]; private _forceUpdate = false; + constructor() { super(); + this.setShadowMap(Engine3D.setting.shadow.shadowSize, CSM.Cascades); this.passType = RendererType.SHADOW; - this.setShadowMap(Engine3D.setting.shadow.shadowSize); - if (Engine3D.setting.shadow.debug) { - this.debug(); - } - } - - debug() { } - setShadowMap(size: number) { + setShadowMap(size: number, cascades: number) { this.rendererPassStates = []; - this.depth2DArrayTexture = new Depth2DTextureArray(size, size); + this.depth2DArrayTexture = new Depth2DTextureArray(size, size, GPUTextureFormat.depth32float, 8); Reference.getInstance().attached(this.depth2DArrayTexture, this); for (let i = 0; i < 8; i++) { @@ -54,45 +48,38 @@ export class ShadowMapPassRenderer extends RendererBase { const tex = new VirtualTexture(size, size, GPUTextureFormat.depth32float, false); tex.name = `shadowDepthTexture_${i}`; rtFrame.depthTexture = tex; - rtFrame.label = "shadowRender" + rtFrame.label = "shadowRender"; rtFrame.customSize = true; rtFrame.depthCleanValue = 1; let rendererPassState = WebGPUDescriptorCreator.createRendererPassState(rtFrame); this.rendererPassStates[i] = rendererPassState; } - } - render(view: View3D, occlusionSystem: OcclusionSystem) { - if (!Engine3D.setting.shadow.enable) + let shadowSetting = Engine3D.setting.shadow; + if (!shadowSetting.enable) return; - let camera = view.camera; let scene = view.scene; this.shadowPassCount = 0; - - if (!Engine3D.setting.shadow.needUpdate) + if (!shadowSetting.needUpdate) return; - if (!(Time.frame % Engine3D.setting.shadow.updateFrameRate == 0)) + if (!(Time.frame % shadowSetting.updateFrameRate == 0)) return; camera.transform.updateWorldMatrix(); - - - - //*********************/ //***shadow light******/ //*********************/ - let shadowLight = ShadowLightsCollect.getDirectShadowLightWhichScene(scene); - let li = 0; - for (let si = 0; si < shadowLight.length; si++) { - const light = shadowLight[si] as DirectLight; - if (light.lightData.lightType != LightType.DirectionLight) - continue; - - this.rendererPassState = this.rendererPassStates[light.shadowIndex]; + let shadowLightList = ShadowLightsCollect.getDirectShadowLightWhichScene(scene); + let shadowSize = shadowSetting.shadowSize; + const cascades = CSM.Cascades; + for (let light of shadowLightList) { + const dirLight = light as DirectLight; + let shadowIndex = dirLight.shadowIndex; + this.rendererPassState = this.rendererPassStates[shadowIndex]; + let viewRenderList = EntityCollect.instance.getRenderShaderCollect(view); for (const renderList of viewRenderList) { let nodeMap = renderList[1]; @@ -105,64 +92,80 @@ export class ShadowMapPassRenderer extends RendererBase { } } - if ((light.castShadow && light.needUpdateShadow || this._forceUpdate) || (light.castShadow && Engine3D.setting.shadow.autoUpdate)) { - light.needUpdateShadow = false; - let shadowFar = clamp(Engine3D.setting.shadow.shadowFar, camera.near, camera.far); - - let shadowPos = Vector3.HELP_4; - let shadowLookTarget = Vector3.HELP_5; - shadowLookTarget.copy(camera.lookTarget); - shadowPos.copy(light.direction); - shadowPos.normalize(); - let dis = Vector3.distance(camera.transform.worldPosition, shadowLookTarget); - shadowPos.scaleBy(-shadowFar); - shadowPos.add(shadowLookTarget, shadowPos); - light.shadowCamera.transform.lookAt(shadowPos, shadowLookTarget); - let w = Engine3D.setting.shadow.shadowBound; - let h = Engine3D.setting.shadow.shadowBound; - light.shadowCamera.orthoOffCenter(w / -2, w / 2, h / -2, h / 2, camera.near, camera.far); - this.renderShadow(view, light.shadowCamera, occlusionSystem); - li++; + if ((dirLight.castShadow && dirLight.needUpdateShadow || this._forceUpdate) || (dirLight.castShadow && shadowSetting.autoUpdate)) { + dirLight.needUpdateShadow = false; + if (camera.enableCSM && shadowIndex == 0) { + for (let csmIndex = 0; csmIndex < cascades; csmIndex++) { + this.rendererPassState = this.rendererPassStates[csmIndex]; + let csmChild = camera.csm.children[csmIndex]; + let extents = camera.getCSMShadowWorldExtents(csmIndex); + this.poseShadowCamera(camera, dirLight.direction, csmChild.shadowCamera, extents, csmChild.bound.center); + this.renderShadow(view, csmChild.shadowCamera, occlusionSystem, this.rendererPassState); + this.copyDepthTexture(this.rendererPassState.depthTexture, this.depth2DArrayTexture, csmIndex, shadowSize); + } + } else { + if (camera.enableCSM) { + shadowIndex += cascades - 1; + } + let extents = camera.getShadowWorldExtents(); + this.rendererPassState = this.rendererPassStates[shadowIndex]; + this.poseShadowCamera(camera, dirLight.direction, dirLight.shadowCamera, extents, camera.lookTarget); + this.renderShadow(view, dirLight.shadowCamera, occlusionSystem, this.rendererPassState); + this.copyDepthTexture(this.rendererPassState.depthTexture, this.depth2DArrayTexture, shadowIndex, shadowSize); + } } - - let qCommand = GPUContext.beginCommandEncoder(); - qCommand.copyTextureToTexture( - { - texture: this.rendererPassStates[light.shadowIndex].depthTexture.getGPUTexture(), - mipLevel: 0, - origin: { x: 0, y: 0, z: 0 }, - }, - { - texture: this.depth2DArrayTexture.getGPUTexture(), - mipLevel: 0, - origin: { x: 0, y: 0, z: light.shadowIndex }, - }, - { - width: Engine3D.setting.shadow.shadowSize, - height: Engine3D.setting.shadow.shadowSize, - depthOrArrayLayers: 1, - }, - ); - GPUContext.endCommandEncoder(qCommand); } this._forceUpdate = false; } + private copyDepthTexture(src: Texture, dst: Texture, dstIndex: number, shadowSize: number) { + let qCommand = GPUContext.beginCommandEncoder(); + qCommand.copyTextureToTexture( + { + texture: src.getGPUTexture(), + mipLevel: 0, + origin: { x: 0, y: 0, z: 0 }, + }, + { + texture: dst.getGPUTexture(), + mipLevel: 0, + origin: { x: 0, y: 0, z: dstIndex }, + }, + { + width: shadowSize, + height: shadowSize, + depthOrArrayLayers: 1, + }, + ); + GPUContext.endCommandEncoder(qCommand); + } + + private _shadowPos: Vector3 = new Vector3(); + private _shadowCameraTarget: Vector3 = new Vector3(); + + private poseShadowCamera(viewCamera: Camera3D, direction: Vector3, shadowCamera: Camera3D, extents: number, lookAt: Vector3) { + this._shadowPos.copy(direction).normalize(viewCamera.far); + lookAt.add(this._shadowPos, this._shadowCameraTarget); + lookAt.subtract(this._shadowPos, this._shadowPos); + shadowCamera.transform.lookAt(this._shadowPos, this._shadowCameraTarget); + shadowCamera.orthoOffCenter(-extents, extents, -extents, extents, viewCamera.near, viewCamera.far * 2); + } + public compute() { } - private renderShadow(view: View3D, shadowCamera: Camera3D, occlusionSystem: OcclusionSystem) { + private renderShadow(view: View3D, shadowCamera: Camera3D, occlusionSystem: OcclusionSystem, state: RendererPassState) { let collectInfo = EntityCollect.instance.getRenderNodes(view.scene); let command = GPUContext.beginCommandEncoder(); - let encoder = GPUContext.beginRenderPass(command, this.rendererPassState); + let encoder = GPUContext.beginRenderPass(command, state); - // shadowCamera.transform.updateWorldMatrix(); + shadowCamera.transform.updateWorldMatrix(); occlusionSystem.update(shadowCamera, view.scene); GPUContext.bindCamera(encoder, shadowCamera); - let op_bundleList = this.renderShadowBundleOp(view, shadowCamera); - let tr_bundleList = this.renderShadowBundleTr(view, shadowCamera); + let op_bundleList = this.renderShadowBundleOp(view, shadowCamera, state); + let tr_bundleList = this.renderShadowBundleTr(view, shadowCamera, state); if (op_bundleList.length > 0) { encoder.executeBundles(op_bundleList); @@ -177,7 +180,7 @@ export class ShadowMapPassRenderer extends RendererBase { GPUContext.endCommandEncoder(command); } - protected renderShadowBundleOp(view: View3D, shadowCamera: Camera3D) { + protected renderShadowBundleOp(view: View3D, shadowCamera: Camera3D, state: RendererPassState) { let entityBatchCollect = EntityCollect.instance.getOpRenderGroup(view.scene); if (entityBatchCollect) { let bundlerList = []; @@ -185,7 +188,7 @@ export class ShadowMapPassRenderer extends RendererBase { if (v.bundleMap.has(this._rendererType)) { bundlerList.push(v.bundleMap.get(this._rendererType)); } else { - let renderBundleEncoder = GPUContext.recordBundleEncoder(this.rendererPassState.renderBundleEncoderDescriptor); + let renderBundleEncoder = GPUContext.recordBundleEncoder(state.renderBundleEncoderDescriptor); this.recordShadowRenderBundleNode(view, shadowCamera, renderBundleEncoder, v.renderNodes); let newBundle = renderBundleEncoder.finish(); v.bundleMap.set(this._rendererType, newBundle); @@ -197,7 +200,7 @@ export class ShadowMapPassRenderer extends RendererBase { return []; } - protected renderShadowBundleTr(view: View3D, shadowCamera: Camera3D) { + protected renderShadowBundleTr(view: View3D, shadowCamera: Camera3D, state: RendererPassState) { let entityBatchCollect = EntityCollect.instance.getTrRenderGroup(view.scene); if (entityBatchCollect) { let bundlerList = []; @@ -205,7 +208,7 @@ export class ShadowMapPassRenderer extends RendererBase { if (v.bundleMap.has(this._rendererType)) { bundlerList.push(v.bundleMap.get(this._rendererType)); } else { - let renderBundleEncoder = GPUContext.recordBundleEncoder(this.rendererPassState.renderBundleEncoderDescriptor); + let renderBundleEncoder = GPUContext.recordBundleEncoder(state.renderBundleEncoderDescriptor); this.recordShadowRenderBundleNode(view, shadowCamera, renderBundleEncoder, v.renderNodes); let newBundle = renderBundleEncoder.finish(); v.bundleMap.set(this._rendererType, newBundle); @@ -217,13 +220,13 @@ export class ShadowMapPassRenderer extends RendererBase { return []; } + protected recordShadowRenderBundleNode(view: View3D, shadowCamera: Camera3D, encoder, nodes: RenderNode[], clusterLightingBuffer?: ClusterLightingBuffer) { GPUContext.bindCamera(encoder, shadowCamera); - GPUContext.bindGeometryBuffer(encoder, nodes[0].geometry); if (nodes) { + GPUContext.bindGeometryBuffer(encoder, nodes[0].geometry); for (let i = 0; i < nodes.length; ++i) { let renderNode = nodes[i]; - let matrixIndex = renderNode.transform.worldMatrix.index; if (!renderNode.transform.enable) continue; renderNode.recordRenderPass2(view, this._rendererType, this.rendererPassState, clusterLightingBuffer, encoder); diff --git a/src/index.ts b/src/index.ts index 2ae1b112..49f1ca46 100644 --- a/src/index.ts +++ b/src/index.ts @@ -192,6 +192,8 @@ export * from "./core/bound/BoundingBox" export * from "./core/bound/BoundingSphere" export * from "./core/bound/Frustum" export * from "./core/bound/IBound" +export * from "./core/csm/CSM" +export * from "./core/csm/FrustumCSM" export * from "./core/entities/Entity" export * from "./core/entities/InstancedMesh" export * from "./core/entities/Object3D" diff --git a/src/io/picker/PickCompute.ts b/src/io/picker/PickCompute.ts index 1455c1d9..5b167826 100644 --- a/src/io/picker/PickCompute.ts +++ b/src/io/picker/PickCompute.ts @@ -1,5 +1,4 @@ import { Picker_cs } from '../../assets/shader/compute/Picker_cs'; -import { Camera3D } from '../../core/Camera3D'; import { View3D } from '../../core/View3D'; import { GlobalBindGroup } from '../../gfx/graphics/webGpu/core/bindGroups/GlobalBindGroup'; import { ComputeGPUBuffer } from '../../gfx/graphics/webGpu/core/buffer/ComputeGPUBuffer'; @@ -28,7 +27,7 @@ export class PickCompute { compute(view: View3D) { let stand = GlobalBindGroup.getCameraGroup(view.camera); - this._computeShader.setStorageBuffer('standUniform', stand.uniformGPUBuffer); + this._computeShader.setStorageBuffer('globalUniform', stand.uniformGPUBuffer); let command = GPUContext.beginCommandEncoder(); GPUContext.computeCommand(command, [this._computeShader]); diff --git a/src/setting/ShadowSetting.ts b/src/setting/ShadowSetting.ts index 840f69f6..23f651dc 100644 --- a/src/setting/ShadowSetting.ts +++ b/src/setting/ShadowSetting.ts @@ -4,7 +4,7 @@ * @group Setting */ export type ShadowSetting = { - debug: any; + debug: any, /** * enable */ @@ -30,22 +30,18 @@ export type ShadowSetting = { or the occluded place where the sunlight cannot reach. */ type: `PCF` | `HARD` | `SOFT`; - /** - * Shadow offset - */ - shadowBias: number; /** * Offset of point light shadow */ pointShadowBias: number; - /** - * Shadow quality - */ - shadowQuality: number; + // /** + // * Shadow quality + // */ + // shadowQuality: number; /** * shadow boundary */ - shadowBound: number; + shadowBound?: number; /** * shadow mapping Size */ @@ -59,11 +55,20 @@ export type ShadowSetting = { */ pointShadowSize: number; /** - * Shadow near section + * Blend Shadow(0-1) */ - shadowNear: number; + csmMargin: number; /** - * Shadow Far Section + * scattering csm Area Exponent */ - shadowFar: number; + csmScatteringExp: number; + + // /** + // * Shadow near section + // */ + // shadowNear: number; + // /** + // * Shadow Far Section + // */ + // shadowFar: number; }; \ No newline at end of file diff --git a/src/textures/Depth2DTextureArray.ts b/src/textures/Depth2DTextureArray.ts index 9ce82643..c9aef74f 100644 --- a/src/textures/Depth2DTextureArray.ts +++ b/src/textures/Depth2DTextureArray.ts @@ -15,8 +15,8 @@ export class Depth2DTextureArray extends Texture implements ITexture { * @width texture height (pixel) * @width texture format, default value is depth32float */ - constructor(width: number, height: number, format: GPUTextureFormat = GPUTextureFormat.depth32float) { - super(width, height, 4); + constructor(width: number, height: number, format: GPUTextureFormat = GPUTextureFormat.depth32float, numberLayer: number = 4) { + super(width, height, numberLayer); this.visibility = GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT | GPUShaderStage.COMPUTE;