@zephyr3d/scene 0.9.25 → 0.9.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/animation/animationset.js +92 -30
- package/dist/animation/animationset.js.map +1 -1
- package/dist/animation/hair/gpu_hair_simulation.js +1090 -0
- package/dist/animation/hair/gpu_hair_simulation.js.map +1 -0
- package/dist/animation/joint_dynamics/controller.js +7 -5
- package/dist/animation/joint_dynamics/controller.js.map +1 -1
- package/dist/animation/joint_dynamics/convex_collider.js +320 -0
- package/dist/animation/joint_dynamics/convex_collider.js.map +1 -0
- package/dist/animation/joint_dynamics/joint_dynamics_system.js +81 -5
- package/dist/animation/joint_dynamics/joint_dynamics_system.js.map +1 -1
- package/dist/animation/morphtrack.js +3 -4
- package/dist/animation/morphtrack.js.map +1 -1
- package/dist/animation/skeleton.js +79 -25
- package/dist/animation/skeleton.js.map +1 -1
- package/dist/animation/spring/multi_chain_spring_system.js +577 -179
- package/dist/animation/spring/multi_chain_spring_system.js.map +1 -1
- package/dist/animation/spring/spring_collider.js +4 -3
- package/dist/animation/spring/spring_collider.js.map +1 -1
- package/dist/animation/spring/spring_node_pose_tracker.js +40 -0
- package/dist/animation/spring/spring_node_pose_tracker.js.map +1 -0
- package/dist/animation/spring/spring_particle.js +2 -0
- package/dist/animation/spring/spring_particle.js.map +1 -1
- package/dist/animation/spring/spring_system.js +11 -3
- package/dist/animation/spring/spring_system.js.map +1 -1
- package/dist/animation/spring_modifier.js +10 -0
- package/dist/animation/spring_modifier.js.map +1 -1
- package/dist/app/engine.js +53 -43
- package/dist/app/engine.js.map +1 -1
- package/dist/app/scriptingsystem.js +119 -6
- package/dist/app/scriptingsystem.js.map +1 -1
- package/dist/app/scriptregistry.js +3 -2
- package/dist/app/scriptregistry.js.map +1 -1
- package/dist/asset/assetmanager.js +284 -67
- package/dist/asset/assetmanager.js.map +1 -1
- package/dist/asset/loaders/zhair/zhair_format.js +288 -0
- package/dist/asset/loaders/zhair/zhair_format.js.map +1 -0
- package/dist/asset/loaders/zhair/zhair_loader.js +221 -0
- package/dist/asset/loaders/zhair/zhair_loader.js.map +1 -0
- package/dist/asset/model.js +184 -69
- package/dist/asset/model.js.map +1 -1
- package/dist/camera/camera.js +268 -44
- package/dist/camera/camera.js.map +1 -1
- package/dist/camera/perspectivecamera.js +3 -1
- package/dist/camera/perspectivecamera.js.map +1 -1
- package/dist/index.d.ts +9044 -5139
- package/dist/index.js +17 -6
- package/dist/index.js.map +1 -1
- package/dist/material/eye.js +932 -0
- package/dist/material/eye.js.map +1 -0
- package/dist/material/hair.js +917 -0
- package/dist/material/hair.js.map +1 -0
- package/dist/material/hairstrand.js +842 -0
- package/dist/material/hairstrand.js.map +1 -0
- package/dist/material/hairstrand_data.js +162 -0
- package/dist/material/hairstrand_data.js.map +1 -0
- package/dist/material/lambert.js +5 -2
- package/dist/material/lambert.js.map +1 -1
- package/dist/material/material.js +48 -10
- package/dist/material/material.js.map +1 -1
- package/dist/material/meshmaterial.js +120 -19
- package/dist/material/meshmaterial.js.map +1 -1
- package/dist/material/mixins/albedocolor.js +1 -1
- package/dist/material/mixins/albedocolor.js.map +1 -1
- package/dist/material/mixins/lightmodel/blinnphong.js +5 -3
- package/dist/material/mixins/lightmodel/blinnphong.js.map +1 -1
- package/dist/material/mixins/lightmodel/lambert.js +5 -4
- package/dist/material/mixins/lightmodel/lambert.js.map +1 -1
- package/dist/material/mixins/lightmodel/pbrblueprintmixin.js +3 -3
- package/dist/material/mixins/lightmodel/pbrblueprintmixin.js.map +1 -1
- package/dist/material/mixins/lightmodel/pbrmetallicroughness.js +5 -5
- package/dist/material/mixins/lightmodel/pbrmetallicroughness.js.map +1 -1
- package/dist/material/mixins/lightmodel/pbrspecularglossness.js +3 -3
- package/dist/material/mixins/lightmodel/pbrspecularglossness.js.map +1 -1
- package/dist/material/mixins/lit.js +106 -19
- package/dist/material/mixins/lit.js.map +1 -1
- package/dist/material/mixins/pbr/common.js +4 -4
- package/dist/material/mixins/pbr/common.js.map +1 -1
- package/dist/material/mixins/texture.js +48 -11
- package/dist/material/mixins/texture.js.map +1 -1
- package/dist/material/mtoon.js +6 -5
- package/dist/material/mtoon.js.map +1 -1
- package/dist/material/pbrblueprint.js +146 -28
- package/dist/material/pbrblueprint.js.map +1 -1
- package/dist/material/pbrblueprint_instance.js +501 -0
- package/dist/material/pbrblueprint_instance.js.map +1 -0
- package/dist/material/pbrmr.js +15 -16
- package/dist/material/pbrmr.js.map +1 -1
- package/dist/material/shader/helper.js +734 -23
- package/dist/material/shader/helper.js.map +1 -1
- package/dist/material/shader/terrain_blend.js +55 -0
- package/dist/material/shader/terrain_blend.js.map +1 -0
- package/dist/material/skin.js +192 -32
- package/dist/material/skin.js.map +1 -1
- package/dist/material/spriteblueprint.js +16 -1
- package/dist/material/spriteblueprint.js.map +1 -1
- package/dist/material/subsurfaceprofile.js +86 -23
- package/dist/material/subsurfaceprofile.js.map +1 -1
- package/dist/material/water.js +2059 -72
- package/dist/material/water.js.map +1 -1
- package/dist/posteffect/bloom.js +167 -7
- package/dist/posteffect/bloom.js.map +1 -1
- package/dist/posteffect/coloradjust.js +2 -1
- package/dist/posteffect/coloradjust.js.map +1 -1
- package/dist/posteffect/compositor.js.map +1 -1
- package/dist/posteffect/fxaa.js +2 -1
- package/dist/posteffect/fxaa.js.map +1 -1
- package/dist/posteffect/grayscale.js +3 -1
- package/dist/posteffect/grayscale.js.map +1 -1
- package/dist/posteffect/motionblur.js +664 -56
- package/dist/posteffect/motionblur.js.map +1 -1
- package/dist/posteffect/posteffect.js +12 -1
- package/dist/posteffect/posteffect.js.map +1 -1
- package/dist/posteffect/sao.js +46 -44
- package/dist/posteffect/sao.js.map +1 -1
- package/dist/posteffect/skinsss.js +368 -69
- package/dist/posteffect/skinsss.js.map +1 -1
- package/dist/posteffect/ssgi.js +16 -9
- package/dist/posteffect/ssgi.js.map +1 -1
- package/dist/posteffect/ssr.js +3 -0
- package/dist/posteffect/ssr.js.map +1 -1
- package/dist/posteffect/sss.js +121 -36
- package/dist/posteffect/sss.js.map +1 -1
- package/dist/posteffect/taa.js.map +1 -1
- package/dist/posteffect/tonemap.js +44 -1
- package/dist/posteffect/tonemap.js.map +1 -1
- package/dist/render/abuffer_oit.js +8 -2
- package/dist/render/abuffer_oit.js.map +1 -1
- package/dist/render/clipmap.js +156 -5
- package/dist/render/clipmap.js.map +1 -1
- package/dist/render/cull_visitor.js +20 -0
- package/dist/render/cull_visitor.js.map +1 -1
- package/dist/render/depthpass.js +36 -2
- package/dist/render/depthpass.js.map +1 -1
- package/dist/render/drawable_mixin.js +26 -1
- package/dist/render/drawable_mixin.js.map +1 -1
- package/dist/render/dualdepthpeeling_oit.js +3 -2
- package/dist/render/dualdepthpeeling_oit.js.map +1 -1
- package/dist/render/envlight.js +1 -1
- package/dist/render/envlight.js.map +1 -1
- package/dist/render/fbm_wavegenerator.js +6 -3
- package/dist/render/fbm_wavegenerator.js.map +1 -1
- package/dist/render/fft_wavegenerator.js +57 -15
- package/dist/render/fft_wavegenerator.js.map +1 -1
- package/dist/render/gerstner_wavegenerator.js +130 -20
- package/dist/render/gerstner_wavegenerator.js.map +1 -1
- package/dist/render/globalbindgroup_allocator.js +3 -0
- package/dist/render/globalbindgroup_allocator.js.map +1 -1
- package/dist/render/hzb.js +80 -22
- package/dist/render/hzb.js.map +1 -1
- package/dist/render/lightpass.js +6 -3
- package/dist/render/lightpass.js.map +1 -1
- package/dist/render/oit.js +29 -0
- package/dist/render/oit.js.map +1 -0
- package/dist/render/render_queue.js +39 -0
- package/dist/render/render_queue.js.map +1 -1
- package/dist/render/renderer.js +4 -0
- package/dist/render/renderer.js.map +1 -1
- package/dist/render/rendergraph/blackboard.js +1 -0
- package/dist/render/rendergraph/blackboard.js.map +1 -1
- package/dist/render/rendergraph/device_pool_allocator.js.map +1 -1
- package/dist/render/rendergraph/executor.js +2 -0
- package/dist/render/rendergraph/executor.js.map +1 -1
- package/dist/render/rendergraph/forward_plus_builder.js +379 -10
- package/dist/render/rendergraph/forward_plus_builder.js.map +1 -1
- package/dist/render/rendergraph/frame_resource_requirements.js +1 -0
- package/dist/render/rendergraph/frame_resource_requirements.js.map +1 -1
- package/dist/render/rendergraph/history_resources.js +8 -1
- package/dist/render/rendergraph/history_resources.js.map +1 -1
- package/dist/render/rendergraph/rendergraph.js.map +1 -1
- package/dist/render/rendergraph/texture_affinity_cache.js +1 -1
- package/dist/render/rendergraph/texture_affinity_cache.js.map +1 -1
- package/dist/render/rendergraph/types.js.map +1 -1
- package/dist/render/shadow_mask_pass.js +73 -8
- package/dist/render/shadow_mask_pass.js.map +1 -1
- package/dist/render/sky.js +166 -126
- package/dist/render/sky.js.map +1 -1
- package/dist/render/underwater.js +624 -0
- package/dist/render/underwater.js.map +1 -0
- package/dist/render/water_caustics.js +1380 -0
- package/dist/render/water_caustics.js.map +1 -0
- package/dist/render/weightedblended_oit.js +3 -2
- package/dist/render/weightedblended_oit.js.map +1 -1
- package/dist/scene/basesprite.js +8 -0
- package/dist/scene/basesprite.js.map +1 -1
- package/dist/scene/batchgroup.js +3 -0
- package/dist/scene/batchgroup.js.map +1 -1
- package/dist/scene/environment.js +4 -1
- package/dist/scene/environment.js.map +1 -1
- package/dist/scene/hair.js +963 -0
- package/dist/scene/hair.js.map +1 -0
- package/dist/scene/light.js +5 -1
- package/dist/scene/light.js.map +1 -1
- package/dist/scene/mesh.js +481 -47
- package/dist/scene/mesh.js.map +1 -1
- package/dist/scene/meshdrawable.js +6 -0
- package/dist/scene/meshdrawable.js.map +1 -1
- package/dist/scene/msdftext.js +3 -0
- package/dist/scene/msdftext.js.map +1 -1
- package/dist/scene/msdftextsprite.js +3 -0
- package/dist/scene/msdftextsprite.js.map +1 -1
- package/dist/scene/particlesys.js +6 -0
- package/dist/scene/particlesys.js.map +1 -1
- package/dist/scene/scene.js +3 -0
- package/dist/scene/scene.js.map +1 -1
- package/dist/scene/scene_node.js +6 -1
- package/dist/scene/scene_node.js.map +1 -1
- package/dist/scene/terrain-cm/grass.js +3 -0
- package/dist/scene/terrain-cm/grass.js.map +1 -1
- package/dist/scene/terrain-cm/grassmaterial.js +3 -0
- package/dist/scene/terrain-cm/grassmaterial.js.map +1 -1
- package/dist/scene/terrain-cm/terrain-cm.js +6 -0
- package/dist/scene/terrain-cm/terrain-cm.js.map +1 -1
- package/dist/scene/terrain-cm/texture_cache.js +309 -0
- package/dist/scene/terrain-cm/texture_cache.js.map +1 -0
- package/dist/scene/water.js +546 -4
- package/dist/scene/water.js.map +1 -1
- package/dist/shaders/atmosphere.js.map +1 -1
- package/dist/shaders/diffusion.js +54 -0
- package/dist/shaders/diffusion.js.map +1 -0
- package/dist/shaders/shadow.js +33 -21
- package/dist/shaders/shadow.js.map +1 -1
- package/dist/shaders/temporal.js +71 -33
- package/dist/shaders/temporal.js.map +1 -1
- package/dist/shaders/water.js +22 -19
- package/dist/shaders/water.js.map +1 -1
- package/dist/shaders/water_medium.js +56 -0
- package/dist/shaders/water_medium.js.map +1 -0
- package/dist/shadow/dom.js +546 -0
- package/dist/shadow/dom.js.map +1 -0
- package/dist/shadow/pcf_opt.js +3 -5
- package/dist/shadow/pcf_opt.js.map +1 -1
- package/dist/shadow/pcf_pd.js +1 -1
- package/dist/shadow/pcf_pd.js.map +1 -1
- package/dist/shadow/receiver_bias.js +41 -0
- package/dist/shadow/receiver_bias.js.map +1 -0
- package/dist/shadow/shader.js +20 -60
- package/dist/shadow/shader.js.map +1 -1
- package/dist/shadow/shadow_impl.js +89 -0
- package/dist/shadow/shadow_impl.js.map +1 -1
- package/dist/shadow/shadow_region.js +1 -1
- package/dist/shadow/shadow_region.js.map +1 -1
- package/dist/shadow/shadowmapper.js +150 -37
- package/dist/shadow/shadowmapper.js.map +1 -1
- package/dist/shapes/shape.js +11 -2
- package/dist/shapes/shape.js.map +1 -1
- package/dist/shapes/sphere.js +50 -9
- package/dist/shapes/sphere.js.map +1 -1
- package/dist/utility/blueprint/common/constants.js +112 -2
- package/dist/utility/blueprint/common/constants.js.map +1 -1
- package/dist/utility/blueprint/material/func.js +142 -49
- package/dist/utility/blueprint/material/func.js.map +1 -1
- package/dist/utility/blueprint/material/ir.js +490 -25
- package/dist/utility/blueprint/material/ir.js.map +1 -1
- package/dist/utility/blueprint/material/texture.js +898 -4
- package/dist/utility/blueprint/material/texture.js.map +1 -1
- package/dist/utility/serialization/manager.js +434 -101
- package/dist/utility/serialization/manager.js.map +1 -1
- package/dist/utility/serialization/scene/animation.js +401 -5
- package/dist/utility/serialization/scene/animation.js.map +1 -1
- package/dist/utility/serialization/scene/batch.js +4 -0
- package/dist/utility/serialization/scene/batch.js.map +1 -1
- package/dist/utility/serialization/scene/camera.js +186 -26
- package/dist/utility/serialization/scene/camera.js.map +1 -1
- package/dist/utility/serialization/scene/common.js +26 -5
- package/dist/utility/serialization/scene/common.js.map +1 -1
- package/dist/utility/serialization/scene/hair.js +1141 -0
- package/dist/utility/serialization/scene/hair.js.map +1 -0
- package/dist/utility/serialization/scene/light.js +84 -4
- package/dist/utility/serialization/scene/light.js.map +1 -1
- package/dist/utility/serialization/scene/material.js +1867 -166
- package/dist/utility/serialization/scene/material.js.map +1 -1
- package/dist/utility/serialization/scene/mesh.js +231 -14
- package/dist/utility/serialization/scene/mesh.js.map +1 -1
- package/dist/utility/serialization/scene/node.js +141 -7
- package/dist/utility/serialization/scene/node.js.map +1 -1
- package/dist/utility/serialization/scene/particle.js +4 -0
- package/dist/utility/serialization/scene/particle.js.map +1 -1
- package/dist/utility/serialization/scene/primitive.js +18 -0
- package/dist/utility/serialization/scene/primitive.js.map +1 -1
- package/dist/utility/serialization/scene/sprite.js +4 -0
- package/dist/utility/serialization/scene/sprite.js.map +1 -1
- package/dist/utility/serialization/scene/terrain.js +4 -0
- package/dist/utility/serialization/scene/terrain.js.map +1 -1
- package/dist/utility/serialization/scene/text.js +4 -0
- package/dist/utility/serialization/scene/text.js.map +1 -1
- package/dist/utility/serialization/scene/water.js +1105 -21
- package/dist/utility/serialization/scene/water.js.map +1 -1
- package/dist/utility/serialization/types.js.map +1 -1
- package/dist/utility/shprojector.js +9 -8
- package/dist/utility/shprojector.js.map +1 -1
- package/dist/values.js +49 -2
- package/dist/values.js.map +1 -1
- package/package.json +3 -3
- package/dist/render/rendergraph/frame_graph_context.js +0 -41
- package/dist/render/rendergraph/frame_graph_context.js.map +0 -1
- package/dist/shaders/ssgi.js +0 -63
- package/dist/shaders/ssgi.js.map +0 -1
- package/dist/shaders/ssgi_reprojection.js +0 -118
- package/dist/shaders/ssgi_reprojection.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shader.js","sources":["../../src/shadow/shader.ts"],"sourcesContent":["import type { PBInsideFunctionScope, PBShaderExp } from '@zephyr3d/device';\r\nimport { ShaderHelper } from '../material';\r\nimport { LIGHT_TYPE_DIRECTIONAL
|
|
1
|
+
{"version":3,"file":"shader.js","sources":["../../src/shadow/shader.ts"],"sourcesContent":["import type { PBInsideFunctionScope, PBShaderExp } from '@zephyr3d/device';\r\nimport { ShaderHelper } from '../material/shader/helper';\r\nimport { LIGHT_TYPE_DIRECTIONAL } from '../values';\r\nimport { getShadowReceiverBiasFactor, isMaskedPerspectiveShadowLight } from './receiver_bias';\r\n\r\nfunction getShadowReceiverPerspectiveBiasScale(\r\n scope: PBInsideFunctionScope,\r\n lightType: number,\r\n linearDepth: PBShaderExp,\r\n farNearRatio: PBShaderExp\r\n) {\r\n const pb = scope.$builder;\r\n const alphaCutoff = (scope as PBInsideFunctionScope & { zAlphaCutoff?: PBShaderExp }).zAlphaCutoff;\r\n if (alphaCutoff && isMaskedPerspectiveShadowLight(scope, lightType)) {\r\n const cutoff = pb.clamp(alphaCutoff, 0, 1);\r\n // Perspective shadow cameras (spot / point) can over-amplify receiver bias at depth.\r\n // Keep masked layered geometry in a much tighter range so hair cards can still self-occlude.\r\n const cappedFarNearRatio = pb.min(farNearRatio, pb.mix(1.15, 1.6, cutoff));\r\n return pb.mix(1, cappedFarNearRatio, linearDepth) as PBShaderExp;\r\n }\r\n return pb.mix(1, farNearRatio, linearDepth) as PBShaderExp;\r\n}\r\n\r\n/**\r\n * Constant depth bias for a cascaded directional shadow.\r\n *\r\n * @remarks\r\n * The slope-scaled term that used to occupy `depthBiasValues.y` is gone: `.y`\r\n * now carries the normal offset distance, and normal offsetting (see\r\n * `ShaderHelper.applyShadowNormalOffset`) is what handles receiver slope. A\r\n * depth bias cannot, because the depth spanned by one texel diverges as the\r\n * surface turns away from the light.\r\n */\r\nexport function computeShadowBiasCSM(scope: PBInsideFunctionScope, _NdotL: PBShaderExp, split: PBShaderExp) {\r\n const pb = scope.$builder;\r\n return pb.mul(\r\n ShaderHelper.getDepthBiasValues(scope).x,\r\n getShadowReceiverBiasFactor(scope),\r\n ShaderHelper.getShadowCascadeBiasScale(scope, split)\r\n );\r\n}\r\n\r\n/**\r\n * Constant depth bias for a single-cascade shadow. See {@link computeShadowBiasCSM}\r\n * for why there is no slope-scaled term.\r\n */\r\nexport function computeShadowBias(\r\n lightType: number,\r\n scope: PBInsideFunctionScope,\r\n z: PBShaderExp,\r\n _NdotL: PBShaderExp,\r\n linear: boolean\r\n) {\r\n const pb = scope.$builder;\r\n const depthBiasParam = ShaderHelper.getDepthBiasValues(scope);\r\n const receiverBiasFactor = getShadowReceiverBiasFactor(scope);\r\n if (lightType === LIGHT_TYPE_DIRECTIONAL) {\r\n return pb.mul(depthBiasParam.x, receiverBiasFactor);\r\n } else {\r\n const nearFar = ShaderHelper.getShadowCameraParams(scope).xy;\r\n const linearDepth = linear ? z : ShaderHelper.nonLinearDepthToLinearNormalized(scope, z, nearFar);\r\n const biasScaleFactor = getShadowReceiverPerspectiveBiasScale(\r\n scope,\r\n lightType,\r\n linearDepth,\r\n depthBiasParam.w\r\n );\r\n let bias = pb.mul(depthBiasParam.x, receiverBiasFactor, biasScaleFactor) as PBShaderExp;\r\n if (isMaskedPerspectiveShadowLight(scope, lightType)) {\r\n const alphaCutoff = (scope as PBInsideFunctionScope & { zAlphaCutoff?: PBShaderExp }).zAlphaCutoff!;\r\n bias = pb.mul(bias, pb.mix(0.12, 0.22, pb.clamp(alphaCutoff, 0, 1))) as PBShaderExp;\r\n }\r\n return bias;\r\n }\r\n}\r\n"],"names":["getShadowReceiverPerspectiveBiasScale","scope","lightType","linearDepth","farNearRatio","pb","$builder","alphaCutoff","zAlphaCutoff","isMaskedPerspectiveShadowLight","cutoff","clamp","cappedFarNearRatio","min","mix","computeShadowBiasCSM","_NdotL","split","mul","ShaderHelper","getDepthBiasValues","x","getShadowReceiverBiasFactor","getShadowCascadeBiasScale","computeShadowBias","z","linear","depthBiasParam","receiverBiasFactor","LIGHT_TYPE_DIRECTIONAL","nearFar","getShadowCameraParams","xy","nonLinearDepthToLinearNormalized","biasScaleFactor","w","bias"],"mappings":";;;;AAKA,SAASA,sCACPC,KAA4B,EAC5BC,SAAiB,EACjBC,WAAwB,EACxBC,YAAyB,EAAA;IAEzB,MAAMC,EAAAA,GAAKJ,MAAMK,QAAQ;IACzB,MAAMC,WAAAA,GAAc,KAACN,CAAiEO,YAAY;IAClG,IAAID,WAAAA,IAAeE,8BAA+BR,CAAAA,KAAAA,EAAOC,SAAY,CAAA,EAAA;AACnE,QAAA,MAAMQ,MAASL,GAAAA,EAAAA,CAAGM,KAAK,CAACJ,aAAa,CAAG,EAAA,CAAA,CAAA;;;QAGxC,MAAMK,kBAAAA,GAAqBP,GAAGQ,GAAG,CAACT,cAAcC,EAAGS,CAAAA,GAAG,CAAC,IAAA,EAAM,GAAKJ,EAAAA,MAAAA,CAAAA,CAAAA;AAClE,QAAA,OAAOL,EAAGS,CAAAA,GAAG,CAAC,CAAA,EAAGF,kBAAoBT,EAAAA,WAAAA,CAAAA;AACvC;AACA,IAAA,OAAOE,EAAGS,CAAAA,GAAG,CAAC,CAAA,EAAGV,YAAcD,EAAAA,WAAAA,CAAAA;AACjC;AAEA;;;;;;;;;AASC,IACM,SAASY,oBAAAA,CAAqBd,KAA4B,EAAEe,MAAmB,EAAEC,KAAkB,EAAA;IACxG,MAAMZ,EAAAA,GAAKJ,MAAMK,QAAQ;AACzB,IAAA,OAAOD,EAAGa,CAAAA,GAAG,CACXC,YAAAA,CAAaC,kBAAkB,CAACnB,KAAAA,CAAAA,CAAOoB,CAAC,EACxCC,2BAA4BrB,CAAAA,KAAAA,CAAAA,EAC5BkB,YAAaI,CAAAA,yBAAyB,CAACtB,KAAOgB,EAAAA,KAAAA,CAAAA,CAAAA;AAElD;AAEA;;;IAIO,SAASO,iBAAAA,CACdtB,SAAiB,EACjBD,KAA4B,EAC5BwB,CAAc,EACdT,MAAmB,EACnBU,MAAe,EAAA;IAEf,MAAMrB,EAAAA,GAAKJ,MAAMK,QAAQ;IACzB,MAAMqB,cAAAA,GAAiBR,YAAaC,CAAAA,kBAAkB,CAACnB,KAAAA,CAAAA;AACvD,IAAA,MAAM2B,qBAAqBN,2BAA4BrB,CAAAA,KAAAA,CAAAA;AACvD,IAAA,IAAIC,cAAc2B,sBAAwB,EAAA;AACxC,QAAA,OAAOxB,EAAGa,CAAAA,GAAG,CAACS,cAAAA,CAAeN,CAAC,EAAEO,kBAAAA,CAAAA;KAC3B,MAAA;AACL,QAAA,MAAME,OAAUX,GAAAA,YAAAA,CAAaY,qBAAqB,CAAC9B,OAAO+B,EAAE;AAC5D,QAAA,MAAM7B,cAAcuB,MAASD,GAAAA,CAAAA,GAAIN,aAAac,gCAAgC,CAAChC,OAAOwB,CAAGK,EAAAA,OAAAA,CAAAA;AACzF,QAAA,MAAMI,kBAAkBlC,qCACtBC,CAAAA,KAAAA,EACAC,SACAC,EAAAA,WAAAA,EACAwB,eAAeQ,CAAC,CAAA;AAElB,QAAA,IAAIC,OAAO/B,EAAGa,CAAAA,GAAG,CAACS,cAAeN,CAAAA,CAAC,EAAEO,kBAAoBM,EAAAA,eAAAA,CAAAA;QACxD,IAAIzB,8BAAAA,CAA+BR,OAAOC,SAAY,CAAA,EAAA;YACpD,MAAMK,WAAAA,GAAc,KAACN,CAAiEO,YAAY;AAClG4B,YAAAA,IAAAA,GAAO/B,EAAGa,CAAAA,GAAG,CAACkB,IAAAA,EAAM/B,EAAGS,CAAAA,GAAG,CAAC,IAAA,EAAM,IAAMT,EAAAA,EAAAA,CAAGM,KAAK,CAACJ,aAAa,CAAG,EAAA,CAAA,CAAA,CAAA,CAAA;AAClE;QACA,OAAO6B,IAAAA;AACT;AACF;;;;"}
|
|
@@ -41,6 +41,95 @@ import { getDevice } from '../app/api.js';
|
|
|
41
41
|
const device = getDevice();
|
|
42
42
|
return device.type !== 'webgl' || REVERSE_Z && device.getDeviceCaps().framebufferCaps.supportDepth32float ? 'd32f' : 'd24s8';
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Number of geometry passes this implementation needs over the shadow casters.
|
|
46
|
+
*
|
|
47
|
+
* Almost every technique rasterises the casters once and post-processes the
|
|
48
|
+
* result, which is what {@link postRenderShadowMap} is for - it runs after the
|
|
49
|
+
* geometry is on the map and receives no scene state, so a blur or resolve
|
|
50
|
+
* fits there but a second draw of the casters does not.
|
|
51
|
+
*
|
|
52
|
+
* Deep opacity maps do need that second draw: the first pass establishes the
|
|
53
|
+
* frontmost depth per texel and the second accumulates transmittance in layers
|
|
54
|
+
* measured from it, so the casters must be rasterised again with the first
|
|
55
|
+
* pass's output already available. Returning a count above one makes
|
|
56
|
+
* `ShadowMapper.render` repeat the draw, calling {@link beginGeometryPass}
|
|
57
|
+
* before each one.
|
|
58
|
+
*
|
|
59
|
+
* @returns The number of passes; one unless overridden.
|
|
60
|
+
*/ getGeometryPassCount(_shadowMapParams) {
|
|
61
|
+
return 1;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Whether this implementation can split a directional light into cascades.
|
|
65
|
+
*
|
|
66
|
+
* Cascades need one shadow map per split, which every depth-based technique
|
|
67
|
+
* gets by rendering into array layers. A technique whose storage is already
|
|
68
|
+
* spoken for - a deep opacity map packs layer depth and coverage into the one
|
|
69
|
+
* texture the receiver is allowed to bind - has nowhere to put them, and asks
|
|
70
|
+
* to be driven with a single split instead.
|
|
71
|
+
*
|
|
72
|
+
* @returns True unless overridden.
|
|
73
|
+
*/ supportsCascades() {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Prepares state for the given geometry pass.
|
|
78
|
+
*
|
|
79
|
+
* Only called when {@link getGeometryPassCount} returns more than one. The
|
|
80
|
+
* index is zero-based. Implementations that vary the emitted shader between
|
|
81
|
+
* passes must fold the index into {@link getShaderHash} as well, since the
|
|
82
|
+
* shadow pass keys its programs on that hash.
|
|
83
|
+
*
|
|
84
|
+
* @param shadowMapParams - Current shadow map state.
|
|
85
|
+
* @param pass - Zero-based index of the geometry pass about to run.
|
|
86
|
+
*/ beginGeometryPass(_shadowMapParams, _pass) {}
|
|
87
|
+
/**
|
|
88
|
+
* Last say on the render states a shadow caster is drawn with.
|
|
89
|
+
*
|
|
90
|
+
* Runs after the material has set up its own states, in the same spirit as the
|
|
91
|
+
* OIT hook on the transparent pass. Techniques that rasterise the casters once
|
|
92
|
+
* want the defaults - depth test and write on, no blending - and leave this
|
|
93
|
+
* alone. An accumulating pass wants the opposite and overrides it here rather
|
|
94
|
+
* than teaching the material about shadow implementations.
|
|
95
|
+
*
|
|
96
|
+
* @param shadowMapParams - Current shadow map state.
|
|
97
|
+
* @param stateSet - The state set about to be applied, already populated.
|
|
98
|
+
*/ setCasterRenderStates(_shadowMapParams, _stateSet) {}
|
|
99
|
+
/**
|
|
100
|
+
* Declares any extra resources the caster shader needs, in bind group 0.
|
|
101
|
+
*
|
|
102
|
+
* The shadow map pass otherwise gives a caster only the camera and the light,
|
|
103
|
+
* which is all a technique needs when it just records depth. One that reads
|
|
104
|
+
* back its own earlier output - a deep opacity map measuring layers from the
|
|
105
|
+
* frontmost strand - declares that input here instead of the pass hard-coding
|
|
106
|
+
* knowledge of the implementation.
|
|
107
|
+
*
|
|
108
|
+
* @param scope - Global shader scope of the caster program.
|
|
109
|
+
* @param shadowMapParams - Current shadow map state.
|
|
110
|
+
*/ declareCasterUniforms(_scope, _shadowMapParams) {}
|
|
111
|
+
/**
|
|
112
|
+
* Binds whatever {@link declareCasterUniforms} declared.
|
|
113
|
+
*
|
|
114
|
+
* @param bindGroup - Bind group 0 of the caster program.
|
|
115
|
+
* @param shadowMapParams - Current shadow map state.
|
|
116
|
+
*/ applyCasterUniforms(_bindGroup, _shadowMapParams) {}
|
|
117
|
+
/**
|
|
118
|
+
* Whether casters should still be alpha-clipped before their shadow output.
|
|
119
|
+
*
|
|
120
|
+
* A depth-recording technique wants the clip: a fragment that fails it is not
|
|
121
|
+
* there, and letting it write depth would block light with geometry the eye
|
|
122
|
+
* never sees. A technique that records coverage instead wants the raw alpha,
|
|
123
|
+
* because the clip destroys exactly the quantity it is accumulating - a
|
|
124
|
+
* dithered caster survives with probability alpha and then contributes alpha,
|
|
125
|
+
* which accumulates alpha squared, and a cutoff drops the thin strands that
|
|
126
|
+
* carry most of a groom's transmittance.
|
|
127
|
+
*
|
|
128
|
+
* @param shadowMapParams - Current shadow map state.
|
|
129
|
+
* @returns True unless overridden.
|
|
130
|
+
*/ clipsCasterAlpha(_shadowMapParams) {
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
44
133
|
}
|
|
45
134
|
|
|
46
135
|
export { ShadowImpl, getPackedShadowMapClearColor, getShadowMapFarthestDepth };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shadow_impl.js","sources":["../../src/shadow/shadow_impl.ts"],"sourcesContent":["import type { PBInsideFunctionScope, PBShaderExp, TextureFormat } from '@zephyr3d/device';\r\nimport type { ShadowMapParams, ShadowMapType, ShadowMode } from './shadowmapper';\r\nimport { REVERSE_Z, Vector4, type Nullable } from '@zephyr3d/base';\r\nimport { isDeviceDepthShadow } from '../shaders/shadow';\r\nimport { getDevice } from '../app/api';\r\n\r\n/** @internal */\r\nexport function getShadowMapFarthestDepth(lightType: number) {\r\n return REVERSE_Z && isDeviceDepthShadow(lightType) ? 0 : 1;\r\n}\r\n\r\n/** @internal */\r\nexport function getPackedShadowMapClearColor(lightType: number) {\r\n // The RGBA8 decoder assigns the alpha channel a weight of 1, so the\r\n // normalized depth endpoints are represented by transparent/opaque black.\r\n return new Vector4(0, 0, 0, getShadowMapFarthestDepth(lightType));\r\n}\r\n\r\n/** @internal */\r\nexport abstract class ShadowImpl {\r\n protected _resourceDirty: boolean;\r\n constructor() {\r\n this._resourceDirty = true;\r\n }\r\n invalidateResource() {\r\n this._resourceDirty = true;\r\n }\r\n updateResources(shadowMapParams: ShadowMapParams) {\r\n this.doUpdateResources(shadowMapParams);\r\n }\r\n abstract getType(): ShadowMode;\r\n abstract getShadowMapBorder(shadowMapParams: ShadowMapParams): number;\r\n getShadowMapClearColor(shadowMapParams: ShadowMapParams) {\r\n const colorAttachment = shadowMapParams.shadowMapFramebuffer?.getColorAttachments()[0];\r\n // Empty texels must read as \"farthest\": under reverse-Z that is 0 for\r\n // device-encoded directional/rect maps, 1 for linear point/spot maps.\r\n const farthest = getShadowMapFarthestDepth(shadowMapParams.lightType);\r\n return colorAttachment\r\n ? colorAttachment.isFloatFormat()\r\n ? new Vector4(farthest, farthest, farthest, 1)\r\n : getPackedShadowMapClearColor(shadowMapParams.lightType)\r\n : null;\r\n }\r\n /**\r\n * Depth format for shadow maps rendered into a native depth attachment.\r\n *\r\n * Float depth everywhere except WebGL, where d24s8 is kept under\r\n * standard-Z (WebGL1 compatibility, no precision benefit anyway). Under\r\n * reverse-Z the WebGL2 float depth format is used so shadow maps share\r\n * the near-uniform precision distribution (full benefit with\r\n * EXT_clip_control active).\r\n * @internal\r\n */\r\n protected preferredShadowMapDepthFormat(): TextureFormat {\r\n const device = getDevice();\r\n return device.type !== 'webgl' ||\r\n (REVERSE_Z && device.getDeviceCaps().framebufferCaps.supportDepth32float)\r\n ? 'd32f'\r\n : 'd24s8';\r\n }\r\n abstract getParams(out?: Vector4): Vector4;\r\n abstract getShadowMap(shadowMapParams: ShadowMapParams): ShadowMapType;\r\n abstract postRenderShadowMap(shadowMapParams: ShadowMapParams): void;\r\n abstract getDepthScale(): number;\r\n abstract setDepthScale(val: number): void;\r\n abstract resourceDirty(): boolean;\r\n abstract doUpdateResources(shadowMapParams: ShadowMapParams): void;\r\n abstract getShaderHash(): string;\r\n abstract computeShadowMapDepth(\r\n shadowMapParams: ShadowMapParams,\r\n scope: PBInsideFunctionScope,\r\n worldPos: PBShaderExp\r\n ): PBShaderExp;\r\n abstract computeShadow(\r\n shadowMapParams: ShadowMapParams,\r\n scope: PBInsideFunctionScope,\r\n shadowVertex: PBShaderExp,\r\n NdotL: PBShaderExp\r\n ): PBShaderExp;\r\n abstract computeShadowCSM(\r\n shadowMapParams: ShadowMapParams,\r\n scope: PBInsideFunctionScope,\r\n shadowVertex: PBShaderExp,\r\n NdotL: PBShaderExp,\r\n split: PBShaderExp\r\n ): PBShaderExp;\r\n abstract getShadowMapColorFormat(shadowMapParams: ShadowMapParams): Nullable<TextureFormat>;\r\n abstract getShadowMapDepthFormat(shadowMapParams: ShadowMapParams): TextureFormat;\r\n abstract useNativeShadowMap(shadowMapParams: ShadowMapParams): boolean;\r\n}\r\n"],"names":["getShadowMapFarthestDepth","lightType","REVERSE_Z","isDeviceDepthShadow","getPackedShadowMapClearColor","Vector4","ShadowImpl","_resourceDirty","invalidateResource","updateResources","shadowMapParams","doUpdateResources","getShadowMapClearColor","colorAttachment","shadowMapFramebuffer","getColorAttachments","farthest","isFloatFormat","device","getDevice","type","getDeviceCaps","framebufferCaps","supportDepth32float"],"mappings":";;;;AAMA,iBACO,SAASA,yBAAAA,CAA0BC,SAAiB,EAAA;IACzD,OAAOC,SAAAA,IAAaC,mBAAoBF,CAAAA,SAAAA,CAAAA,GAAa,CAAI,GAAA,CAAA;AAC3D;AAEA,iBACO,SAASG,4BAAAA,CAA6BH,SAAiB,EAAA;;;AAG5D,IAAA,OAAO,IAAII,OAAAA,CAAQ,CAAG,EAAA,CAAA,EAAG,GAAGL,yBAA0BC,CAAAA,SAAAA,CAAAA,CAAAA;AACxD;AAEA,iBACO,MAAeK,UAAAA,CAAAA;IACVC,cAAwB;IAClC,WAAc,EAAA;QACZ,IAAI,CAACA,cAAc,GAAG,IAAA;AACxB;IACAC,kBAAqB,GAAA;QACnB,IAAI,CAACD,cAAc,GAAG,IAAA;AACxB;AACAE,IAAAA,eAAAA,CAAgBC,eAAgC,EAAE;QAChD,IAAI,CAACC,iBAAiB,CAACD,eAAAA,CAAAA;AACzB;AAGAE,IAAAA,sBAAAA,CAAuBF,eAAgC,EAAE;AACvD,QAAA,MAAMG,kBAAkBH,eAAgBI,CAAAA,oBAAoB,EAAEC,mBAAAA,EAAqB,CAAC,CAAE,CAAA;;;QAGtF,MAAMC,QAAAA,GAAWhB,yBAA0BU,CAAAA,eAAAA,CAAgBT,SAAS,CAAA;AACpE,QAAA,OAAOY,eACHA,GAAAA,eAAAA,CAAgBI,aAAa,EAAA,GAC3B,IAAIZ,OAAAA,CAAQW,QAAUA,EAAAA,QAAAA,EAAUA,QAAU,EAAA,CAAA,CAAA,GAC1CZ,4BAA6BM,CAAAA,eAAAA,CAAgBT,SAAS,CACxD,GAAA,IAAA;AACN;AACA;;;;;;;;;AASC,MACD,6BAAyD,GAAA;AACvD,QAAA,MAAMiB,MAASC,GAAAA,SAAAA,EAAAA;AACf,QAAA,OAAOD,MAAOE,CAAAA,IAAI,KAAK,OAAA,IACpBlB,SAAagB,IAAAA,MAAAA,CAAOG,aAAa,EAAA,CAAGC,eAAe,CAACC,mBAAmB,GACtE,MACA,GAAA,OAAA;AACN;AA8BF;;;;"}
|
|
1
|
+
{"version":3,"file":"shadow_impl.js","sources":["../../src/shadow/shadow_impl.ts"],"sourcesContent":["import type {\r\n BindGroup,\r\n PBGlobalScope,\r\n PBInsideFunctionScope,\r\n PBShaderExp,\r\n RenderStateSet,\r\n TextureFormat\r\n} from '@zephyr3d/device';\r\nimport type { ShadowMapParams, ShadowMapType, ShadowMode } from './shadowmapper';\r\nimport { REVERSE_Z, Vector4, type Nullable } from '@zephyr3d/base';\r\nimport { isDeviceDepthShadow } from '../shaders/shadow';\r\nimport { getDevice } from '../app/api';\r\n\r\n/** @internal */\r\nexport function getShadowMapFarthestDepth(lightType: number) {\r\n return REVERSE_Z && isDeviceDepthShadow(lightType) ? 0 : 1;\r\n}\r\n\r\n/** @internal */\r\nexport function getPackedShadowMapClearColor(lightType: number) {\r\n // The RGBA8 decoder assigns the alpha channel a weight of 1, so the\r\n // normalized depth endpoints are represented by transparent/opaque black.\r\n return new Vector4(0, 0, 0, getShadowMapFarthestDepth(lightType));\r\n}\r\n\r\n/** @internal */\r\nexport abstract class ShadowImpl {\r\n protected _resourceDirty: boolean;\r\n constructor() {\r\n this._resourceDirty = true;\r\n }\r\n invalidateResource() {\r\n this._resourceDirty = true;\r\n }\r\n updateResources(shadowMapParams: ShadowMapParams) {\r\n this.doUpdateResources(shadowMapParams);\r\n }\r\n abstract getType(): ShadowMode;\r\n abstract getShadowMapBorder(shadowMapParams: ShadowMapParams): number;\r\n getShadowMapClearColor(shadowMapParams: ShadowMapParams) {\r\n const colorAttachment = shadowMapParams.shadowMapFramebuffer?.getColorAttachments()[0];\r\n // Empty texels must read as \"farthest\": under reverse-Z that is 0 for\r\n // device-encoded directional/rect maps, 1 for linear point/spot maps.\r\n const farthest = getShadowMapFarthestDepth(shadowMapParams.lightType);\r\n return colorAttachment\r\n ? colorAttachment.isFloatFormat()\r\n ? new Vector4(farthest, farthest, farthest, 1)\r\n : getPackedShadowMapClearColor(shadowMapParams.lightType)\r\n : null;\r\n }\r\n /**\r\n * Depth format for shadow maps rendered into a native depth attachment.\r\n *\r\n * Float depth everywhere except WebGL, where d24s8 is kept under\r\n * standard-Z (WebGL1 compatibility, no precision benefit anyway). Under\r\n * reverse-Z the WebGL2 float depth format is used so shadow maps share\r\n * the near-uniform precision distribution (full benefit with\r\n * EXT_clip_control active).\r\n * @internal\r\n */\r\n protected preferredShadowMapDepthFormat(): TextureFormat {\r\n const device = getDevice();\r\n return device.type !== 'webgl' ||\r\n (REVERSE_Z && device.getDeviceCaps().framebufferCaps.supportDepth32float)\r\n ? 'd32f'\r\n : 'd24s8';\r\n }\r\n abstract getParams(out?: Vector4): Vector4;\r\n /**\r\n * Number of geometry passes this implementation needs over the shadow casters.\r\n *\r\n * Almost every technique rasterises the casters once and post-processes the\r\n * result, which is what {@link postRenderShadowMap} is for - it runs after the\r\n * geometry is on the map and receives no scene state, so a blur or resolve\r\n * fits there but a second draw of the casters does not.\r\n *\r\n * Deep opacity maps do need that second draw: the first pass establishes the\r\n * frontmost depth per texel and the second accumulates transmittance in layers\r\n * measured from it, so the casters must be rasterised again with the first\r\n * pass's output already available. Returning a count above one makes\r\n * `ShadowMapper.render` repeat the draw, calling {@link beginGeometryPass}\r\n * before each one.\r\n *\r\n * @returns The number of passes; one unless overridden.\r\n */\r\n getGeometryPassCount(_shadowMapParams: ShadowMapParams): number {\r\n return 1;\r\n }\r\n /**\r\n * Whether this implementation can split a directional light into cascades.\r\n *\r\n * Cascades need one shadow map per split, which every depth-based technique\r\n * gets by rendering into array layers. A technique whose storage is already\r\n * spoken for - a deep opacity map packs layer depth and coverage into the one\r\n * texture the receiver is allowed to bind - has nowhere to put them, and asks\r\n * to be driven with a single split instead.\r\n *\r\n * @returns True unless overridden.\r\n */\r\n supportsCascades(): boolean {\r\n return true;\r\n }\r\n /**\r\n * Prepares state for the given geometry pass.\r\n *\r\n * Only called when {@link getGeometryPassCount} returns more than one. The\r\n * index is zero-based. Implementations that vary the emitted shader between\r\n * passes must fold the index into {@link getShaderHash} as well, since the\r\n * shadow pass keys its programs on that hash.\r\n *\r\n * @param shadowMapParams - Current shadow map state.\r\n * @param pass - Zero-based index of the geometry pass about to run.\r\n */\r\n beginGeometryPass(_shadowMapParams: ShadowMapParams, _pass: number): void {}\r\n /**\r\n * Last say on the render states a shadow caster is drawn with.\r\n *\r\n * Runs after the material has set up its own states, in the same spirit as the\r\n * OIT hook on the transparent pass. Techniques that rasterise the casters once\r\n * want the defaults - depth test and write on, no blending - and leave this\r\n * alone. An accumulating pass wants the opposite and overrides it here rather\r\n * than teaching the material about shadow implementations.\r\n *\r\n * @param shadowMapParams - Current shadow map state.\r\n * @param stateSet - The state set about to be applied, already populated.\r\n */\r\n setCasterRenderStates(_shadowMapParams: ShadowMapParams, _stateSet: RenderStateSet): void {}\r\n /**\r\n * Declares any extra resources the caster shader needs, in bind group 0.\r\n *\r\n * The shadow map pass otherwise gives a caster only the camera and the light,\r\n * which is all a technique needs when it just records depth. One that reads\r\n * back its own earlier output - a deep opacity map measuring layers from the\r\n * frontmost strand - declares that input here instead of the pass hard-coding\r\n * knowledge of the implementation.\r\n *\r\n * @param scope - Global shader scope of the caster program.\r\n * @param shadowMapParams - Current shadow map state.\r\n */\r\n declareCasterUniforms(_scope: PBGlobalScope, _shadowMapParams: ShadowMapParams): void {}\r\n /**\r\n * Binds whatever {@link declareCasterUniforms} declared.\r\n *\r\n * @param bindGroup - Bind group 0 of the caster program.\r\n * @param shadowMapParams - Current shadow map state.\r\n */\r\n applyCasterUniforms(_bindGroup: BindGroup, _shadowMapParams: ShadowMapParams): void {}\r\n /**\r\n * Whether casters should still be alpha-clipped before their shadow output.\r\n *\r\n * A depth-recording technique wants the clip: a fragment that fails it is not\r\n * there, and letting it write depth would block light with geometry the eye\r\n * never sees. A technique that records coverage instead wants the raw alpha,\r\n * because the clip destroys exactly the quantity it is accumulating - a\r\n * dithered caster survives with probability alpha and then contributes alpha,\r\n * which accumulates alpha squared, and a cutoff drops the thin strands that\r\n * carry most of a groom's transmittance.\r\n *\r\n * @param shadowMapParams - Current shadow map state.\r\n * @returns True unless overridden.\r\n */\r\n clipsCasterAlpha(_shadowMapParams: ShadowMapParams): boolean {\r\n return true;\r\n }\r\n abstract getShadowMap(shadowMapParams: ShadowMapParams): ShadowMapType;\r\n abstract postRenderShadowMap(shadowMapParams: ShadowMapParams): void;\r\n abstract getDepthScale(): number;\r\n abstract setDepthScale(val: number): void;\r\n abstract resourceDirty(): boolean;\r\n abstract doUpdateResources(shadowMapParams: ShadowMapParams): void;\r\n abstract getShaderHash(): string;\r\n /**\r\n * Fragment output for a shadow caster.\r\n *\r\n * @param shadowMapParams - Current shadow map state.\r\n * @param scope - Caster fragment scope.\r\n * @param worldPos - World position of the fragment.\r\n * @param alpha - The material's own alpha, or null when it computes no fragment\r\n * colour and the fragment is therefore fully opaque. Depth-recording techniques\r\n * ignore it - by the time this runs the fragment has already survived whatever\r\n * alpha test applies - but one that records absorption needs the value itself.\r\n * It must be passed rather than read out of `scope`, because the caller's\r\n * `outColor` is local to the material's own shader function and defaults to a\r\n * zero vector when no colour was computed, which is indistinguishable from a\r\n * genuinely transparent fragment.\r\n */\r\n abstract computeShadowMapDepth(\r\n shadowMapParams: ShadowMapParams,\r\n scope: PBInsideFunctionScope,\r\n worldPos: PBShaderExp,\r\n alpha: Nullable<PBShaderExp>\r\n ): PBShaderExp;\r\n abstract computeShadow(\r\n shadowMapParams: ShadowMapParams,\r\n scope: PBInsideFunctionScope,\r\n shadowVertex: PBShaderExp,\r\n NdotL: PBShaderExp\r\n ): PBShaderExp;\r\n abstract computeShadowCSM(\r\n shadowMapParams: ShadowMapParams,\r\n scope: PBInsideFunctionScope,\r\n shadowVertex: PBShaderExp,\r\n NdotL: PBShaderExp,\r\n split: PBShaderExp\r\n ): PBShaderExp;\r\n abstract getShadowMapColorFormat(shadowMapParams: ShadowMapParams): Nullable<TextureFormat>;\r\n abstract getShadowMapDepthFormat(shadowMapParams: ShadowMapParams): TextureFormat;\r\n abstract useNativeShadowMap(shadowMapParams: ShadowMapParams): boolean;\r\n}\r\n"],"names":["getShadowMapFarthestDepth","lightType","REVERSE_Z","isDeviceDepthShadow","getPackedShadowMapClearColor","Vector4","ShadowImpl","_resourceDirty","invalidateResource","updateResources","shadowMapParams","doUpdateResources","getShadowMapClearColor","colorAttachment","shadowMapFramebuffer","getColorAttachments","farthest","isFloatFormat","device","getDevice","type","getDeviceCaps","framebufferCaps","supportDepth32float","getGeometryPassCount","_shadowMapParams","supportsCascades","beginGeometryPass","_pass","setCasterRenderStates","_stateSet","declareCasterUniforms","_scope","applyCasterUniforms","_bindGroup","clipsCasterAlpha"],"mappings":";;;;AAaA,iBACO,SAASA,yBAAAA,CAA0BC,SAAiB,EAAA;IACzD,OAAOC,SAAAA,IAAaC,mBAAoBF,CAAAA,SAAAA,CAAAA,GAAa,CAAI,GAAA,CAAA;AAC3D;AAEA,iBACO,SAASG,4BAAAA,CAA6BH,SAAiB,EAAA;;;AAG5D,IAAA,OAAO,IAAII,OAAAA,CAAQ,CAAG,EAAA,CAAA,EAAG,GAAGL,yBAA0BC,CAAAA,SAAAA,CAAAA,CAAAA;AACxD;AAEA,iBACO,MAAeK,UAAAA,CAAAA;IACVC,cAAwB;IAClC,WAAc,EAAA;QACZ,IAAI,CAACA,cAAc,GAAG,IAAA;AACxB;IACAC,kBAAqB,GAAA;QACnB,IAAI,CAACD,cAAc,GAAG,IAAA;AACxB;AACAE,IAAAA,eAAAA,CAAgBC,eAAgC,EAAE;QAChD,IAAI,CAACC,iBAAiB,CAACD,eAAAA,CAAAA;AACzB;AAGAE,IAAAA,sBAAAA,CAAuBF,eAAgC,EAAE;AACvD,QAAA,MAAMG,kBAAkBH,eAAgBI,CAAAA,oBAAoB,EAAEC,mBAAAA,EAAqB,CAAC,CAAE,CAAA;;;QAGtF,MAAMC,QAAAA,GAAWhB,yBAA0BU,CAAAA,eAAAA,CAAgBT,SAAS,CAAA;AACpE,QAAA,OAAOY,eACHA,GAAAA,eAAAA,CAAgBI,aAAa,EAAA,GAC3B,IAAIZ,OAAAA,CAAQW,QAAUA,EAAAA,QAAAA,EAAUA,QAAU,EAAA,CAAA,CAAA,GAC1CZ,4BAA6BM,CAAAA,eAAAA,CAAgBT,SAAS,CACxD,GAAA,IAAA;AACN;AACA;;;;;;;;;AASC,MACD,6BAAyD,GAAA;AACvD,QAAA,MAAMiB,MAASC,GAAAA,SAAAA,EAAAA;AACf,QAAA,OAAOD,MAAOE,CAAAA,IAAI,KAAK,OAAA,IACpBlB,SAAagB,IAAAA,MAAAA,CAAOG,aAAa,EAAA,CAAGC,eAAe,CAACC,mBAAmB,GACtE,MACA,GAAA,OAAA;AACN;AAEA;;;;;;;;;;;;;;;;MAiBAC,oBAAAA,CAAqBC,gBAAiC,EAAU;QAC9D,OAAO,CAAA;AACT;AACA;;;;;;;;;;AAUC,MACDC,gBAA4B,GAAA;QAC1B,OAAO,IAAA;AACT;AACA;;;;;;;;;;AAUC,MACDC,iBAAkBF,CAAAA,gBAAiC,EAAEG,KAAa,EAAQ;AAC1E;;;;;;;;;;;AAWC,MACDC,qBAAsBJ,CAAAA,gBAAiC,EAAEK,SAAyB,EAAQ;AAC1F;;;;;;;;;;;AAWC,MACDC,qBAAsBC,CAAAA,MAAqB,EAAEP,gBAAiC,EAAQ;AACtF;;;;;AAKC,MACDQ,mBAAoBC,CAAAA,UAAqB,EAAET,gBAAiC,EAAQ;AACpF;;;;;;;;;;;;;MAcAU,gBAAAA,CAAiBV,gBAAiC,EAAW;QAC3D,OAAO,IAAA;AACT;AA6CF;;;;"}
|
|
@@ -170,7 +170,7 @@ import { Disposable, AABB, DWeakRef } from '@zephyr3d/base';
|
|
|
170
170
|
return this._staticCasters.some((entry)=>entry.ref.get() === caster) || this._dynamicCasters.some((entry)=>entry.ref.get() === caster);
|
|
171
171
|
}
|
|
172
172
|
/** @internal */ isShadowCaster(caster) {
|
|
173
|
-
return !caster.disposed && (caster.isMesh() || caster.isClipmapTerrain());
|
|
173
|
+
return !caster.disposed && (caster.isMesh() || caster.isClipmapTerrain() || caster.isHair());
|
|
174
174
|
}
|
|
175
175
|
/** @internal */ getCasterAABB(caster) {
|
|
176
176
|
const bbox = caster.getWorldBoundingVolume()?.toAABB();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shadow_region.js","sources":["../../src/shadow/shadow_region.ts"],"sourcesContent":["import type { Nullable } from '@zephyr3d/base';\r\nimport { AABB, Disposable, DWeakRef } from '@zephyr3d/base';\r\nimport type { SceneNode } from '../scene/scene_node';\r\n\r\ntype StaticShadowCasterEntry = {\r\n ref: DWeakRef<SceneNode>;\r\n aabb: AABB;\r\n};\r\n\r\ntype DynamicShadowCasterEntry = {\r\n ref: DWeakRef<SceneNode>;\r\n callback: (node: SceneNode) => void;\r\n};\r\n\r\n/**\r\n * Maintains the world-space region used by directional light shadow maps.\r\n *\r\n * @public\r\n */\r\nexport class ShadowRegion extends Disposable {\r\n /** @internal */\r\n private _manualRegion: Nullable<AABB>;\r\n /** @internal */\r\n private readonly _staticRegion: AABB;\r\n /** @internal */\r\n private readonly _dynamicRegion: AABB;\r\n /** @internal */\r\n private readonly _region: AABB;\r\n /** @internal */\r\n private readonly _staticCasters: StaticShadowCasterEntry[];\r\n /** @internal */\r\n private readonly _dynamicCasters: DynamicShadowCasterEntry[];\r\n\r\n /**\r\n * Creates a shadow region.\r\n *\r\n * @param region - Optional manual world-space AABB used to initialize the region.\r\n */\r\n constructor(region?: Nullable<AABB>) {\r\n super();\r\n this._manualRegion = region ? new AABB(region) : null;\r\n this._staticRegion = new AABB().beginExtend();\r\n this._dynamicRegion = new AABB().beginExtend();\r\n this._region = new AABB().beginExtend();\r\n this._staticCasters = [];\r\n this._dynamicCasters = [];\r\n this.updateRegion();\r\n }\r\n\r\n /**\r\n * Final world-space shadow region.\r\n *\r\n * @returns The union of the manual, static caster, and dynamic caster regions, or `null` if no valid region exists.\r\n */\r\n get region(): Nullable<AABB> {\r\n return this._region.isValid() ? this._region : null;\r\n }\r\n\r\n /**\r\n * Region contributed by manually assigned AABB.\r\n *\r\n * @returns The manually assigned region, or `null` if no manual region is set.\r\n */\r\n get manualRegion(): Nullable<AABB> {\r\n return this._manualRegion;\r\n }\r\n\r\n /**\r\n * Region contributed by static shadow casters.\r\n *\r\n * @returns The combined static caster region, or `null` if no valid static caster region exists.\r\n */\r\n get staticRegion(): Nullable<AABB> {\r\n return this._staticRegion.isValid() ? this._staticRegion : null;\r\n }\r\n\r\n /**\r\n * Region contributed by dynamic shadow casters.\r\n *\r\n * @returns The combined dynamic caster region, or `null` if no valid dynamic caster region exists.\r\n */\r\n get dynamicRegion(): Nullable<AABB> {\r\n return this._dynamicRegion.isValid() ? this._dynamicRegion : null;\r\n }\r\n\r\n /**\r\n * Assigns a manual world-space AABB to the region.\r\n *\r\n * @param region - The manual world-space AABB to use, or `null` to clear the manual region.\r\n */\r\n setRegion(region: Nullable<AABB>) {\r\n this._manualRegion = region ? new AABB(region) : null;\r\n this.updateRegion();\r\n }\r\n\r\n /**\r\n * Adds a static shadow caster using a snapshot of its current world-space AABB.\r\n *\r\n * @param caster - The mesh or clipmap terrain node to include as a static caster.\r\n * @returns `this` for chaining.\r\n */\r\n addStaticCaster(caster: SceneNode): this {\r\n if (!this.isShadowCaster(caster) || this.hasCaster(caster)) {\r\n return this;\r\n }\r\n const aabb = this.getCasterAABB(caster);\r\n if (!aabb) {\r\n return this;\r\n }\r\n this._staticCasters.push({\r\n ref: new DWeakRef(caster),\r\n aabb\r\n });\r\n this.extendRegion(this._staticRegion, aabb);\r\n this.updateRegion();\r\n return this;\r\n }\r\n\r\n /**\r\n * Adds a dynamic shadow caster and tracks its bounding volume changes.\r\n *\r\n * @param caster - The mesh or clipmap terrain node to include as a dynamic caster.\r\n * @returns `this` for chaining.\r\n */\r\n addDynamicCaster(caster: SceneNode): this {\r\n if (!this.isShadowCaster(caster) || this.hasCaster(caster)) {\r\n return this;\r\n }\r\n const callback = () => {\r\n this.rebuildDynamicRegion();\r\n };\r\n caster.on('bvchanged', callback);\r\n this._dynamicCasters.push({\r\n ref: new DWeakRef(caster),\r\n callback\r\n });\r\n const aabb = this.getCasterAABB(caster);\r\n if (aabb) {\r\n this.extendRegion(this._dynamicRegion, aabb);\r\n this.updateRegion();\r\n }\r\n return this;\r\n }\r\n\r\n /**\r\n * Removes a previously added shadow caster.\r\n *\r\n * @param caster - The caster node to remove from the static or dynamic caster list.\r\n * @returns `this` for chaining.\r\n */\r\n removeCaster(caster: SceneNode): this {\r\n let removed = false;\r\n let changed = false;\r\n for (let i = this._staticCasters.length - 1; i >= 0; i--) {\r\n const entry = this._staticCasters[i];\r\n const node = entry.ref.get();\r\n if (!node || node === caster) {\r\n entry.ref.dispose();\r\n this._staticCasters.splice(i, 1);\r\n changed = true;\r\n removed ||= node === caster;\r\n }\r\n }\r\n for (let i = this._dynamicCasters.length - 1; i >= 0; i--) {\r\n const entry = this._dynamicCasters[i];\r\n const node = entry.ref.get();\r\n if (!node || node === caster) {\r\n (node ?? caster).off('bvchanged', entry.callback);\r\n entry.ref.dispose();\r\n this._dynamicCasters.splice(i, 1);\r\n changed = true;\r\n removed ||= node === caster;\r\n }\r\n }\r\n if (changed) {\r\n this.rebuildStaticRegion();\r\n this.rebuildDynamicRegion();\r\n }\r\n return this;\r\n }\r\n\r\n /**\r\n * Removes all shadow casters while keeping the manual region.\r\n */\r\n clearCasters() {\r\n for (const entry of this._dynamicCasters) {\r\n entry.ref.get()?.off('bvchanged', entry.callback);\r\n entry.ref.dispose();\r\n }\r\n for (const entry of this._staticCasters) {\r\n entry.ref.dispose();\r\n }\r\n this._staticCasters.length = 0;\r\n this._dynamicCasters.length = 0;\r\n this._staticRegion.beginExtend();\r\n this._dynamicRegion.beginExtend();\r\n this.updateRegion();\r\n }\r\n\r\n /**\r\n * Clears the manual region and all shadow casters.\r\n *\r\n * @returns `this` for chaining.\r\n */\r\n clear(): this {\r\n this._manualRegion = null;\r\n this.clearCasters();\r\n return this;\r\n }\r\n\r\n /** {@inheritDoc Disposable.onDispose} */\r\n protected onDispose(): void {\r\n this.clear();\r\n }\r\n\r\n /** @internal */\r\n private hasCaster(caster: SceneNode): boolean {\r\n return (\r\n this._staticCasters.some((entry) => entry.ref.get() === caster) ||\r\n this._dynamicCasters.some((entry) => entry.ref.get() === caster)\r\n );\r\n }\r\n\r\n /** @internal */\r\n private isShadowCaster(caster: SceneNode): boolean {\r\n return !caster.disposed && (caster.isMesh() || caster.isClipmapTerrain());\r\n }\r\n\r\n /** @internal */\r\n private getCasterAABB(caster: SceneNode): Nullable<AABB> {\r\n const bbox = caster.getWorldBoundingVolume()?.toAABB();\r\n return bbox?.isValid() ? new AABB(bbox) : null;\r\n }\r\n\r\n /** @internal */\r\n private rebuildStaticRegion() {\r\n this._staticRegion.beginExtend();\r\n for (let i = this._staticCasters.length - 1; i >= 0; i--) {\r\n const entry = this._staticCasters[i];\r\n if (!entry.ref.get()) {\r\n entry.ref.dispose();\r\n this._staticCasters.splice(i, 1);\r\n } else {\r\n this.extendRegion(this._staticRegion, entry.aabb);\r\n }\r\n }\r\n this.updateRegion();\r\n }\r\n\r\n /** @internal */\r\n private rebuildDynamicRegion() {\r\n this._dynamicRegion.beginExtend();\r\n for (let i = this._dynamicCasters.length - 1; i >= 0; i--) {\r\n const entry = this._dynamicCasters[i];\r\n const caster = entry.ref.get();\r\n if (!caster || !this.isShadowCaster(caster)) {\r\n caster?.off('bvchanged', entry.callback);\r\n entry.ref.dispose();\r\n this._dynamicCasters.splice(i, 1);\r\n continue;\r\n }\r\n const aabb = this.getCasterAABB(caster);\r\n if (aabb) {\r\n this.extendRegion(this._dynamicRegion, aabb);\r\n }\r\n }\r\n this.updateRegion();\r\n }\r\n\r\n /** @internal */\r\n private updateRegion() {\r\n this._region.beginExtend();\r\n if (this._manualRegion?.isValid()) {\r\n this.extendRegion(this._region, this._manualRegion);\r\n }\r\n if (this._staticRegion.isValid()) {\r\n this.extendRegion(this._region, this._staticRegion);\r\n }\r\n if (this._dynamicRegion.isValid()) {\r\n this.extendRegion(this._region, this._dynamicRegion);\r\n }\r\n }\r\n\r\n /** @internal */\r\n private extendRegion(region: AABB, aabb: AABB) {\r\n region.extend(aabb.minPoint);\r\n region.extend(aabb.maxPoint);\r\n }\r\n}\r\n"],"names":["ShadowRegion","Disposable","region","_manualRegion","AABB","_staticRegion","beginExtend","_dynamicRegion","_region","_staticCasters","_dynamicCasters","updateRegion","isValid","manualRegion","staticRegion","dynamicRegion","setRegion","addStaticCaster","caster","isShadowCaster","hasCaster","aabb","getCasterAABB","push","ref","DWeakRef","extendRegion","addDynamicCaster","callback","rebuildDynamicRegion","on","removeCaster","changed","i","length","entry","node","get","dispose","splice","off","rebuildStaticRegion","clearCasters","clear","some","disposed","isMesh","isClipmapTerrain","bbox","getWorldBoundingVolume","toAABB","extend","minPoint","maxPoint"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"shadow_region.js","sources":["../../src/shadow/shadow_region.ts"],"sourcesContent":["import type { Nullable } from '@zephyr3d/base';\r\nimport { AABB, Disposable, DWeakRef } from '@zephyr3d/base';\r\nimport type { SceneNode } from '../scene/scene_node';\r\n\r\ntype StaticShadowCasterEntry = {\r\n ref: DWeakRef<SceneNode>;\r\n aabb: AABB;\r\n};\r\n\r\ntype DynamicShadowCasterEntry = {\r\n ref: DWeakRef<SceneNode>;\r\n callback: (node: SceneNode) => void;\r\n};\r\n\r\n/**\r\n * Maintains the world-space region used by directional light shadow maps.\r\n *\r\n * @public\r\n */\r\nexport class ShadowRegion extends Disposable {\r\n /** @internal */\r\n private _manualRegion: Nullable<AABB>;\r\n /** @internal */\r\n private readonly _staticRegion: AABB;\r\n /** @internal */\r\n private readonly _dynamicRegion: AABB;\r\n /** @internal */\r\n private readonly _region: AABB;\r\n /** @internal */\r\n private readonly _staticCasters: StaticShadowCasterEntry[];\r\n /** @internal */\r\n private readonly _dynamicCasters: DynamicShadowCasterEntry[];\r\n\r\n /**\r\n * Creates a shadow region.\r\n *\r\n * @param region - Optional manual world-space AABB used to initialize the region.\r\n */\r\n constructor(region?: Nullable<AABB>) {\r\n super();\r\n this._manualRegion = region ? new AABB(region) : null;\r\n this._staticRegion = new AABB().beginExtend();\r\n this._dynamicRegion = new AABB().beginExtend();\r\n this._region = new AABB().beginExtend();\r\n this._staticCasters = [];\r\n this._dynamicCasters = [];\r\n this.updateRegion();\r\n }\r\n\r\n /**\r\n * Final world-space shadow region.\r\n *\r\n * @returns The union of the manual, static caster, and dynamic caster regions, or `null` if no valid region exists.\r\n */\r\n get region(): Nullable<AABB> {\r\n return this._region.isValid() ? this._region : null;\r\n }\r\n\r\n /**\r\n * Region contributed by manually assigned AABB.\r\n *\r\n * @returns The manually assigned region, or `null` if no manual region is set.\r\n */\r\n get manualRegion(): Nullable<AABB> {\r\n return this._manualRegion;\r\n }\r\n\r\n /**\r\n * Region contributed by static shadow casters.\r\n *\r\n * @returns The combined static caster region, or `null` if no valid static caster region exists.\r\n */\r\n get staticRegion(): Nullable<AABB> {\r\n return this._staticRegion.isValid() ? this._staticRegion : null;\r\n }\r\n\r\n /**\r\n * Region contributed by dynamic shadow casters.\r\n *\r\n * @returns The combined dynamic caster region, or `null` if no valid dynamic caster region exists.\r\n */\r\n get dynamicRegion(): Nullable<AABB> {\r\n return this._dynamicRegion.isValid() ? this._dynamicRegion : null;\r\n }\r\n\r\n /**\r\n * Assigns a manual world-space AABB to the region.\r\n *\r\n * @param region - The manual world-space AABB to use, or `null` to clear the manual region.\r\n */\r\n setRegion(region: Nullable<AABB>) {\r\n this._manualRegion = region ? new AABB(region) : null;\r\n this.updateRegion();\r\n }\r\n\r\n /**\r\n * Adds a static shadow caster using a snapshot of its current world-space AABB.\r\n *\r\n * @param caster - The mesh or clipmap terrain node to include as a static caster.\r\n * @returns `this` for chaining.\r\n */\r\n addStaticCaster(caster: SceneNode): this {\r\n if (!this.isShadowCaster(caster) || this.hasCaster(caster)) {\r\n return this;\r\n }\r\n const aabb = this.getCasterAABB(caster);\r\n if (!aabb) {\r\n return this;\r\n }\r\n this._staticCasters.push({\r\n ref: new DWeakRef(caster),\r\n aabb\r\n });\r\n this.extendRegion(this._staticRegion, aabb);\r\n this.updateRegion();\r\n return this;\r\n }\r\n\r\n /**\r\n * Adds a dynamic shadow caster and tracks its bounding volume changes.\r\n *\r\n * @param caster - The mesh or clipmap terrain node to include as a dynamic caster.\r\n * @returns `this` for chaining.\r\n */\r\n addDynamicCaster(caster: SceneNode): this {\r\n if (!this.isShadowCaster(caster) || this.hasCaster(caster)) {\r\n return this;\r\n }\r\n const callback = () => {\r\n this.rebuildDynamicRegion();\r\n };\r\n caster.on('bvchanged', callback);\r\n this._dynamicCasters.push({\r\n ref: new DWeakRef(caster),\r\n callback\r\n });\r\n const aabb = this.getCasterAABB(caster);\r\n if (aabb) {\r\n this.extendRegion(this._dynamicRegion, aabb);\r\n this.updateRegion();\r\n }\r\n return this;\r\n }\r\n\r\n /**\r\n * Removes a previously added shadow caster.\r\n *\r\n * @param caster - The caster node to remove from the static or dynamic caster list.\r\n * @returns `this` for chaining.\r\n */\r\n removeCaster(caster: SceneNode): this {\r\n let removed = false;\r\n let changed = false;\r\n for (let i = this._staticCasters.length - 1; i >= 0; i--) {\r\n const entry = this._staticCasters[i];\r\n const node = entry.ref.get();\r\n if (!node || node === caster) {\r\n entry.ref.dispose();\r\n this._staticCasters.splice(i, 1);\r\n changed = true;\r\n removed ||= node === caster;\r\n }\r\n }\r\n for (let i = this._dynamicCasters.length - 1; i >= 0; i--) {\r\n const entry = this._dynamicCasters[i];\r\n const node = entry.ref.get();\r\n if (!node || node === caster) {\r\n (node ?? caster).off('bvchanged', entry.callback);\r\n entry.ref.dispose();\r\n this._dynamicCasters.splice(i, 1);\r\n changed = true;\r\n removed ||= node === caster;\r\n }\r\n }\r\n if (changed) {\r\n this.rebuildStaticRegion();\r\n this.rebuildDynamicRegion();\r\n }\r\n return this;\r\n }\r\n\r\n /**\r\n * Removes all shadow casters while keeping the manual region.\r\n */\r\n clearCasters() {\r\n for (const entry of this._dynamicCasters) {\r\n entry.ref.get()?.off('bvchanged', entry.callback);\r\n entry.ref.dispose();\r\n }\r\n for (const entry of this._staticCasters) {\r\n entry.ref.dispose();\r\n }\r\n this._staticCasters.length = 0;\r\n this._dynamicCasters.length = 0;\r\n this._staticRegion.beginExtend();\r\n this._dynamicRegion.beginExtend();\r\n this.updateRegion();\r\n }\r\n\r\n /**\r\n * Clears the manual region and all shadow casters.\r\n *\r\n * @returns `this` for chaining.\r\n */\r\n clear(): this {\r\n this._manualRegion = null;\r\n this.clearCasters();\r\n return this;\r\n }\r\n\r\n /** {@inheritDoc Disposable.onDispose} */\r\n protected onDispose(): void {\r\n this.clear();\r\n }\r\n\r\n /** @internal */\r\n private hasCaster(caster: SceneNode): boolean {\r\n return (\r\n this._staticCasters.some((entry) => entry.ref.get() === caster) ||\r\n this._dynamicCasters.some((entry) => entry.ref.get() === caster)\r\n );\r\n }\r\n\r\n /** @internal */\r\n private isShadowCaster(caster: SceneNode): boolean {\r\n return !caster.disposed && (caster.isMesh() || caster.isClipmapTerrain() || caster.isHair());\r\n }\r\n\r\n /** @internal */\r\n private getCasterAABB(caster: SceneNode): Nullable<AABB> {\r\n const bbox = caster.getWorldBoundingVolume()?.toAABB();\r\n return bbox?.isValid() ? new AABB(bbox) : null;\r\n }\r\n\r\n /** @internal */\r\n private rebuildStaticRegion() {\r\n this._staticRegion.beginExtend();\r\n for (let i = this._staticCasters.length - 1; i >= 0; i--) {\r\n const entry = this._staticCasters[i];\r\n if (!entry.ref.get()) {\r\n entry.ref.dispose();\r\n this._staticCasters.splice(i, 1);\r\n } else {\r\n this.extendRegion(this._staticRegion, entry.aabb);\r\n }\r\n }\r\n this.updateRegion();\r\n }\r\n\r\n /** @internal */\r\n private rebuildDynamicRegion() {\r\n this._dynamicRegion.beginExtend();\r\n for (let i = this._dynamicCasters.length - 1; i >= 0; i--) {\r\n const entry = this._dynamicCasters[i];\r\n const caster = entry.ref.get();\r\n if (!caster || !this.isShadowCaster(caster)) {\r\n caster?.off('bvchanged', entry.callback);\r\n entry.ref.dispose();\r\n this._dynamicCasters.splice(i, 1);\r\n continue;\r\n }\r\n const aabb = this.getCasterAABB(caster);\r\n if (aabb) {\r\n this.extendRegion(this._dynamicRegion, aabb);\r\n }\r\n }\r\n this.updateRegion();\r\n }\r\n\r\n /** @internal */\r\n private updateRegion() {\r\n this._region.beginExtend();\r\n if (this._manualRegion?.isValid()) {\r\n this.extendRegion(this._region, this._manualRegion);\r\n }\r\n if (this._staticRegion.isValid()) {\r\n this.extendRegion(this._region, this._staticRegion);\r\n }\r\n if (this._dynamicRegion.isValid()) {\r\n this.extendRegion(this._region, this._dynamicRegion);\r\n }\r\n }\r\n\r\n /** @internal */\r\n private extendRegion(region: AABB, aabb: AABB) {\r\n region.extend(aabb.minPoint);\r\n region.extend(aabb.maxPoint);\r\n }\r\n}\r\n"],"names":["ShadowRegion","Disposable","region","_manualRegion","AABB","_staticRegion","beginExtend","_dynamicRegion","_region","_staticCasters","_dynamicCasters","updateRegion","isValid","manualRegion","staticRegion","dynamicRegion","setRegion","addStaticCaster","caster","isShadowCaster","hasCaster","aabb","getCasterAABB","push","ref","DWeakRef","extendRegion","addDynamicCaster","callback","rebuildDynamicRegion","on","removeCaster","changed","i","length","entry","node","get","dispose","splice","off","rebuildStaticRegion","clearCasters","clear","some","disposed","isMesh","isClipmapTerrain","isHair","bbox","getWorldBoundingVolume","toAABB","extend","minPoint","maxPoint"],"mappings":"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"}
|
|
@@ -5,8 +5,8 @@ import { ESM } from './esm.js';
|
|
|
5
5
|
import { VSM } from './vsm.js';
|
|
6
6
|
import { PCFOPT } from './pcf_opt.js';
|
|
7
7
|
import { PCSS } from './pcss.js';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import { DOM } from './dom.js';
|
|
9
|
+
import { LIGHT_TYPE_NONE, LIGHT_TYPE_POINT, LIGHT_TYPE_DIRECTIONAL } from '../values.js';
|
|
10
10
|
import { getDevice } from '../app/api.js';
|
|
11
11
|
import { ShadowRegion } from './shadow_region.js';
|
|
12
12
|
|
|
@@ -38,6 +38,9 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
|
|
|
38
38
|
/** @internal */ _pcssFilterSampleCount;
|
|
39
39
|
/** @internal */ _pcssMaxFilterRadius;
|
|
40
40
|
/** @internal */ _pcssTemporalJitter;
|
|
41
|
+
/** @internal */ _domLayerDistance;
|
|
42
|
+
/** @internal */ _domDensity;
|
|
43
|
+
/** @internal */ _domFilterSize;
|
|
41
44
|
/** @internal */ _vsmBlurKernelSize;
|
|
42
45
|
/** @internal */ _vsmBlurRadius;
|
|
43
46
|
/** @internal */ _vsmDarkness;
|
|
@@ -59,7 +62,7 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
|
|
|
59
62
|
numCascades: 1,
|
|
60
63
|
splitLambda: 0.5,
|
|
61
64
|
depthBias: 0.003,
|
|
62
|
-
normalBias:
|
|
65
|
+
normalBias: 1.5,
|
|
63
66
|
nearClip: 1
|
|
64
67
|
};
|
|
65
68
|
this._resourceDirty = true;
|
|
@@ -74,6 +77,9 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
|
|
|
74
77
|
this._pcssFilterSampleCount = 32;
|
|
75
78
|
this._pcssMaxFilterRadius = 32;
|
|
76
79
|
this._pcssTemporalJitter = true;
|
|
80
|
+
this._domLayerDistance = 0.25;
|
|
81
|
+
this._domDensity = 1;
|
|
82
|
+
this._domFilterSize = 5;
|
|
77
83
|
this._vsmBlurKernelSize = 5;
|
|
78
84
|
this._vsmBlurRadius = 4;
|
|
79
85
|
this._vsmDarkness = 0.3;
|
|
@@ -174,7 +180,20 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
|
|
|
174
180
|
set depthBias(val) {
|
|
175
181
|
this._config.depthBias = val;
|
|
176
182
|
}
|
|
177
|
-
/**
|
|
183
|
+
/**
|
|
184
|
+
* Normal offset bias, in shadow map texels.
|
|
185
|
+
*
|
|
186
|
+
* @remarks
|
|
187
|
+
* Before sampling, the receiver is moved along its normal by this many texel
|
|
188
|
+
* widths, scaled by `sin(theta)` between the normal and the light so the
|
|
189
|
+
* offset is zero facing the light and largest at grazing incidence. This is
|
|
190
|
+
* what removes grazing-angle self-shadowing acne; {@link depthBias} alone
|
|
191
|
+
* cannot, because the depth error over one texel grows without bound as the
|
|
192
|
+
* surface turns away from the light.
|
|
193
|
+
*
|
|
194
|
+
* Useful values are roughly 1 to 4. Too little leaves acne on curved surfaces
|
|
195
|
+
* near the terminator; too much detaches contact shadows from thin geometry.
|
|
196
|
+
*/ get normalBias() {
|
|
178
197
|
return this._config.normalBias;
|
|
179
198
|
}
|
|
180
199
|
set normalBias(val) {
|
|
@@ -215,7 +234,7 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
|
|
|
215
234
|
this.shadowDistance = 120;
|
|
216
235
|
this.splitLambda = 0.75;
|
|
217
236
|
this.depthBias = 0.005;
|
|
218
|
-
this.normalBias =
|
|
237
|
+
this.normalBias = 2;
|
|
219
238
|
this.nearClip = 0.2;
|
|
220
239
|
this.pcfKernelSize = 5;
|
|
221
240
|
this.numShadowCascades = this._light.isDirectionLight() ? 3 : 1;
|
|
@@ -226,7 +245,7 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
|
|
|
226
245
|
this.shadowDistance = 800;
|
|
227
246
|
this.splitLambda = 0.6;
|
|
228
247
|
this.depthBias = 0.0035;
|
|
229
|
-
this.normalBias =
|
|
248
|
+
this.normalBias = 1.5;
|
|
230
249
|
this.nearClip = 1;
|
|
231
250
|
this.pcfKernelSize = 3;
|
|
232
251
|
this.numShadowCascades = this._light.isDirectionLight() ? 4 : 1;
|
|
@@ -283,6 +302,69 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
|
|
|
283
302
|
}
|
|
284
303
|
}
|
|
285
304
|
}
|
|
305
|
+
/**
|
|
306
|
+
* Depth spanned by the deep opacity map's layers, in world units along the
|
|
307
|
+
* light direction. Only meaningful when the shadow mode is `dom`.
|
|
308
|
+
*
|
|
309
|
+
* @remarks
|
|
310
|
+
* Set it to roughly the thickness of the hair. Too small and everything past
|
|
311
|
+
* the outer strands saturates the last layer, which is a hard shadow again;
|
|
312
|
+
* too large and all three layers fall inside the first strand, leaving the
|
|
313
|
+
* interior unshadowed.
|
|
314
|
+
*/ get domLayerDistance() {
|
|
315
|
+
return this._domLayerDistance;
|
|
316
|
+
}
|
|
317
|
+
set domLayerDistance(val) {
|
|
318
|
+
val = Math.max(0.0001, Number(val) || 0);
|
|
319
|
+
if (val !== this._domLayerDistance) {
|
|
320
|
+
this._domLayerDistance = val;
|
|
321
|
+
const dom = this.asDOM();
|
|
322
|
+
if (dom) {
|
|
323
|
+
dom.layerDistance = this._domLayerDistance;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* How strongly accumulated hair coverage attenuates light. Only meaningful when
|
|
329
|
+
* the shadow mode is `dom`.
|
|
330
|
+
*
|
|
331
|
+
* @remarks
|
|
332
|
+
* Raise it to darken the interior of a groom without moving the layers.
|
|
333
|
+
*/ get domDensity() {
|
|
334
|
+
return this._domDensity;
|
|
335
|
+
}
|
|
336
|
+
set domDensity(val) {
|
|
337
|
+
val = Math.max(0, Number(val) || 0);
|
|
338
|
+
if (val !== this._domDensity) {
|
|
339
|
+
this._domDensity = val;
|
|
340
|
+
const dom = this.asDOM();
|
|
341
|
+
if (dom) {
|
|
342
|
+
dom.density = this._domDensity;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Width of the deep opacity map's sampling kernel in texels: 1, 3, 5 or 7. Only
|
|
348
|
+
* meaningful when the shadow mode is `dom`.
|
|
349
|
+
*
|
|
350
|
+
* @remarks
|
|
351
|
+
* The technique's own gradation runs along the light rather than across the
|
|
352
|
+
* map, so without a kernel the silhouette of a groom is as hard an edge as an
|
|
353
|
+
* unfiltered shadow map gives. This is what softens it, at one layer lookup per
|
|
354
|
+
* tap. Defaults to 5.
|
|
355
|
+
*/ get domFilterSize() {
|
|
356
|
+
return this._domFilterSize;
|
|
357
|
+
}
|
|
358
|
+
set domFilterSize(val) {
|
|
359
|
+
val = val !== 1 && val !== 3 && val !== 5 && val !== 7 ? 5 : val;
|
|
360
|
+
if (val !== this._domFilterSize) {
|
|
361
|
+
this._domFilterSize = val;
|
|
362
|
+
const dom = this.asDOM();
|
|
363
|
+
if (dom) {
|
|
364
|
+
dom.filterSize = this._domFilterSize;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
286
368
|
/** Blocker search sample count for PCSS shadow */ get pcssBlockerSampleCount() {
|
|
287
369
|
return this._pcssBlockerSampleCount;
|
|
288
370
|
}
|
|
@@ -456,25 +538,6 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
|
|
|
456
538
|
ctx.shadowMapInfo = null;
|
|
457
539
|
}
|
|
458
540
|
}
|
|
459
|
-
/** @internal */ static computeShadowBias(shadowMapParams, scope, z, NdotL, linear) {
|
|
460
|
-
const pb = scope.$builder;
|
|
461
|
-
const depthBiasParam = ShaderHelper.getDepthBiasValues(scope);
|
|
462
|
-
if (shadowMapParams.lightType === LIGHT_TYPE_DIRECTIONAL) {
|
|
463
|
-
return pb.dot(pb.mul(depthBiasParam.xy, pb.vec2(1, pb.sub(1, NdotL))), pb.vec2(1, 1));
|
|
464
|
-
} else {
|
|
465
|
-
const nearFar = ShaderHelper.getShadowCameraParams(scope).xy;
|
|
466
|
-
const linearDepth = linear ? z : ShaderHelper.nonLinearDepthToLinearNormalized(scope, z, nearFar);
|
|
467
|
-
const biasScaleFactor = pb.mix(1, depthBiasParam.w, linearDepth);
|
|
468
|
-
return pb.dot(pb.mul(depthBiasParam.xy, pb.vec2(1, pb.sub(1, NdotL)), biasScaleFactor), pb.vec2(1, 1));
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
/** @internal */ static computeShadowBiasCSM(shadowMapParams, scope, NdotL, split) {
|
|
472
|
-
const pb = scope.$builder;
|
|
473
|
-
const depthBiasParam = ShaderHelper.getDepthBiasValues(scope);
|
|
474
|
-
const splitFlags = pb.vec4(pb.float(pb.equal(split, 0)), pb.float(pb.equal(split, 1)), pb.float(pb.equal(split, 2)), pb.float(pb.equal(split, 3)));
|
|
475
|
-
const depthBiasScale = pb.dot(ShaderHelper.getDepthBiasScales(scope), splitFlags);
|
|
476
|
-
return pb.dot(pb.mul(depthBiasParam.xy, pb.vec2(1, pb.sub(1, NdotL)), depthBiasScale), pb.vec2(1, 1));
|
|
477
|
-
}
|
|
478
541
|
/** @internal */ isTextureInvalid(texture, target, format, width, height) {
|
|
479
542
|
return texture && (texture.target !== target || texture.format !== format || texture.width !== width || texture.height !== height || texture.depth !== this.numShadowCascades);
|
|
480
543
|
}
|
|
@@ -766,8 +829,15 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
|
|
|
766
829
|
/** @internal */ calcDepthBiasParams(camera, shadowMapSize, depthBias, normalBias, depthScale, result) {
|
|
767
830
|
const sizeNear = Math.min(camera.getProjectionMatrix().getNearPlaneWidth(), camera.getProjectionMatrix().getNearPlaneHeight());
|
|
768
831
|
const sizeFar = Math.min(camera.getProjectionMatrix().getFarPlaneWidth(), camera.getProjectionMatrix().getFarPlaneHeight());
|
|
769
|
-
|
|
770
|
-
|
|
832
|
+
// World-space size of one shadow map texel at the near plane. Both bias
|
|
833
|
+
// terms are expressed in multiples of it, so a tighter cascade automatically
|
|
834
|
+
// gets a proportionally smaller offset.
|
|
835
|
+
const texelWorldSize = sizeNear / shadowMapSize;
|
|
836
|
+
result.setXYZW(depthBias * texelWorldSize * 2, // Normal offset distance in world units. Unlike a depth bias this moves
|
|
837
|
+
// the receiver sideways out of the texel that produces grazing-angle
|
|
838
|
+
// acne, which no amount of depth bias can fix (the required depth offset
|
|
839
|
+
// diverges as NdotL approaches zero).
|
|
840
|
+
normalBias * texelWorldSize, depthScale, sizeFar / sizeNear);
|
|
771
841
|
}
|
|
772
842
|
/** @internal */ postRenderShadowMap(shadowMapParams) {
|
|
773
843
|
this._impl.postRenderShadowMap(shadowMapParams);
|
|
@@ -779,7 +849,7 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
|
|
|
779
849
|
const shadowMapParams = ShadowMapper.fetchShadowMapParams();
|
|
780
850
|
shadowMapParams.impl = this._impl;
|
|
781
851
|
shadowMapParams.lightType = this.light.lightType;
|
|
782
|
-
shadowMapParams.numShadowCascades = shadowMapParams.lightType === LIGHT_TYPE_DIRECTIONAL ? this._config.numCascades ?? 1 : 1;
|
|
852
|
+
shadowMapParams.numShadowCascades = shadowMapParams.lightType === LIGHT_TYPE_DIRECTIONAL && this._impl.supportsCascades() ? this._config.numCascades ?? 1 : 1;
|
|
783
853
|
ctx.shadowMapInfo.set(this.light, shadowMapParams);
|
|
784
854
|
const scene = ctx.scene;
|
|
785
855
|
const camera = ctx.camera;
|
|
@@ -791,6 +861,32 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
|
|
|
791
861
|
shadowMapParams.depthClampEnabled = false;
|
|
792
862
|
renderPass.clearColor = this._impl.getShadowMapClearColor(shadowMapParams);
|
|
793
863
|
const depthScale = this._impl.getDepthScale();
|
|
864
|
+
const geometryPassCount = Math.max(1, this._impl.getGeometryPassCount(shadowMapParams) >> 0);
|
|
865
|
+
// Repeats the caster draw for implementations that need more than one look at
|
|
866
|
+
// the geometry (deep opacity maps). Single-pass implementations - every one
|
|
867
|
+
// but DOM - take the `pass === 0` path once and behave exactly as before.
|
|
868
|
+
const renderGeometry = (renderCamera, cullCamera)=>{
|
|
869
|
+
if (geometryPassCount === 1) {
|
|
870
|
+
renderPass.render(ctx, renderCamera, cullCamera);
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
// Culling depends only on the camera, so the extra passes reuse the first
|
|
874
|
+
// pass's queue rather than walking the scene graph again per pass.
|
|
875
|
+
const renderQueue = renderPass.cullScene(ctx, cullCamera ?? renderCamera);
|
|
876
|
+
try {
|
|
877
|
+
for(let pass = 0; pass < geometryPassCount; pass++){
|
|
878
|
+
this._impl.beginGeometryPass(shadowMapParams, pass);
|
|
879
|
+
// The emitted caster shader differs between passes, and the shadow pass
|
|
880
|
+
// keys its programs on this hash, so it has to be refreshed per pass
|
|
881
|
+
// rather than computed once up front.
|
|
882
|
+
shadowMapParams.shaderHash = this.getShaderHash(shadowMapParams);
|
|
883
|
+
renderPass.clearColor = this._impl.getShadowMapClearColor(shadowMapParams);
|
|
884
|
+
renderPass.render(ctx, renderCamera, cullCamera, renderQueue);
|
|
885
|
+
}
|
|
886
|
+
} finally{
|
|
887
|
+
renderQueue.dispose();
|
|
888
|
+
}
|
|
889
|
+
};
|
|
794
890
|
const directionalShadowRegion = this._shadowRegion.region;
|
|
795
891
|
const shadowRegion = this._light.isDirectionLight() && directionalShadowRegion?.isValid() ? directionalShadowRegion : scene.boundingBox;
|
|
796
892
|
if (this._light.isPointLight()) {
|
|
@@ -811,20 +907,24 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
|
|
|
811
907
|
shadowMapRenderCamera.lookAtCubeFace(face);
|
|
812
908
|
fb.setColorAttachmentCubeFace(0, face);
|
|
813
909
|
fb.setDepthAttachmentCubeFace(face);
|
|
814
|
-
|
|
910
|
+
renderGeometry(shadowMapRenderCamera);
|
|
815
911
|
}
|
|
816
912
|
shadowMapParams.shadowMatrices.set(Matrix4x4.identity());
|
|
817
913
|
ShadowMapper.releaseCamera(shadowMapRenderCamera);
|
|
818
914
|
} else {
|
|
819
|
-
|
|
820
|
-
|
|
915
|
+
// Reads the resolved count, not the configured one: an implementation that
|
|
916
|
+
// cannot store cascades has already collapsed it to a single split, and the
|
|
917
|
+
// cascade path would index array layers its shadow map does not have.
|
|
918
|
+
const numCascades = shadowMapParams.numShadowCascades;
|
|
919
|
+
if (numCascades > 1) {
|
|
920
|
+
const distances = this.calcSplitDistances(camera.getNearPlane(), Math.min(this._shadowDistance, camera.getFarPlane()), numCascades);
|
|
821
921
|
const cascadeCamera = ShadowMapper.fetchCameraForScene(scene);
|
|
822
922
|
const shadowMapRenderCamera = ShadowMapper.fetchCameraForScene(scene);
|
|
823
923
|
const shadowMapCullCamera = ShadowMapper.fetchCameraForScene(scene);
|
|
824
924
|
shadowMapCullCamera.clipMask = AABB.ClipLeft | AABB.ClipRight | AABB.ClipBottom | AABB.ClipTop;
|
|
825
925
|
cascadeCamera.parent = camera;
|
|
826
926
|
shadowMapParams.depthClampEnabled = getDevice().getDeviceCaps().shaderCaps.supportFragmentDepth;
|
|
827
|
-
for(let split = 0; split <
|
|
927
|
+
for(let split = 0; split < numCascades; split++){
|
|
828
928
|
cascadeCamera.setPerspective(camera.getFOV(), camera.getAspect(), distances[split], distances[split + 1]);
|
|
829
929
|
const snapMatrix = ShadowMapper._snapMatrix;
|
|
830
930
|
const border = shadowMapParams.impl.getShadowMapBorder(shadowMapParams); //20 / this._config.shadowMapSize;
|
|
@@ -839,8 +939,8 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
|
|
|
839
939
|
fb.setColorAttachmentLayer(0, split);
|
|
840
940
|
fb.setDepthAttachmentLayer(split);
|
|
841
941
|
} else {
|
|
842
|
-
const numRows =
|
|
843
|
-
const numCols =
|
|
942
|
+
const numRows = numCascades > 2 ? 2 : 1;
|
|
943
|
+
const numCols = numCascades > 1 ? 2 : 1;
|
|
844
944
|
const adjMatrix = new Matrix4x4();
|
|
845
945
|
const col = split % 2;
|
|
846
946
|
const row = split >> 1;
|
|
@@ -867,7 +967,7 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
|
|
|
867
967
|
}
|
|
868
968
|
device.setFramebuffer(fb);
|
|
869
969
|
device.setScissor(scissor);
|
|
870
|
-
|
|
970
|
+
renderGeometry(shadowMapRenderCamera, shadowMapCullCamera);
|
|
871
971
|
shadowMapParams.shadowMatrices.set(Matrix4x4.transpose(shadowMapRenderCamera.viewProjectionMatrix), split * 16);
|
|
872
972
|
shadowMapParams.cascadeDistances[split] = distances[split + 1];
|
|
873
973
|
}
|
|
@@ -890,7 +990,7 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
|
|
|
890
990
|
shadowMapParams.cameraParams.setXYZW(shadowMapRenderCamera.getNearPlane(), shadowMapRenderCamera.getFarPlane(), this._config.shadowMapSize, this._shadowDistance);
|
|
891
991
|
device.setFramebuffer(fb);
|
|
892
992
|
shadowMapRenderCamera.setProjectionMatrix(Matrix4x4.multiply(snapMatrix, shadowMapRenderCamera.getProjectionMatrix()));
|
|
893
|
-
|
|
993
|
+
renderGeometry(shadowMapRenderCamera);
|
|
894
994
|
shadowMapParams.shadowMatrices.set(Matrix4x4.transpose(shadowMapRenderCamera.viewProjectionMatrix));
|
|
895
995
|
ShadowMapper.releaseCamera(shadowMapRenderCamera);
|
|
896
996
|
}
|
|
@@ -898,10 +998,18 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
|
|
|
898
998
|
this.postRenderShadowMap(shadowMapParams);
|
|
899
999
|
}
|
|
900
1000
|
/** @internal */ applyMode(mode) {
|
|
901
|
-
if (mode !== 'hard' && mode !== 'vsm' && mode !== 'esm' && mode !== 'pcf' && mode !== 'pcf-pd' && mode !== 'pcf-opt' && mode !== 'pcss') {
|
|
1001
|
+
if (mode !== 'hard' && mode !== 'vsm' && mode !== 'esm' && mode !== 'pcf' && mode !== 'pcf-pd' && mode !== 'pcf-opt' && mode !== 'pcss' && mode !== 'dom') {
|
|
902
1002
|
console.error(`ShadowMapper.setShadowMode() failed: invalid mode: ${mode}`);
|
|
903
1003
|
return;
|
|
904
1004
|
}
|
|
1005
|
+
if (mode === 'dom' && getDevice().type !== 'webgpu') {
|
|
1006
|
+
// The strand geometry this mode exists to shadow is WebGPU only, so the
|
|
1007
|
+
// WebGL paths were never built. Falling back keeps a scene authored with it
|
|
1008
|
+
// renderable rather than failing to produce a shadow map at all.
|
|
1009
|
+
console.warn(`ShadowMapper.setShadowMode(): 'dom' requires WebGPU, falling back to 'pcf'`);
|
|
1010
|
+
this.applyMode('pcf');
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
905
1013
|
this._impl = null;
|
|
906
1014
|
if (mode === 'hard') {
|
|
907
1015
|
this._impl = new SSM();
|
|
@@ -914,6 +1022,8 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
|
|
|
914
1022
|
esm.logSpace = this._esmLogSpace;
|
|
915
1023
|
} else if (mode === 'pcf' || mode === 'pcf-opt' || mode === 'pcf-pd') {
|
|
916
1024
|
this._impl = new PCFOPT(this._pcfKernelSize);
|
|
1025
|
+
} else if (mode === 'dom') {
|
|
1026
|
+
this._impl = new DOM(this._domLayerDistance, this._domDensity, this._domFilterSize);
|
|
917
1027
|
} else if (mode === 'pcss') {
|
|
918
1028
|
this._impl = new PCSS(this._pcssLightRadius, this._pcssBlockerSampleCount, this._pcssFilterSampleCount, this._pcssMaxFilterRadius, this._pcssTemporalJitter);
|
|
919
1029
|
}
|
|
@@ -921,6 +1031,9 @@ const tmpFrustum = new Frustum(Matrix4x4.identity());
|
|
|
921
1031
|
/** @internal */ asVSM() {
|
|
922
1032
|
return this._impl?.getType() === 'vsm' ? this._impl : null;
|
|
923
1033
|
}
|
|
1034
|
+
/** @internal */ asDOM() {
|
|
1035
|
+
return this._impl?.getType() === 'dom' ? this._impl : null;
|
|
1036
|
+
}
|
|
924
1037
|
/** @internal */ asESM() {
|
|
925
1038
|
return this._impl?.getType() === 'esm' ? this._impl : null;
|
|
926
1039
|
}
|