@zephyr3d/scene 0.9.27 → 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 +54 -0
- 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 +279 -55
- 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 +171 -1
- 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 +104 -178
- 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 +25436 -15990
- package/dist/index.js +65 -11
- 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/lambert.js +4 -1
- package/dist/material/lambert.js.map +1 -1
- package/dist/material/material.js +66 -4
- 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 +191 -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 +333 -191
- 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 +1261 -1438
- 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 +106 -106
- package/dist/posteffect/motionblur.js.map +1 -1
- package/dist/posteffect/postsss.js +572 -0
- package/dist/posteffect/postsss.js.map +1 -0
- package/dist/posteffect/sao.js +2 -6
- 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 +4 -13
- 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 +3 -2
- package/dist/render/abuffer_oit.js.map +1 -1
- package/dist/render/clipmap.js +179 -64
- 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 +25 -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 +5 -5
- 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 +5 -10
- package/dist/render/fbm_wavegenerator.js.map +1 -1
- package/dist/render/fft_wavegenerator.js +247 -157
- package/dist/render/fft_wavegenerator.js.map +1 -1
- package/dist/render/gerstner_wavegenerator.js +16 -38
- 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/instance_culling.js +499 -0
- package/dist/render/instance_culling.js.map +1 -0
- package/dist/render/lightpass.js +19 -8
- 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/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 +162 -11
- package/dist/render/render_queue.js.map +1 -1
- package/dist/render/renderer.js +10 -7
- package/dist/render/renderer.js.map +1 -1
- package/dist/render/rendergraph/blackboard.js +14 -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 +272 -55
- package/dist/render/rendergraph/forward_plus_builder.js.map +1 -1
- package/dist/render/rendergraph/frame_graph_context.js +41 -0
- package/dist/render/rendergraph/frame_graph_context.js.map +1 -0
- 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 +58 -158
- package/dist/render/underwater.js.map +1 -1
- 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 +134 -319
- package/dist/render/water_caustics.js.map +1 -1
- 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 +3 -3
- 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 +633 -268
- 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/ssgi.js +63 -0
- package/dist/shaders/ssgi.js.map +1 -0
- package/dist/shaders/ssgi_reprojection.js +118 -0
- package/dist/shaders/ssgi_reprojection.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 +32 -82
- package/dist/shaders/water.js.map +1 -1
- package/dist/shaders/water_medium.js +7 -21
- package/dist/shaders/water_medium.js.map +1 -1
- 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 +40 -2
- 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 +34 -6
- package/dist/utility/serialization/manager.js.map +1 -1
- package/dist/utility/serialization/scene/animation.js +46 -5
- 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 +87 -254
- 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 +1543 -264
- package/dist/utility/serialization/scene/water.js.map +1 -1
- package/dist/utility/serialization/types.js.map +1 -1
- package/dist/utility/shprojector.js +10 -22
- 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
- package/dist/animation/joint_dynamics/convex_collider.js +0 -320
- package/dist/animation/joint_dynamics/convex_collider.js.map +0 -1
|
@@ -2,25 +2,21 @@ import { Vector4, Vector3, Matrix4x4, REVERSE_Z } from '@zephyr3d/base';
|
|
|
2
2
|
import { drawFullscreenQuad } from './fullscreenquad.js';
|
|
3
3
|
import { fetchSampler } from '../utility/misc.js';
|
|
4
4
|
import { MAX_CAUSTIC_WATERS, ShaderHelper, CAUSTIC_HEIGHT_BIAS } from '../material/shader/helper.js';
|
|
5
|
+
import { warnUnsupported } from '../utility/unsupported.js';
|
|
5
6
|
|
|
6
7
|
/** Index of refraction of water relative to air. */ const WATER_IOR = 1.333;
|
|
7
8
|
/** Air-to-water eta used when refracting the sun ray at the surface. */ const WATER_ETA = 1 / WATER_IOR;
|
|
8
9
|
/**
|
|
9
|
-
* Minimum |sin(sun elevation)| for caustics to be generated
|
|
10
|
-
*
|
|
11
|
-
* simply switches off.
|
|
10
|
+
* Minimum |sin(sun elevation)| for caustics to be generated; below it the
|
|
11
|
+
* photon sweep degenerates and the pass switches off.
|
|
12
12
|
*/ const MIN_SUN_ELEVATION = 0.15;
|
|
13
13
|
/**
|
|
14
14
|
* Fixed-point steps taken to land a photon on the scene rather than on the
|
|
15
|
-
* focal plane. Two suffice for a
|
|
16
|
-
* moves; the third covers a steep slope. Beyond that the sequence has either
|
|
17
|
-
* converged or is oscillating between two surfaces.
|
|
15
|
+
* focal plane. Two suffice for a flat receiver; the third covers a steep slope.
|
|
18
16
|
*/ const SCENE_DEPTH_ITERATIONS = 3;
|
|
19
17
|
/**
|
|
20
|
-
* Photons per covered map texel the automatic grid size solves for.
|
|
21
|
-
*
|
|
22
|
-
* Measured against a converged (16x denser) map: four lands within about 4% of
|
|
23
|
-
* it, for a quarter of the photons full convergence would need.
|
|
18
|
+
* Photons per covered map texel the automatic grid size solves for. Four lands
|
|
19
|
+
* within about 4% of a converged (16x denser) map.
|
|
24
20
|
*/ const PHOTONS_PER_TEXEL = 4;
|
|
25
21
|
/** Automatic edge fade width, as a fraction of the map half-extent. */ const AUTO_FADE_FRACTION = 0.2;
|
|
26
22
|
/** Floor in meters under the automatic fade width. */ const AUTO_FADE_MIN_DISTANCE = 3;
|
|
@@ -33,36 +29,25 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
33
29
|
*/ const MAX_SLICE_ASPECT = 2;
|
|
34
30
|
/**
|
|
35
31
|
* Steps per octave the fitted half-extents are quantised to. Four costs at most
|
|
36
|
-
* 19% of linear resolution
|
|
37
|
-
* enough to pass as a slight change in sharpness rather than a pop.
|
|
32
|
+
* 19% of linear resolution and keeps a level change from reading as a pop.
|
|
38
33
|
*/ const SLICE_QUANTISE_STEPS = 4;
|
|
39
34
|
/** Smallest half-extent in meters a fitted slice may shrink to. */ const MIN_SLICE_EXTENT = 0.5;
|
|
40
35
|
/**
|
|
41
|
-
* Divisions of the half-extent the slice centre snaps to under a warp.
|
|
42
|
-
*
|
|
43
|
-
* A whole-texel shift is exact only on a linear map; under a warp it is a
|
|
44
|
-
* different number of texels at each position, so the map has to be resampled
|
|
45
|
-
* whenever the centre moves. Snapping coarsely holds the centre still for many
|
|
46
|
-
* frames and pays for one resample, which the resolve's neighbourhood clamp
|
|
47
|
-
* absorbs.
|
|
36
|
+
* Divisions of the half-extent the slice centre snaps to under a warp. A warped
|
|
37
|
+
* map must be resampled whenever the centre moves, so it snaps coarsely.
|
|
48
38
|
*/ const WARPED_CENTER_SNAP_DIVISIONS = 8;
|
|
49
39
|
/**
|
|
50
40
|
* How far past the map border the photon grid is laid out, as a fraction of the
|
|
51
41
|
* half-extent, on the sides where the range - not the water - is what bounds it.
|
|
52
42
|
*
|
|
53
|
-
* Horizontal displacement carries
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* outside feeds the border from both sides.
|
|
58
|
-
*
|
|
59
|
-
* Only the launch area grows - `_gridFraction` scales the per-photon weight and
|
|
60
|
-
* the solved grid size by it - so density per texel is unchanged and the cost is
|
|
61
|
-
* photon count. At the default warp this margin buys about 11% more photons.
|
|
43
|
+
* Horizontal displacement carries photons across the border both ways, so
|
|
44
|
+
* launching from a margin outside keeps the border density consistent.
|
|
45
|
+
* `_gridFraction` accounts for the larger launch area, so density per texel is
|
|
46
|
+
* unchanged.
|
|
62
47
|
*/ const GRID_BORDER_MARGIN = 0.15;
|
|
63
48
|
/**
|
|
64
49
|
* Concentrates a map coordinate towards the centre. CPU twin of
|
|
65
|
-
* `ShaderHelper.warpCausticNDC
|
|
50
|
+
* `ShaderHelper.warpCausticNDC`.
|
|
66
51
|
*
|
|
67
52
|
* @internal
|
|
68
53
|
*/ function warpCausticNDC(ndc, strength) {
|
|
@@ -83,21 +68,17 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
83
68
|
return range / Math.pow(2, steps / SLICE_QUANTISE_STEPS);
|
|
84
69
|
}
|
|
85
70
|
/**
|
|
86
|
-
* Vertices per side of the grid the height map rasterises the surface with
|
|
87
|
-
*
|
|
88
|
-
* Laid out in warped map space over the body's footprint, so each cell covers a
|
|
89
|
-
* few map texels at most and the linear height across a cell is well inside what
|
|
90
|
-
* a wave of any visible wavelength needs.
|
|
71
|
+
* Vertices per side of the grid the height map rasterises the surface with, laid
|
|
72
|
+
* out in warped map space over the body's footprint.
|
|
91
73
|
*/ const HEIGHT_GRID_SIZE = 256;
|
|
92
74
|
/**
|
|
93
75
|
* Shader for the photon splat pass.
|
|
94
76
|
*
|
|
95
|
-
*
|
|
96
|
-
* position it emits is where that photon lands. The fragment stage only deposits
|
|
77
|
+
* One vertex is one photon, emitted where it lands; the fragment stage deposits
|
|
97
78
|
* the weight, which additive blending accumulates.
|
|
98
79
|
*
|
|
99
|
-
* Returned as a descriptor
|
|
100
|
-
*
|
|
80
|
+
* Returned as a descriptor so it can be compiled against a bare
|
|
81
|
+
* {@link ProgramBuilder} in tests, without a device.
|
|
101
82
|
*
|
|
102
83
|
* @param waveGenerator - Supplies the surface displacement and normal.
|
|
103
84
|
* @internal
|
|
@@ -113,50 +94,37 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
113
94
|
scope.causticSplatParams = pb.vec4().uniform(0);
|
|
114
95
|
/** (grid size, 1 / grid size, 0, 0) */ scope.causticPhotonParams = pb.vec4().uniform(0);
|
|
115
96
|
if (sceneDepth) {
|
|
116
|
-
// The sun's
|
|
117
|
-
// scene: already orthographic, along the light, covering what the light
|
|
118
|
-
// reaches, and costing no extra geometry pass.
|
|
97
|
+
// The sun's shadow cascade, reused as an orthographic light-space depth map.
|
|
119
98
|
scope.causticSceneMatrix = pb.mat4().uniform(0);
|
|
120
99
|
scope.causticSceneMatrixInv = pb.mat4().uniform(0);
|
|
121
100
|
/** (cascade layer, 0, 0, 0) */ scope.causticSceneParams = pb.vec4().uniform(0);
|
|
122
|
-
//
|
|
123
|
-
// takes an ordinary sampler rather than the comparison one the lighting
|
|
124
|
-
// pass binds to the same texture.
|
|
101
|
+
// Read for its stored depth, not compared.
|
|
125
102
|
scope.causticSceneDepth = pb.tex2DArrayShadow().uniform(0);
|
|
126
103
|
}
|
|
127
|
-
//
|
|
128
|
-
//
|
|
104
|
+
// Wave generators animate from camera.elapsedTime, which this standalone
|
|
105
|
+
// pass supplies itself.
|
|
129
106
|
ShaderHelper.declareStandaloneCameraTime(scope, 0);
|
|
130
107
|
waveGenerator.setupUniforms(scope, 0);
|
|
131
108
|
};
|
|
132
109
|
return {
|
|
133
110
|
vertex (pb) {
|
|
134
|
-
// Photon ordinal
|
|
135
|
-
//
|
|
136
|
-
// size - see _updatePhotonLayout.
|
|
111
|
+
// Photon ordinal; the grid coordinate is derived from it, so the buffer
|
|
112
|
+
// serves any grid size.
|
|
137
113
|
this.$inputs.photonIndex = pb.float().attrib('position');
|
|
138
114
|
this.$outputs.photonWeight = pb.float();
|
|
139
115
|
setupUniforms(this);
|
|
140
116
|
pb.main(function() {
|
|
141
|
-
// Per-axis: the slice
|
|
142
|
-
// are independent and the map is only square in texels.
|
|
117
|
+
// Per-axis: the slice's two half-extents are independent.
|
|
143
118
|
this.$l.invRadius = pb.vec2(this.causticFrameX.w, this.causticFrameY.w);
|
|
144
119
|
this.$l.radius = pb.div(pb.vec2(1), this.invRadius);
|
|
145
120
|
this.$l.focalDepth = this.causticSplatParams.y;
|
|
146
121
|
this.$l.eta = this.causticSplatParams.z;
|
|
147
122
|
this.$l.L = this.causticLightDir.xyz;
|
|
148
123
|
this.$l.waterLevel = this.causticCenter.w;
|
|
149
|
-
// Grid position on the orthographic slice through the map centre.
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
// a tenth of it would leave most of the lit area with no photon at all,
|
|
154
|
-
// and a caustic value of zero puts the sun out.
|
|
155
|
-
//
|
|
156
|
-
// Laid out in warped map space and unwarped to reach the plane, so the
|
|
157
|
-
// grid is uniform in texels rather than in meters. That is what keeps
|
|
158
|
-
// photon (i,j) on texel (i,j) for calm water under any warp strength,
|
|
159
|
-
// and with it the map's normalisation to 1.0.
|
|
124
|
+
// Grid position on the orthographic slice through the map centre. The
|
|
125
|
+
// grid spans only the part of the slice the water casts through, and is
|
|
126
|
+
// uniform in warped map space so calm water keeps photon (i,j) on texel
|
|
127
|
+
// (i,j) and the map normalised to 1.0.
|
|
160
128
|
//
|
|
161
129
|
// Ordinal to grid coordinate: row is the quotient, column the remainder.
|
|
162
130
|
this.$l.gridSize = this.causticPhotonParams.x;
|
|
@@ -171,17 +139,11 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
171
139
|
this.$l.sweep = pb.div(pb.sub(this.waterLevel, this.planePos.y), this.L.y);
|
|
172
140
|
this.$l.surfaceXZ = pb.add(this.planePos, pb.mul(this.L, this.sweep));
|
|
173
141
|
this.$l.outside = pb.or(pb.any(pb.lessThan(this.surfaceXZ.xz, this.causticRegion.xy)), pb.any(pb.greaterThan(this.surfaceXZ.xz, this.causticRegion.zw)));
|
|
174
|
-
// Displaced surface point and the detail normal that bends the ray.
|
|
175
|
-
// wave generators sample with an explicit LOD,
|
|
176
|
-
// legal in a vertex shader.
|
|
142
|
+
// Displaced surface point and the detail normal that bends the ray. The
|
|
143
|
+
// wave generators sample with an explicit LOD, legal in a vertex shader.
|
|
177
144
|
//
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
// uniform in world xz - the horizontal displacement bunches the launch
|
|
181
|
-
// points by the surface Jacobian, over-weighting compressed crests by
|
|
182
|
-
// 1/J. Inverting the displacement per photon would fix that, but the
|
|
183
|
-
// inversion only converges where the surface does not fold, and a choppy
|
|
184
|
-
// FFT folds exactly at the crests it would matter for.
|
|
145
|
+
// Launching from the rest point over-weights compressed crests by 1/J;
|
|
146
|
+
// inverting the displacement does not converge where the surface folds.
|
|
185
147
|
this.$l.surfacePos = pb.vec3();
|
|
186
148
|
this.$l.coarseNormal = pb.vec3();
|
|
187
149
|
waveGenerator.calcVertexPositionAndNormal(this, this.surfaceXZ, this.surfacePos, this.coarseNormal);
|
|
@@ -192,54 +154,35 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
192
154
|
this.$l.refracted = pb.refract(this.L, this.normal, this.eta);
|
|
193
155
|
this.$l.refractedFlat = pb.refract(this.L, pb.vec3(0, 1, 0), this.eta);
|
|
194
156
|
this.$l.dir = pb.normalize(pb.add(pb.sub(this.refracted, this.refractedFlat), this.L));
|
|
195
|
-
// Intersect the horizontal focal plane below the surface
|
|
196
|
-
//
|
|
197
|
-
// produce an enormous or negative step.
|
|
157
|
+
// Intersect the horizontal focal plane below the surface; clamped so a
|
|
158
|
+
// grazing ray cannot produce an enormous or negative step.
|
|
198
159
|
this.$l.invDirY = pb.min(this.dir.y, -1e-4);
|
|
199
160
|
this.$l.targetY = pb.sub(this.waterLevel, this.focalDepth);
|
|
200
161
|
this.$l.hitT = pb.div(pb.sub(this.targetY, this.surfacePos.y), this.invDirY);
|
|
201
162
|
this.$l.hitPos = pb.add(this.surfacePos, pb.mul(this.dir, pb.max(this.hitT, 0)));
|
|
202
163
|
if (sceneDepth) {
|
|
203
|
-
// Land the photon on the scene
|
|
204
|
-
//
|
|
205
|
-
//
|
|
206
|
-
//
|
|
207
|
-
// plane at that height, repeat. Each step is one texture fetch and the
|
|
208
|
-
// sequence converges in two or three for any receiver that is smooth
|
|
209
|
-
// over the distance the guess moves. A march would need an order of
|
|
210
|
-
// magnitude more fetches, which the photon count cannot afford - at
|
|
211
|
-
// the default density a full-coverage 1024 map launches four million.
|
|
212
|
-
//
|
|
213
|
-
// Where the assumption fails - a silhouette, an overhang - the
|
|
214
|
-
// iteration lands on one of the two surfaces rather than diverging,
|
|
215
|
-
// because every step is still a valid intersection of the ray with
|
|
216
|
-
// some horizontal plane.
|
|
164
|
+
// Land the photon on the scene by fixed-point iteration: read the scene
|
|
165
|
+
// height under the current guess, re-intersect the ray with a
|
|
166
|
+
// horizontal plane at that height, repeat. One fetch per step; at a
|
|
167
|
+
// silhouette it lands on one of the two surfaces rather than diverging.
|
|
217
168
|
this.$l.probe = this.hitPos;
|
|
218
169
|
this.$l.layer = pb.int(this.causticSceneParams.x);
|
|
219
170
|
for(let i = 0; i < SCENE_DEPTH_ITERATIONS; i++){
|
|
220
171
|
this.$l[`clip${i}`] = pb.mul(this.causticSceneMatrix, pb.vec4(this.probe, 1));
|
|
221
172
|
this.$l[`ndc${i}`] = pb.div(this[`clip${i}`].xyz, this[`clip${i}`].w);
|
|
222
173
|
this.$l[`uv${i}`] = pb.add(pb.mul(this[`ndc${i}`].xy, 0.5), pb.vec2(0.5));
|
|
223
|
-
// Outside the cascade
|
|
224
|
-
// whatever the previous step produced, which for the first step is
|
|
225
|
-
// the focal plane hit the flat path would have used.
|
|
174
|
+
// Outside the cascade, keep the previous step's result.
|
|
226
175
|
this.$l[`inside${i}`] = pb.and(pb.all(pb.greaterThanEqual(this[`uv${i}`], pb.vec2(0))), pb.all(pb.lessThanEqual(this[`uv${i}`], pb.vec2(1))));
|
|
227
|
-
// texelFetch
|
|
228
|
-
//
|
|
229
|
-
//
|
|
230
|
-
// shadow-sampler texture, which is what gates this whole path.
|
|
176
|
+
// texelFetch, since filtered depths across a silhouette are wrong.
|
|
177
|
+
// WebGPU only - GLSL ES 3.0 forbids texelFetch on a shadow-sampler
|
|
178
|
+
// texture, which gates this whole path.
|
|
231
179
|
this.$l[`depth${i}`] = pb.textureArrayLoad(this.causticSceneDepth, pb.ivec2(pb.mul(this[`uv${i}`], this.causticSceneParams.y)), this.layer, pb.int(0));
|
|
232
|
-
// Back to a world position
|
|
233
|
-
// stored depth and the sample's uv are enough to place the point.
|
|
180
|
+
// Back to a world position through the orthographic cascade.
|
|
234
181
|
this.$l[`sceneNDC${i}`] = pb.vec3(this[`ndc${i}`].xy, REVERSE_Z ? this[`depth${i}`] : pb.sub(pb.mul(this[`depth${i}`], 2), 1));
|
|
235
182
|
this.$l[`sceneClip${i}`] = pb.mul(this.causticSceneMatrixInv, pb.vec4(this[`sceneNDC${i}`], 1));
|
|
236
183
|
this.$l[`scenePos${i}`] = pb.div(this[`sceneClip${i}`].xyz, this[`sceneClip${i}`].w);
|
|
237
|
-
// Re-intersect at the height just read
|
|
238
|
-
//
|
|
239
|
-
// point above that is not something the photon passed through. The
|
|
240
|
-
// rest level is the wrong bar - it would drop every photon landing
|
|
241
|
-
// on a receiver that pokes above the rest plane under a passing
|
|
242
|
-
// crest, cutting the caustics off along a flat line.
|
|
184
|
+
// Re-intersect at the height just read, accepted only below the
|
|
185
|
+
// *displaced* surface at this photon's entry (not the rest level).
|
|
243
186
|
this.$l[`t${i}`] = pb.div(pb.sub(this[`scenePos${i}`].y, this.surfacePos.y), this.invDirY);
|
|
244
187
|
this.$l[`next${i}`] = pb.add(this.surfacePos, pb.mul(this.dir, pb.max(this[`t${i}`], 0)));
|
|
245
188
|
// mix rather than select: the builder has no vec3 select.
|
|
@@ -254,19 +197,15 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
254
197
|
// Into warped space, where the map's texels live.
|
|
255
198
|
this.$l.hitWarped = ShaderHelper.warpCausticNDC(this, this.hitNDC, this.causticSplatParams.x);
|
|
256
199
|
this.$outputs.photonWeight = this.causticSplatParams.w;
|
|
257
|
-
// No per-backend y flip
|
|
258
|
-
// texture row on both backends, so the map the receiver samples with
|
|
259
|
-
// `ndc * 0.5 + 0.5` is the one this writes at clip `ndc`.
|
|
200
|
+
// No per-backend y flip here; the blur and resolve quads account for it.
|
|
260
201
|
this.$builtins.position = pb.vec4(this.hitWarped, 0, 1);
|
|
261
202
|
if (pb.getDevice().type !== 'webgpu') {
|
|
262
|
-
// GLSL
|
|
263
|
-
//
|
|
264
|
-
// one pixel.
|
|
203
|
+
// GLSL rasterises nothing unless gl_PointSize is written; WebGPU points
|
|
204
|
+
// are always one pixel.
|
|
265
205
|
this.$builtins.pointSize = 1;
|
|
266
206
|
}
|
|
267
207
|
this.$if(this.outside, function() {
|
|
268
|
-
// Park the photon outside the clip volume
|
|
269
|
-
// w at 1: a zero w would divide to NaN instead of clipping.
|
|
208
|
+
// Park the photon outside the clip volume; w stays 1 to avoid NaN.
|
|
270
209
|
this.$builtins.position = pb.vec4(-2, -2, 0, 1);
|
|
271
210
|
});
|
|
272
211
|
});
|
|
@@ -282,30 +221,17 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
282
221
|
/**
|
|
283
222
|
* Shader for the displaced surface height map.
|
|
284
223
|
*
|
|
285
|
-
* Rasterises the water surface
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
* overlaps itself in the light's view the last triangle wins rather than the two
|
|
293
|
-
* heights adding. The target is cleared first, and a texel the surface never
|
|
294
|
-
* reaches stays at zero, which the receiver reads as "no water on this ray".
|
|
224
|
+
* Rasterises the water surface into the light-space slice, one body per draw. A
|
|
225
|
+
* grid over the body's footprint in warped map space is swept along the light
|
|
226
|
+
* onto the rest plane, displaced by the wave generator, and projected back into
|
|
227
|
+
* the map. The fragment writes its height above the rest plane, offset by
|
|
228
|
+
* `CAUSTIC_HEIGHT_BIAS`, into this body's channel via a colour write mask (last
|
|
229
|
+
* triangle wins). A texel the surface never reaches stays zero, which the
|
|
230
|
+
* receiver reads as "no water on this ray".
|
|
295
231
|
*
|
|
296
|
-
* Rasterising
|
|
297
|
-
* the
|
|
298
|
-
*
|
|
299
|
-
* not fold. It also makes the footprint edge right - a receiver whose sun ray
|
|
300
|
-
* crosses the rest plane outside the footprint finds an empty texel and stays
|
|
301
|
-
* dry, where a flat stored height would put it under water that does not exist.
|
|
302
|
-
*
|
|
303
|
-
* The receiver looks this up with the very uv it uses for the caustic pattern:
|
|
304
|
-
* its fragment and the ray's crossing of the rest plane lie on one light ray,
|
|
305
|
-
* and the map's projection is invariant along the light.
|
|
306
|
-
*
|
|
307
|
-
* Everything runs in the vertex stage; the generators sample with an explicit
|
|
308
|
-
* LOD, which is what makes that legal.
|
|
232
|
+
* Rasterising handles horizontal displacement without inverting it, and keeps
|
|
233
|
+
* the footprint edge exact. The receiver looks this up with the same uv as the
|
|
234
|
+
* caustic pattern, since the projection is invariant along the light.
|
|
309
235
|
*
|
|
310
236
|
* @param waveGenerator - Supplies the surface displacement of this body.
|
|
311
237
|
* @internal
|
|
@@ -330,10 +256,8 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
330
256
|
this.$l.radius = pb.div(pb.vec2(1), this.invRadius);
|
|
331
257
|
this.$l.L = this.causticLightDir.xyz;
|
|
332
258
|
this.$l.waterLevel = this.causticCenter.w;
|
|
333
|
-
// Ordinal to grid coordinate, (gridSize + 1) vertices per side.
|
|
334
|
-
//
|
|
335
|
-
// that is an exact multiple of the row length must not round down into
|
|
336
|
-
// the previous row.
|
|
259
|
+
// Ordinal to grid coordinate, (gridSize + 1) vertices per side. The +0.5
|
|
260
|
+
// keeps the inexact reciprocal from rounding into the previous row.
|
|
337
261
|
this.$l.row = pb.floor(pb.mul(pb.add(this.$inputs.vertexIndex, 0.5), this.causticHeightParams.z));
|
|
338
262
|
this.$l.col = pb.sub(this.$inputs.vertexIndex, pb.mul(this.row, pb.add(this.causticHeightParams.y, 1)));
|
|
339
263
|
this.$l.gridUV = pb.mul(pb.vec2(this.col, this.row), this.causticHeightParams.w);
|
|
@@ -344,33 +268,27 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
344
268
|
// Along the light onto the rest plane: the rest point of this vertex.
|
|
345
269
|
this.$l.sweep = pb.div(pb.sub(this.waterLevel, this.planePos.y), this.L.y);
|
|
346
270
|
this.$l.surfaceXZ = pb.add(this.planePos, pb.mul(this.L, this.sweep));
|
|
347
|
-
//
|
|
348
|
-
// interpolated, which fades the edge over one grid cell.
|
|
271
|
+
// Footprint test, interpolated so the edge fades over one grid cell.
|
|
349
272
|
this.$outputs.inside = pb.float(pb.and(pb.all(pb.greaterThanEqual(this.surfaceXZ.xz, this.causticRegion.xy)), pb.all(pb.lessThanEqual(this.surfaceXZ.xz, this.causticRegion.zw))));
|
|
350
273
|
// Where the surface carried this rest point, and how high it is there.
|
|
351
274
|
this.$l.surfacePos = pb.vec3();
|
|
352
275
|
this.$l.surfaceNormal = pb.vec3();
|
|
353
276
|
waveGenerator.calcVertexPositionAndNormal(this, this.surfaceXZ, this.surfacePos, this.surfaceNormal);
|
|
354
277
|
this.$outputs.height = pb.sub(this.surfacePos.y, this.waterLevel);
|
|
355
|
-
// The displaced point back into the map,
|
|
356
|
-
// photon hit: two dot products, then the warp.
|
|
278
|
+
// The displaced point back into the map, as the splat projects a hit.
|
|
357
279
|
this.$l.rel = pb.sub(this.surfacePos, this.causticCenter.xyz);
|
|
358
280
|
this.$l.hitNDC = pb.mul(pb.vec2(pb.dot(this.rel, this.causticFrameX.xyz), pb.dot(this.rel, this.causticFrameY.xyz)), this.invRadius);
|
|
359
281
|
this.$l.hitWarped = ShaderHelper.warpCausticNDC(this, this.hitNDC, this.causticHeightParams.x);
|
|
360
|
-
// The receiver reads the map at `v = 0.5 + 0.5 * ndc.y`. On
|
|
361
|
-
//
|
|
362
|
-
//
|
|
363
|
-
// looks. The splat needs no such flip because its map reaches the
|
|
364
|
-
// receiver through the blur and resolve quads, each of which flips it
|
|
365
|
-
// once more on WebGPU; this map goes straight.
|
|
282
|
+
// The receiver reads the map at `v = 0.5 + 0.5 * ndc.y`. On WebGPU clip
|
|
283
|
+
// +1 is the first row, so the surface is drawn flipped. The splat needs
|
|
284
|
+
// no flip because its map passes through the blur and resolve quads.
|
|
366
285
|
this.$builtins.position = pb.getDevice().type === 'webgpu' ? pb.vec4(this.hitWarped.x, pb.neg(this.hitWarped.y), 0, 1) : pb.vec4(this.hitWarped, 0, 1);
|
|
367
286
|
});
|
|
368
287
|
},
|
|
369
288
|
fragment (pb) {
|
|
370
289
|
this.$outputs.outColor = pb.vec4();
|
|
371
290
|
pb.main(function() {
|
|
372
|
-
// Outside the footprint nothing is written
|
|
373
|
-
// reading as "no surface" rather than as a surface at height zero.
|
|
291
|
+
// Outside the footprint nothing is written, so the texel reads "no surface".
|
|
374
292
|
this.$if(pb.lessThan(this.$inputs.inside, 0.5), function() {
|
|
375
293
|
pb.discard();
|
|
376
294
|
});
|
|
@@ -383,9 +301,8 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
383
301
|
/**
|
|
384
302
|
* Shader for the caustic map blur.
|
|
385
303
|
*
|
|
386
|
-
* Four bilinear taps at the texel corners average a 2x2 neighbourhood
|
|
387
|
-
*
|
|
388
|
-
* the accumulation pass survives any number of iterations.
|
|
304
|
+
* Four bilinear taps at the texel corners average a 2x2 neighbourhood; weights
|
|
305
|
+
* sum to one, preserving the "calm water reads 1.0" normalisation.
|
|
389
306
|
*
|
|
390
307
|
* @internal
|
|
391
308
|
*/ function createCausticBlurShader() {
|
|
@@ -413,15 +330,12 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
413
330
|
/**
|
|
414
331
|
* Shader for the temporal resolve.
|
|
415
332
|
*
|
|
416
|
-
* Reprojection is exact and matrix-free
|
|
417
|
-
*
|
|
418
|
-
*
|
|
419
|
-
* two dot products. Photons travel along the light and the slice is normal to
|
|
420
|
-
* it, so the plane the point is taken on does not matter.
|
|
333
|
+
* Reprojection is exact and matrix-free: both frames are orthographic slices
|
|
334
|
+
* perpendicular to the light, so a texel maps to the world with two scaled axes
|
|
335
|
+
* and into the previous frame with two dot products.
|
|
421
336
|
*
|
|
422
337
|
* Map NDC relates to texture coordinate by the receiver's `ndc * 0.5 + 0.5`
|
|
423
|
-
* (see `ShaderHelper.calculateWaterCaustic`)
|
|
424
|
-
* independent of the clip-space y flip the splat has to apply.
|
|
338
|
+
* (see `ShaderHelper.calculateWaterCaustic`).
|
|
425
339
|
*
|
|
426
340
|
* @internal
|
|
427
341
|
*/ function createCausticResolveShader() {
|
|
@@ -448,10 +362,8 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
448
362
|
pb.main(function() {
|
|
449
363
|
this.$l.texel = this.causticResolveParams.y;
|
|
450
364
|
this.$l.current = pb.textureSampleLevel(this.causticCurrent, this.$inputs.uv, 0).x;
|
|
451
|
-
//
|
|
452
|
-
//
|
|
453
|
-
// an animated pattern: where the waves have moved on, the neighbourhood
|
|
454
|
-
// no longer covers the old value.
|
|
365
|
+
// Neighbourhood range of the current frame; history is clamped into it
|
|
366
|
+
// so a long blend can follow an animated pattern.
|
|
455
367
|
this.$l.mn = this.current;
|
|
456
368
|
this.$l.mx = this.current;
|
|
457
369
|
for(let dy = -1; dy <= 1; dy++){
|
|
@@ -464,10 +376,8 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
464
376
|
this.mx = pb.max(this.mx, this.s);
|
|
465
377
|
}
|
|
466
378
|
}
|
|
467
|
-
// This texel's world point
|
|
468
|
-
//
|
|
469
|
-
// leaves it to reach the world and re-enters it to land on the history:
|
|
470
|
-
// the two frames may differ in slice and in warp strength alike.
|
|
379
|
+
// This texel's world point, then its place in the previous frame; the
|
|
380
|
+
// frames may differ in slice and warp strength.
|
|
471
381
|
this.$l.warpedNDC = pb.sub(pb.mul(this.$inputs.uv, 2), pb.vec2(1));
|
|
472
382
|
this.$l.ndc = ShaderHelper.unwarpCausticNDC(this, this.warpedNDC, this.causticResolveParams.z);
|
|
473
383
|
this.$l.radius = pb.div(pb.vec2(1), pb.vec2(this.causticFrameX.w, this.causticFrameY.w));
|
|
@@ -491,11 +401,9 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
491
401
|
* One photon is emitted per texel of a uniform grid laid out on the same
|
|
492
402
|
* orthographic light-space slice the map covers. Each photon is refracted at the
|
|
493
403
|
* water surface, intersected with the scene (or a horizontal focal plane) below
|
|
494
|
-
* it, and splatted additively at the hit position.
|
|
495
|
-
*
|
|
496
|
-
*
|
|
497
|
-
* `(i,j)` and the map converges to a uniform 1.0 - which the receiver treats as
|
|
498
|
-
* "no caustics".
|
|
404
|
+
* it, and splatted additively at the hit position. With the flat-surface
|
|
405
|
+
* deflection removed, calm water maps photon `(i,j)` onto texel `(i,j)` and the
|
|
406
|
+
* map converges to a uniform 1.0 - which the receiver treats as "no caustics".
|
|
499
407
|
*
|
|
500
408
|
* @internal
|
|
501
409
|
*/ class WaterCausticsRenderer {
|
|
@@ -504,22 +412,13 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
504
412
|
/** One per water slot, differing only in which channel they let through. */ _heightStates;
|
|
505
413
|
_heightParams;
|
|
506
414
|
/** The surface grid the height map is rasterised with; built once. */ _heightLayout;
|
|
507
|
-
_heightVertexBuffer;
|
|
508
|
-
_heightIndexBuffer;
|
|
509
415
|
_heightIndexCount;
|
|
510
416
|
_blurProgram;
|
|
511
417
|
_blurBindGroup;
|
|
512
418
|
_resolveProgram;
|
|
513
419
|
_resolveBindGroup;
|
|
514
420
|
_resolveStates;
|
|
515
|
-
/**
|
|
516
|
-
* Slice parameters the last map committed as history was built with, per
|
|
517
|
-
* camera.
|
|
518
|
-
*
|
|
519
|
-
* Per camera because the history textures are, and this has to describe the
|
|
520
|
-
* exact map the resolve is about to reproject; one shared entry would make a
|
|
521
|
-
* second viewport reproject through the first one's slice.
|
|
522
|
-
*/ _prevSlices;
|
|
421
|
+
/** Slice parameters of the last map committed as history, per camera. */ _prevSlices;
|
|
523
422
|
_resolveParams;
|
|
524
423
|
_photonLayout;
|
|
525
424
|
/** Photons the buffer can serve; it grows and is never rebuilt smaller. */ _photonCapacity;
|
|
@@ -556,8 +455,6 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
556
455
|
this._heightStates = [];
|
|
557
456
|
this._heightParams = new Vector4();
|
|
558
457
|
this._heightLayout = null;
|
|
559
|
-
this._heightVertexBuffer = null;
|
|
560
|
-
this._heightIndexBuffer = null;
|
|
561
458
|
this._heightIndexCount = 0;
|
|
562
459
|
this._blurProgram = null;
|
|
563
460
|
this._blurBindGroup = null;
|
|
@@ -618,9 +515,8 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
618
515
|
return info.renderable && info.filterable ? 'r16f' : 'rgba16f';
|
|
619
516
|
}
|
|
620
517
|
/**
|
|
621
|
-
* Storage format for the displaced surface height map
|
|
622
|
-
* slot
|
|
623
|
-
* millimetre at any plausible level.
|
|
518
|
+
* Storage format for the displaced surface height map: one channel per water
|
|
519
|
+
* slot.
|
|
624
520
|
*/ static getHeightMapFormat() {
|
|
625
521
|
return 'rgba16f';
|
|
626
522
|
}
|
|
@@ -659,8 +555,7 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
659
555
|
const device = ctx.device;
|
|
660
556
|
this._updateUniforms(ctx, waters, light, map.width);
|
|
661
557
|
const sceneDepth = this._anySceneDepth(waters) ? this._resolveSceneDepth(ctx, light) : null;
|
|
662
|
-
// The height map first
|
|
663
|
-
// needs it whatever the splat produces.
|
|
558
|
+
// The height map first; it depends on the slice alone.
|
|
664
559
|
device.pushDeviceStates();
|
|
665
560
|
device.setFramebuffer(createFramebuffer(heights));
|
|
666
561
|
device.setViewport(null);
|
|
@@ -673,15 +568,12 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
673
568
|
device.popDeviceStates();
|
|
674
569
|
device.pushDeviceStates();
|
|
675
570
|
device.setFramebuffer(createFramebuffer(map));
|
|
676
|
-
//
|
|
677
|
-
// straight after the shadow maps, whose targets are a different size.
|
|
571
|
+
// Viewport and scissor survive a framebuffer change; reset them.
|
|
678
572
|
device.setViewport(null);
|
|
679
573
|
device.setScissor(null);
|
|
680
574
|
device.clearFrameBuffer(Vector4.zero(), null, null);
|
|
681
575
|
device.setRenderStates(this._getSplatStates(device));
|
|
682
|
-
// One draw per water body, accumulating into the one map.
|
|
683
|
-
// slice, so a receiver still resolves the pattern with a single lookup; the
|
|
684
|
-
// footprint, surface height and wave generator are per-draw state.
|
|
576
|
+
// One draw per water body, accumulating into the one shared-slice map.
|
|
685
577
|
for(let i = 0; i < waters.length; i++){
|
|
686
578
|
this._splatOne(device, ctx, waters[i], i, map, sceneDepth);
|
|
687
579
|
}
|
|
@@ -690,14 +582,12 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
690
582
|
}
|
|
691
583
|
/**
|
|
692
584
|
* Writes one body's displaced surface height into its channel of the height
|
|
693
|
-
* map.
|
|
694
|
-
* generator, rest level and footprint.
|
|
585
|
+
* map.
|
|
695
586
|
* @internal
|
|
696
587
|
*/ _renderHeightsOne(device, ctx, water, index) {
|
|
697
588
|
const material = water.material;
|
|
698
589
|
const waveGenerator = material.waveGenerator;
|
|
699
|
-
// The grid covers this body's footprint
|
|
700
|
-
// the map does not reach has nothing to draw.
|
|
590
|
+
// The grid covers this body's footprint.
|
|
701
591
|
this._updateGridBounds(water);
|
|
702
592
|
if (!(this._gridFraction > 0)) {
|
|
703
593
|
return;
|
|
@@ -754,8 +644,6 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
754
644
|
}
|
|
755
645
|
const vertexBuffer = device.createVertexBuffer('position_f32', ordinals);
|
|
756
646
|
const indexBuffer = device.createIndexBuffer(indices);
|
|
757
|
-
this._heightVertexBuffer = vertexBuffer;
|
|
758
|
-
this._heightIndexBuffer = indexBuffer;
|
|
759
647
|
this._heightLayout = device.createVertexLayout({
|
|
760
648
|
vertexBuffers: [
|
|
761
649
|
{
|
|
@@ -769,16 +657,13 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
769
657
|
/** @internal */ _splatOne(device, ctx, water, index, map, sceneDepth) {
|
|
770
658
|
const material = water.material;
|
|
771
659
|
const waveGenerator = material.waveGenerator;
|
|
772
|
-
// Photons are laid out over this body's own footprint
|
|
773
|
-
// and the fraction of the map they cover are per body, not per map.
|
|
660
|
+
// Photons are laid out over this body's own footprint.
|
|
774
661
|
this._updateGridBounds(water);
|
|
775
662
|
if (!(this._gridFraction > 0)) {
|
|
776
663
|
return;
|
|
777
664
|
}
|
|
778
665
|
const photonGrid = this._resolvePhotonGrid(material.causticsPhotonResolution, map.width);
|
|
779
666
|
this._updatePhotonLayout(device, photonGrid);
|
|
780
|
-
// Scene-depth intersection is per material, but the program is per draw, so
|
|
781
|
-
// a body that has it off simply compiles the flat variant.
|
|
782
667
|
const useSceneDepth = !!sceneDepth && material.causticsSceneDepth;
|
|
783
668
|
const splat = this._getSplatProgram(device, waveGenerator, useSceneDepth);
|
|
784
669
|
// A bind group per slot; see _getSplatBindGroup.
|
|
@@ -788,8 +673,6 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
788
673
|
bindGroup.setValue('causticSceneMatrixInv', sceneDepth.matrixInv);
|
|
789
674
|
this._sceneDepthParams.setXYZW(sceneDepth.layer, sceneDepth.size, 0, 0);
|
|
790
675
|
bindGroup.setValue('causticSceneParams', this._sceneDepthParams);
|
|
791
|
-
// Point sampling: the stored value is a depth, and averaging two depths
|
|
792
|
-
// across a silhouette names a surface that is not there.
|
|
793
676
|
bindGroup.setTexture('causticSceneDepth', sceneDepth.texture, fetchSampler('clamp_nearest_nomip'));
|
|
794
677
|
}
|
|
795
678
|
bindGroup.setValue('causticFrameX', this._uniforms.frameX);
|
|
@@ -800,10 +683,8 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
800
683
|
bindGroup.setValue('causticLightDir', this._uniforms.lightDir);
|
|
801
684
|
bindGroup.setValue('causticRegion', material.region);
|
|
802
685
|
bindGroup.setValue('causticGridBounds', this._gridBounds);
|
|
803
|
-
//
|
|
804
|
-
//
|
|
805
|
-
// grid fraction is part of that: concentrating the same photons onto a
|
|
806
|
-
// smaller part of the map raises the density there by exactly its inverse.
|
|
686
|
+
// Scale the deposit by covered texels per photon so a flat surface
|
|
687
|
+
// integrates to exactly 1.
|
|
807
688
|
const photonWeight = this._gridFraction * map.width * map.height / (photonGrid * photonGrid);
|
|
808
689
|
this._splatParams.setXYZW(this._warp, material.causticsDepth, WATER_ETA, photonWeight);
|
|
809
690
|
bindGroup.setValue('causticSplatParams', this._splatParams);
|
|
@@ -817,12 +698,8 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
817
698
|
device.draw('point-list', 0, this._photonCount);
|
|
818
699
|
}
|
|
819
700
|
/**
|
|
820
|
-
* Blur and temporal resolve,
|
|
821
|
-
*
|
|
822
|
-
*
|
|
823
|
-
* The settings come from one body's material because the map is one texture.
|
|
824
|
-
* The largest body supplies them, which is the one the viewer is most likely
|
|
825
|
-
* under.
|
|
701
|
+
* Blur and temporal resolve, run once over the finished map. Settings come
|
|
702
|
+
* from the largest body's material.
|
|
826
703
|
* @internal
|
|
827
704
|
*/ _postProcess(device, material, map, scratch, history, ctx, createFramebuffer) {
|
|
828
705
|
// Ping-pong between the two targets. The count is rounded up to an even
|
|
@@ -833,19 +710,15 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
833
710
|
const dst = i % 2 === 0 ? scratch : map;
|
|
834
711
|
this._blur(device, src, createFramebuffer(dst));
|
|
835
712
|
}
|
|
836
|
-
// The blur
|
|
837
|
-
// resolve. Doing it the other way round would make the resolve read and
|
|
838
|
-
// write one texture.
|
|
713
|
+
// The blur lands back in `map`, so `scratch` takes the resolve.
|
|
839
714
|
const strength = material.causticsTemporalStrength;
|
|
840
715
|
if (strength <= 0) {
|
|
841
|
-
//
|
|
842
|
-
// keep describing whichever map the history slot still holds.
|
|
716
|
+
// No history committed; keep the recorded slice matching the history slot.
|
|
843
717
|
return map;
|
|
844
718
|
}
|
|
845
719
|
const camera = ctx.camera;
|
|
846
720
|
const previous = this._prevSlices.get(camera);
|
|
847
|
-
//
|
|
848
|
-
// for a while. The current map stands on its own and becomes the history.
|
|
721
|
+
// Without history the current map stands on its own and becomes the history.
|
|
849
722
|
const resolved = history && previous ? scratch : map;
|
|
850
723
|
if (resolved === scratch) {
|
|
851
724
|
this._resolve(device, map, history, previous, strength, createFramebuffer(scratch));
|
|
@@ -858,23 +731,19 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
858
731
|
* this light cannot supply one.
|
|
859
732
|
* @internal
|
|
860
733
|
*/ _resolveSceneDepth(ctx, light) {
|
|
861
|
-
// WebGPU only
|
|
862
|
-
// fetch, and GLSL ES 3.0 does not allow one on a shadow-sampler texture.
|
|
863
|
-
// WebGL2 keeps the focal plane.
|
|
734
|
+
// WebGPU only: GLSL ES 3.0 forbids texelFetch on a shadow-sampler texture.
|
|
864
735
|
if (ctx.device.type !== 'webgpu') {
|
|
736
|
+
warnUnsupported('Water caustics on scene depth (WaterMaterial.causticsSceneDepth)', 'WebGPU', 'caustics are projected onto the flat plane at causticsDepth');
|
|
865
737
|
return null;
|
|
866
738
|
}
|
|
867
739
|
const params = ctx.shadowMapInfo?.get(light);
|
|
868
740
|
const shadowMap = params?.shadowMap;
|
|
869
|
-
// Only
|
|
870
|
-
// distance rather than a sampled depth, and a non-array map has no cascade
|
|
871
|
-
// to select; both fall back to the plane.
|
|
741
|
+
// Only cascaded native-depth maps; others fall back to the plane.
|
|
872
742
|
if (!params || !shadowMap?.isTexture2DArray() || !shadowMap.isDepth()) {
|
|
873
743
|
return null;
|
|
874
744
|
}
|
|
875
|
-
// The cascade
|
|
876
|
-
//
|
|
877
|
-
// slice outruns the shadow map and the last cascade is all there is.
|
|
745
|
+
// The smallest cascade whose split distance clears the slice's reach, else
|
|
746
|
+
// the last.
|
|
878
747
|
const count = Math.max(1, Math.min(params.numShadowCascades, 4));
|
|
879
748
|
const reach = Math.max(1, this._sliceReach);
|
|
880
749
|
let layer = count - 1;
|
|
@@ -884,8 +753,7 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
884
753
|
break;
|
|
885
754
|
}
|
|
886
755
|
}
|
|
887
|
-
// shadowMatrices holds each cascade's view-projection transposed
|
|
888
|
-
// rows per cascade, which is the layout the lighting pass dots against.
|
|
756
|
+
// shadowMatrices holds each cascade's view-projection transposed.
|
|
889
757
|
this._sceneMatrixT.set(params.shadowMatrices.subarray(layer * 16, layer * 16 + 16));
|
|
890
758
|
Matrix4x4.transpose(this._sceneMatrixT, this._sceneMatrix);
|
|
891
759
|
Matrix4x4.invert(this._sceneMatrix, this._sceneMatrixInv);
|
|
@@ -902,8 +770,7 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
902
770
|
*
|
|
903
771
|
* Auto solves the grid for a fixed density in photons per covered map texel:
|
|
904
772
|
* the grid covers `gridFraction` of the map, so the target is reached at
|
|
905
|
-
* `grid = size * sqrt(target * gridFraction)`.
|
|
906
|
-
* the density swinging with the water's footprint.
|
|
773
|
+
* `grid = size * sqrt(target * gridFraction)`.
|
|
907
774
|
*
|
|
908
775
|
* @param requested - Material setting; 0 asks for the automatic size.
|
|
909
776
|
* @param mapSize - Edge length of the caustic map.
|
|
@@ -989,13 +856,9 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
989
856
|
}
|
|
990
857
|
this._heightPrograms.clear();
|
|
991
858
|
this._heightStates.length = 0;
|
|
992
|
-
// The layout does not own its buffers
|
|
859
|
+
// The layout does not own its buffers.
|
|
993
860
|
this._heightLayout?.dispose();
|
|
994
861
|
this._heightLayout = null;
|
|
995
|
-
this._heightVertexBuffer?.dispose();
|
|
996
|
-
this._heightVertexBuffer = null;
|
|
997
|
-
this._heightIndexBuffer?.dispose();
|
|
998
|
-
this._heightIndexBuffer = null;
|
|
999
862
|
this._heightIndexCount = 0;
|
|
1000
863
|
this._blurBindGroup?.dispose();
|
|
1001
864
|
this._blurBindGroup = null;
|
|
@@ -1008,8 +871,6 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
1008
871
|
this._blurProgram = null;
|
|
1009
872
|
this._photonLayout?.dispose();
|
|
1010
873
|
this._photonLayout = null;
|
|
1011
|
-
// Disposing the layout releases its vertex array object, not the buffer it
|
|
1012
|
-
// was built over.
|
|
1013
874
|
this._photonBuffer?.dispose();
|
|
1014
875
|
this._photonBuffer = null;
|
|
1015
876
|
this._photonCapacity = 0;
|
|
@@ -1033,15 +894,12 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
1033
894
|
* Recomputes the orthographic light-space slice the map covers.
|
|
1034
895
|
* @internal
|
|
1035
896
|
*/ _updateUniforms(ctx, waters, light, mapSize) {
|
|
1036
|
-
// Slice-wide settings come from the first body
|
|
1037
|
-
// largest first. They describe the map, not the water, so one body has to
|
|
1038
|
-
// win: a range or a warp per body would ask one texture for two resolutions.
|
|
897
|
+
// Slice-wide settings come from the first (largest) body.
|
|
1039
898
|
const material = waters[0].material;
|
|
1040
899
|
const dir = this._tmpDir;
|
|
1041
900
|
dir.set(light.directionAndCutoff.xyz());
|
|
1042
901
|
dir.inplaceNormalize();
|
|
1043
|
-
// Two axes perpendicular to the light
|
|
1044
|
-
// world axis that is not parallel to the light keeps the cross products stable.
|
|
902
|
+
// Two axes perpendicular to the light, seeded by a non-parallel world axis.
|
|
1045
903
|
const seed = Math.abs(dir.y) > 0.99 ? Vector3.axisPZ() : Vector3.axisPY();
|
|
1046
904
|
Vector3.cross(seed, dir, this._tmpRight);
|
|
1047
905
|
this._tmpRight.inplaceNormalize();
|
|
@@ -1058,13 +916,7 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
1058
916
|
// The component along the light direction is arbitrary: both the projection
|
|
1059
917
|
// into the map and the photon sweep are invariant along it.
|
|
1060
918
|
const alongDir = Vector3.dot(center, dir);
|
|
1061
|
-
//
|
|
1062
|
-
// parallelogram here, so its bounds come from the four corners; the map is a
|
|
1063
|
-
// box in these coordinates, which is why the fit is against the AABB.
|
|
1064
|
-
//
|
|
1065
|
-
// Over every body at once, so the slice covers all of them. Bodies far
|
|
1066
|
-
// apart make the union mostly empty, but `causticsRange` already bounds how
|
|
1067
|
-
// much of that reaches the map.
|
|
919
|
+
// Light-space AABB of all bodies' regions, from their four corners.
|
|
1068
920
|
let regionMinR = Infinity;
|
|
1069
921
|
let regionMaxR = -Infinity;
|
|
1070
922
|
let regionMinU = Infinity;
|
|
@@ -1099,20 +951,15 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
1099
951
|
regionMaxU = Math.max(regionMaxU, v);
|
|
1100
952
|
}
|
|
1101
953
|
}
|
|
1102
|
-
// Fit the slice to
|
|
1103
|
-
//
|
|
1104
|
-
// of it permanently zero for a pool far smaller than the range.
|
|
1105
|
-
// `causticsRange` becomes a cap on how far from the camera the map reaches
|
|
1106
|
-
// rather than its literal half-extent.
|
|
954
|
+
// Fit the slice to the water; `causticsRange` caps how far from the camera
|
|
955
|
+
// the map reaches.
|
|
1107
956
|
let loR = Math.max(regionMinR, cameraRight - range);
|
|
1108
957
|
let hiR = Math.min(regionMaxR, cameraRight + range);
|
|
1109
958
|
let loU = Math.max(regionMinU, cameraUp - range);
|
|
1110
959
|
let hiU = Math.min(regionMaxU, cameraUp + range);
|
|
1111
960
|
if (hiR <= loR || hiU <= loU) {
|
|
1112
|
-
//
|
|
1113
|
-
//
|
|
1114
|
-
// the map holds, and the grid bounds below have to come out empty rather
|
|
1115
|
-
// than inverted.
|
|
961
|
+
// No water within range; keep the slice finite so the grid bounds come
|
|
962
|
+
// out empty rather than inverted.
|
|
1116
963
|
loR = cameraRight - range;
|
|
1117
964
|
hiR = cameraRight + range;
|
|
1118
965
|
loU = cameraUp - range;
|
|
@@ -1120,27 +967,18 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
1120
967
|
}
|
|
1121
968
|
let halfR = Math.max(MIN_SLICE_EXTENT, (hiR - loR) * 0.5);
|
|
1122
969
|
let halfU = Math.max(MIN_SLICE_EXTENT, (hiU - loU) * 0.5);
|
|
1123
|
-
//
|
|
1124
|
-
// world texel sizes, which reads as directional smearing. Capping the ratio
|
|
1125
|
-
// keeps most of the fit while bounding that, and bounds the same asymmetry
|
|
1126
|
-
// in the edge fade, whose band is a share of each axis.
|
|
970
|
+
// Cap the aspect ratio to bound directional smearing on the square map.
|
|
1127
971
|
halfR = Math.max(halfR, halfU / MAX_SLICE_ASPECT);
|
|
1128
972
|
halfU = Math.max(halfU, halfR / MAX_SLICE_ASPECT);
|
|
1129
|
-
// Quantise so the
|
|
1130
|
-
//
|
|
1131
|
-
// a continuously shrinking slice resamples the temporal history every frame.
|
|
1132
|
-
// The two common cases - a pool wholly inside the range, a sea wholly
|
|
1133
|
-
// covering it - sit on a fixed level and never change at all.
|
|
973
|
+
// Quantise so the texel size holds still while the camera moves, which the
|
|
974
|
+
// centre snapping and the temporal history rely on.
|
|
1134
975
|
halfR = Math.min(range, quantiseSliceExtent(halfR, range));
|
|
1135
976
|
halfU = Math.min(range, quantiseSliceExtent(halfU, range));
|
|
1136
|
-
//
|
|
1137
|
-
// A slice already fitted to a pool has every texel on water the camera can
|
|
1138
|
-
// see, and concentrating them further would just blur its far side.
|
|
977
|
+
// Warp only when the range, not the water, bounds the map.
|
|
1139
978
|
const rangeLimited = regionMinR < cameraRight - range || regionMaxR > cameraRight + range || regionMinU < cameraUp - range || regionMaxU > cameraUp + range;
|
|
1140
979
|
const warp = rangeLimited ? material.causticsWarp : 0;
|
|
1141
|
-
// Snap the centre so the map does not crawl
|
|
1142
|
-
//
|
|
1143
|
-
// warp, where no shift is exact and the goal is instead to shift rarely.
|
|
980
|
+
// Snap the centre so the map does not crawl: whole texels when linear, a
|
|
981
|
+
// coarse ladder under a warp.
|
|
1144
982
|
const snapR = warp > 0 ? halfR / WARPED_CENTER_SNAP_DIVISIONS : 2 * halfR / mapSize;
|
|
1145
983
|
const snapU = warp > 0 ? halfU / WARPED_CENTER_SNAP_DIVISIONS : 2 * halfU / mapSize;
|
|
1146
984
|
const centerR = Math.round((loR + hiR) * 0.5 / snapR) * snapR;
|
|
@@ -1151,34 +989,24 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
1151
989
|
u.frameY.setXYZW(up.x, up.y, up.z, 1 / halfU);
|
|
1152
990
|
u.center.setXYZW(center.x, center.y, center.z, waterLevel);
|
|
1153
991
|
u.lightDir.setXYZW(dir.x, dir.y, dir.z, 1 / Math.max(-dir.y, MIN_SUN_ELEVATION));
|
|
1154
|
-
// Edge fade,
|
|
1155
|
-
//
|
|
1156
|
-
// keeps the shader free of the extents and lets the auto width put a floor
|
|
1157
|
-
// in meters under a band the map fraction alone would collapse.
|
|
1158
|
-
//
|
|
1159
|
-
// Only a border the range put there needs fading. Where the fit stopped at
|
|
1160
|
-
// the water instead, the border already coincides with the region the
|
|
1161
|
-
// receiver gates on, so a band there is at best a no-op and at worst eats
|
|
1162
|
-
// caustics from the last few meters of a pool the map now fits exactly.
|
|
992
|
+
// Edge fade start, in units of the map half-extent. Only a border set by the
|
|
993
|
+
// range is faded; a border fitted to the water coincides with the region.
|
|
1163
994
|
const minHalf = Math.min(halfR, halfU);
|
|
1164
995
|
const fadeDistance = !rangeLimited ? 0 : material.causticsFadeDistance > 0 ? material.causticsFadeDistance : Math.max(AUTO_FADE_MIN_DISTANCE, AUTO_FADE_FRACTION * minHalf);
|
|
1165
996
|
const fadeFraction = Math.max(MIN_FADE_FRACTION, Math.min(MAX_FADE_FRACTION, fadeDistance / minHalf));
|
|
1166
997
|
u.params.setXYZW(material.causticsIntensity, material.causticsDepth, material.causticsDefocus, 1 - fadeFraction);
|
|
1167
|
-
u.extinction.setXYZW(
|
|
998
|
+
u.extinction.setXYZW(material.causticsMinDepth, 0, 0, warp);
|
|
1168
999
|
this._warp = warp;
|
|
1169
|
-
// Retained for the per-body grid fit
|
|
1000
|
+
// Retained for the per-body grid fit.
|
|
1170
1001
|
this._sliceRight = right;
|
|
1171
1002
|
this._sliceUp = up;
|
|
1172
1003
|
this._sliceCenterR = centerR;
|
|
1173
1004
|
this._sliceCenterU = centerU;
|
|
1174
1005
|
this._sliceHalfR = halfR;
|
|
1175
1006
|
this._sliceHalfU = halfU;
|
|
1176
|
-
//
|
|
1177
|
-
// coarse a shadow cascade has to be to contain it.
|
|
1007
|
+
// Slice reach from the camera, for shadow cascade selection.
|
|
1178
1008
|
this._sliceReach = Math.hypot(halfR, halfU);
|
|
1179
|
-
//
|
|
1180
|
-
// decide which body is above a given point; the medium is what attenuates
|
|
1181
|
-
// the light once one is.
|
|
1009
|
+
// Per-body table the receiver searches: footprint, medium and surface height.
|
|
1182
1010
|
u.waterCount = Math.min(waters.length, MAX_CAUSTIC_WATERS);
|
|
1183
1011
|
for(let i = 0; i < u.waterCount; i++){
|
|
1184
1012
|
const body = waters[i];
|
|
@@ -1197,13 +1025,9 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
1197
1025
|
/**
|
|
1198
1026
|
* Map-NDC bounds of one body's footprint, so its photon grid covers just that.
|
|
1199
1027
|
*
|
|
1200
|
-
*
|
|
1201
|
-
*
|
|
1202
|
-
*
|
|
1203
|
-
* to the map, because photons outside it are rasterised away and the fraction
|
|
1204
|
-
* has to describe what is left. Warped, because the grid is laid out in the
|
|
1205
|
-
* space the map's texels live in - the warp is monotone and separable, so
|
|
1206
|
-
* warping the interval's ends gives the warped interval exactly.
|
|
1028
|
+
* The body's light-space bounds carry over directly since the projection is
|
|
1029
|
+
* invariant along the light. Clamped to the map, then warped; the warp is
|
|
1030
|
+
* monotone and separable, so warping the interval's ends is exact.
|
|
1207
1031
|
* @internal
|
|
1208
1032
|
*/ _updateGridBounds(water) {
|
|
1209
1033
|
const region = water.material.region;
|
|
@@ -1240,12 +1064,8 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
1240
1064
|
maxU = Math.max(maxU, v);
|
|
1241
1065
|
}
|
|
1242
1066
|
const warp = this._warp;
|
|
1243
|
-
// Clamped to the map plus a margin, but never past
|
|
1244
|
-
// the
|
|
1245
|
-
// photon launched off the water is killed by the region test and would only
|
|
1246
|
-
// dilute the weight the fraction below hands out. Where the water carries on
|
|
1247
|
-
// past the border, the margin lets the displaced surface throw photons back
|
|
1248
|
-
// in across it - see GRID_BORDER_MARGIN.
|
|
1067
|
+
// Clamped to the map plus a margin (see GRID_BORDER_MARGIN), but never past
|
|
1068
|
+
// the water itself.
|
|
1249
1069
|
const m = 1 + GRID_BORDER_MARGIN;
|
|
1250
1070
|
const lo = (v)=>Math.max(-1.15, Math.min(m, v));
|
|
1251
1071
|
const minX = warpCausticNDC(lo((minR - this._sliceCenterR) / this._sliceHalfR), warp);
|
|
@@ -1253,9 +1073,8 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
1253
1073
|
const minY = warpCausticNDC(lo((minU - this._sliceCenterU) / this._sliceHalfU), warp);
|
|
1254
1074
|
const maxY = warpCausticNDC(lo((maxU - this._sliceCenterU) / this._sliceHalfU), warp);
|
|
1255
1075
|
this._gridBounds.setXYZW(minX, minY, maxX, maxY);
|
|
1256
|
-
// Share of the map the grid covers
|
|
1257
|
-
//
|
|
1258
|
-
// proportionally less each.
|
|
1076
|
+
// Share of the map the grid covers; scales the photon weight to keep calm
|
|
1077
|
+
// water at 1.0.
|
|
1259
1078
|
this._gridFraction = Math.max(0, (maxX - minX) * (maxY - minY) / 4);
|
|
1260
1079
|
}
|
|
1261
1080
|
/** @internal */ _updatePhotonLayout(device, gridSize) {
|
|
@@ -1263,10 +1082,7 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
1263
1082
|
if (this._photonCapacity >= this._photonCount) {
|
|
1264
1083
|
return;
|
|
1265
1084
|
}
|
|
1266
|
-
// Grow only
|
|
1267
|
-
//
|
|
1268
|
-
// The buffer holds each photon's ordinal, and the shader divides that
|
|
1269
|
-
// by the grid size to place it.
|
|
1085
|
+
// Grow only. The buffer holds each photon's ordinal.
|
|
1270
1086
|
const capacity = Math.max(this._photonCount, this._photonCapacity * 2);
|
|
1271
1087
|
const indices = new Float32Array(capacity);
|
|
1272
1088
|
for(let i = 0; i < capacity; i++){
|
|
@@ -1303,8 +1119,7 @@ const MAX_FADE_FRACTION = 0.9;
|
|
|
1303
1119
|
states = device.createRenderStateSet();
|
|
1304
1120
|
states.useRasterizerState().setCullMode('none');
|
|
1305
1121
|
states.useDepthState().enableTest(false).enableWrite(false);
|
|
1306
|
-
// Plain overwrite into this slot's channel only.
|
|
1307
|
-
// heights wherever the surface overlaps itself in the light's view.
|
|
1122
|
+
// Plain overwrite into this slot's channel only.
|
|
1308
1123
|
states.useColorState().setColorMask(slot === 0, slot === 1, slot === 2, slot === 3);
|
|
1309
1124
|
this._heightStates[slot] = states;
|
|
1310
1125
|
}
|