@zephyr3d/scene 0.9.26 → 0.10.0
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/README.md +60 -27
- package/dist/animation/animation_bank.js +634 -0
- package/dist/animation/animation_bank.js.map +1 -0
- package/dist/animation/animation_bank_gpu.js +256 -0
- package/dist/animation/animation_bank_gpu.js.map +1 -0
- package/dist/animation/animationset.js +146 -30
- package/dist/animation/animationset.js.map +1 -1
- package/dist/animation/cloth/gpu_cloth_component.js +406 -0
- package/dist/animation/cloth/gpu_cloth_component.js.map +1 -0
- package/dist/animation/cloth/gpu_cloth_system.js +332 -202
- package/dist/animation/cloth/gpu_cloth_system.js.map +1 -1
- package/dist/animation/gpu_animation_driver.js +289 -0
- package/dist/animation/gpu_animation_driver.js.map +1 -0
- package/dist/animation/joint_dynamics/collision.js +112 -1
- package/dist/animation/joint_dynamics/collision.js.map +1 -1
- package/dist/animation/joint_dynamics/controller.js +6 -0
- package/dist/animation/joint_dynamics/controller.js.map +1 -1
- package/dist/animation/joint_dynamics/joint_dynamics_system.js +11 -0
- package/dist/animation/joint_dynamics/joint_dynamics_system.js.map +1 -1
- package/dist/animation/joint_dynamics/solver.js +50 -12
- package/dist/animation/joint_dynamics/solver.js.map +1 -1
- package/dist/animation/joint_dynamics/types.js.map +1 -1
- package/dist/animation/skeleton.js +300 -56
- package/dist/animation/skeleton.js.map +1 -1
- package/dist/animation/skin_palette_atlas.js +252 -0
- package/dist/animation/skin_palette_atlas.js.map +1 -0
- package/dist/animation/spring/multi_chain_spring_system.js +298 -38
- package/dist/animation/spring/multi_chain_spring_system.js.map +1 -1
- package/dist/animation/spring/spring_chain.js +2 -0
- package/dist/animation/spring/spring_chain.js.map +1 -1
- package/dist/animation/spring/spring_collider.js +159 -21
- package/dist/animation/spring/spring_collider.js.map +1 -1
- package/dist/animation/spring/spring_particle.js +1 -0
- package/dist/animation/spring/spring_particle.js.map +1 -1
- package/dist/animation/spring/spring_solver.js +173 -0
- package/dist/animation/spring/spring_solver.js.map +1 -0
- package/dist/animation/spring/spring_system.js +249 -24
- package/dist/animation/spring/spring_system.js.map +1 -1
- package/dist/animation/spring_modifier.js +32 -1
- package/dist/animation/spring_modifier.js.map +1 -1
- package/dist/app/app.js +26 -0
- package/dist/app/app.js.map +1 -1
- package/dist/app/engine.js +86 -15
- package/dist/app/engine.js.map +1 -1
- package/dist/app/gamepad.js +191 -0
- package/dist/app/gamepad.js.map +1 -0
- package/dist/app/inputactions.js +271 -0
- package/dist/app/inputactions.js.map +1 -0
- package/dist/app/inputmgr.js +331 -22
- package/dist/app/inputmgr.js.map +1 -1
- package/dist/app/loading.js +138 -0
- package/dist/app/loading.js.map +1 -0
- package/dist/app/pipelinecache.js +209 -0
- package/dist/app/pipelinecache.js.map +1 -0
- package/dist/app/runtimescript.js +25 -0
- package/dist/app/runtimescript.js.map +1 -1
- package/dist/app/scriptingsystem.js +54 -0
- package/dist/app/scriptingsystem.js.map +1 -1
- package/dist/app/scriptregistry.js +7 -2
- package/dist/app/scriptregistry.js.map +1 -1
- package/dist/asset/asset_manifest.js +59 -0
- package/dist/asset/asset_manifest.js.map +1 -0
- package/dist/asset/asset_preload.js +258 -0
- package/dist/asset/asset_preload.js.map +1 -0
- package/dist/asset/assetmanager.js +117 -65
- package/dist/asset/assetmanager.js.map +1 -1
- package/dist/asset/loaders/ktx2/basis/LICENSE +201 -0
- package/dist/asset/loaders/ktx2/basis/basis_transcoder.js +2 -0
- package/dist/asset/loaders/ktx2/basis/basis_transcoder.wasm +0 -0
- package/dist/asset/loaders/ktx2/basis_location.js +17 -0
- package/dist/asset/loaders/ktx2/basis_location.js.map +1 -0
- package/dist/asset/loaders/ktx2/ktx2_loader.js +88 -0
- package/dist/asset/loaders/ktx2/ktx2_loader.js.map +1 -0
- package/dist/asset/loaders/ktx2/ktx2_targets.js +118 -0
- package/dist/asset/loaders/ktx2/ktx2_targets.js.map +1 -0
- package/dist/asset/loaders/ktx2/transcoder_pool.js +259 -0
- package/dist/asset/loaders/ktx2/transcoder_pool.js.map +1 -0
- package/dist/asset/mesh_lod_builder.js +104 -0
- package/dist/asset/mesh_lod_builder.js.map +1 -0
- package/dist/asset/mesh_settings.js +111 -0
- package/dist/asset/mesh_settings.js.map +1 -0
- package/dist/asset/meshopt_decoder.js +18 -0
- package/dist/asset/meshopt_decoder.js.map +1 -0
- package/dist/asset/meshopt_simplifier.js +26 -0
- package/dist/asset/meshopt_simplifier.js.map +1 -0
- package/dist/asset/model.js +177 -3
- package/dist/asset/model.js.map +1 -1
- package/dist/asset/texture_settings.js +231 -0
- package/dist/asset/texture_settings.js.map +1 -0
- package/dist/asset/zmsh_binary.js +131 -0
- package/dist/asset/zmsh_binary.js.map +1 -0
- package/dist/asset/zsdf_binary.js +111 -0
- package/dist/asset/zsdf_binary.js.map +1 -0
- package/dist/blitter/bilateralblur.js +1 -2
- package/dist/blitter/bilateralblur.js.map +1 -1
- package/dist/blitter/gaussianblur.js +1 -6
- package/dist/blitter/gaussianblur.js.map +1 -1
- package/dist/camera/base.js +31 -10
- package/dist/camera/base.js.map +1 -1
- package/dist/camera/camera.js +178 -200
- package/dist/camera/camera.js.map +1 -1
- package/dist/camera/follow.js +342 -0
- package/dist/camera/follow.js.map +1 -0
- package/dist/camera/fps.js +9 -7
- package/dist/camera/fps.js.map +1 -1
- package/dist/camera/orbit.js +46 -19
- package/dist/camera/orbit.js.map +1 -1
- package/dist/index.d.ts +30475 -19865
- package/dist/index.js +67 -12
- package/dist/index.js.map +1 -1
- package/dist/material/blinn.js +3 -0
- package/dist/material/blinn.js.map +1 -1
- package/dist/material/eye.js +1 -1
- package/dist/material/eye.js.map +1 -1
- package/dist/material/hair.js +2 -3
- package/dist/material/hair.js.map +1 -1
- package/dist/material/hairstrand.js +235 -235
- package/dist/material/hairstrand.js.map +1 -1
- package/dist/material/lambert.js +4 -1
- package/dist/material/lambert.js.map +1 -1
- package/dist/material/material.js +362 -300
- package/dist/material/material.js.map +1 -1
- package/dist/material/meshmaterial.js +118 -45
- package/dist/material/meshmaterial.js.map +1 -1
- package/dist/material/mixins/lightmodel/blinnphong.js +1 -1
- package/dist/material/mixins/lightmodel/blinnphong.js.map +1 -1
- package/dist/material/mixins/lightmodel/lambert.js +1 -1
- package/dist/material/mixins/lightmodel/lambert.js.map +1 -1
- package/dist/material/mixins/lightmodel/pbrblueprintmixin.js +34 -61
- package/dist/material/mixins/lightmodel/pbrblueprintmixin.js.map +1 -1
- package/dist/material/mixins/lightmodel/pbrmetallicroughness.js +16 -2
- package/dist/material/mixins/lightmodel/pbrmetallicroughness.js.map +1 -1
- package/dist/material/mixins/lightmodel/pbrspecularglossness.js +2 -1
- package/dist/material/mixins/lightmodel/pbrspecularglossness.js.map +1 -1
- package/dist/material/mixins/lit.js +223 -83
- package/dist/material/mixins/lit.js.map +1 -1
- package/dist/material/mixins/pbr/common.js +31 -206
- package/dist/material/mixins/pbr/common.js.map +1 -1
- package/dist/material/mtoon.js +1 -1
- package/dist/material/mtoon.js.map +1 -1
- package/dist/material/pbrmr.js +3 -0
- package/dist/material/pbrmr.js.map +1 -1
- package/dist/material/pbrsg.js +3 -0
- package/dist/material/pbrsg.js.map +1 -1
- package/dist/material/sdf_debug.js +78 -0
- package/dist/material/sdf_debug.js.map +1 -0
- package/dist/material/shader/helper.js +733 -190
- package/dist/material/shader/helper.js.map +1 -1
- package/dist/material/shader/terrain_blend.js +204 -43
- package/dist/material/shader/terrain_blend.js.map +1 -1
- package/dist/material/skin.js +331 -297
- package/dist/material/skin.js.map +1 -1
- package/dist/material/skinprofile.js +1058 -0
- package/dist/material/skinprofile.js.map +1 -0
- package/dist/material/sss.js +353 -0
- package/dist/material/sss.js.map +1 -0
- package/dist/material/sss_kernel.js +265 -0
- package/dist/material/sss_kernel.js.map +1 -0
- package/dist/material/sssprofile.js +879 -0
- package/dist/material/sssprofile.js.map +1 -0
- package/dist/material/subsurfaceprofile.js +4 -8
- package/dist/material/subsurfaceprofile.js.map +1 -1
- package/dist/material/terrain-cm.js +464 -188
- package/dist/material/terrain-cm.js.map +1 -1
- package/dist/material/unlit.js +3 -0
- package/dist/material/unlit.js.map +1 -1
- package/dist/material/water.js +2009 -201
- package/dist/material/water.js.map +1 -1
- package/dist/node_modules/meshoptimizer/LICENSE +21 -0
- package/dist/node_modules/meshoptimizer/meshopt_decoder.js +197 -0
- package/dist/node_modules/meshoptimizer/meshopt_decoder.js.map +1 -0
- package/dist/node_modules/meshoptimizer/meshopt_simplifier.js +677 -0
- package/dist/node_modules/meshoptimizer/meshopt_simplifier.js.map +1 -0
- package/dist/physics/character.js +226 -0
- package/dist/physics/character.js.map +1 -0
- package/dist/physics/collider.js +209 -0
- package/dist/physics/collider.js.map +1 -0
- package/dist/physics/events.js +79 -0
- package/dist/physics/events.js.map +1 -0
- package/dist/physics/joint.js +272 -0
- package/dist/physics/joint.js.map +1 -0
- package/dist/physics/node_physics.js +234 -0
- package/dist/physics/node_physics.js.map +1 -0
- package/dist/physics/part.js +91 -0
- package/dist/physics/part.js.map +1 -0
- package/dist/physics/rigid_body.js +379 -0
- package/dist/physics/rigid_body.js.map +1 -0
- package/dist/physics/vehicle.js +335 -0
- package/dist/physics/vehicle.js.map +1 -0
- package/dist/physics/world.js +25 -0
- package/dist/physics/world.js.map +1 -0
- package/dist/posteffect/bloom.js +49 -14
- package/dist/posteffect/bloom.js.map +1 -1
- package/dist/posteffect/motionblur.js +668 -63
- 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/postsss.js +572 -0
- package/dist/posteffect/postsss.js.map +1 -0
- package/dist/posteffect/sao.js +48 -50
- package/dist/posteffect/sao.js.map +1 -1
- package/dist/posteffect/skinsss.js +605 -355
- package/dist/posteffect/skinsss.js.map +1 -1
- package/dist/posteffect/ssgi.js +91 -162
- package/dist/posteffect/ssgi.js.map +1 -1
- package/dist/posteffect/ssr.js +3 -11
- package/dist/posteffect/ssr.js.map +1 -1
- package/dist/posteffect/sss.js +34 -15
- package/dist/posteffect/sss.js.map +1 -1
- package/dist/posteffect/taa.js +1 -0
- package/dist/posteffect/taa.js.map +1 -1
- package/dist/render/abuffer_oit.js +40 -39
- package/dist/render/abuffer_oit.js.map +1 -1
- package/dist/render/clipmap.js +328 -62
- package/dist/render/clipmap.js.map +1 -1
- package/dist/render/cluster_light.js +958 -224
- package/dist/render/cluster_light.js.map +1 -1
- package/dist/render/cull_visitor.js +26 -5
- package/dist/render/cull_visitor.js.map +1 -1
- package/dist/render/debug_views.js +37 -0
- package/dist/render/debug_views.js.map +1 -0
- package/dist/render/depthpass.js +5 -1
- package/dist/render/depthpass.js.map +1 -1
- package/dist/render/drawable_mixin.js +53 -6
- package/dist/render/drawable_mixin.js.map +1 -1
- package/dist/render/dualdepthpeeling_oit.js +53 -53
- package/dist/render/dualdepthpeeling_oit.js.map +1 -1
- package/dist/render/envlight.js +196 -113
- package/dist/render/envlight.js.map +1 -1
- package/dist/render/fbm_wavegenerator.js +8 -13
- package/dist/render/fbm_wavegenerator.js.map +1 -1
- package/dist/render/fft_wavegenerator.js +268 -136
- package/dist/render/fft_wavegenerator.js.map +1 -1
- package/dist/render/gerstner_wavegenerator.js +113 -28
- package/dist/render/gerstner_wavegenerator.js.map +1 -1
- package/dist/render/gi/probe_volume.js +1334 -0
- package/dist/render/gi/probe_volume.js.map +1 -0
- package/dist/render/gi/scene_gi.js +282 -0
- package/dist/render/gi/scene_gi.js.map +1 -0
- package/dist/render/gi/voxel_atlas.js +198 -0
- package/dist/render/gi/voxel_atlas.js.map +1 -0
- package/dist/render/gi/voxel_lighting.js +652 -0
- package/dist/render/gi/voxel_lighting.js.map +1 -0
- package/dist/render/gi/voxel_scene.js +642 -0
- package/dist/render/gi/voxel_scene.js.map +1 -0
- package/dist/render/gi/voxelize_pass.js +223 -0
- package/dist/render/gi/voxelize_pass.js.map +1 -0
- package/dist/render/globalbindgroup_allocator.js +3 -2
- package/dist/render/globalbindgroup_allocator.js.map +1 -1
- package/dist/render/gpu_picking.js +9 -36
- package/dist/render/gpu_picking.js.map +1 -1
- package/dist/render/hzb.js +80 -22
- package/dist/render/hzb.js.map +1 -1
- package/dist/render/instance_culling.js +499 -0
- package/dist/render/instance_culling.js.map +1 -0
- package/dist/render/lightpass.js +23 -9
- package/dist/render/lightpass.js.map +1 -1
- package/dist/render/lod.js +46 -0
- package/dist/render/lod.js.map +1 -0
- package/dist/render/oit.js +11 -11
- package/dist/render/oit.js.map +1 -1
- package/dist/render/primitive.js +301 -9
- package/dist/render/primitive.js.map +1 -1
- package/dist/render/primitive_readback.js +209 -0
- package/dist/render/primitive_readback.js.map +1 -0
- package/dist/render/render_queue.js +198 -11
- package/dist/render/render_queue.js.map +1 -1
- package/dist/render/renderer.js +14 -7
- package/dist/render/renderer.js.map +1 -1
- package/dist/render/rendergraph/blackboard.js +15 -1
- package/dist/render/rendergraph/blackboard.js.map +1 -1
- package/dist/render/rendergraph/executor.js +136 -0
- package/dist/render/rendergraph/executor.js.map +1 -1
- package/dist/render/rendergraph/forward_plus_builder.js +585 -59
- 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/scene_render_context.js +31 -2
- package/dist/render/rendergraph/scene_render_context.js.map +1 -1
- package/dist/render/rendergraph/types.js.map +1 -1
- package/dist/render/renderpass.js +28 -5
- package/dist/render/renderpass.js.map +1 -1
- package/dist/render/sdf/box_allocator.js +98 -0
- package/dist/render/sdf/box_allocator.js.map +1 -0
- package/dist/render/sdf/global_sdf.js +865 -0
- package/dist/render/sdf/global_sdf.js.map +1 -0
- package/dist/render/sdf/scene_sdf.js +562 -0
- package/dist/render/sdf/scene_sdf.js.map +1 -0
- package/dist/render/sdf/sdf_atlas.js +227 -0
- package/dist/render/sdf/sdf_atlas.js.map +1 -0
- package/dist/render/sdf/sdf_programs.js +42 -0
- package/dist/render/sdf/sdf_programs.js.map +1 -0
- package/dist/render/sdf/sdf_view.js +199 -0
- package/dist/render/sdf/sdf_view.js.map +1 -0
- package/dist/render/sdf/waterline.js +610 -0
- package/dist/render/sdf/waterline.js.map +1 -0
- package/dist/render/shadow_mask_pass.js +142 -25
- package/dist/render/shadow_mask_pass.js.map +1 -1
- package/dist/render/shore_wave_generator.js +1193 -0
- package/dist/render/shore_wave_generator.js.map +1 -0
- package/dist/render/sky.js +516 -86
- package/dist/render/sky.js.map +1 -1
- package/dist/render/transmission_thickness_pass.js +477 -0
- package/dist/render/transmission_thickness_pass.js.map +1 -0
- package/dist/render/underwater.js +524 -0
- package/dist/render/underwater.js.map +1 -0
- package/dist/render/virtualtexture/virtual_texture.js +1214 -0
- package/dist/render/virtualtexture/virtual_texture.js.map +1 -0
- package/dist/render/volumetric_cloud/cloud_common.js +472 -0
- package/dist/render/volumetric_cloud/cloud_common.js.map +1 -0
- package/dist/render/volumetric_cloud/cloud_noise.js +189 -0
- package/dist/render/volumetric_cloud/cloud_noise.js.map +1 -0
- package/dist/render/volumetric_cloud/debug_view.js +131 -0
- package/dist/render/volumetric_cloud/debug_view.js.map +1 -0
- package/dist/render/volumetric_cloud/resources.js +380 -0
- package/dist/render/volumetric_cloud/resources.js.map +1 -0
- package/dist/render/volumetric_cloud/sky_cache.js +380 -0
- package/dist/render/volumetric_cloud/sky_cache.js.map +1 -0
- package/dist/render/volumetric_cloud/volumetric_cloud.js +318 -0
- package/dist/render/volumetric_cloud/volumetric_cloud.js.map +1 -0
- package/dist/render/water_caustics.js +1195 -0
- package/dist/render/water_caustics.js.map +1 -0
- package/dist/render/water_foam.js +627 -0
- package/dist/render/water_foam.js.map +1 -0
- package/dist/render/water_interaction.js +1795 -0
- package/dist/render/water_interaction.js.map +1 -0
- package/dist/render/weightedblended_oit.js +36 -36
- package/dist/render/weightedblended_oit.js.map +1 -1
- package/dist/scene/basesprite.js +9 -4
- package/dist/scene/basesprite.js.map +1 -1
- package/dist/scene/batchgroup.js +108 -12
- package/dist/scene/batchgroup.js.map +1 -1
- package/dist/scene/environment.js +28 -7
- package/dist/scene/environment.js.map +1 -1
- package/dist/scene/foliage.js +1020 -0
- package/dist/scene/foliage.js.map +1 -0
- package/dist/scene/foliage_buckets.js +147 -0
- package/dist/scene/foliage_buckets.js.map +1 -0
- package/dist/scene/foliage_mask.js +358 -0
- package/dist/scene/foliage_mask.js.map +1 -0
- package/dist/scene/hair.js +15 -4
- package/dist/scene/hair.js.map +1 -1
- package/dist/scene/light.js +179 -13
- package/dist/scene/light.js.map +1 -1
- package/dist/scene/mesh.js +327 -45
- 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 +9 -3
- package/dist/scene/msdftext.js.map +1 -1
- package/dist/scene/msdftextsprite.js +9 -3
- package/dist/scene/msdftextsprite.js.map +1 -1
- package/dist/scene/particlesys.js +8 -2
- package/dist/scene/particlesys.js.map +1 -1
- package/dist/scene/physics_settings.js +157 -0
- package/dist/scene/physics_settings.js.map +1 -0
- package/dist/scene/scene.js +190 -44
- package/dist/scene/scene.js.map +1 -1
- package/dist/scene/scene_node.js +147 -6
- package/dist/scene/scene_node.js.map +1 -1
- package/dist/scene/terrain-cm/bladegrassmaterial.js +735 -0
- package/dist/scene/terrain-cm/bladegrassmaterial.js.map +1 -0
- package/dist/scene/terrain-cm/grass.js +881 -42
- package/dist/scene/terrain-cm/grass.js.map +1 -1
- package/dist/scene/terrain-cm/grass_gpu.js +929 -0
- package/dist/scene/terrain-cm/grass_gpu.js.map +1 -0
- package/dist/scene/terrain-cm/grassmaterial.js +180 -9
- package/dist/scene/terrain-cm/grassmaterial.js.map +1 -1
- package/dist/scene/terrain-cm/terrain-cm.js +465 -36
- package/dist/scene/terrain-cm/terrain-cm.js.map +1 -1
- package/dist/scene/terrain-cm/terrain_gi_albedo.js +257 -0
- package/dist/scene/terrain-cm/terrain_gi_albedo.js.map +1 -0
- package/dist/scene/terrain-cm/terrain_rvt.js +395 -0
- package/dist/scene/terrain-cm/terrain_rvt.js.map +1 -0
- package/dist/scene/update_queue.js +54 -0
- package/dist/scene/update_queue.js.map +1 -0
- package/dist/scene/water.js +997 -96
- package/dist/scene/water.js.map +1 -1
- package/dist/scene/water_surface_sampler.js +324 -0
- package/dist/scene/water_surface_sampler.js.map +1 -0
- package/dist/scene/wind.js +317 -0
- package/dist/scene/wind.js.map +1 -0
- package/dist/shaders/atmosphere.js +388 -166
- package/dist/shaders/atmosphere.js.map +1 -1
- package/dist/shaders/fog.js +100 -32
- package/dist/shaders/fog.js.map +1 -1
- package/dist/shaders/gi.js +365 -0
- package/dist/shaders/gi.js.map +1 -0
- package/dist/shaders/ltc_rect.js +202 -0
- package/dist/shaders/ltc_rect.js.map +1 -0
- package/dist/shaders/misc.js +1 -10
- package/dist/shaders/misc.js.map +1 -1
- package/dist/shaders/noise.js +5 -1
- package/dist/shaders/noise.js.map +1 -1
- package/dist/shaders/probes.js +309 -0
- package/dist/shaders/probes.js.map +1 -0
- package/dist/shaders/sdf.js +537 -0
- package/dist/shaders/sdf.js.map +1 -0
- package/dist/shaders/shadow.js +104 -70
- package/dist/shaders/shadow.js.map +1 -1
- package/dist/shaders/skin_brdf.js +243 -0
- package/dist/shaders/skin_brdf.js.map +1 -0
- package/dist/shaders/ssr.js +6 -159
- package/dist/shaders/ssr.js.map +1 -1
- package/dist/shaders/temporal.js +38 -27
- package/dist/shaders/temporal.js.map +1 -1
- package/dist/shaders/water.js +40 -87
- package/dist/shaders/water.js.map +1 -1
- package/dist/shaders/water_medium.js +42 -0
- package/dist/shaders/water_medium.js.map +1 -0
- package/dist/shadow/dom.js +110 -19
- package/dist/shadow/dom.js.map +1 -1
- package/dist/shadow/esm.js +3 -3
- package/dist/shadow/esm.js.map +1 -1
- package/dist/shadow/pcf_opt.js +15 -48
- package/dist/shadow/pcf_opt.js.map +1 -1
- package/dist/shadow/pcf_pd.js +15 -53
- package/dist/shadow/pcf_pd.js.map +1 -1
- package/dist/shadow/pcss.js +34 -9
- package/dist/shadow/pcss.js.map +1 -1
- package/dist/shadow/shadow_impl.js +3 -8
- package/dist/shadow/shadow_impl.js.map +1 -1
- package/dist/shadow/shadow_region.js +87 -49
- package/dist/shadow/shadow_region.js.map +1 -1
- package/dist/shadow/shadowmapper.js +93 -53
- package/dist/shadow/shadowmapper.js.map +1 -1
- package/dist/shadow/ssm.js +18 -72
- package/dist/shadow/ssm.js.map +1 -1
- package/dist/shadow/vsm.js +3 -3
- package/dist/shadow/vsm.js.map +1 -1
- package/dist/shapes/shape.js +44 -1
- package/dist/shapes/shape.js.map +1 -1
- package/dist/utility/buoyancy.js +287 -0
- package/dist/utility/buoyancy.js.map +1 -0
- package/dist/utility/physical.js +10 -1
- package/dist/utility/physical.js.map +1 -1
- package/dist/utility/pmrem.js +17 -43
- package/dist/utility/pmrem.js.map +1 -1
- package/dist/utility/sdf/bake.js +435 -0
- package/dist/utility/sdf/bake.js.map +1 -0
- package/dist/utility/sdf/bvh.js +576 -0
- package/dist/utility/sdf/bvh.js.map +1 -0
- package/dist/utility/serialization/manager.js +36 -6
- package/dist/utility/serialization/manager.js.map +1 -1
- package/dist/utility/serialization/scene/animation.js +77 -7
- package/dist/utility/serialization/scene/animation.js.map +1 -1
- package/dist/utility/serialization/scene/batch.js +11 -4
- package/dist/utility/serialization/scene/batch.js.map +1 -1
- package/dist/utility/serialization/scene/camera.js +140 -279
- package/dist/utility/serialization/scene/camera.js.map +1 -1
- package/dist/utility/serialization/scene/cloth.js +25 -0
- package/dist/utility/serialization/scene/cloth.js.map +1 -0
- package/dist/utility/serialization/scene/common.js +14 -6
- package/dist/utility/serialization/scene/common.js.map +1 -1
- package/dist/utility/serialization/scene/foliage.js +821 -0
- package/dist/utility/serialization/scene/foliage.js.map +1 -0
- package/dist/utility/serialization/scene/hair.js +11 -4
- package/dist/utility/serialization/scene/hair.js.map +1 -1
- package/dist/utility/serialization/scene/light.js +115 -7
- package/dist/utility/serialization/scene/light.js.map +1 -1
- package/dist/utility/serialization/scene/material.js +388 -220
- package/dist/utility/serialization/scene/material.js.map +1 -1
- package/dist/utility/serialization/scene/mesh.js +69 -4
- package/dist/utility/serialization/scene/mesh.js.map +1 -1
- package/dist/utility/serialization/scene/node.js +140 -5
- package/dist/utility/serialization/scene/node.js.map +1 -1
- package/dist/utility/serialization/scene/node_physics.js +1318 -0
- package/dist/utility/serialization/scene/node_physics.js.map +1 -0
- package/dist/utility/serialization/scene/particle.js +11 -4
- package/dist/utility/serialization/scene/particle.js.map +1 -1
- package/dist/utility/serialization/scene/physics.js +120 -0
- package/dist/utility/serialization/scene/physics.js.map +1 -0
- package/dist/utility/serialization/scene/primitive.js +124 -10
- package/dist/utility/serialization/scene/primitive.js.map +1 -1
- package/dist/utility/serialization/scene/scene.js +1687 -51
- package/dist/utility/serialization/scene/scene.js.map +1 -1
- package/dist/utility/serialization/scene/sprite.js +11 -4
- package/dist/utility/serialization/scene/sprite.js.map +1 -1
- package/dist/utility/serialization/scene/terrain.js +163 -33
- package/dist/utility/serialization/scene/terrain.js.map +1 -1
- package/dist/utility/serialization/scene/text.js +11 -4
- package/dist/utility/serialization/scene/text.js.map +1 -1
- package/dist/utility/serialization/scene/water.js +2473 -114
- package/dist/utility/serialization/scene/water.js.map +1 -1
- package/dist/utility/serialization/types.js.map +1 -1
- package/dist/utility/shprojector.js +97 -109
- package/dist/utility/shprojector.js.map +1 -1
- package/dist/utility/textures/foam.js +170 -0
- package/dist/utility/textures/foam.js.map +1 -0
- package/dist/utility/textures/ggxlut.js +17 -43
- package/dist/utility/textures/ggxlut.js.map +1 -1
- package/dist/utility/textures/ltcdata.js +4 -4
- package/dist/utility/textures/ltcdata.js.map +1 -1
- package/dist/utility/textures/ltclut.js +16 -6
- package/dist/utility/textures/ltclut.js.map +1 -1
- package/dist/utility/unsupported.js +24 -0
- package/dist/utility/unsupported.js.map +1 -0
- package/dist/values.js +13 -1
- package/dist/values.js.map +1 -1
- package/package.json +25 -12
- package/src/asset/loaders/ktx2/basis/LICENSE +201 -0
- package/src/asset/loaders/ktx2/basis/basis_transcoder.js +2 -0
- package/src/asset/loaders/ktx2/basis/basis_transcoder.wasm +0 -0
- package/src/asset/meshoptimizer/LICENSE +21 -0
|
@@ -1,22 +1,51 @@
|
|
|
1
1
|
import { Vector3, Vector4, Vector2, REVERSE_Z, DEPTH_FARTHEST, DEPTH_REDUCE_CLOSER, DEPTH_REDUCE_FARTHER } from '@zephyr3d/base';
|
|
2
|
-
import { RENDER_PASS_TYPE_DEPTH, RENDER_PASS_TYPE_SHADOWMAP, RENDER_PASS_TYPE_OBJECT_COLOR, RENDER_PASS_TYPE_LIGHT, MaterialVaryingFlags,
|
|
2
|
+
import { RENDER_PASS_TYPE_DEPTH, RENDER_PASS_TYPE_SHADOWMAP, RENDER_PASS_TYPE_OBJECT_COLOR, RENDER_PASS_TYPE_VOXELIZE, RENDER_PASS_TYPE_LIGHT, MaterialVaryingFlags, MORPH_WEIGHTS_VECTOR_COUNT, MAX_SKIN_EXTRA_INFLUENCE_PAIRS, MORPH_TARGET_POSITION, MORPH_TARGET_NORMAL, MORPH_TARGET_TANGENT, MORPH_ATTRIBUTE_VECTOR_COUNT, LIGHT_TYPE_DIRECTIONAL } from '../../values.js';
|
|
3
3
|
import { ProgramBuilder } from '@zephyr3d/device';
|
|
4
|
-
import {
|
|
4
|
+
import { linearToGamma } from '../../shaders/misc.js';
|
|
5
|
+
import { declareVoxelizeUniforms } from '../../shaders/gi.js';
|
|
5
6
|
import { getSamplerOptions, fetchSampler } from '../../utility/misc.js';
|
|
6
|
-
import { PHYSICAL_BAKE_EXPOSURE } from '../../utility/physical.js';
|
|
7
|
+
import { getGICacheExposure, PHYSICAL_BAKE_EXPOSURE } from '../../utility/physical.js';
|
|
7
8
|
import { getDevice } from '../../app/api.js';
|
|
8
9
|
import { getDefaultAtmosphereParams, getAtmosphereParamsStruct } from '../../shaders/atmosphere.js';
|
|
9
10
|
import { getDefaultHeightFogParams, getHeightFogParamsStruct, calculateFog } from '../../shaders/fog.js';
|
|
10
11
|
import { getShadowReceiverNoL, getShadowReceiverBiasFactor } from '../../shadow/receiver_bias.js';
|
|
12
|
+
import { declareGlobalSDFUniforms, declareDynamicSDFUniforms, GLOBAL_SDF_TEXTURE, sampleSceneSDF } from '../../shaders/sdf.js';
|
|
11
13
|
|
|
14
|
+
/** Clipmap levels of the scene field declared in a program, by program builder. */ const sceneSDFLevels = new WeakMap();
|
|
12
15
|
const UNIFORM_NAME_LIGHT_BUFFER = 'Z_UniformLightBuffer';
|
|
13
16
|
const UNIFORM_NAME_LIGHT_INDEX_TEXTURE = 'Z_UniformLightIndexTex';
|
|
17
|
+
const UNIFORM_NAME_CLUSTER_GRID = 'Z_ClusterGrid';
|
|
18
|
+
const UNIFORM_NAME_CLUSTER_LIGHT_LIST = 'Z_ClusterLightList';
|
|
14
19
|
const UNIFORM_NAME_BAKED_SKY_MAP = 'Z_UniformBakedSky';
|
|
15
20
|
const UNIFORM_NAME_AERIALPERSPECTIVE_LUT = 'Z_UniformAerialPerspectiveLUT';
|
|
16
21
|
const UNIFORM_NAME_SKYDISTANTLIGHT_LUT = 'Z_UniformSkyDistantLightLUT';
|
|
22
|
+
const UNIFORM_NAME_FOG_SKYLIGHT_CUBEMAP = 'Z_UniformFogSkyLightCubemap';
|
|
17
23
|
const UNIFORM_NAME_SHADOW_MAP = 'Z_UniformShadowMap';
|
|
18
24
|
const UNIFORM_NAME_SHADOW_MASK = 'Z_UniformShadowMask';
|
|
19
25
|
const UNIFORM_NAME_SHADOW_MASK_MODE = 'Z_UniformShadowMaskMode';
|
|
26
|
+
const UNIFORM_NAME_TRANSMISSION_THICKNESS = 'Z_UniformTransmissionThickness';
|
|
27
|
+
const UNIFORM_NAME_CAUSTIC_MAP = 'Z_UniformCausticMap';
|
|
28
|
+
const UNIFORM_NAME_CAUSTIC_HEIGHT_MAP = 'Z_UniformCausticHeightMap';
|
|
29
|
+
/**
|
|
30
|
+
* Offset added to a surface height before it is stored in the caustic height
|
|
31
|
+
* map, so that a cleared texel - no surface on that ray - is distinguishable
|
|
32
|
+
* from a surface at the rest level. A ray with no water on it must read as
|
|
33
|
+
* dry, not as submerged under a flat surface: on the down-sun side of a
|
|
34
|
+
* footprint edge, a receiver below the rest level but above a trough would
|
|
35
|
+
* otherwise be lit through water that is not there. Eight keeps troughs down
|
|
36
|
+
* to seven metres positive and leaves half floats millimetre precision.
|
|
37
|
+
* @internal
|
|
38
|
+
*/ const CAUSTIC_HEIGHT_BIAS = 8;
|
|
39
|
+
/**
|
|
40
|
+
* Water bodies that can cast caustics into one map.
|
|
41
|
+
*
|
|
42
|
+
* They share the map and the slice, so this is not a limit on how much water a
|
|
43
|
+
* scene may hold - only on how many separate surfaces the receiver will test
|
|
44
|
+
* itself against, which it does per fragment. Four covers a lake with a couple
|
|
45
|
+
* of pools beside it; beyond that the renderer keeps the largest four, which
|
|
46
|
+
* are the ones a viewer is most likely to be standing over.
|
|
47
|
+
*/ const MAX_CAUSTIC_WATERS = 4;
|
|
48
|
+
const UNIFORM_NAME_CAUSTIC_PARAMS = 'Z_UniformCausticParams';
|
|
20
49
|
const UNIFORM_NAME_LINEAR_DEPTH_MAP = 'Z_UniformLinearDepth';
|
|
21
50
|
const UNIFORM_NAME_LINEAR_DEPTH_MAP_SIZE = 'Z_UniformLinearDepthSize';
|
|
22
51
|
const UNIFORM_NAME_SCENE_COLOR_MAP = 'Z_UniformSceneColor';
|
|
@@ -33,6 +62,7 @@ const UNIFORM_NAME_INSTANCE_DATA_OFFSET = 'Z_UniformInstanceDataOffset';
|
|
|
33
62
|
const UNIFORM_NAME_BONE_MATRICES = 'Z_UniformBoneMatrices';
|
|
34
63
|
const UNIFORM_NAME_BONE_TEXTURE_SIZE = 'Z_UniformBoneTexSize';
|
|
35
64
|
const UNIFORM_NAME_BONE_INV_BIND_MATRIX = 'Z_UniformBoneInvBindMatrix';
|
|
65
|
+
const UNIFORM_NAME_SKIN_PALETTE_BASE = 'Z_UniformSkinPaletteBase';
|
|
36
66
|
const UNIFORM_NAME_SKIN_INFLUENCE_DATA = 'Z_UniformSkinInfluenceData';
|
|
37
67
|
const UNIFORM_NAME_SKIN_INFLUENCE_INFO = 'Z_UniformSkinInfluenceInfo';
|
|
38
68
|
const UNIFORM_NAME_MORPH_DATA = 'Z_UniformMorphData';
|
|
@@ -46,6 +76,7 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
46
76
|
static MATERIAL_INSTANCE_DATA_OFFSET = 9;
|
|
47
77
|
/** @internal */ static defaultSunDir = Vector3.one().inplaceNormalize();
|
|
48
78
|
/** @internal 1x1x1 fallback bound to the shadow-mask uniform when no mask exists this frame. */ static _dummyShadowMask = null;
|
|
79
|
+
static _dummyTransmissionThickness = null;
|
|
49
80
|
/** @internal */ static SKIN_MATRIX_NAME = 'Z_SkinMatrix';
|
|
50
81
|
static SKIN_PREV_MATRIX_NAME = 'Z_PrevSkinMatrix';
|
|
51
82
|
static SKIN_BONE_OFFSET = 'Z_boneOffset';
|
|
@@ -105,6 +136,9 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
105
136
|
static getBoneInvBindMatrixUniformName() {
|
|
106
137
|
return UNIFORM_NAME_BONE_INV_BIND_MATRIX;
|
|
107
138
|
}
|
|
139
|
+
static getSkinPaletteBaseUniformName() {
|
|
140
|
+
return UNIFORM_NAME_SKIN_PALETTE_BASE;
|
|
141
|
+
}
|
|
108
142
|
static getSkinInfluenceDataUniformName() {
|
|
109
143
|
return UNIFORM_NAME_SKIN_INFLUENCE_DATA;
|
|
110
144
|
}
|
|
@@ -209,8 +243,12 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
209
243
|
if (casterParams) {
|
|
210
244
|
casterParams.impl?.declareCasterUniforms(scope, casterParams);
|
|
211
245
|
}
|
|
212
|
-
} else if (ctx.renderPass.type === RENDER_PASS_TYPE_DEPTH || ctx.renderPass.type === RENDER_PASS_TYPE_OBJECT_COLOR) {
|
|
246
|
+
} else if (ctx.renderPass.type === RENDER_PASS_TYPE_DEPTH || ctx.renderPass.type === RENDER_PASS_TYPE_OBJECT_COLOR || ctx.renderPass.type === RENDER_PASS_TYPE_VOXELIZE) {
|
|
213
247
|
scope.camera = cameraStruct().uniform(0);
|
|
248
|
+
// Write-only storage textures are fragment-only in WebGPU
|
|
249
|
+
if (ctx.renderPass.type === RENDER_PASS_TYPE_VOXELIZE && pb.shaderKind === 'fragment') {
|
|
250
|
+
declareVoxelizeUniforms(scope, 0);
|
|
251
|
+
}
|
|
214
252
|
} else if (ctx.renderPass.type === RENDER_PASS_TYPE_LIGHT) {
|
|
215
253
|
const useClusteredLighting = !ctx.currentShadowLight;
|
|
216
254
|
if (ctx.materialFlags & MaterialVaryingFlags.APPLY_FOG) {
|
|
@@ -226,6 +264,8 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
226
264
|
// Static samplers: LUTs are rgba16f without mipmaps
|
|
227
265
|
scope[UNIFORM_NAME_AERIALPERSPECTIVE_LUT] = pb.tex2D().uniform(0).withSampler(getSamplerOptions('clamp_linear_nomip'));
|
|
228
266
|
scope[UNIFORM_NAME_SKYDISTANTLIGHT_LUT] = pb.tex2D().uniform(0).withSampler(getSamplerOptions('clamp_linear_nomip'));
|
|
267
|
+
// Mipmapped radiance map, sampled at a roughness-selected LOD
|
|
268
|
+
scope[UNIFORM_NAME_FOG_SKYLIGHT_CUBEMAP] = pb.texCube().uniform(0).withSampler(getSamplerOptions('clamp_linear'));
|
|
229
269
|
}
|
|
230
270
|
const lightStruct = ctx.currentShadowLight ? pb.defineStruct([
|
|
231
271
|
pb.vec3('sunDir'),
|
|
@@ -249,7 +289,12 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
249
289
|
pb.float('envLightSpecularStrength'),
|
|
250
290
|
pb.vec4('clusterParams'),
|
|
251
291
|
pb.ivec4('countParams'),
|
|
292
|
+
// Buffer indices of the lights shaded outside the clusters, zero-terminated.
|
|
293
|
+
pb.ivec4('globalLights'),
|
|
252
294
|
pb.ivec2('lightIndexTexSize'),
|
|
295
|
+
// Nonzero when the clusters were sliced for an orthographic camera
|
|
296
|
+
// (linear depth slices instead of exponential).
|
|
297
|
+
pb.int('clusterOrtho'),
|
|
253
298
|
// Number of shadow-casting lights at the head of the clustered light
|
|
254
299
|
// buffer (indices 1..N). Only present on the screen-space shadow mask
|
|
255
300
|
// path (part of the global bind group hash), so the declared and bound
|
|
@@ -261,9 +306,17 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
261
306
|
scope.camera = cameraStruct().uniform(0);
|
|
262
307
|
scope.light = lightStruct().uniform(0);
|
|
263
308
|
if (useClusteredLighting) {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
309
|
+
if (this.usesClusterLightLists()) {
|
|
310
|
+
// Storage buffers sized to the frame's lights: no cap on the lights in total or
|
|
311
|
+
// per cluster. See ClusteredLight for the layout of the grid and the lists.
|
|
312
|
+
scope[UNIFORM_NAME_LIGHT_BUFFER] = pb.vec4[0]().storageBufferReadonly(0);
|
|
313
|
+
scope[UNIFORM_NAME_CLUSTER_GRID] = pb.uint[0]().storageBufferReadonly(0);
|
|
314
|
+
scope[UNIFORM_NAME_CLUSTER_LIGHT_LIST] = pb.uint[0]().storageBufferReadonly(0);
|
|
315
|
+
} else {
|
|
316
|
+
scope[UNIFORM_NAME_LIGHT_BUFFER] = pb.vec4[(this.getMaxClusterLights() + 1) * 4]().uniformBuffer(0);
|
|
317
|
+
// The light index texture is fetched with textureLoad
|
|
318
|
+
scope[UNIFORM_NAME_LIGHT_INDEX_TEXTURE] = pb.utex2D().noSampler().uniform(0);
|
|
319
|
+
}
|
|
267
320
|
// Screen-space shadow mask array (rgba8unorm, 4 shadow lights per layer).
|
|
268
321
|
// Sampled by clustered lights whose buffer index is <= numShadowLights.
|
|
269
322
|
// Presence is keyed into the global bind group hash, so declared and bound
|
|
@@ -276,6 +329,12 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
276
329
|
// e.g. OIT hair, where the opaque-depth mask would be incorrect).
|
|
277
330
|
scope[UNIFORM_NAME_SHADOW_MASK_MODE] = pb.int().uniform(0);
|
|
278
331
|
}
|
|
332
|
+
// Light-space thickness for subsurface transmission, packed exactly like
|
|
333
|
+
// the shadow mask. Presence is keyed into the bind group hash through
|
|
334
|
+
// ctx.transmissionThickness, so declared and bound layouts always agree.
|
|
335
|
+
if (ctx.transmissionThickness) {
|
|
336
|
+
scope[UNIFORM_NAME_TRANSMISSION_THICKNESS] = pb.tex2DArray().uniform(0);
|
|
337
|
+
}
|
|
279
338
|
}
|
|
280
339
|
// Baked sky cube: rgba16f without mipmaps
|
|
281
340
|
scope[UNIFORM_NAME_BAKED_SKY_MAP] = pb.texCube().uniform(0).withSampler(getSamplerOptions('clamp_linear_nomip'));
|
|
@@ -294,6 +353,9 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
294
353
|
}
|
|
295
354
|
scope[UNIFORM_NAME_SHADOW_MAP] = tex.uniform(0);
|
|
296
355
|
}
|
|
356
|
+
if (this.usesWaterCaustics(ctx)) {
|
|
357
|
+
this.declareWaterCausticUniforms(pb);
|
|
358
|
+
}
|
|
297
359
|
if (ctx.drawEnvLight) {
|
|
298
360
|
ctx.env.light.envLight.initShaderBindings(pb, ctx);
|
|
299
361
|
}
|
|
@@ -304,8 +366,18 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
304
366
|
scope[UNIFORM_NAME_LINEAR_DEPTH_MAP_SIZE] = pb.vec2().uniform(0);
|
|
305
367
|
}
|
|
306
368
|
if (ctx.sceneColorTexture) {
|
|
307
|
-
//
|
|
308
|
-
|
|
369
|
+
// Mip-filtered, because the refraction background is read at a LOD and
|
|
370
|
+
// not only at mip 0: the water picks one from how deep and how turbid
|
|
371
|
+
// the column it is looking through is. A `nomip` sampler pins mip 0
|
|
372
|
+
// whatever LOD the shader passes, so this and the copy's level count
|
|
373
|
+
// have to change together - and each backend only honours one of them.
|
|
374
|
+
// WebGPU binds the sampler declared here as a static binding the runtime
|
|
375
|
+
// cannot overwrite; WebGL falls back to the texture's own sampler, whose
|
|
376
|
+
// mip filter follows its level count.
|
|
377
|
+
//
|
|
378
|
+
// Every other reader of this texture passes an explicit LOD 0 (SSR hits,
|
|
379
|
+
// the sky blend), so none of them changes behaviour.
|
|
380
|
+
scope[UNIFORM_NAME_SCENE_COLOR_MAP] = pb.tex2D().uniform(0).withSampler(getSamplerOptions('clamp_linear'));
|
|
309
381
|
scope[UNIFORM_NAME_SCENE_COLOR_MAP_SIZE] = pb.vec2().uniform(0);
|
|
310
382
|
}
|
|
311
383
|
if (ctx.HiZTexture) {
|
|
@@ -343,75 +415,13 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
343
415
|
if (!this.hasSkinning(scope)) {
|
|
344
416
|
return null;
|
|
345
417
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
pb.func(funcNameGetBoneMatrixFromTexture, [
|
|
351
|
-
pb.int('boneIndex')
|
|
352
|
-
], function() {
|
|
353
|
-
const boneTexture = this[UNIFORM_NAME_BONE_MATRICES];
|
|
354
|
-
this.$l.uvOffsets = pb.textureSampleLevel(boneTexture, pb.div(pb.vec2(0.5), this[UNIFORM_NAME_BONE_TEXTURE_SIZE]), 0);
|
|
355
|
-
this.$l.currentOffset = pb.int(this.uvOffsets.x);
|
|
356
|
-
this.$l.w = this[UNIFORM_NAME_BONE_TEXTURE_SIZE].x;
|
|
357
|
-
this.$l.pixelIndex = pb.float(pb.mul(pb.add(this.boneIndex, this.currentOffset), 4));
|
|
358
|
-
this.$l.xIndex = pb.mod(this.pixelIndex, this.w);
|
|
359
|
-
this.$l.yIndex = pb.floor(pb.div(this.pixelIndex, this.w));
|
|
360
|
-
this.$l.u1 = pb.div(pb.add(this.xIndex, 0.5), this.w);
|
|
361
|
-
this.$l.u2 = pb.div(pb.add(this.xIndex, 1.5), this.w);
|
|
362
|
-
this.$l.u3 = pb.div(pb.add(this.xIndex, 2.5), this.w);
|
|
363
|
-
this.$l.u4 = pb.div(pb.add(this.xIndex, 3.5), this.w);
|
|
364
|
-
this.$l.v = pb.div(pb.add(this.yIndex, 0.5), this.w);
|
|
365
|
-
this.$l.row1 = pb.textureSampleLevel(boneTexture, pb.vec2(this.u1, this.v), 0);
|
|
366
|
-
this.$l.row2 = pb.textureSampleLevel(boneTexture, pb.vec2(this.u2, this.v), 0);
|
|
367
|
-
this.$l.row3 = pb.textureSampleLevel(boneTexture, pb.vec2(this.u3, this.v), 0);
|
|
368
|
-
this.$l.row4 = pb.textureSampleLevel(boneTexture, pb.vec2(this.u4, this.v), 0);
|
|
369
|
-
this.$return(pb.mat4(this.row1, this.row2, this.row3, this.row4));
|
|
370
|
-
});
|
|
371
|
-
const funcNameGetSkinningMatrix = 'Z_getSkinningMatrix';
|
|
372
|
-
pb.func(funcNameGetSkinningMatrix, [], function() {
|
|
373
|
-
const invBindMatrix = this[UNIFORM_NAME_BONE_INV_BIND_MATRIX];
|
|
374
|
-
const blendIndices = scope.$getVertexAttrib('blendIndices');
|
|
375
|
-
const blendWeights = scope.$getVertexAttrib('blendWeights');
|
|
376
|
-
this.$l.m0 = scope.$g[funcNameGetBoneMatrixFromTexture](pb.int(blendIndices[0]));
|
|
377
|
-
this.$l.m1 = scope.$g[funcNameGetBoneMatrixFromTexture](pb.int(blendIndices[1]));
|
|
378
|
-
this.$l.m2 = scope.$g[funcNameGetBoneMatrixFromTexture](pb.int(blendIndices[2]));
|
|
379
|
-
this.$l.m3 = scope.$g[funcNameGetBoneMatrixFromTexture](pb.int(blendIndices[3]));
|
|
380
|
-
this.$l.m = pb.add(pb.mul(this.m0, blendWeights.x), pb.mul(this.m1, blendWeights.y), pb.mul(this.m2, blendWeights.z), pb.mul(this.m3, blendWeights.w));
|
|
381
|
-
this.$l.skinInfo = this[UNIFORM_NAME_SKIN_INFLUENCE_INFO];
|
|
382
|
-
this.$if(pb.greaterThan(this.skinInfo.z, 4), function() {
|
|
383
|
-
this.$l.vertexIndex = isWebGL ? pb.int(scope.$inputs.zFakeVertexID) : pb.int(scope.$builtins.vertexIndex);
|
|
384
|
-
this.$l.texWidth = pb.int(this.skinInfo.x);
|
|
385
|
-
this.$l.pairCount = pb.int(this.skinInfo.w);
|
|
386
|
-
this.$for(pb.int('pairIndex'), 0, MAX_SKIN_EXTRA_INFLUENCE_PAIRS, function() {
|
|
387
|
-
this.$if(pb.greaterThanEqual(this.pairIndex, this.pairCount), function() {
|
|
388
|
-
this.$break();
|
|
389
|
-
});
|
|
390
|
-
this.$l.pixelIndex = pb.add(pb.mul(this.vertexIndex, this.pairCount), this.pairIndex);
|
|
391
|
-
this.$l.xIndex = pb.mod(this.pixelIndex, this.texWidth);
|
|
392
|
-
this.$l.yIndex = pb.div(this.pixelIndex, this.texWidth);
|
|
393
|
-
if (supportsTextureLoad) {
|
|
394
|
-
this.$l.extra = pb.textureLoad(this[UNIFORM_NAME_SKIN_INFLUENCE_DATA], pb.ivec2(this.xIndex, this.yIndex), 0);
|
|
395
|
-
} else {
|
|
396
|
-
this.$l.u = pb.div(pb.add(pb.float(this.xIndex), 0.5), this.skinInfo.x);
|
|
397
|
-
this.$l.v = pb.div(pb.add(pb.float(this.yIndex), 0.5), this.skinInfo.y);
|
|
398
|
-
this.$l.extra = pb.textureSampleLevel(this[UNIFORM_NAME_SKIN_INFLUENCE_DATA], pb.vec2(this.u, this.v), 0);
|
|
399
|
-
}
|
|
400
|
-
this.$if(pb.greaterThan(this.extra.y, 0), function() {
|
|
401
|
-
this.m = pb.add(this.m, pb.mul(scope.$g[funcNameGetBoneMatrixFromTexture](pb.int(this.extra.x)), this.extra.y));
|
|
402
|
-
});
|
|
403
|
-
this.$if(pb.greaterThan(this.extra.w, 0), function() {
|
|
404
|
-
this.m = pb.add(this.m, pb.mul(scope.$g[funcNameGetBoneMatrixFromTexture](pb.int(this.extra.z)), this.extra.w));
|
|
405
|
-
});
|
|
406
|
-
});
|
|
407
|
-
});
|
|
408
|
-
this.$return(pb.mul(invBindMatrix, this.m));
|
|
409
|
-
});
|
|
410
|
-
return scope.$g[funcNameGetSkinningMatrix]();
|
|
418
|
+
if (!scope[this.SKIN_MATRIX_NAME]) {
|
|
419
|
+
this.prepareSkinAnimation(scope);
|
|
420
|
+
}
|
|
421
|
+
return scope[this.SKIN_MATRIX_NAME];
|
|
411
422
|
}
|
|
412
423
|
static calculateMorphDelta(scope, attrib) {
|
|
413
424
|
const pb = scope.$builder;
|
|
414
|
-
const isWebGL1 = pb.getDevice().type === 'webgl';
|
|
415
425
|
if (pb.shaderKind !== 'vertex') {
|
|
416
426
|
throw new Error(`ShaderHelper.calculateMorphDelta(): must be called at vertex stage`);
|
|
417
427
|
}
|
|
@@ -423,7 +433,7 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
423
433
|
this.$if(pb.lessThan(this.offset, 0), function() {
|
|
424
434
|
this.$return(pb.vec4(0));
|
|
425
435
|
});
|
|
426
|
-
this.$l.vertexIndex =
|
|
436
|
+
this.$l.vertexIndex = pb.int(scope.$builtins.vertexIndex);
|
|
427
437
|
const morphInfo = scope[that.getMorphInfoUniformName()];
|
|
428
438
|
this.$l.metaData = pb.ivec4(morphInfo[0]);
|
|
429
439
|
this.$l.texWidth = pb.float(this.metaData.x);
|
|
@@ -431,43 +441,21 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
431
441
|
this.$l.numVertices = this.metaData.z;
|
|
432
442
|
this.$l.numTargets = this.metaData.w;
|
|
433
443
|
this.$l.value = pb.vec4(0);
|
|
434
|
-
|
|
435
|
-
this.$
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
this.$l.u = pb.div(pb.add(this.xIndex, 0.5), this.texWidth);
|
|
448
|
-
this.$l.v = pb.div(pb.add(this.yIndex, 0.5), this.texHeight);
|
|
449
|
-
this.$l.morphValue = pb.textureSampleLevel(this[that.getMorphDataUniformName()], pb.vec2(this.u, this.v), 0);
|
|
450
|
-
this.value = pb.add(this.value, pb.mul(this.morphValue, this.weight));
|
|
451
|
-
});
|
|
452
|
-
});
|
|
453
|
-
});
|
|
454
|
-
} else {
|
|
455
|
-
this.$for(pb.int('t'), 0, this.numTargets, function() {
|
|
456
|
-
this.$l.i = pb.sar(this.t, 2);
|
|
457
|
-
this.$l.j = pb.compAnd(this.t, 3);
|
|
458
|
-
this.$l.weight = morphInfo.at(pb.add(1, this.i)).at(this.j);
|
|
459
|
-
this.$if(pb.notEqual(this.weight, 0), function() {
|
|
460
|
-
this.$l.targetIndex = pb.int(morphInfo.at(pb.add(1 + MORPH_WEIGHTS_VECTOR_COUNT, this.i)).at(this.j));
|
|
461
|
-
this.$l.pixelIndex = pb.float(pb.add(this.offset, pb.mul(this.targetIndex, this.numVertices), this.vertexIndex));
|
|
462
|
-
this.$l.xIndex = pb.mod(this.pixelIndex, this.texWidth);
|
|
463
|
-
this.$l.yIndex = pb.floor(pb.div(this.pixelIndex, this.texWidth));
|
|
464
|
-
this.$l.u = pb.div(pb.add(this.xIndex, 0.5), this.texWidth);
|
|
465
|
-
this.$l.v = pb.div(pb.add(this.yIndex, 0.5), this.texHeight);
|
|
466
|
-
this.$l.morphValue = pb.textureSampleLevel(this[that.getMorphDataUniformName()], pb.vec2(this.u, this.v), 0);
|
|
467
|
-
this.value = pb.add(this.value, pb.mul(this.morphValue, this.weight));
|
|
468
|
-
});
|
|
444
|
+
this.$for(pb.int('t'), 0, this.numTargets, function() {
|
|
445
|
+
this.$l.i = pb.sar(this.t, 2);
|
|
446
|
+
this.$l.j = pb.compAnd(this.t, 3);
|
|
447
|
+
this.$l.weight = morphInfo.at(pb.add(1, this.i)).at(this.j);
|
|
448
|
+
this.$if(pb.notEqual(this.weight, 0), function() {
|
|
449
|
+
this.$l.targetIndex = pb.int(morphInfo.at(pb.add(1 + MORPH_WEIGHTS_VECTOR_COUNT, this.i)).at(this.j));
|
|
450
|
+
this.$l.pixelIndex = pb.float(pb.add(this.offset, pb.mul(this.targetIndex, this.numVertices), this.vertexIndex));
|
|
451
|
+
this.$l.xIndex = pb.mod(this.pixelIndex, this.texWidth);
|
|
452
|
+
this.$l.yIndex = pb.floor(pb.div(this.pixelIndex, this.texWidth));
|
|
453
|
+
this.$l.u = pb.div(pb.add(this.xIndex, 0.5), this.texWidth);
|
|
454
|
+
this.$l.v = pb.div(pb.add(this.yIndex, 0.5), this.texHeight);
|
|
455
|
+
this.$l.morphValue = pb.textureSampleLevel(this[that.getMorphDataUniformName()], pb.vec2(this.u, this.v), 0);
|
|
456
|
+
this.value = pb.add(this.value, pb.mul(this.morphValue, this.weight));
|
|
469
457
|
});
|
|
470
|
-
}
|
|
458
|
+
});
|
|
471
459
|
this.$return(this.value);
|
|
472
460
|
});
|
|
473
461
|
const pos = 1 + MORPH_WEIGHTS_VECTOR_COUNT * 2 + (attrib >> 2);
|
|
@@ -514,7 +502,6 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
514
502
|
* @internal
|
|
515
503
|
*/ static calculateMorphDirectionByDisplacement(scope, base, attrib) {
|
|
516
504
|
const pb = scope.$builder;
|
|
517
|
-
const isWebGL1 = pb.getDevice().type === 'webgl';
|
|
518
505
|
if (pb.shaderKind !== 'vertex') {
|
|
519
506
|
throw new Error(`ShaderHelper.calculateMorphDirectionByDisplacement(): must be called at vertex stage`);
|
|
520
507
|
}
|
|
@@ -526,7 +513,7 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
526
513
|
pb.int('directionOffset'),
|
|
527
514
|
pb.int('positionOffset')
|
|
528
515
|
], function() {
|
|
529
|
-
this.$l.vertexIndex =
|
|
516
|
+
this.$l.vertexIndex = pb.int(scope.$builtins.vertexIndex);
|
|
530
517
|
const morphInfo = scope[that.getMorphInfoUniformName()];
|
|
531
518
|
this.$l.metaData = pb.ivec4(morphInfo[0]);
|
|
532
519
|
this.$l.texWidth = pb.float(this.metaData.x);
|
|
@@ -564,27 +551,13 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
564
551
|
this.weightSum = pb.add(this.weightSum, this.displacementWeight);
|
|
565
552
|
});
|
|
566
553
|
};
|
|
567
|
-
|
|
568
|
-
this.$
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
this.$l.weight = morphInfo.at(pb.add(1, this.i)).at(this.j);
|
|
575
|
-
this.$l.targetIndex = pb.int(morphInfo.at(pb.add(1 + MORPH_WEIGHTS_VECTOR_COUNT, this.i)).at(this.j));
|
|
576
|
-
accumulateTarget(this);
|
|
577
|
-
});
|
|
578
|
-
});
|
|
579
|
-
} else {
|
|
580
|
-
this.$for(pb.int('t'), 0, this.numTargets, function() {
|
|
581
|
-
this.$l.i = pb.sar(this.t, 2);
|
|
582
|
-
this.$l.j = pb.compAnd(this.t, 3);
|
|
583
|
-
this.$l.weight = morphInfo.at(pb.add(1, this.i)).at(this.j);
|
|
584
|
-
this.$l.targetIndex = pb.int(morphInfo.at(pb.add(1 + MORPH_WEIGHTS_VECTOR_COUNT, this.i)).at(this.j));
|
|
585
|
-
accumulateTarget(this);
|
|
586
|
-
});
|
|
587
|
-
}
|
|
554
|
+
this.$for(pb.int('t'), 0, this.numTargets, function() {
|
|
555
|
+
this.$l.i = pb.sar(this.t, 2);
|
|
556
|
+
this.$l.j = pb.compAnd(this.t, 3);
|
|
557
|
+
this.$l.weight = morphInfo.at(pb.add(1, this.i)).at(this.j);
|
|
558
|
+
this.$l.targetIndex = pb.int(morphInfo.at(pb.add(1 + MORPH_WEIGHTS_VECTOR_COUNT, this.i)).at(this.j));
|
|
559
|
+
accumulateTarget(this);
|
|
560
|
+
});
|
|
588
561
|
this.$l.fallback = scope.$g[normalizeName](this.base, this.directionDelta);
|
|
589
562
|
this.$if(pb.lessThan(this.weightSum, 1e-6), function() {
|
|
590
563
|
this.$return(this.fallback);
|
|
@@ -628,8 +601,6 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
628
601
|
}
|
|
629
602
|
const that = this;
|
|
630
603
|
const pb = scope.$builder;
|
|
631
|
-
const isWebGL = pb.getDevice().type === 'webgl';
|
|
632
|
-
const supportsTextureLoad = !isWebGL;
|
|
633
604
|
const funcNameGetBoneMatrixFromTexture = 'Z_getBoneMatrixFromTexture';
|
|
634
605
|
pb.func(funcNameGetBoneMatrixFromTexture, [
|
|
635
606
|
pb.float('boneIndex'),
|
|
@@ -637,6 +608,7 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
637
608
|
], function() {
|
|
638
609
|
const boneTexture = this[UNIFORM_NAME_BONE_MATRICES];
|
|
639
610
|
this.$l.w = this[UNIFORM_NAME_BONE_TEXTURE_SIZE].x;
|
|
611
|
+
this.$l.h = this[UNIFORM_NAME_BONE_TEXTURE_SIZE].y;
|
|
640
612
|
this.$l.pixelIndex = pb.mul(pb.add(this.boneIndex, this.boneOffset), 4);
|
|
641
613
|
this.$l.xIndex = pb.mod(this.pixelIndex, this.w);
|
|
642
614
|
this.$l.yIndex = pb.floor(pb.div(this.pixelIndex, this.w));
|
|
@@ -644,7 +616,7 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
644
616
|
this.$l.u2 = pb.div(pb.add(this.xIndex, 1.5), this.w);
|
|
645
617
|
this.$l.u3 = pb.div(pb.add(this.xIndex, 2.5), this.w);
|
|
646
618
|
this.$l.u4 = pb.div(pb.add(this.xIndex, 3.5), this.w);
|
|
647
|
-
this.$l.v = pb.div(pb.add(this.yIndex, 0.5), this.
|
|
619
|
+
this.$l.v = pb.div(pb.add(this.yIndex, 0.5), this.h);
|
|
648
620
|
this.$l.row1 = pb.textureSampleLevel(boneTexture, pb.vec2(this.u1, this.v), 0);
|
|
649
621
|
this.$l.row2 = pb.textureSampleLevel(boneTexture, pb.vec2(this.u2, this.v), 0);
|
|
650
622
|
this.$l.row3 = pb.textureSampleLevel(boneTexture, pb.vec2(this.u3, this.v), 0);
|
|
@@ -665,7 +637,7 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
665
637
|
this.$l.m = pb.add(pb.mul(this.m0, blendWeights.x), pb.mul(this.m1, blendWeights.y), pb.mul(this.m2, blendWeights.z), pb.mul(this.m3, blendWeights.w));
|
|
666
638
|
this.$l.skinInfo = this[UNIFORM_NAME_SKIN_INFLUENCE_INFO];
|
|
667
639
|
this.$if(pb.greaterThan(this.skinInfo.z, 4), function() {
|
|
668
|
-
this.$l.vertexIndex =
|
|
640
|
+
this.$l.vertexIndex = pb.int(scope.$builtins.vertexIndex);
|
|
669
641
|
this.$l.texWidth = pb.int(this.skinInfo.x);
|
|
670
642
|
this.$l.pairCount = pb.int(this.skinInfo.w);
|
|
671
643
|
this.$for(pb.int('pairIndex'), 0, MAX_SKIN_EXTRA_INFLUENCE_PAIRS, function() {
|
|
@@ -675,13 +647,7 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
675
647
|
this.$l.pixelIndex = pb.add(pb.mul(this.vertexIndex, this.pairCount), this.pairIndex);
|
|
676
648
|
this.$l.xIndex = pb.mod(this.pixelIndex, this.texWidth);
|
|
677
649
|
this.$l.yIndex = pb.div(this.pixelIndex, this.texWidth);
|
|
678
|
-
|
|
679
|
-
this.$l.extra = pb.textureLoad(this[UNIFORM_NAME_SKIN_INFLUENCE_DATA], pb.ivec2(this.xIndex, this.yIndex), 0);
|
|
680
|
-
} else {
|
|
681
|
-
this.$l.u = pb.div(pb.add(pb.float(this.xIndex), 0.5), this.skinInfo.x);
|
|
682
|
-
this.$l.v = pb.div(pb.add(pb.float(this.yIndex), 0.5), this.skinInfo.y);
|
|
683
|
-
this.$l.extra = pb.textureSampleLevel(this[UNIFORM_NAME_SKIN_INFLUENCE_DATA], pb.vec2(this.u, this.v), 0);
|
|
684
|
-
}
|
|
650
|
+
this.$l.extra = pb.textureLoad(this[UNIFORM_NAME_SKIN_INFLUENCE_DATA], pb.ivec2(this.xIndex, this.yIndex), 0);
|
|
685
651
|
this.$if(pb.greaterThan(this.extra.y, 0), function() {
|
|
686
652
|
this.m = pb.add(this.m, pb.mul(scope.$g[funcNameGetBoneMatrixFromTexture](this.extra.x, this.boneOffset), this.extra.y));
|
|
687
653
|
});
|
|
@@ -693,8 +659,12 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
693
659
|
this.$return(pb.mul(invBindMatrix, this.m));
|
|
694
660
|
});
|
|
695
661
|
const motionVector = !!this.getUnjitteredViewProjectionMatrix(scope);
|
|
662
|
+
// The palette header slot holds the slot indices of the current and previous matrix sets
|
|
696
663
|
const boneTexture = scope[UNIFORM_NAME_BONE_MATRICES];
|
|
697
|
-
scope
|
|
664
|
+
const paletteBase = scope[UNIFORM_NAME_SKIN_PALETTE_BASE] ?? this.getInstancedUniform(scope, 4).z;
|
|
665
|
+
const boneTextureSize = scope[UNIFORM_NAME_BONE_TEXTURE_SIZE];
|
|
666
|
+
scope.$l.Z_skinHeaderTexel = pb.mul(paletteBase, 4);
|
|
667
|
+
scope.$l[that.SKIN_BONE_OFFSET] = pb.textureSampleLevel(boneTexture, pb.div(pb.add(pb.vec2(pb.mod(scope.Z_skinHeaderTexel, boneTextureSize.x), pb.floor(pb.div(scope.Z_skinHeaderTexel, boneTextureSize.x))), pb.vec2(0.5)), boneTextureSize), 0).xy;
|
|
698
668
|
scope.$l[that.SKIN_MATRIX_NAME] = scope[funcNameGetSkinningMatrix](scope[that.SKIN_BONE_OFFSET].x);
|
|
699
669
|
if (motionVector) {
|
|
700
670
|
scope.$l[that.SKIN_PREV_MATRIX_NAME] = scope[funcNameGetSkinningMatrix](scope[that.SKIN_BONE_OFFSET].y);
|
|
@@ -929,6 +899,9 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
929
899
|
scope[UNIFORM_NAME_BONE_MATRICES] = pb.tex2D().uniform(1).sampleType('unfilterable-float');
|
|
930
900
|
scope[UNIFORM_NAME_BONE_INV_BIND_MATRIX] = pb.mat4().uniform(1);
|
|
931
901
|
scope[UNIFORM_NAME_BONE_TEXTURE_SIZE] = pb.vec2().uniform(1);
|
|
902
|
+
if (!instanced) {
|
|
903
|
+
scope[UNIFORM_NAME_SKIN_PALETTE_BASE] = pb.float().uniform(1);
|
|
904
|
+
}
|
|
932
905
|
scope[UNIFORM_NAME_SKIN_INFLUENCE_DATA] = pb.tex2D().uniform(1).sampleType('unfilterable-float');
|
|
933
906
|
scope[UNIFORM_NAME_SKIN_INFLUENCE_INFO] = pb.vec4().uniform(1);
|
|
934
907
|
}
|
|
@@ -942,7 +915,7 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
942
915
|
}
|
|
943
916
|
/** @internal */ static setCameraUniforms(bindGroup, ctx, camera, linear) {
|
|
944
917
|
const pos = camera.getWorldPosition();
|
|
945
|
-
const useJitter = ctx.motionVectors && ctx.renderPass.type !== RENDER_PASS_TYPE_SHADOWMAP && ctx.renderPass.type !== RENDER_PASS_TYPE_OBJECT_COLOR;
|
|
918
|
+
const useJitter = ctx.motionVectors && ctx.renderPass.type !== RENDER_PASS_TYPE_SHADOWMAP && ctx.renderPass.type !== RENDER_PASS_TYPE_OBJECT_COLOR && ctx.renderPass.type !== RENDER_PASS_TYPE_VOXELIZE;
|
|
946
919
|
const cameraStruct = {
|
|
947
920
|
position: new Vector4(pos.x, pos.y, pos.z, 0),
|
|
948
921
|
renderSize: new Vector2(ctx.renderWidth, ctx.renderHeight),
|
|
@@ -991,12 +964,14 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
991
964
|
viewMatrix: light.viewMatrix,
|
|
992
965
|
depthBias: shadowMapParams.depthBiasValues[0],
|
|
993
966
|
implParams: shadowMapParams.impl.getParams(this._lightUniformShadow.implParams),
|
|
994
|
-
|
|
967
|
+
// The shadow projection, not the light's own type: a rect light renders a point light's
|
|
968
|
+
// cube, so its casters must write the point light's radial depth.
|
|
969
|
+
lightType: shadowMapParams.lightType
|
|
995
970
|
});
|
|
996
971
|
shadowMapParams.impl?.applyCasterUniforms(bindGroup, shadowMapParams);
|
|
997
972
|
}
|
|
998
973
|
}
|
|
999
|
-
/** @internal */ static setFogUniforms(bindGroup, withAerialPerspective, fogType, additive, atmosphereParams, heightFogParams, aerialPerspectiveLUT, skyDistantLightLUT) {
|
|
974
|
+
/** @internal */ static setFogUniforms(bindGroup, withAerialPerspective, fogType, additive, atmosphereParams, heightFogParams, aerialPerspectiveLUT, skyDistantLightLUT, skyLightCubemap) {
|
|
1000
975
|
this._fogUniforms.withAerialPerspective = withAerialPerspective;
|
|
1001
976
|
this._fogUniforms.fogType = fogType;
|
|
1002
977
|
this._fogUniforms.additive = additive;
|
|
@@ -1005,6 +980,7 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
1005
980
|
bindGroup.setValue('fog', this._fogUniforms);
|
|
1006
981
|
bindGroup.setTexture(UNIFORM_NAME_AERIALPERSPECTIVE_LUT, aerialPerspectiveLUT);
|
|
1007
982
|
bindGroup.setTexture(UNIFORM_NAME_SKYDISTANTLIGHT_LUT, skyDistantLightLUT);
|
|
983
|
+
bindGroup.setTexture(UNIFORM_NAME_FOG_SKYLIGHT_CUBEMAP, skyLightCubemap);
|
|
1008
984
|
}
|
|
1009
985
|
/**
|
|
1010
986
|
* @internal
|
|
@@ -1018,6 +994,10 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
1018
994
|
* quantity on the CPU so the HDR target stays near 1.0 and downstream passes need no unit
|
|
1019
995
|
* awareness. Legacy returns 1 so its uploads stay byte-identical.
|
|
1020
996
|
*/ static getPreExposure(ctx) {
|
|
997
|
+
// The voxelize pass writes the global illumination's caches, kept at a fixed exposure
|
|
998
|
+
if (ctx.renderPass?.type === RENDER_PASS_TYPE_VOXELIZE) {
|
|
999
|
+
return getGICacheExposure(ctx.scene.lightingMode);
|
|
1000
|
+
}
|
|
1021
1001
|
return ctx.scene.lightingMode === 'physical' ? ctx.camera.exposure : 1;
|
|
1022
1002
|
}
|
|
1023
1003
|
/**
|
|
@@ -1065,24 +1045,29 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
1065
1045
|
const strength = env.light.strength ?? 0;
|
|
1066
1046
|
return ctx.scene.lightingMode === 'physical' ? this.getPreExposure(ctx) / PHYSICAL_BAKE_EXPOSURE * strength : strength;
|
|
1067
1047
|
}
|
|
1068
|
-
/** @internal */ static setLightUniforms(bindGroup, ctx,
|
|
1048
|
+
/** @internal */ static setLightUniforms(bindGroup, ctx, clusteredLight) {
|
|
1069
1049
|
const envLightStrength = this.getEnvLightLuminance(ctx);
|
|
1050
|
+
const lightIndexTexture = clusteredLight.lightIndexTexture;
|
|
1070
1051
|
bindGroup.setValue('light', {
|
|
1071
1052
|
sunDir: ctx.sunLight ? ctx.sunLight.directionAndCutoff.xyz().scaleBy(-1) : this.defaultSunDir,
|
|
1072
|
-
clusterParams:
|
|
1073
|
-
countParams:
|
|
1053
|
+
clusterParams: clusteredLight.clusterParam,
|
|
1054
|
+
countParams: clusteredLight.countParam,
|
|
1055
|
+
globalLights: clusteredLight.globalLights,
|
|
1074
1056
|
envLightStrength,
|
|
1075
1057
|
envLightSpecularStrength: ctx.env.light.specularStrength ?? 1,
|
|
1076
|
-
lightIndexTexSize:
|
|
1077
|
-
|
|
1078
|
-
lightIndexTexture.height
|
|
1079
|
-
]),
|
|
1058
|
+
lightIndexTexSize: clusteredLight.lightIndexTexSize,
|
|
1059
|
+
clusterOrtho: clusteredLight.orthographic ? 1 : 0,
|
|
1080
1060
|
...ctx.screenSpaceShadowMask ? {
|
|
1081
|
-
numShadowLights:
|
|
1061
|
+
numShadowLights: clusteredLight.numShadowLights
|
|
1082
1062
|
} : {}
|
|
1083
1063
|
});
|
|
1084
|
-
bindGroup.setBuffer(UNIFORM_NAME_LIGHT_BUFFER, lightBuffer);
|
|
1085
|
-
|
|
1064
|
+
bindGroup.setBuffer(UNIFORM_NAME_LIGHT_BUFFER, clusteredLight.lightBuffer);
|
|
1065
|
+
if (this.usesClusterLightLists()) {
|
|
1066
|
+
bindGroup.setBuffer(UNIFORM_NAME_CLUSTER_GRID, clusteredLight.clusterGridBuffer);
|
|
1067
|
+
bindGroup.setBuffer(UNIFORM_NAME_CLUSTER_LIGHT_LIST, clusteredLight.lightListBuffer);
|
|
1068
|
+
} else {
|
|
1069
|
+
bindGroup.setTexture(UNIFORM_NAME_LIGHT_INDEX_TEXTURE, lightIndexTexture);
|
|
1070
|
+
}
|
|
1086
1071
|
if (ctx.screenSpaceShadowMask) {
|
|
1087
1072
|
// The mask array is declared for the clustered light pass whenever the flag is
|
|
1088
1073
|
// on (keyed into the global bind group hash). When there are no shadow lights
|
|
@@ -1090,11 +1075,396 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
1090
1075
|
// be bound: fall back to a 1x1 dummy.
|
|
1091
1076
|
bindGroup.setTexture(UNIFORM_NAME_SHADOW_MASK, ctx.shadowMaskTexture ?? this.getDummyShadowMask(ctx.device), fetchSampler('clamp_nearest'));
|
|
1092
1077
|
}
|
|
1078
|
+
if (ctx.transmissionThickness) {
|
|
1079
|
+
// Falls back to a white dummy, not the shadow mask's zero one: 0 encodes
|
|
1080
|
+
// maximum optical depth here, which would read as full transmission.
|
|
1081
|
+
bindGroup.setTexture(UNIFORM_NAME_TRANSMISSION_THICKNESS, ctx.transmissionThicknessTexture ?? this.getDummyTransmissionThickness(ctx.device), fetchSampler('clamp_nearest'));
|
|
1082
|
+
}
|
|
1093
1083
|
bindGroup.setTexture(UNIFORM_NAME_BAKED_SKY_MAP, ctx.scene.env.sky.getBakedSkyTexture(ctx));
|
|
1084
|
+
if (this.usesWaterCaustics(ctx)) {
|
|
1085
|
+
this.setWaterCausticUniforms(bindGroup, ctx);
|
|
1086
|
+
}
|
|
1094
1087
|
if (ctx.drawEnvLight) {
|
|
1095
1088
|
ctx.env.light.envLight.updateBindGroup(bindGroup, ctx);
|
|
1096
1089
|
}
|
|
1097
1090
|
}
|
|
1091
|
+
/**
|
|
1092
|
+
* Whether the pass being built modulates its light with the water caustic map.
|
|
1093
|
+
*
|
|
1094
|
+
* True for every light pass of a frame that produced a map; which light the
|
|
1095
|
+
* caustics actually attach to is decided per-fragment by
|
|
1096
|
+
* {@link ShaderHelper.calculateWaterCaustic}. Both the shader variant hash and
|
|
1097
|
+
* the global bind group hash fold this in, so a program that declares the
|
|
1098
|
+
* caustic uniforms is always paired with a bind group that supplies them.
|
|
1099
|
+
*
|
|
1100
|
+
* Deliberately a function of the draw context alone, with no material input.
|
|
1101
|
+
* The caustic uniforms sit in the global bind group, whose layout is built
|
|
1102
|
+
* once per {@link DrawContext.renderPassHash} and shared by every material in
|
|
1103
|
+
* the pass, so the declarations have to be uniform across the pass. A material
|
|
1104
|
+
* that must not receive caustics - water itself - opts out of the sampling
|
|
1105
|
+
* instead, through `receivesWaterCaustics()`; the uniforms stay declared and
|
|
1106
|
+
* simply go unread.
|
|
1107
|
+
*
|
|
1108
|
+
* @param ctx - Current draw context.
|
|
1109
|
+
* @returns True when the caustic uniforms take part in this pass.
|
|
1110
|
+
* @internal
|
|
1111
|
+
*/ /**
|
|
1112
|
+
* The scene's signed distance field as light-pass materials see it, or null
|
|
1113
|
+
* when it is off or cannot be built (WebGL2). Part of the light pass's global
|
|
1114
|
+
* bind group and shader variant hashes.
|
|
1115
|
+
* @internal
|
|
1116
|
+
*/ static getSceneSDF(ctx) {
|
|
1117
|
+
if (ctx.renderPass?.type !== RENDER_PASS_TYPE_LIGHT || !ctx.scene?.sdf.enabled) {
|
|
1118
|
+
return null;
|
|
1119
|
+
}
|
|
1120
|
+
return ctx.scene.sdf.global;
|
|
1121
|
+
}
|
|
1122
|
+
/**
|
|
1123
|
+
* Declares the scene signed distance field in a material's bind group, when
|
|
1124
|
+
* the scene has one for this draw ({@link ShaderHelper.getSceneSDF}).
|
|
1125
|
+
* Opt-in per material, so the textures it binds only count against the
|
|
1126
|
+
* shaders that read them. Bind it with {@link ShaderHelper.applySceneSDF}.
|
|
1127
|
+
*
|
|
1128
|
+
* @returns Whether it was declared; without it, fall back to something else.
|
|
1129
|
+
*/ static declareSceneSDF(scope, ctx, group) {
|
|
1130
|
+
const sdf = this.getSceneSDF(ctx);
|
|
1131
|
+
if (!sdf) {
|
|
1132
|
+
return false;
|
|
1133
|
+
}
|
|
1134
|
+
declareGlobalSDFUniforms(scope, group, sdf.levels);
|
|
1135
|
+
declareDynamicSDFUniforms(scope, group);
|
|
1136
|
+
sceneSDFLevels.set(scope.$builder, sdf.levels);
|
|
1137
|
+
return true;
|
|
1138
|
+
}
|
|
1139
|
+
/**
|
|
1140
|
+
* Binds what {@link ShaderHelper.declareSceneSDF} declared. The values change
|
|
1141
|
+
* every frame (the clipmap follows the camera, dynamic meshes move), so call
|
|
1142
|
+
* it from `applyUniforms` on every draw, not from `applyUniformValues`, which
|
|
1143
|
+
* only runs when the material itself changed.
|
|
1144
|
+
*/ static applySceneSDF(bindGroup, ctx) {
|
|
1145
|
+
this.getSceneSDF(ctx)?.applyBindGroup(bindGroup);
|
|
1146
|
+
}
|
|
1147
|
+
/** Whether {@link ShaderHelper.declareSceneSDF} declared the field in this program. */ static hasSceneSDF(scope) {
|
|
1148
|
+
return !!scope[GLOBAL_SDF_TEXTURE];
|
|
1149
|
+
}
|
|
1150
|
+
/**
|
|
1151
|
+
* Signed distance in world units from a world position to the scene's
|
|
1152
|
+
* geometry: static meshes and terrain from the global clipmap, merged with
|
|
1153
|
+
* the nearest dynamic meshes at their current transforms. Negative inside.
|
|
1154
|
+
* Only valid when {@link ShaderHelper.hasSceneSDF} is true.
|
|
1155
|
+
*
|
|
1156
|
+
* Near surfaces the value is accurate to a fraction of the finest voxel
|
|
1157
|
+
* (0.25 m by default); farther away it is a lower bound clamped to a few
|
|
1158
|
+
* voxels of the level covering the point, safe for sphere tracing.
|
|
1159
|
+
*/ static sampleSceneSDF(scope, worldPos) {
|
|
1160
|
+
const levels = sceneSDFLevels.get(scope.$builder);
|
|
1161
|
+
if (!levels) {
|
|
1162
|
+
throw new Error('ShaderHelper.sampleSceneSDF(): the scene SDF is not bound in this program');
|
|
1163
|
+
}
|
|
1164
|
+
return sampleSceneSDF(scope, worldPos, levels);
|
|
1165
|
+
}
|
|
1166
|
+
static usesWaterCaustics(ctx) {
|
|
1167
|
+
return !!ctx.waterCaustics && !!ctx.waterCausticLight && ctx.renderPass.type === RENDER_PASS_TYPE_LIGHT;
|
|
1168
|
+
}
|
|
1169
|
+
/**
|
|
1170
|
+
* Declares the global uniforms {@link ShaderHelper.calculateWaterCaustic} reads.
|
|
1171
|
+
*
|
|
1172
|
+
* Split out of the global uniform setup so a test can build a program that
|
|
1173
|
+
* exercises the sampling code without standing up a whole light pass.
|
|
1174
|
+
*
|
|
1175
|
+
* @param pb - Program builder whose global scope receives the uniforms.
|
|
1176
|
+
* @internal
|
|
1177
|
+
*/ /**
|
|
1178
|
+
* Warps a linear caustic-map coordinate so texels concentrate near the centre.
|
|
1179
|
+
*
|
|
1180
|
+
* The map covers a fixed footprint, so its world texel size is the range
|
|
1181
|
+
* divided by the resolution and a range large enough to light the scene is
|
|
1182
|
+
* already too coarse to resolve a caustic cell. This trades the edge of the
|
|
1183
|
+
* map, which is far from the camera, for the middle, which is not:
|
|
1184
|
+
*
|
|
1185
|
+
* ```
|
|
1186
|
+
* warp(u) = u (1 + k) / (1 + k |u|)
|
|
1187
|
+
* unwarp(w) = w / ((1 + k) - k |w|)
|
|
1188
|
+
* ```
|
|
1189
|
+
*
|
|
1190
|
+
* Texel density is `1 + k` times uniform at the centre and `1 / (1 + k)` times
|
|
1191
|
+
* it at the border, and `k = 0` is exactly the identity, so the pass can apply
|
|
1192
|
+
* this unconditionally. Both ends of `[-1, 1]` are fixed points, which is what
|
|
1193
|
+
* lets the warp sit inside the existing parameterisation without moving the
|
|
1194
|
+
* footprint the receiver gates on. The first derivative is continuous at the
|
|
1195
|
+
* centre; the second flips sign there, which is not visible in a pattern this
|
|
1196
|
+
* noisy.
|
|
1197
|
+
*
|
|
1198
|
+
* Applied per axis rather than radially: the photon grid, the map and the blur
|
|
1199
|
+
* are all axis-aligned, so a separable warp keeps photon `(i,j)` on texel
|
|
1200
|
+
* `(i,j)` for calm water and leaves the map's normalisation to 1.0 intact.
|
|
1201
|
+
*
|
|
1202
|
+
* @param scope - Current shader scope.
|
|
1203
|
+
* @param ndc - Linear map coordinate in `[-1, 1]`.
|
|
1204
|
+
* @param strength - Warp strength `k`; 0 is the identity.
|
|
1205
|
+
* @returns The warped coordinate, also in `[-1, 1]`.
|
|
1206
|
+
* @internal
|
|
1207
|
+
*/ static warpCausticNDC(scope, ndc, strength) {
|
|
1208
|
+
const pb = scope.$builder;
|
|
1209
|
+
const funcName = 'Z_warpCausticNDC';
|
|
1210
|
+
pb.func(funcName, [
|
|
1211
|
+
pb.vec2('ndc'),
|
|
1212
|
+
pb.float('k')
|
|
1213
|
+
], function() {
|
|
1214
|
+
this.$return(pb.div(pb.mul(this.ndc, pb.add(this.k, 1)), pb.add(pb.vec2(1), pb.mul(pb.abs(this.ndc), this.k))));
|
|
1215
|
+
});
|
|
1216
|
+
return pb.getGlobalScope()[funcName](ndc, strength);
|
|
1217
|
+
}
|
|
1218
|
+
/**
|
|
1219
|
+
* Inverse of {@link ShaderHelper.warpCausticNDC}.
|
|
1220
|
+
*
|
|
1221
|
+
* The denominator is `1` at the border and `1 + k` at the centre, so it stays
|
|
1222
|
+
* positive for any input the passes feed it - all of which come from `[-1, 1]`.
|
|
1223
|
+
*
|
|
1224
|
+
* @param scope - Current shader scope.
|
|
1225
|
+
* @param warped - Warped map coordinate in `[-1, 1]`.
|
|
1226
|
+
* @param strength - Warp strength `k`; 0 is the identity.
|
|
1227
|
+
* @returns The linear coordinate the warp came from.
|
|
1228
|
+
* @internal
|
|
1229
|
+
*/ static unwarpCausticNDC(scope, warped, strength) {
|
|
1230
|
+
const pb = scope.$builder;
|
|
1231
|
+
const funcName = 'Z_unwarpCausticNDC';
|
|
1232
|
+
pb.func(funcName, [
|
|
1233
|
+
pb.vec2('warped'),
|
|
1234
|
+
pb.float('k')
|
|
1235
|
+
], function() {
|
|
1236
|
+
this.$return(pb.div(this.warped, pb.sub(pb.vec2(pb.add(this.k, 1)), pb.mul(pb.abs(this.warped), this.k))));
|
|
1237
|
+
});
|
|
1238
|
+
return pb.getGlobalScope()[funcName](warped, strength);
|
|
1239
|
+
}
|
|
1240
|
+
/** @internal */ static declareWaterCausticUniforms(pb) {
|
|
1241
|
+
const scope = pb.getGlobalScope();
|
|
1242
|
+
const causticStruct = pb.defineStruct([
|
|
1243
|
+
pb.vec4('frameX'),
|
|
1244
|
+
pb.vec4('frameY'),
|
|
1245
|
+
pb.vec4('center'),
|
|
1246
|
+
pb.vec4('lightDir'),
|
|
1247
|
+
pb.vec4('params'),
|
|
1248
|
+
pb.vec4('extinction'),
|
|
1249
|
+
// Per water body, because the slice is shared but the water is not: each
|
|
1250
|
+
// has its own surface height, footprint and medium, and none of the three
|
|
1251
|
+
// means anything until a receiver has resolved which water is above it.
|
|
1252
|
+
pb.int('waterCount'),
|
|
1253
|
+
pb.vec4[MAX_CAUSTIC_WATERS]('waterRegions'),
|
|
1254
|
+
// (sigma_t.xyz, surface height)
|
|
1255
|
+
pb.vec4[MAX_CAUSTIC_WATERS]('waterMedia')
|
|
1256
|
+
]);
|
|
1257
|
+
scope[UNIFORM_NAME_CAUSTIC_PARAMS] = causticStruct().uniform(0);
|
|
1258
|
+
// The map is single mip and read bilinearly; clamping keeps receivers
|
|
1259
|
+
// outside the covered area reading the border rather than wrapping.
|
|
1260
|
+
scope[UNIFORM_NAME_CAUSTIC_MAP] = pb.tex2D().uniform(0).withSampler(getSamplerOptions('clamp_linear_nomip'));
|
|
1261
|
+
// Displaced surface height per water slot, on the same slice as the map.
|
|
1262
|
+
// Read with the map's uv, so it shares the map's sampling.
|
|
1263
|
+
scope[UNIFORM_NAME_CAUSTIC_HEIGHT_MAP] = pb.tex2D().uniform(0).withSampler(getSamplerOptions('clamp_linear_nomip'));
|
|
1264
|
+
}
|
|
1265
|
+
/**
|
|
1266
|
+
* Attenuation the water caustics apply to one light at a world position.
|
|
1267
|
+
*
|
|
1268
|
+
* Returns the product of two effects: the caustic pattern focused by the waves,
|
|
1269
|
+
* and the Beer-Lambert transmittance of the water column the light travelled
|
|
1270
|
+
* through to reach the point. The result is 1 for any light that is not the
|
|
1271
|
+
* caustic light, above the surface, and outside the water region, so the caller
|
|
1272
|
+
* can multiply unconditionally.
|
|
1273
|
+
*
|
|
1274
|
+
* The caustic light is identified at runtime by comparing directions rather
|
|
1275
|
+
* than by which pass is running. Shadow-casting lights reach the opaque queue
|
|
1276
|
+
* through the clustered path when the screen-space shadow mask is on (the
|
|
1277
|
+
* default) and through a per-light additive pass when it is off; only a runtime
|
|
1278
|
+
* test covers both. Two directional lights pointing the same way would both
|
|
1279
|
+
* pick up the caustics, but they are physically the same light.
|
|
1280
|
+
*
|
|
1281
|
+
* @param scope - Current shader scope.
|
|
1282
|
+
* @param worldPos - World position of the shaded fragment.
|
|
1283
|
+
* @param lightType - The light's type constant.
|
|
1284
|
+
* @param lightDirection - Direction the light travels, i.e. `directionAndCutoff.xyz`.
|
|
1285
|
+
* @param ctx - Current draw context.
|
|
1286
|
+
* @returns A `vec3` multiplier, or null when this pass has no caustics.
|
|
1287
|
+
*/ static calculateWaterCaustic(scope, worldPos, lightType, lightDirection, ctx) {
|
|
1288
|
+
if (!this.usesWaterCaustics(ctx)) {
|
|
1289
|
+
return null;
|
|
1290
|
+
}
|
|
1291
|
+
return this.sampleWaterCaustic(scope, worldPos, lightType, lightDirection);
|
|
1292
|
+
}
|
|
1293
|
+
/**
|
|
1294
|
+
* {@link ShaderHelper.calculateWaterCaustic} without the pass gate.
|
|
1295
|
+
*
|
|
1296
|
+
* The gate asks whether the *light pass* has caustics, which is the right
|
|
1297
|
+
* question for a material and the wrong one for a standalone full-screen pass
|
|
1298
|
+
* that declared the same uniforms itself. Such a pass has already decided it
|
|
1299
|
+
* has a map; this is the sampling half on its own.
|
|
1300
|
+
*
|
|
1301
|
+
* @param scope - Current shader scope.
|
|
1302
|
+
* @param worldPos - World position of the shaded fragment.
|
|
1303
|
+
* @param lightType - The light's type constant.
|
|
1304
|
+
* @param lightDirection - Direction the light travels, i.e. `directionAndCutoff.xyz`.
|
|
1305
|
+
* @param defocus - Whether to fade the pattern's contrast by how far the point
|
|
1306
|
+
* is from the map's focal depth. True for a receiver surface, which is what
|
|
1307
|
+
* that fade describes. False for a point inside the water column: see the
|
|
1308
|
+
* comment on the term itself.
|
|
1309
|
+
* @returns A `vec3` multiplier.
|
|
1310
|
+
* @internal
|
|
1311
|
+
*/ static sampleWaterCaustic(scope, worldPos, lightType, lightDirection, defocus = true) {
|
|
1312
|
+
const pb = scope.$builder;
|
|
1313
|
+
const funcName = defocus ? 'Z_calculateWaterCaustic' : 'Z_calculateWaterCausticVolume';
|
|
1314
|
+
pb.func(funcName, [
|
|
1315
|
+
pb.vec3('worldPos'),
|
|
1316
|
+
pb.int('lightType'),
|
|
1317
|
+
pb.vec3('lightDirection')
|
|
1318
|
+
], function() {
|
|
1319
|
+
this.$l.cu = this[UNIFORM_NAME_CAUSTIC_PARAMS];
|
|
1320
|
+
// Only the one light the map was built for casts caustics.
|
|
1321
|
+
this.$if(pb.or(pb.notEqual(this.lightType, LIGHT_TYPE_DIRECTIONAL), pb.lessThan(pb.dot(this.lightDirection, this.cu.lightDir.xyz), 0.999)), function() {
|
|
1322
|
+
this.$return(pb.vec3(1));
|
|
1323
|
+
});
|
|
1324
|
+
// Project into the map: frameX/frameY are orthonormal and perpendicular to
|
|
1325
|
+
// the light, so the same two dot products the splat pass used invert it.
|
|
1326
|
+
this.$l.rel = pb.sub(this.worldPos, this.cu.center.xyz);
|
|
1327
|
+
// Per-axis inverse extents: the slice is fitted to the water, so it is
|
|
1328
|
+
// only square in texels, not in meters.
|
|
1329
|
+
this.$l.mapNDC = pb.mul(pb.vec2(pb.dot(this.rel, this.cu.frameX.xyz), pb.dot(this.rel, this.cu.frameY.xyz)), pb.vec2(this.cu.frameX.w, this.cu.frameY.w));
|
|
1330
|
+
// Texels are concentrated towards the middle of the map, so the lookup
|
|
1331
|
+
// goes through the same warp the splat laid the photons out under.
|
|
1332
|
+
this.$l.warpedNDC = ShaderHelper.warpCausticNDC(this, this.mapNDC, this.cu.extinction.w);
|
|
1333
|
+
this.$l.uv = pb.add(pb.mul(this.warpedNDC, 0.5), pb.vec2(0.5));
|
|
1334
|
+
// The displaced surface height of each water slot on this fragment's own
|
|
1335
|
+
// sun ray, biased by CAUSTIC_HEIGHT_BIAS. The height map rasterises the
|
|
1336
|
+
// displaced surface into the same light-space slice as the pattern, so
|
|
1337
|
+
// the texel under this uv holds the surface the ray through this fragment
|
|
1338
|
+
// passed through - no entry point has to be estimated. Beyond the map the
|
|
1339
|
+
// sampler would repeat its border, which is some other ray's wave; the
|
|
1340
|
+
// surface is taken as flat there.
|
|
1341
|
+
this.$l.insideMap = pb.all(pb.lessThanEqual(pb.abs(this.mapNDC), pb.vec2(1)));
|
|
1342
|
+
// How far this fragment is towards the map border, and how much the map is
|
|
1343
|
+
// therefore trusted. Hoisted above the water loop because the wave height
|
|
1344
|
+
// is faded by it too, not just the pattern.
|
|
1345
|
+
//
|
|
1346
|
+
// Measured on the linear coordinate, not the warped one. The warp fixes
|
|
1347
|
+
// both ends of [-1, 1], so the border is in the same world place either
|
|
1348
|
+
// way, but the band between them is not: warping it would make its world
|
|
1349
|
+
// width depend on the warp strength, and the CPU sized it in meters.
|
|
1350
|
+
//
|
|
1351
|
+
// Distance is measured under an L4 norm rather than per-axis. A per-axis
|
|
1352
|
+
// fade is a rectangle in light space, and the sun tilts that rectangle, so
|
|
1353
|
+
// its border lands on the sea bed as straight lines - which the eye picks
|
|
1354
|
+
// out of a caustic pattern however wide the gradient is. An L2 circle has
|
|
1355
|
+
// no straight part anywhere but discards the 21% of the map outside its
|
|
1356
|
+
// inscribed disc; L4 gives up 7% instead and still curves everywhere.
|
|
1357
|
+
this.$l.ndcSq = pb.mul(this.mapNDC, this.mapNDC);
|
|
1358
|
+
this.$l.edgeDist = pb.sqrt(pb.sqrt(pb.dot(this.ndcSq, this.ndcSq)));
|
|
1359
|
+
this.$l.edge = pb.sub(1, pb.smoothStep(this.cu.params.w, 1, this.edgeDist));
|
|
1360
|
+
this.$l.waveHeights = pb.textureSampleLevel(this[UNIFORM_NAME_CAUSTIC_HEIGHT_MAP], this.uv, 0);
|
|
1361
|
+
// Find the water above this point. Several can qualify - a pool inside a
|
|
1362
|
+
// lake, water on terraces - and the one that matters is the lowest of
|
|
1363
|
+
// them, because that is the last surface the sun crossed on its way here.
|
|
1364
|
+
this.$l.depth = pb.float(-1);
|
|
1365
|
+
this.$l.medium = pb.vec3(0);
|
|
1366
|
+
this.$for(pb.int('i'), 0, MAX_CAUSTIC_WATERS, function() {
|
|
1367
|
+
this.$if(pb.greaterThanEqual(this.i, this.cu.waterCount), function() {
|
|
1368
|
+
this.$break();
|
|
1369
|
+
});
|
|
1370
|
+
this.$l.slotMedium = this.cu.waterMedia.at(this.i);
|
|
1371
|
+
// Where the sun ray that lights this point crossed that water's rest
|
|
1372
|
+
// plane, for the footprint test. The splat pass clips photons by that
|
|
1373
|
+
// same crossing, so the receiver must ask the same question: a point
|
|
1374
|
+
// under the footprint that the sun reaches from outside it saw no water
|
|
1375
|
+
// at all, and a point beyond the footprint that the sun reaches through
|
|
1376
|
+
// it did. Testing the point's own xz gets both wrong by the offset
|
|
1377
|
+
// depth / tan(elevation). Only the footprint is decided here; the depth
|
|
1378
|
+
// itself comes from the height map, which needs no crossing point.
|
|
1379
|
+
this.$l.slotFlat = pb.sub(this.slotMedium.w, this.worldPos.y);
|
|
1380
|
+
this.$l.slotPath = pb.mul(this.slotFlat, this.cu.lightDir.w);
|
|
1381
|
+
this.$l.slotEntry = pb.sub(this.worldPos.xz, pb.mul(this.cu.lightDir.xz, this.slotPath));
|
|
1382
|
+
this.$l.slotRegion = this.cu.waterRegions.at(this.i);
|
|
1383
|
+
this.$if(pb.and(pb.all(pb.greaterThanEqual(this.slotEntry, this.slotRegion.xy)), pb.all(pb.lessThanEqual(this.slotEntry, this.slotRegion.zw))), function() {
|
|
1384
|
+
// Depth under the *displaced* surface on this ray, which is what
|
|
1385
|
+
// the splat refracted the photons through. Gating on the rest plane
|
|
1386
|
+
// instead cut the caustics off at a flat line across any receiver
|
|
1387
|
+
// that pokes above it under a crest. Slot i lives in channel i.
|
|
1388
|
+
this.$l.slotMask = pb.vec4(pb.float(pb.equal(this.i, 0)), pb.float(pb.equal(this.i, 1)), pb.float(pb.equal(this.i, 2)), pb.float(pb.equal(this.i, 3)));
|
|
1389
|
+
this.$l.slotSample = pb.dot(this.waveHeights, this.slotMask);
|
|
1390
|
+
// A cleared texel inside the map means this ray never meets that
|
|
1391
|
+
// body's surface - it crosses the rest plane beyond the footprint,
|
|
1392
|
+
// or the surface is on the far side of the receiver - and the
|
|
1393
|
+
// receiver is dry as far as this body is concerned. Outside the map
|
|
1394
|
+
// nothing is known and the surface is taken as flat.
|
|
1395
|
+
this.$l.slotCovered = pb.or(pb.not(this.insideMap), pb.greaterThan(this.slotSample, 0.5));
|
|
1396
|
+
this.$if(this.slotCovered, function() {
|
|
1397
|
+
// Faded into the rest plane over the same band the pattern fades
|
|
1398
|
+
// on, rather than switched off at the border. Beyond the map the
|
|
1399
|
+
// surface is taken as flat, and exp(-sigma * depth) is convex, so
|
|
1400
|
+
// a hard switch makes the wavy side systematically brighter than
|
|
1401
|
+
// the flat side - E[exp(-sigma*w)] > 1 - and the step lands as a
|
|
1402
|
+
// mean brightness seam that grows with both the swell and the
|
|
1403
|
+
// turbidity. Ramping the wave to zero puts the two sides on the
|
|
1404
|
+
// same value at the border.
|
|
1405
|
+
this.$l.slotWave = pb.mul(pb.sub(this.slotSample, CAUSTIC_HEIGHT_BIAS), pb.float(this.insideMap), this.edge);
|
|
1406
|
+
this.$l.slotDepth = pb.sub(pb.add(this.slotMedium.w, this.slotWave), this.worldPos.y);
|
|
1407
|
+
this.$if(pb.greaterThan(this.slotDepth, 0), function() {
|
|
1408
|
+
this.$if(pb.or(pb.lessThan(this.depth, 0), pb.lessThan(this.slotDepth, this.depth)), function() {
|
|
1409
|
+
this.depth = this.slotDepth;
|
|
1410
|
+
this.medium = this.slotMedium.xyz;
|
|
1411
|
+
});
|
|
1412
|
+
});
|
|
1413
|
+
});
|
|
1414
|
+
});
|
|
1415
|
+
});
|
|
1416
|
+
this.$if(pb.lessThanEqual(this.depth, 0), function() {
|
|
1417
|
+
this.$return(pb.vec3(1));
|
|
1418
|
+
});
|
|
1419
|
+
this.$l.pathLength = pb.mul(this.depth, this.cu.lightDir.w);
|
|
1420
|
+
this.$l.transmittance = pb.exp(pb.neg(pb.mul(this.medium, this.pathLength)));
|
|
1421
|
+
this.$l.pattern = pb.textureSampleLevel(this[UNIFORM_NAME_CAUSTIC_MAP], this.uv, 0).x;
|
|
1422
|
+
if (defocus) {
|
|
1423
|
+
// Receivers away from the focal plane see a defocused, lower-contrast
|
|
1424
|
+
// pattern rather than a displaced one.
|
|
1425
|
+
this.$l.defocus = pb.div(1, pb.add(1, pb.mul(pb.abs(pb.sub(this.depth, this.cu.params.y)), this.cu.params.z)));
|
|
1426
|
+
} else {
|
|
1427
|
+
// A point inside the water column is not a receiver, and has no depth of
|
|
1428
|
+
// its own to be out of focus at - the volumetric march visits every
|
|
1429
|
+
// depth along the ray. Charging it the receiver's fade makes the light
|
|
1430
|
+
// shafts fade according to where the *sea bed* was told to be sharpest:
|
|
1431
|
+
// the open-ocean preset focuses the map at 26m for its bed, which at the
|
|
1432
|
+
// default defocus leaves a point 2m down at 26% contrast, and the shafts
|
|
1433
|
+
// dissolve into grain. The shafts instead take the pattern as the map
|
|
1434
|
+
// holds it and fade with the edge and the transmittance like everything
|
|
1435
|
+
// else in the column.
|
|
1436
|
+
this.$l.defocus = pb.float(1);
|
|
1437
|
+
}
|
|
1438
|
+
// None in water shallower than the minimum depth, faded in across it:
|
|
1439
|
+
// where the water thins out on a beach the surface is too coarse to
|
|
1440
|
+
// say where the water ends, and the pattern there would come from
|
|
1441
|
+
// photons that never reached it
|
|
1442
|
+
this.$l.minDepth = this.cu.extinction.x;
|
|
1443
|
+
this.$l.shallow = pb.smoothStep(pb.mul(this.minDepth, 0.5), pb.add(pb.mul(this.minDepth, 1.5), 1e-4), this.depth);
|
|
1444
|
+
this.$l.weight = pb.mul(this.cu.params.x, this.defocus, this.edge, this.shallow);
|
|
1445
|
+
this.$l.caustic = pb.add(1, pb.mul(pb.sub(this.pattern, 1), this.weight));
|
|
1446
|
+
// Soften the waterline so the transmittance does not switch on abruptly.
|
|
1447
|
+
this.$l.submerged = pb.smoothStep(0, 0.05, this.depth);
|
|
1448
|
+
this.$return(pb.mix(pb.vec3(1), pb.mul(pb.vec3(this.caustic), this.transmittance), this.submerged));
|
|
1449
|
+
});
|
|
1450
|
+
return pb.getGlobalScope()[funcName](worldPos, lightType, lightDirection);
|
|
1451
|
+
}
|
|
1452
|
+
/** @internal */ static setWaterCausticUniforms(bindGroup, ctx) {
|
|
1453
|
+
const uniforms = ctx.waterCausticUniforms;
|
|
1454
|
+
bindGroup.setValue(UNIFORM_NAME_CAUSTIC_PARAMS, {
|
|
1455
|
+
frameX: uniforms.frameX,
|
|
1456
|
+
frameY: uniforms.frameY,
|
|
1457
|
+
center: uniforms.center,
|
|
1458
|
+
lightDir: uniforms.lightDir,
|
|
1459
|
+
params: uniforms.params,
|
|
1460
|
+
extinction: uniforms.extinction,
|
|
1461
|
+
waterCount: uniforms.waterCount,
|
|
1462
|
+
waterRegions: uniforms.waterRegions,
|
|
1463
|
+
waterMedia: uniforms.waterMedia
|
|
1464
|
+
});
|
|
1465
|
+
bindGroup.setTexture(UNIFORM_NAME_CAUSTIC_MAP, ctx.waterCausticTexture, fetchSampler('clamp_linear_nomip'));
|
|
1466
|
+
bindGroup.setTexture(UNIFORM_NAME_CAUSTIC_HEIGHT_MAP, ctx.waterCausticHeightTexture, fetchSampler('clamp_linear_nomip'));
|
|
1467
|
+
}
|
|
1098
1468
|
/** @internal */ static setLightUniformsShadow(bindGroup, ctx, light) {
|
|
1099
1469
|
const shadowMapParams = ctx.shadowMapInfo.get(light);
|
|
1100
1470
|
this._lightUniformShadow.sunDir = ctx.sunLight ? ctx.sunLight.directionAndCutoff.xyz().scaleBy(-1) : this.defaultSunDir;
|
|
@@ -1114,6 +1484,9 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
1114
1484
|
this._lightUniformShadow.envLightSpecularStrength = ctx.env?.light.specularStrength ?? 1;
|
|
1115
1485
|
bindGroup.setValue('light', this._lightUniformShadow);
|
|
1116
1486
|
bindGroup.setTexture(UNIFORM_NAME_SHADOW_MAP, shadowMapParams.shadowMap, shadowMapParams.shadowMapSampler);
|
|
1487
|
+
if (this.usesWaterCaustics(ctx)) {
|
|
1488
|
+
this.setWaterCausticUniforms(bindGroup, ctx);
|
|
1489
|
+
}
|
|
1117
1490
|
bindGroup.setTexture(UNIFORM_NAME_BAKED_SKY_MAP, ctx.scene.env.sky.getBakedSkyTexture(ctx));
|
|
1118
1491
|
if (ctx.drawEnvLight) {
|
|
1119
1492
|
ctx.env.light.envLight.updateBindGroup(bindGroup, ctx);
|
|
@@ -1235,6 +1608,51 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
1235
1608
|
return scope.camera.elapsedTime;
|
|
1236
1609
|
}
|
|
1237
1610
|
/**
|
|
1611
|
+
* Declares the one camera field {@link ShaderHelper.getElapsedTime} reads.
|
|
1612
|
+
*
|
|
1613
|
+
* Wave generators animate off `camera.elapsedTime`, which the light and depth
|
|
1614
|
+
* passes provide as part of the full camera struct. A standalone program that
|
|
1615
|
+
* drives a wave generator - the caustics splat pass - has no such struct, so it
|
|
1616
|
+
* declares that single field under the same name. Any generator that starts
|
|
1617
|
+
* reading a second camera field will need it added here.
|
|
1618
|
+
*
|
|
1619
|
+
* The three padding floats round the struct up to 16 bytes. std140 aligns a
|
|
1620
|
+
* struct to a vec4 boundary, so a lone float leaves the GLSL block larger than
|
|
1621
|
+
* the buffer the engine sizes from the struct's own members - WebGL2 then
|
|
1622
|
+
* rejects every draw with "uniform buffer that is too small" and rasterises
|
|
1623
|
+
* nothing, while WebGPU is unaffected.
|
|
1624
|
+
*
|
|
1625
|
+
* @param scope - Global scope of the standalone program.
|
|
1626
|
+
* @param uniformGroup - Bind group the uniform belongs to.
|
|
1627
|
+
* @internal
|
|
1628
|
+
*/ static declareStandaloneCameraTime(scope, uniformGroup) {
|
|
1629
|
+
const pb = scope.$builder;
|
|
1630
|
+
scope.camera = pb.defineStruct([
|
|
1631
|
+
pb.float('elapsedTime'),
|
|
1632
|
+
pb.float('_pad0'),
|
|
1633
|
+
pb.float('_pad1'),
|
|
1634
|
+
pb.float('_pad2')
|
|
1635
|
+
])().uniform(uniformGroup);
|
|
1636
|
+
}
|
|
1637
|
+
/**
|
|
1638
|
+
* Uploads the value declared by {@link ShaderHelper.declareStandaloneCameraTime}.
|
|
1639
|
+
*
|
|
1640
|
+
* Must be the same clock the light pass uses, or a wave generator's caustics
|
|
1641
|
+
* would animate out of step with the surface they are refracted through.
|
|
1642
|
+
*
|
|
1643
|
+
* @param bindGroup - Bind group of the standalone program.
|
|
1644
|
+
* @param ctx - Current draw context; the current device when omitted, for
|
|
1645
|
+
* a program run outside drawing.
|
|
1646
|
+
* @internal
|
|
1647
|
+
*/ static setStandaloneCameraTime(bindGroup, ctx) {
|
|
1648
|
+
bindGroup.setValue('camera', {
|
|
1649
|
+
elapsedTime: (ctx?.device ?? getDevice()).frameInfo.elapsedOverall * 0.001,
|
|
1650
|
+
_pad0: 0,
|
|
1651
|
+
_pad1: 0,
|
|
1652
|
+
_pad2: 0
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
/**
|
|
1238
1656
|
* Gets the elapsed time since last frame in seconds
|
|
1239
1657
|
* @param scope - Current shader scope
|
|
1240
1658
|
* @returns The elapsed time since last frame in seconds
|
|
@@ -1297,6 +1715,20 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
1297
1715
|
return scope.light.countParams;
|
|
1298
1716
|
}
|
|
1299
1717
|
/**
|
|
1718
|
+
* Buffer indices of the clustered lights every fragment shades regardless of its
|
|
1719
|
+
* cluster, zero-terminated.
|
|
1720
|
+
* @internal
|
|
1721
|
+
*/ static getGlobalLights(scope) {
|
|
1722
|
+
return scope.light.globalLights;
|
|
1723
|
+
}
|
|
1724
|
+
/**
|
|
1725
|
+
* Nonzero when the clusters were sliced for an orthographic camera: depth slices
|
|
1726
|
+
* are then linear in view depth rather than exponential.
|
|
1727
|
+
* @internal
|
|
1728
|
+
*/ static getClusterOrthographic(scope) {
|
|
1729
|
+
return scope.light.clusterOrtho;
|
|
1730
|
+
}
|
|
1731
|
+
/**
|
|
1300
1732
|
* Number of shadow-casting lights at the head of the clustered light buffer.
|
|
1301
1733
|
* Only defined on the screen-space shadow mask path (ctx.screenSpaceShadowMask);
|
|
1302
1734
|
* a clustered light whose buffer index is `<= N` samples the shadow mask.
|
|
@@ -1337,10 +1769,65 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
1337
1769
|
}
|
|
1338
1770
|
return tex;
|
|
1339
1771
|
}
|
|
1772
|
+
/**
|
|
1773
|
+
* Lazily-created 1x1x1 rgba8unorm array texture used as the transmission
|
|
1774
|
+
* thickness binding fallback.
|
|
1775
|
+
*
|
|
1776
|
+
* @remarks
|
|
1777
|
+
* Filled with 1, not left at the zero-initialised default. The thickness
|
|
1778
|
+
* channel encodes `1 - opticalDepth / 5`, so 0 is *maximum* optical depth —
|
|
1779
|
+
* the worst possible value to fall back to, and the opposite of what the real
|
|
1780
|
+
* pass clears its unused channels to. {@link ShaderHelper.getDummyShadowMask}
|
|
1781
|
+
* gets away with zeroes because 0 reads as "fully shadowed" there.
|
|
1782
|
+
*
|
|
1783
|
+
* On the default pipeline this is never sampled: `ctx.transmissionThickness`
|
|
1784
|
+
* and the thickness module's `enabled` are computed from the same condition.
|
|
1785
|
+
* A custom `camera.renderPipeline` that omits the module can break that tie,
|
|
1786
|
+
* and this fallback is what keeps that from turning into full-strength
|
|
1787
|
+
* transmission across the whole screen.
|
|
1788
|
+
* @internal
|
|
1789
|
+
*/ static getDummyTransmissionThickness(device) {
|
|
1790
|
+
let tex = this._dummyTransmissionThickness;
|
|
1791
|
+
if (!tex || tex.disposed) {
|
|
1792
|
+
tex = device.createTexture2DArray('rgba8unorm', 1, 1, 1, {
|
|
1793
|
+
mipmapping: false
|
|
1794
|
+
});
|
|
1795
|
+
tex.name = 'DummyTransmissionThickness';
|
|
1796
|
+
tex.update(new Uint8Array([
|
|
1797
|
+
255,
|
|
1798
|
+
255,
|
|
1799
|
+
255,
|
|
1800
|
+
255
|
|
1801
|
+
]), 0, 0, 0, 1, 1, 1);
|
|
1802
|
+
this._dummyTransmissionThickness = tex;
|
|
1803
|
+
}
|
|
1804
|
+
return tex;
|
|
1805
|
+
}
|
|
1340
1806
|
/** @internal */ static getClusteredLightIndexTexture(scope) {
|
|
1341
1807
|
return scope[UNIFORM_NAME_LIGHT_INDEX_TEXTURE];
|
|
1342
1808
|
}
|
|
1343
1809
|
/**
|
|
1810
|
+
* Whether clusters hold variable-length light lists in storage buffers (WebGPU),
|
|
1811
|
+
* rather than 16 byte-sized slots in a texture.
|
|
1812
|
+
* @internal
|
|
1813
|
+
*/ static usesClusterLightLists() {
|
|
1814
|
+
return getDevice().type === 'webgpu';
|
|
1815
|
+
}
|
|
1816
|
+
/**
|
|
1817
|
+
* Per cluster, the offset of its light list and the number of lights in it, two
|
|
1818
|
+
* uints each. Only on {@link ShaderHelper.usesClusterLightLists}.
|
|
1819
|
+
* @internal
|
|
1820
|
+
*/ static getClusterGrid(scope) {
|
|
1821
|
+
return scope[UNIFORM_NAME_CLUSTER_GRID];
|
|
1822
|
+
}
|
|
1823
|
+
/**
|
|
1824
|
+
* Light buffer indices of every cluster's lights, one list after another. Only on
|
|
1825
|
+
* {@link ShaderHelper.usesClusterLightLists}.
|
|
1826
|
+
* @internal
|
|
1827
|
+
*/ static getClusterLightList(scope) {
|
|
1828
|
+
return scope[UNIFORM_NAME_CLUSTER_LIGHT_LIST];
|
|
1829
|
+
}
|
|
1830
|
+
/**
|
|
1344
1831
|
* Clustered shadow factor for a light, in `[0,1]` (1 = fully lit).
|
|
1345
1832
|
*
|
|
1346
1833
|
* - Lights whose clustered buffer index is greater than `numShadowLights` are not
|
|
@@ -1383,6 +1870,30 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
1383
1870
|
return pb.getGlobalScope()[funcName](lightIndex);
|
|
1384
1871
|
}
|
|
1385
1872
|
/**
|
|
1873
|
+
* Encoded light-space thickness for a light.
|
|
1874
|
+
*
|
|
1875
|
+
* @internal
|
|
1876
|
+
*/ static sampleTransmissionThickness(scope, lightIndex) {
|
|
1877
|
+
const pb = scope.$builder;
|
|
1878
|
+
const that = this;
|
|
1879
|
+
const funcName = 'Z_sampleTransmissionThickness';
|
|
1880
|
+
pb.func(funcName, [
|
|
1881
|
+
pb.int('lightIndex')
|
|
1882
|
+
], function() {
|
|
1883
|
+
this.$if(pb.greaterThan(this.lightIndex, that.getNumShadowLights(this)), function() {
|
|
1884
|
+
this.$return(pb.float(1));
|
|
1885
|
+
});
|
|
1886
|
+
this.$l.ordinal = pb.sub(this.lightIndex, 1);
|
|
1887
|
+
this.$l.layer = pb.div(this.ordinal, 4);
|
|
1888
|
+
this.$l.channel = pb.sub(this.ordinal, pb.mul(this.layer, 4));
|
|
1889
|
+
this.$l.uv = pb.div(pb.vec2(this.$builtins.fragCoord.xy), that.getRenderSize(this));
|
|
1890
|
+
this.$l.texel = pb.textureArraySampleLevel(this[UNIFORM_NAME_TRANSMISSION_THICKNESS], this.uv, this.layer, 0);
|
|
1891
|
+
this.$l.selector = pb.vec4(pb.float(pb.equal(this.channel, 0)), pb.float(pb.equal(this.channel, 1)), pb.float(pb.equal(this.channel, 2)), pb.float(pb.equal(this.channel, 3)));
|
|
1892
|
+
this.$return(pb.dot(this.texel, this.selector));
|
|
1893
|
+
});
|
|
1894
|
+
return pb.getGlobalScope()[funcName](lightIndex);
|
|
1895
|
+
}
|
|
1896
|
+
/**
|
|
1386
1897
|
* Gets the uniform variable that contains atmosphere parameters
|
|
1387
1898
|
* @param scope - Current shader scope
|
|
1388
1899
|
* @returns The atmosphere parameters
|
|
@@ -1505,16 +2016,21 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
1505
2016
|
}
|
|
1506
2017
|
}
|
|
1507
2018
|
/** @internal */ static getLightPositionAndRange(scope, lightIndex) {
|
|
1508
|
-
return
|
|
2019
|
+
return this.getLightVector(scope, lightIndex, 0);
|
|
1509
2020
|
}
|
|
1510
2021
|
/** @internal */ static getLightDirectionAndCutoff(scope, lightIndex) {
|
|
1511
|
-
return
|
|
2022
|
+
return this.getLightVector(scope, lightIndex, 1);
|
|
1512
2023
|
}
|
|
1513
2024
|
/** @internal */ static getLightColorAndIntensity(scope, lightIndex) {
|
|
1514
|
-
return
|
|
2025
|
+
return this.getLightVector(scope, lightIndex, 2);
|
|
1515
2026
|
}
|
|
1516
2027
|
/** @internal */ static getLightExtra(scope, lightIndex) {
|
|
1517
|
-
return
|
|
2028
|
+
return this.getLightVector(scope, lightIndex, 3);
|
|
2029
|
+
}
|
|
2030
|
+
/** Vector `k` of the 4 of a clustered light, read from the light buffer. */ static getLightVector(scope, lightIndex, k) {
|
|
2031
|
+
const pb = scope.$builder;
|
|
2032
|
+
const buffer = scope[UNIFORM_NAME_LIGHT_BUFFER];
|
|
2033
|
+
return buffer.at(pb.add(pb.mul(lightIndex, 4), k));
|
|
1518
2034
|
}
|
|
1519
2035
|
/**
|
|
1520
2036
|
* Sets the clip space position in vertex shader
|
|
@@ -1596,6 +2112,36 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
1596
2112
|
return pb.add(worldPos, pb.mul(unitNormal, offset));
|
|
1597
2113
|
}
|
|
1598
2114
|
/**
|
|
2115
|
+
* How far a rect light's shadow lookup can be trusted at a receiver, as a
|
|
2116
|
+
* weight for `mix(1, shadow, weight)`.
|
|
2117
|
+
*
|
|
2118
|
+
* @remarks
|
|
2119
|
+
* A rect light's shadow is a cube map rendered from its centre, but its
|
|
2120
|
+
* lighting integrates the whole panel. Where the surface turns away from the
|
|
2121
|
+
* centre the panel's far edge still lights it, while the centre already sees
|
|
2122
|
+
* the receiver occlude itself - so the centre's shadow cuts a hard, texel
|
|
2123
|
+
* stepped terminator through lit surface. The weight fades that lookup out as
|
|
2124
|
+
* the centre sinks to the receiver's horizon.
|
|
2125
|
+
*
|
|
2126
|
+
* The band is a fixed, narrow cosine range rather than one scaled by the
|
|
2127
|
+
* panel's size: the stepped edge only lives where the centre grazes the
|
|
2128
|
+
* surface, and its width is set by shadow bias and texel footprint, not by
|
|
2129
|
+
* the panel. A band as wide as the panel's angular radius also caught flat
|
|
2130
|
+
* receivers - a floor seen low from a large upright panel - and faded their
|
|
2131
|
+
* cast shadows, which have no terminator to hide.
|
|
2132
|
+
*
|
|
2133
|
+
* @param worldNormal - Geometric normal; the terminator follows the surface,
|
|
2134
|
+
* not its normal map.
|
|
2135
|
+
* @param posRange - Light position (xyz).
|
|
2136
|
+
* @internal
|
|
2137
|
+
*/ static getRectLightShadowWeight(scope, worldPos, worldNormal, posRange) {
|
|
2138
|
+
const pb = scope.$builder;
|
|
2139
|
+
const toCenter = pb.sub(posRange.xyz, worldPos);
|
|
2140
|
+
const unitNormal = pb.mul(worldNormal, pb.div(1, pb.max(pb.length(worldNormal), 1e-6)));
|
|
2141
|
+
const cosCenter = pb.div(pb.dot(unitNormal, toCenter), pb.max(pb.length(toCenter), 1e-6));
|
|
2142
|
+
return pb.smoothStep(0, 0.01, cosCenter);
|
|
2143
|
+
}
|
|
2144
|
+
/**
|
|
1599
2145
|
* Calculates shadow of current fragment
|
|
1600
2146
|
*
|
|
1601
2147
|
* @param scope - Shader scope
|
|
@@ -1623,17 +2169,7 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
1623
2169
|
// would self-occlude it before projecting into shadow space. Scaled per
|
|
1624
2170
|
// cascade, because each cascade has its own texel world size.
|
|
1625
2171
|
this.$l.biasedPos = that.applyShadowNormalOffset(shadowMapParams.lightType, this, this.worldPos, this.worldNormal, this.NoL, that.getShadowCascadeBiasScale(this, this.split));
|
|
1626
|
-
|
|
1627
|
-
this.$l.shadowVertex = pb.vec4();
|
|
1628
|
-
this.$for(pb.int('cascade'), 0, 4, function() {
|
|
1629
|
-
this.$if(pb.equal(this.cascade, this.split), function() {
|
|
1630
|
-
this.shadowVertex = that.calculateShadowSpaceVertex(this, pb.vec4(this.biasedPos, 1), this.cascade);
|
|
1631
|
-
this.$break();
|
|
1632
|
-
});
|
|
1633
|
-
});
|
|
1634
|
-
} else {
|
|
1635
|
-
this.$l.shadowVertex = that.calculateShadowSpaceVertex(this, pb.vec4(this.biasedPos, 1), this.split);
|
|
1636
|
-
}
|
|
2172
|
+
this.$l.shadowVertex = that.calculateShadowSpaceVertex(this, pb.vec4(this.biasedPos, 1), this.split);
|
|
1637
2173
|
this.$l.shadow = shadowMapParams.impl.computeShadowCSM(shadowMapParams, this, this.shadowVertex, this.NoL, this.split);
|
|
1638
2174
|
this.shadow = pb.clamp(this.shadow, 0, 1);
|
|
1639
2175
|
this.$l.shadowDistance = that.getShadowCameraParams(scope).w;
|
|
@@ -1648,7 +2184,7 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
1648
2184
|
// Normal offset bias - see the cascaded branch above.
|
|
1649
2185
|
this.$l.biasedPos = that.applyShadowNormalOffset(shadowMapParams.lightType, this, this.worldPos, this.worldNormal, this.NoL);
|
|
1650
2186
|
this.$l.shadowVertex = that.calculateShadowSpaceVertex(this, pb.vec4(this.biasedPos, 1));
|
|
1651
|
-
this.$l.shadow = shadowMapParams.impl.computeShadow(shadowMapParams, this, this.shadowVertex, this.NoL);
|
|
2187
|
+
this.$l.shadow = shadowMapParams.impl.computeShadow(shadowMapParams, this, this.shadowVertex, this.NoL, this.worldNormal);
|
|
1652
2188
|
this.shadow = pb.clamp(this.shadow, 0, 1);
|
|
1653
2189
|
this.$l.shadowDistance = that.getShadowCameraParams(scope).w;
|
|
1654
2190
|
this.shadow = pb.mix(this.shadow, 1, pb.smoothStep(pb.mul(this.shadowDistance, 0.8), this.shadowDistance, pb.distance(that.getCameraPosition(this), this.worldPos)));
|
|
@@ -1659,24 +2195,22 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
1659
2195
|
});
|
|
1660
2196
|
return pb.getGlobalScope()[funcName](worldPos, worldNormal, NoL);
|
|
1661
2197
|
}
|
|
1662
|
-
static applyFog(scope, worldPos, color, ctx) {
|
|
2198
|
+
static applyFog(scope, worldPos, color, ctx, premultiplied = true) {
|
|
1663
2199
|
const pb = scope.$builder;
|
|
1664
2200
|
const that = this;
|
|
1665
2201
|
if (ctx.materialFlags & MaterialVaryingFlags.APPLY_FOG) {
|
|
1666
|
-
const funcName = 'Z_applyFog';
|
|
2202
|
+
const funcName = premultiplied ? 'Z_applyFog' : 'Z_applyFogStraight';
|
|
1667
2203
|
pb.func(funcName, [
|
|
1668
2204
|
pb.vec3('worldPos'),
|
|
1669
2205
|
pb.vec4('color').inout()
|
|
1670
2206
|
], function() {
|
|
1671
|
-
this.$if(pb.notEqual(this.fog.additive, 0), function() {
|
|
1672
|
-
this.$return();
|
|
1673
|
-
});
|
|
1674
2207
|
this.$l.uv = pb.div(pb.vec2(this.$builtins.fragCoord.xy), that.getRenderSize(this));
|
|
1675
|
-
this.$l.fogging = calculateFog(this, this.fog.withAerialPerspective, this.fog.fogType, this.fog.atmosphereParams, this.fog.heightFogParams, this.uv, false, that.getCameraPosition(this).xyz, this.worldPos, this.fog.additive, this[UNIFORM_NAME_AERIALPERSPECTIVE_LUT], this[UNIFORM_NAME_SKYDISTANTLIGHT_LUT]);
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
2208
|
+
this.$l.fogging = calculateFog(this, this.fog.withAerialPerspective, this.fog.fogType, this.fog.atmosphereParams, this.fog.heightFogParams, this.uv, false, that.getCameraPosition(this).xyz, this.worldPos, this.fog.additive, this[UNIFORM_NAME_AERIALPERSPECTIVE_LUT], this[UNIFORM_NAME_SKYDISTANTLIGHT_LUT], this[UNIFORM_NAME_FOG_SKYLIGHT_CUBEMAP]);
|
|
2209
|
+
// Premultiplied color: attenuate by the fog transmittance and add the in-scattering weighted
|
|
2210
|
+
// by coverage. Additive light passes and additive blending (alpha 0) get attenuation only --
|
|
2211
|
+
// calculateFog already zeroes the in-scattering for the former. Straight (non-premultiplied)
|
|
2212
|
+
// color is scaled by coverage later, so the in-scattering is added unweighted.
|
|
2213
|
+
this.color = pb.vec4(pb.add(pb.mul(this.color.rgb, this.fogging.a), premultiplied ? pb.mul(this.fogging.rgb, this.color.a) : this.fogging.rgb), this.color.a);
|
|
1680
2214
|
});
|
|
1681
2215
|
scope[funcName](worldPos, color);
|
|
1682
2216
|
}
|
|
@@ -1809,7 +2343,7 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
1809
2343
|
*/ static sampleLinearDepth(scope, tex, uv, level) {
|
|
1810
2344
|
const pb = scope.$builder;
|
|
1811
2345
|
const depth = pb.textureSampleLevel(tex, uv, level);
|
|
1812
|
-
return
|
|
2346
|
+
return depth.r;
|
|
1813
2347
|
}
|
|
1814
2348
|
static samplePositionFromDepth(scope, depthTex, uv, mat, cameraNearFar) {
|
|
1815
2349
|
const pb = scope.$builder;
|
|
@@ -1860,10 +2394,19 @@ const UNIFORM_NAME_MORPH_INFO = 'Z_UniformMorphInfo';
|
|
|
1860
2394
|
});
|
|
1861
2395
|
return pb.getGlobalScope()[funcName](outputColor);
|
|
1862
2396
|
}
|
|
1863
|
-
/**
|
|
1864
|
-
|
|
2397
|
+
/**
|
|
2398
|
+
* Capacity of the clustered light buffer, excluding the unused slot 0.
|
|
2399
|
+
*
|
|
2400
|
+
* @remarks
|
|
2401
|
+
* On WebGPU the buffers are storage buffers grown to fit and light indices are 32
|
|
2402
|
+
* bits, so this only bounds the CPU-side work. On WebGL2 the buffer is 4 vec4 per
|
|
2403
|
+
* light in a uniform block, and 255 is the most a byte-sized cluster slot can address.
|
|
2404
|
+
* @internal
|
|
2405
|
+
*/ static getMaxClusterLights() {
|
|
2406
|
+
const device = getDevice();
|
|
2407
|
+
return device.type === 'webgpu' ? 65535 : 255;
|
|
1865
2408
|
}
|
|
1866
2409
|
}
|
|
1867
2410
|
|
|
1868
|
-
export { ShaderHelper };
|
|
2411
|
+
export { CAUSTIC_HEIGHT_BIAS, MAX_CAUSTIC_WATERS, ShaderHelper };
|
|
1869
2412
|
//# sourceMappingURL=helper.js.map
|