@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,17 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Vector4, DEPTH_CLEAR_VALUE, DEPTH_COMPARE_DEFAULT } from '@zephyr3d/base';
|
|
2
2
|
import { RenderQueue } from '../render_queue.js';
|
|
3
3
|
import { LightPass } from '../lightpass.js';
|
|
4
4
|
import { ShadowMapPass } from '../shadowmap_pass.js';
|
|
5
5
|
import { DepthPass } from '../depthpass.js';
|
|
6
6
|
import { ClusteredLight } from '../cluster_light.js';
|
|
7
7
|
import { ShadowMaskRenderer } from '../shadow_mask_pass.js';
|
|
8
|
-
import {
|
|
8
|
+
import { TransmissionThicknessRenderer } from '../transmission_thickness_pass.js';
|
|
9
|
+
import { getHiZFormat, buildHiZ } from '../hzb.js';
|
|
9
10
|
import '../primitive.js';
|
|
10
11
|
import '@zephyr3d/device';
|
|
11
12
|
import { CopyBlitter } from '../../blitter/copy.js';
|
|
12
13
|
import { fetchSampler } from '../../utility/misc.js';
|
|
13
14
|
import { MaterialVaryingFlags } from '../../values.js';
|
|
14
|
-
import { ShaderHelper } from '../../material/shader/helper.js';
|
|
15
|
+
import { ShaderHelper, MAX_CAUSTIC_WATERS } from '../../material/shader/helper.js';
|
|
16
|
+
import { REFRACT_BLUR_MAX_LOD } from '../../material/water.js';
|
|
15
17
|
import { PostEffectLayer, AbstractPostEffect } from '../../posteffect/posteffect.js';
|
|
16
18
|
import { RenderGraph } from './rendergraph.js';
|
|
17
19
|
import { RenderGraphExecutor } from './executor.js';
|
|
@@ -24,6 +26,7 @@ import { OrderingScope } from './render_context.js';
|
|
|
24
26
|
import { RenderPipeline } from './render_pipeline.js';
|
|
25
27
|
import { mergeFrameResourceRequirements } from './frame_resource_requirements.js';
|
|
26
28
|
import { renderObjectColors } from '../gpu_picking.js';
|
|
29
|
+
import '../../node_modules/meshoptimizer/meshopt_simplifier.js';
|
|
27
30
|
import { BoxShape } from '../../shapes/box.js';
|
|
28
31
|
import '../../shapes/capsule.js';
|
|
29
32
|
import '../../shapes/cylinder.js';
|
|
@@ -31,13 +34,24 @@ import '../../shapes/torus.js';
|
|
|
31
34
|
import '../../shapes/plane.js';
|
|
32
35
|
import '../../shapes/sphere.js';
|
|
33
36
|
import '../../shapes/tetrahedron.js';
|
|
37
|
+
import { WaterCausticsRenderer } from '../water_caustics.js';
|
|
38
|
+
import { UnderwaterRenderer, selectUnderwaterSource } from '../underwater.js';
|
|
34
39
|
|
|
35
40
|
const _scenePass = new LightPass();
|
|
36
41
|
const _depthPass = new DepthPass();
|
|
37
42
|
const _shadowMapPass = new ShadowMapPass();
|
|
38
|
-
const _clusters = [];
|
|
39
43
|
const _shadowMaskRenderer = new ShadowMaskRenderer();
|
|
44
|
+
const _transmissionThicknessRenderer = new TransmissionThicknessRenderer();
|
|
45
|
+
const _waterCausticsRenderer = new WaterCausticsRenderer();
|
|
46
|
+
const _underwaterRenderer = new UnderwaterRenderer();
|
|
40
47
|
const _devicePoolAllocator = new DevicePoolAllocator();
|
|
48
|
+
/**
|
|
49
|
+
* Clear value for the depth prepass's skin profile id target.
|
|
50
|
+
*
|
|
51
|
+
* @remarks
|
|
52
|
+
* 0 is "not skin". It has to differ from the depth target's clear, which is the
|
|
53
|
+
* far plane rather than an id.
|
|
54
|
+
*/ const SKIN_PROFILE_ID_CLEAR = new Vector4(0, 0, 0, 0);
|
|
41
55
|
const _textureAffinityCaches = new WeakMap();
|
|
42
56
|
function getTextureAffinityCache(camera, device) {
|
|
43
57
|
const current = _textureAffinityCaches.get(camera);
|
|
@@ -54,20 +68,14 @@ function getTextureAffinityCache(camera, device) {
|
|
|
54
68
|
let _backDepthColorState = null;
|
|
55
69
|
let _frontDepthColorState = null;
|
|
56
70
|
const SURFACE_MRT_FLAGS = MaterialVaryingFlags.SCENE_STORE_ROUGHNESS | MaterialVaryingFlags.SSS_STORE_PROFILE | MaterialVaryingFlags.SSS_STORE_DIFFUSE | MaterialVaryingFlags.SCENE_STORE_NORMAL | MaterialVaryingFlags.SSS_STORE_TRANSMISSION | MaterialVaryingFlags.SKIN_SSS_STORE;
|
|
57
|
-
function getClusteredLight() {
|
|
58
|
-
return _clusters.length > 0 ? _clusters.pop() : new ClusteredLight();
|
|
59
|
-
}
|
|
60
|
-
function freeClusteredLight(cl) {
|
|
61
|
-
_clusters.push(cl);
|
|
62
|
-
}
|
|
63
71
|
function getCoreMaterial(material) {
|
|
64
72
|
return material?.coreMaterial ?? material ?? null;
|
|
65
73
|
}
|
|
66
74
|
function hasSSSMaterialCore(material) {
|
|
67
75
|
return !!getCoreMaterial(material)?.subsurfaceProfile;
|
|
68
76
|
}
|
|
69
|
-
function
|
|
70
|
-
return !!getCoreMaterial(material)?.
|
|
77
|
+
function hasPostSSSMaterialCore(material) {
|
|
78
|
+
return !!getCoreMaterial(material)?.postSSS;
|
|
71
79
|
}
|
|
72
80
|
function renderQueueHasActiveSSS(renderQueue) {
|
|
73
81
|
const itemList = renderQueue.itemList;
|
|
@@ -87,7 +95,7 @@ function renderQueueHasActiveSSS(renderQueue) {
|
|
|
87
95
|
}
|
|
88
96
|
return false;
|
|
89
97
|
}
|
|
90
|
-
function
|
|
98
|
+
function renderQueueHasActivePostSSS(renderQueue) {
|
|
91
99
|
const itemList = renderQueue.itemList;
|
|
92
100
|
if (!itemList) {
|
|
93
101
|
return false;
|
|
@@ -98,7 +106,7 @@ function renderQueueHasActiveSkinSSS(renderQueue) {
|
|
|
98
106
|
];
|
|
99
107
|
for (const list of lists){
|
|
100
108
|
for (const material of list.materialList){
|
|
101
|
-
if (
|
|
109
|
+
if (hasPostSSSMaterialCore(material)) {
|
|
102
110
|
return true;
|
|
103
111
|
}
|
|
104
112
|
}
|
|
@@ -124,8 +132,10 @@ function cloneActualSSSListInfo(source, _targetQueue) {
|
|
|
124
132
|
morphItemList: filterActualSSSItemList(source.morphItemList),
|
|
125
133
|
skinAndMorphItemList: filterActualSSSItemList(source.skinAndMorphItemList),
|
|
126
134
|
instanceItemList: filterActualSSSItemList(source.instanceItemList),
|
|
135
|
+
skinInstanceItemList: filterActualSSSItemList(source.skinInstanceItemList),
|
|
127
136
|
materialList: filterActualSSSMaterialList(source.materialList),
|
|
128
137
|
instanceList: {},
|
|
138
|
+
instanceLods: {},
|
|
129
139
|
renderQueue: source.renderQueue
|
|
130
140
|
};
|
|
131
141
|
}
|
|
@@ -136,7 +146,7 @@ function cloneActualSSSBundle(source, targetQueue) {
|
|
|
136
146
|
};
|
|
137
147
|
}
|
|
138
148
|
function hasAnyActualSSSItems(renderItems) {
|
|
139
|
-
return renderItems.some((info)=>info.itemList.length > 0 || info.skinItemList.length > 0 || info.morphItemList.length > 0 || info.skinAndMorphItemList.length > 0 || info.instanceItemList.length > 0);
|
|
149
|
+
return renderItems.some((info)=>info.itemList.length > 0 || info.skinItemList.length > 0 || info.morphItemList.length > 0 || info.skinAndMorphItemList.length > 0 || info.instanceItemList.length > 0 || info.skinInstanceItemList.length > 0);
|
|
140
150
|
}
|
|
141
151
|
function createActualSSSRenderQueue(renderQueue) {
|
|
142
152
|
const itemList = renderQueue.itemList;
|
|
@@ -210,6 +220,21 @@ function getSSSLightingTextureFormat(ctx, attachmentCount, surfaceAttachmentCoun
|
|
|
210
220
|
function getFullMipLevelCount(width, height) {
|
|
211
221
|
return Math.max(1, Math.floor(Math.log2(Math.max(1, width, height))) + 1);
|
|
212
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* Levels the scene colour copy is allocated with.
|
|
225
|
+
*
|
|
226
|
+
* Deep enough to serve the refraction blur, which clamps its own LOD, and no
|
|
227
|
+
* deeper: the coarsest levels of a full chain are averages of the whole frame,
|
|
228
|
+
* which is not a plausible answer for "what is behind this water", and every
|
|
229
|
+
* level costs bandwidth to generate on each grab. Capped against the full chain
|
|
230
|
+
* so a small render target stays legal.
|
|
231
|
+
*
|
|
232
|
+
* Derived from the water material's clamp rather than restated, so the two
|
|
233
|
+
* cannot drift: a level count one short of it would leave the sampler asking for
|
|
234
|
+
* a level that does not exist.
|
|
235
|
+
*/ function getSceneColorMipLevelCount(width, height) {
|
|
236
|
+
return Math.min(REFRACT_BLUR_MAX_LOD + 1, getFullMipLevelCount(width, height));
|
|
237
|
+
}
|
|
213
238
|
function hasSurfaceMRT(ctx) {
|
|
214
239
|
return !!(ctx.materialFlags & SURFACE_MRT_FLAGS);
|
|
215
240
|
}
|
|
@@ -233,21 +258,27 @@ function getLightPassColorAttachments(ctx, colorAttachment) {
|
|
|
233
258
|
attachments.push(ctx.SSSTransmissionTexture);
|
|
234
259
|
}
|
|
235
260
|
if (ctx.materialFlags & MaterialVaryingFlags.SKIN_SSS_STORE) {
|
|
236
|
-
attachments.push(ctx.
|
|
261
|
+
attachments.push(ctx.SSSMaskTexture);
|
|
237
262
|
}
|
|
238
263
|
return attachments.length === 1 ? attachments[0] : attachments;
|
|
239
264
|
}
|
|
240
|
-
/** Derive Forward+ options from scene and camera state. @internal */ function deriveForwardPlusOptions(scene, camera,
|
|
265
|
+
/** Derive Forward+ options from scene and camera state. @internal */ function deriveForwardPlusOptions(scene, camera, _deviceType, renderQueue) {
|
|
241
266
|
const ssr = camera.SSR && scene.env.light.envLight && scene.env.light.envLight.hasRadiance();
|
|
242
267
|
const ssgi = camera.SSGI && camera.HDR && camera.ssgiIntensity > 0 && scene.env.light.type === 'ibl' && !!scene.env.light.envLight?.hasRadiance() && !!scene.env.light.envLight?.hasIrradiance();
|
|
243
268
|
const sss = camera.SSS && renderQueueHasActiveSSS(renderQueue);
|
|
244
|
-
|
|
269
|
+
// No camera switch for this one: a skin material always wants its diffusion,
|
|
270
|
+
// and the queue already says whether the frame draws any.
|
|
271
|
+
const postSSS = renderQueueHasActivePostSSS(renderQueue);
|
|
245
272
|
const needSceneColor = renderQueue.needSceneColor();
|
|
246
273
|
const needSceneColorWithDepth = renderQueue.needSceneColorWithDepth();
|
|
274
|
+
// Requested by materials rather than by a post effect, so it is resolved here
|
|
275
|
+
// instead of in resolveFrameResourceRequirements.
|
|
276
|
+
const needHiZNearest = renderQueue.needHiZNearest() || camera.HiZNearest;
|
|
247
277
|
return {
|
|
248
278
|
depthPrepass: true,
|
|
249
279
|
motionVectors: false,
|
|
250
|
-
hiZ:
|
|
280
|
+
hiZ: needHiZNearest,
|
|
281
|
+
hiZNearest: needHiZNearest,
|
|
251
282
|
sceneNormal: false,
|
|
252
283
|
sceneRoughness: false,
|
|
253
284
|
shadowMask: false,
|
|
@@ -259,26 +290,22 @@ function getLightPassColorAttachments(ctx, colorAttachment) {
|
|
|
259
290
|
needSceneColorWithDepth,
|
|
260
291
|
needsTransmissionDepthForSSR: !!ssr && needSceneColor && !needSceneColorWithDepth,
|
|
261
292
|
sss: !!sss,
|
|
262
|
-
|
|
293
|
+
postSSS: !!postSSS,
|
|
294
|
+
// Driven entirely by postSSS: the id exists to serve the diffusion and the
|
|
295
|
+
// transmission thickness pass, and both are gated on the same condition.
|
|
296
|
+
sssProfileId: !!postSSS,
|
|
263
297
|
fogPresents: !!scene.env.sky?.fogPresents
|
|
264
298
|
};
|
|
265
299
|
}
|
|
266
300
|
function resolveFrameResourceRequirements(ctx, options, requirements) {
|
|
267
|
-
const deviceType = ctx.device.type;
|
|
268
301
|
options.motionVectors ||= !!requirements.motionVector;
|
|
269
|
-
options.
|
|
302
|
+
options.hiZNearest ||= !!requirements.hiZNearest;
|
|
303
|
+
// The nearest channel lives on the same pyramid, so asking for it is asking
|
|
304
|
+
// for the pyramid.
|
|
305
|
+
options.hiZ ||= !!requirements.hiZ || options.hiZNearest;
|
|
270
306
|
options.sceneNormal ||= !!requirements.sceneNormal;
|
|
271
307
|
options.sceneRoughness ||= !!requirements.sceneRoughness;
|
|
272
308
|
options.shadowMask ||= !!requirements.shadowMask;
|
|
273
|
-
if (options.motionVectors && deviceType === 'webgl') {
|
|
274
|
-
throw new Error('Forward+: MotionVector was requested but is not supported by the WebGL backend.');
|
|
275
|
-
}
|
|
276
|
-
if (options.hiZ && deviceType === 'webgl') {
|
|
277
|
-
throw new Error('Forward+: HiZ was requested but is not supported by the WebGL backend.');
|
|
278
|
-
}
|
|
279
|
-
if (options.shadowMask && deviceType === 'webgl') {
|
|
280
|
-
throw new Error('Forward+: ShadowMask was requested but is not supported by the WebGL backend.');
|
|
281
|
-
}
|
|
282
309
|
const surfaceAttachmentCount = Number(options.sceneNormal) + Number(options.sceneRoughness);
|
|
283
310
|
if (surfaceAttachmentCount > 0) {
|
|
284
311
|
const caps = ctx.device.getDeviceCaps();
|
|
@@ -292,6 +319,7 @@ function resolveFrameResourceRequirements(ctx, options, requirements) {
|
|
|
292
319
|
}
|
|
293
320
|
}
|
|
294
321
|
ctx.motionVectors = options.motionVectors;
|
|
322
|
+
ctx.sssProfileId = options.sssProfileId;
|
|
295
323
|
ctx.HiZ = options.hiZ;
|
|
296
324
|
ctx.SSGI = options.ssgi;
|
|
297
325
|
ctx.screenSpaceShadowMask = options.shadowMask;
|
|
@@ -359,8 +387,9 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
359
387
|
ordering.emit(builder, 'ClusterLightsDone');
|
|
360
388
|
builder.sideEffect();
|
|
361
389
|
builder.setExecute(()=>{
|
|
362
|
-
ctx.clusteredLight =
|
|
363
|
-
|
|
390
|
+
ctx.clusteredLight = ClusteredLight.acquire(ctx.renderWidth, ctx.renderHeight);
|
|
391
|
+
// The light passes draw into targets of the render size (see renderOpaqueScenePass).
|
|
392
|
+
ctx.clusteredLight.calculateLightIndex(ctx.camera, renderQueue, ctx.renderWidth, ctx.renderHeight, ctx.screenSpaceShadowMask, ShaderHelper.getPreExposure(ctx));
|
|
364
393
|
});
|
|
365
394
|
});
|
|
366
395
|
}
|
|
@@ -401,12 +430,159 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
401
430
|
});
|
|
402
431
|
}
|
|
403
432
|
};
|
|
433
|
+
/**
|
|
434
|
+
* Picks the water surface and light the caustic map is built from.
|
|
435
|
+
*
|
|
436
|
+
* Only one pair is handled per frame: the caustics ride on a single light's
|
|
437
|
+
* additive pass, and a second water body would need a second map. The largest
|
|
438
|
+
* visible water surface wins, which is the one a camera near several of them is
|
|
439
|
+
* most likely to be looking through.
|
|
440
|
+
* @internal
|
|
441
|
+
*/ function selectWaterCausticSource(ctx, renderQueue) {
|
|
442
|
+
if (renderQueue.waters.length === 0) {
|
|
443
|
+
return null;
|
|
444
|
+
}
|
|
445
|
+
// The caustic light must own an additive shadow pass, because that is the pass
|
|
446
|
+
// the caustic uniforms are declared and bound on.
|
|
447
|
+
const candidate = renderQueue.sunLight ?? renderQueue.primaryDirectionalLight;
|
|
448
|
+
const light = candidate && candidate.castShadow && renderQueue.shadowedLights.indexOf(candidate) >= 0 ? candidate : null;
|
|
449
|
+
if (!light) {
|
|
450
|
+
return null;
|
|
451
|
+
}
|
|
452
|
+
const waters = renderQueue.waters.filter((water)=>WaterCausticsRenderer.canRender(water, light));
|
|
453
|
+
if (waters.length === 0) {
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
// Largest first, which decides both what survives the cap and which body's
|
|
457
|
+
// settings describe the shared map. Area is a stand-in for "the one the viewer
|
|
458
|
+
// is most likely to be over"; a camera-distance sort would swap the map's
|
|
459
|
+
// whole configuration as the viewer walks between two pools, and swapping it
|
|
460
|
+
// discards the temporal history every time.
|
|
461
|
+
waters.sort((a, b)=>{
|
|
462
|
+
const ra = a.material.region;
|
|
463
|
+
const rb = b.material.region;
|
|
464
|
+
const areaA = Math.max(0, ra.z - ra.x) * Math.max(0, ra.w - ra.y);
|
|
465
|
+
const areaB = Math.max(0, rb.z - rb.x) * Math.max(0, rb.w - rb.y);
|
|
466
|
+
return areaB - areaA;
|
|
467
|
+
});
|
|
468
|
+
return {
|
|
469
|
+
waters: waters.slice(0, MAX_CAUSTIC_WATERS),
|
|
470
|
+
light
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
/** @internal */ const WaterCausticsModule = {
|
|
474
|
+
type: 'WaterCaustics',
|
|
475
|
+
writes: [
|
|
476
|
+
FrameResources.WaterCaustics
|
|
477
|
+
],
|
|
478
|
+
prepare: ({ ctx, renderQueue })=>({
|
|
479
|
+
enabled: !!selectWaterCausticSource(ctx, renderQueue)
|
|
480
|
+
}),
|
|
481
|
+
setup (fg) {
|
|
482
|
+
const { graph, ctx, renderQueue, ordering, blackboard } = fg;
|
|
483
|
+
const source = selectWaterCausticSource(ctx, renderQueue);
|
|
484
|
+
// Map-wide settings come from the largest body; see selectWaterCausticSource.
|
|
485
|
+
const material = source.waters[0].material;
|
|
486
|
+
const size = material.causticsResolution;
|
|
487
|
+
const format = WaterCausticsRenderer.getMapFormat(ctx.device);
|
|
488
|
+
const history = fg.history;
|
|
489
|
+
const temporal = material.causticsTemporalStrength > 0;
|
|
490
|
+
const mapDesc = {
|
|
491
|
+
format,
|
|
492
|
+
sizeMode: 'absolute',
|
|
493
|
+
width: size,
|
|
494
|
+
height: size
|
|
495
|
+
};
|
|
496
|
+
const mapSize = {
|
|
497
|
+
width: size,
|
|
498
|
+
height: size
|
|
499
|
+
};
|
|
500
|
+
// Null on the first frame, after a resize, and whenever the map format
|
|
501
|
+
// changes; the renderer falls back to the current frame alone.
|
|
502
|
+
const previousHandle = temporal && history ? history.importPreviousIfCompatible(graph, RGHistoryResources.WATER_CAUSTICS, mapDesc, mapSize) : null;
|
|
503
|
+
const result = graph.addPass('WaterCaustics', (builder)=>{
|
|
504
|
+
// The wave textures the splat pass samples are updated by the scene, not by
|
|
505
|
+
// the graph, so chain after the passes that have already run rather than
|
|
506
|
+
// declaring a resource dependency on them.
|
|
507
|
+
ordering.chainInto(builder);
|
|
508
|
+
ordering.emit(builder, 'WaterCausticsDone');
|
|
509
|
+
// The map leaves the graph through the draw context rather than through a
|
|
510
|
+
// handle a later pass samples, so nothing would otherwise keep this alive.
|
|
511
|
+
builder.sideEffect();
|
|
512
|
+
// Fixed-size, unlike almost every other graph texture: the map covers a
|
|
513
|
+
// world-space footprint, not the screen.
|
|
514
|
+
const mapHandle = builder.createTexture({
|
|
515
|
+
format,
|
|
516
|
+
sizeMode: 'absolute',
|
|
517
|
+
width: size,
|
|
518
|
+
height: size,
|
|
519
|
+
label: 'waterCaustics',
|
|
520
|
+
allocationKey: 'ForwardPlus.WaterCaustics'
|
|
521
|
+
});
|
|
522
|
+
const scratchHandle = builder.createTexture({
|
|
523
|
+
format,
|
|
524
|
+
sizeMode: 'absolute',
|
|
525
|
+
width: size,
|
|
526
|
+
height: size,
|
|
527
|
+
label: 'waterCausticsScratch',
|
|
528
|
+
allocationKey: 'ForwardPlus.WaterCausticsScratch'
|
|
529
|
+
});
|
|
530
|
+
// The displaced surface height along each map ray, which the receiver
|
|
531
|
+
// gates its depth on. Same slice and size as the map so it shares the
|
|
532
|
+
// map's uv; four channels, one per water slot.
|
|
533
|
+
const heightHandle = builder.createTexture({
|
|
534
|
+
format: WaterCausticsRenderer.getHeightMapFormat(),
|
|
535
|
+
sizeMode: 'absolute',
|
|
536
|
+
width: size,
|
|
537
|
+
height: size,
|
|
538
|
+
label: 'waterCausticHeights',
|
|
539
|
+
allocationKey: 'ForwardPlus.WaterCausticHeights'
|
|
540
|
+
});
|
|
541
|
+
if (previousHandle) {
|
|
542
|
+
builder.read(previousHandle);
|
|
543
|
+
}
|
|
544
|
+
builder.setExecute((rgCtx)=>{
|
|
545
|
+
const map = rgCtx.getTexture(mapHandle);
|
|
546
|
+
const scratch = rgCtx.getTexture(scratchHandle);
|
|
547
|
+
const heights = rgCtx.getTexture(heightHandle);
|
|
548
|
+
const previous = previousHandle ? rgCtx.getTexture(previousHandle) : null;
|
|
549
|
+
const resolved = _waterCausticsRenderer.render(ctx, source.waters, source.light, map, scratch, heights, previous, (texture)=>rgCtx.createFramebuffer({
|
|
550
|
+
width: texture.width,
|
|
551
|
+
height: texture.height,
|
|
552
|
+
colorAttachments: texture,
|
|
553
|
+
depthAttachment: null
|
|
554
|
+
}));
|
|
555
|
+
if (temporal && history) {
|
|
556
|
+
// Commit whichever texture the renderer settled on, so the next frame
|
|
557
|
+
// reprojects the map that was actually shown rather than the
|
|
558
|
+
// pre-resolve one.
|
|
559
|
+
history.queueRetainedCommit(RGHistoryResources.WATER_CAUSTICS, mapDesc, mapSize, resolved);
|
|
560
|
+
}
|
|
561
|
+
// Publish only after the map exists: the light pass keys its shader
|
|
562
|
+
// variant on this flag and binds the texture in the same step.
|
|
563
|
+
ctx.waterCaustics = true;
|
|
564
|
+
ctx.waterCausticLight = source.light;
|
|
565
|
+
ctx.waterCausticTexture = resolved;
|
|
566
|
+
ctx.waterCausticHeightTexture = heights;
|
|
567
|
+
ctx.waterCausticUniforms = _waterCausticsRenderer.uniforms;
|
|
568
|
+
});
|
|
569
|
+
return {
|
|
570
|
+
mapHandle,
|
|
571
|
+
scratchHandle
|
|
572
|
+
};
|
|
573
|
+
});
|
|
574
|
+
// Which target holds the result depends on whether the resolve ran. Both are
|
|
575
|
+
// declared by this pass, so either is a valid handle for consumers to read.
|
|
576
|
+
blackboard.set(FrameResources.WaterCaustics, temporal && previousHandle ? result.scratchHandle : result.mapHandle);
|
|
577
|
+
}
|
|
578
|
+
};
|
|
404
579
|
/** @internal */ const DepthPrepassModule = {
|
|
405
580
|
type: 'DepthPrepass',
|
|
406
581
|
writes: [
|
|
407
582
|
FrameResources.LinearDepth,
|
|
408
583
|
FrameResources.MotionVector,
|
|
409
|
-
FrameResources.SceneDepthAttachment
|
|
584
|
+
FrameResources.SceneDepthAttachment,
|
|
585
|
+
FrameResources.SSSProfileId
|
|
410
586
|
],
|
|
411
587
|
prepare: ()=>({
|
|
412
588
|
enabled: true
|
|
@@ -415,7 +591,7 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
415
591
|
const { graph, ctx, frame, ordering, blackboard, options } = fg;
|
|
416
592
|
const result = graph.addPass('DepthPrepass', (builder)=>{
|
|
417
593
|
ordering.chainInto(builder);
|
|
418
|
-
const format = ctx.
|
|
594
|
+
const format = ctx.SSRCalcThickness ? 'rg32f' : 'r32f';
|
|
419
595
|
const mvFormat = 'rgba16f';
|
|
420
596
|
const depthHandle = builder.createTexture({
|
|
421
597
|
format,
|
|
@@ -427,6 +603,14 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
427
603
|
label: 'motionVector',
|
|
428
604
|
allocationKey: 'ForwardPlus.MotionVector'
|
|
429
605
|
}) : undefined;
|
|
606
|
+
// r8unorm is exact for this: the material writes `SSSProfile.encodedId`,
|
|
607
|
+
// i.e. `id / 255`, and the table holds 256 rows, so every representable id
|
|
608
|
+
// survives the round trip. 0 means "not skin".
|
|
609
|
+
const sssProfileIdHandle = options.sssProfileId ? builder.createTexture({
|
|
610
|
+
format: 'r8unorm',
|
|
611
|
+
label: 'sssProfileId',
|
|
612
|
+
allocationKey: 'ForwardPlus.SSSProfileId'
|
|
613
|
+
}) : undefined;
|
|
430
614
|
const finalDepthAttachment = ctx.finalFramebuffer?.getDepthAttachment();
|
|
431
615
|
const externalDepthAttachment = finalDepthAttachment?.isTexture2D() ? finalDepthAttachment : null;
|
|
432
616
|
const externalDepthAttachmentHandle = externalDepthAttachment ? graph.importTexture('externalSceneDepth') : undefined;
|
|
@@ -436,14 +620,23 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
436
620
|
allocationKey: 'ForwardPlus.SceneDepth'
|
|
437
621
|
});
|
|
438
622
|
const depthAttachmentOrFormat = externalDepthAttachmentHandle ?? graphDepthAttachmentHandle ?? ctx.depthFormat;
|
|
623
|
+
// Order matters and has to agree with the fragment output declaration
|
|
624
|
+
// order in MeshMaterial._createProgram: linear depth, then the motion
|
|
625
|
+
// vector, then the profile id.
|
|
626
|
+
const prepassColorAttachments = [
|
|
627
|
+
depthHandle
|
|
628
|
+
];
|
|
629
|
+
if (motionVectorHandle) {
|
|
630
|
+
prepassColorAttachments.push(motionVectorHandle);
|
|
631
|
+
}
|
|
632
|
+
if (sssProfileIdHandle) {
|
|
633
|
+
prepassColorAttachments.push(sssProfileIdHandle);
|
|
634
|
+
}
|
|
439
635
|
const depthFramebufferHandle = builder.createFramebuffer({
|
|
440
636
|
label: 'DepthPrepassFramebuffer',
|
|
441
637
|
width: ctx.renderWidth,
|
|
442
638
|
height: ctx.renderHeight,
|
|
443
|
-
colorAttachments:
|
|
444
|
-
depthHandle,
|
|
445
|
-
motionVectorHandle
|
|
446
|
-
] : depthHandle,
|
|
639
|
+
colorAttachments: prepassColorAttachments.length === 1 ? depthHandle : prepassColorAttachments,
|
|
447
640
|
depthAttachment: depthAttachmentOrFormat,
|
|
448
641
|
ignoreDepthStencil: false
|
|
449
642
|
});
|
|
@@ -471,6 +664,7 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
471
664
|
return {
|
|
472
665
|
depthHandle,
|
|
473
666
|
motionVectorHandle,
|
|
667
|
+
sssProfileIdHandle: sssProfileIdHandle,
|
|
474
668
|
graphDepthAttachmentHandle,
|
|
475
669
|
externalDepthAttachmentHandle,
|
|
476
670
|
externalDepthAttachment,
|
|
@@ -483,6 +677,9 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
483
677
|
if (result.motionVectorHandle) {
|
|
484
678
|
blackboard.set(FrameResources.MotionVector, result.motionVectorHandle);
|
|
485
679
|
}
|
|
680
|
+
if (result.sssProfileIdHandle) {
|
|
681
|
+
blackboard.set(FrameResources.SSSProfileId, result.sssProfileIdHandle);
|
|
682
|
+
}
|
|
486
683
|
blackboard.set(FrameResources.SceneDepthAttachment, result.graphDepthAttachmentHandle ?? result.externalDepthAttachmentHandle);
|
|
487
684
|
// Later modules share this attachment without owning it.
|
|
488
685
|
fg.state.renderDepthAttachment = result.graphDepthAttachmentHandle ?? result.externalDepthAttachmentHandle ?? null;
|
|
@@ -500,7 +697,7 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
500
697
|
prepare: ({ ctx, options, renderQueue })=>({
|
|
501
698
|
enabled: options.shadowMask && renderQueue.shadowedLights.length > 0,
|
|
502
699
|
requirements: {
|
|
503
|
-
shadowMask: ctx.
|
|
700
|
+
shadowMask: ctx.camera.screenSpaceShadowMask && renderQueue.shadowedLights.length > 0
|
|
504
701
|
}
|
|
505
702
|
}),
|
|
506
703
|
setup (fg) {
|
|
@@ -539,6 +736,107 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
539
736
|
blackboard.set(FrameResources.ShadowMask, maskPassResult.maskHandle);
|
|
540
737
|
}
|
|
541
738
|
};
|
|
739
|
+
/** Orders a pass drawing scene materials after this camera's virtual texture update. @internal */ function readVirtualTextureToken(builder, blackboard) {
|
|
740
|
+
const token = blackboard.get(FrameResources.VirtualTexture);
|
|
741
|
+
if (token) {
|
|
742
|
+
builder.read(token);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
/** Scene virtual texture clients active for this camera's frame. @internal */ function activeVirtualTextureClients(ctx, renderQueue) {
|
|
746
|
+
if (ctx.device.type !== 'webgpu') {
|
|
747
|
+
return [];
|
|
748
|
+
}
|
|
749
|
+
return ctx.scene.virtualTextureClients.filter((c)=>c.isActive?.(ctx, renderQueue) ?? true);
|
|
750
|
+
}
|
|
751
|
+
/**
|
|
752
|
+
* Updates the scene's virtual textures for this camera: mark from the depth prepass, map, fill.
|
|
753
|
+
*/ const VirtualTextureModule = {
|
|
754
|
+
type: 'VirtualTextureUpdate',
|
|
755
|
+
reads: [
|
|
756
|
+
{
|
|
757
|
+
resource: FrameResources.LinearDepth
|
|
758
|
+
}
|
|
759
|
+
],
|
|
760
|
+
writes: [
|
|
761
|
+
FrameResources.VirtualTexture
|
|
762
|
+
],
|
|
763
|
+
prepare: ({ ctx, renderQueue })=>({
|
|
764
|
+
enabled: activeVirtualTextureClients(ctx, renderQueue).length > 0
|
|
765
|
+
}),
|
|
766
|
+
setup (fg) {
|
|
767
|
+
const { graph, ctx, renderQueue, blackboard } = fg;
|
|
768
|
+
const clients = activeVirtualTextureClients(ctx, renderQueue);
|
|
769
|
+
const token = graph.addPass('VirtualTextureUpdate', (builder)=>{
|
|
770
|
+
const depthHandle = blackboard.expect(FrameResources.LinearDepth);
|
|
771
|
+
builder.read(depthHandle);
|
|
772
|
+
// Page tables and pools are scene state the graph does not track
|
|
773
|
+
builder.sideEffect();
|
|
774
|
+
const token = builder.createToken('VirtualTextureDone');
|
|
775
|
+
builder.setExecute((rgCtx)=>{
|
|
776
|
+
const depthTex = rgCtx.getTexture(depthHandle);
|
|
777
|
+
const stamp = ctx.device.frameInfo.frameCounter;
|
|
778
|
+
for (const client of clients){
|
|
779
|
+
client.markFromDepth?.(ctx, depthTex);
|
|
780
|
+
}
|
|
781
|
+
for (const client of clients){
|
|
782
|
+
client.virtualTexture.update(stamp);
|
|
783
|
+
client.fill(ctx);
|
|
784
|
+
}
|
|
785
|
+
});
|
|
786
|
+
return token;
|
|
787
|
+
});
|
|
788
|
+
blackboard.set(FrameResources.VirtualTexture, token);
|
|
789
|
+
}
|
|
790
|
+
};
|
|
791
|
+
/** @internal */ const TransmissionThicknessModule = {
|
|
792
|
+
type: 'TransmissionThicknessPass',
|
|
793
|
+
writes: [
|
|
794
|
+
FrameResources.TransmissionThickness
|
|
795
|
+
],
|
|
796
|
+
prepare: ({ options, renderQueue })=>({
|
|
797
|
+
// Not gated on the device: transmission lights the subject directly in the
|
|
798
|
+
// base pass, so it does not depend on the WebGPU-only PostSSS diffusion.
|
|
799
|
+
enabled: options.postSSS && renderQueue.shadowedLights.some((light)=>light.transmission)
|
|
800
|
+
}),
|
|
801
|
+
setup (fg) {
|
|
802
|
+
const { graph, ctx, renderQueue, blackboard } = fg;
|
|
803
|
+
const depthPassResult = requireBuildState(fg, 'depth', 'DepthPrepass', 'TransmissionThicknessPass');
|
|
804
|
+
// The ordinal space is shared with the shadow mask so that a light's
|
|
805
|
+
// thickness and its shadow factor are found with the same arithmetic; that
|
|
806
|
+
// means the layer count follows the shadow light count, not the (smaller)
|
|
807
|
+
// transmission light count.
|
|
808
|
+
const numShadowLights = renderQueue.shadowedLights.length;
|
|
809
|
+
const numLayers = ShadowMaskRenderer.getLayerCount(numShadowLights);
|
|
810
|
+
const passResult = graph.addPass('TransmissionThicknessPass', (builder)=>{
|
|
811
|
+
const depthHandle = blackboard.expect(FrameResources.LinearDepth);
|
|
812
|
+
const sssProfileIdHandle = blackboard.expect(FrameResources.SSSProfileId);
|
|
813
|
+
builder.read(depthHandle);
|
|
814
|
+
builder.read(sssProfileIdHandle);
|
|
815
|
+
builder.read(depthPassResult.depthFramebufferHandle);
|
|
816
|
+
const thicknessHandle = builder.createTexture({
|
|
817
|
+
format: 'rgba8unorm',
|
|
818
|
+
label: 'transmissionThickness',
|
|
819
|
+
arrayLayers: numLayers,
|
|
820
|
+
allocationKey: 'ForwardPlus.TransmissionThickness'
|
|
821
|
+
});
|
|
822
|
+
builder.setExecute((rgCtx)=>{
|
|
823
|
+
const depthTex = rgCtx.getTexture(depthHandle);
|
|
824
|
+
const thicknessTex = rgCtx.getTexture(thicknessHandle);
|
|
825
|
+
_transmissionThicknessRenderer.render(ctx, depthTex, rgCtx.getTexture(sssProfileIdHandle), renderQueue.shadowedLights, (layer)=>rgCtx.createFramebuffer({
|
|
826
|
+
width: thicknessTex.width,
|
|
827
|
+
height: thicknessTex.height,
|
|
828
|
+
colorAttachments: thicknessTex,
|
|
829
|
+
depthAttachment: null,
|
|
830
|
+
attachmentLayer: layer
|
|
831
|
+
}));
|
|
832
|
+
});
|
|
833
|
+
return {
|
|
834
|
+
thicknessHandle
|
|
835
|
+
};
|
|
836
|
+
});
|
|
837
|
+
blackboard.set(FrameResources.TransmissionThickness, passResult.thicknessHandle);
|
|
838
|
+
}
|
|
839
|
+
};
|
|
542
840
|
/** @internal */ const TransmissionDepthForSSRModule = {
|
|
543
841
|
type: 'TransmissionDepthForSSR',
|
|
544
842
|
writes: [
|
|
@@ -580,7 +878,7 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
580
878
|
enabled: options.hiZ
|
|
581
879
|
}),
|
|
582
880
|
setup (fg) {
|
|
583
|
-
const { graph, ctx, frame, blackboard } = fg;
|
|
881
|
+
const { graph, ctx, frame, blackboard, options } = fg;
|
|
584
882
|
const depthPassResult = requireBuildState(fg, 'depth', 'DepthPrepass', 'HiZ');
|
|
585
883
|
const preLightTransmissionDepthToken = fg.state.preLightTransmissionDepthToken;
|
|
586
884
|
//const hiZWidth = nextPowerOf2(ctx.renderWidth);
|
|
@@ -593,11 +891,14 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
593
891
|
builder.read(preLightTransmissionDepthToken);
|
|
594
892
|
}
|
|
595
893
|
hiZHandle = builder.createTexture({
|
|
596
|
-
format:
|
|
894
|
+
format: getHiZFormat(options.hiZNearest),
|
|
597
895
|
label: 'hiZ',
|
|
598
896
|
sizeMode: 'backbuffer-relative',
|
|
599
897
|
mipLevels: getFullMipLevelCount(ctx.renderWidth, ctx.renderHeight),
|
|
600
|
-
|
|
898
|
+
// Keyed on the channel count: a one- and a two-channel pyramid are
|
|
899
|
+
// different textures, and pooling them under one key would hand a
|
|
900
|
+
// consumer a pyramid with no nearest channel in it.
|
|
901
|
+
allocationKey: `ForwardPlus.HiZ${options.hiZNearest ? '.MinMax' : ''}`
|
|
601
902
|
});
|
|
602
903
|
const hiZFramebufferHandle = builder.createFramebuffer({
|
|
603
904
|
label: 'HiZFramebuffer',
|
|
@@ -633,9 +934,12 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
633
934
|
const depthPassResult = requireBuildState(fg, 'depth', 'DepthPrepass', 'SSSProfile');
|
|
634
935
|
const preLightTransmissionDepthToken = fg.state.preLightTransmissionDepthToken;
|
|
635
936
|
const shadowMaskHandle = blackboard.get(FrameResources.ShadowMask);
|
|
937
|
+
const thicknessHandle = blackboard.get(FrameResources.TransmissionThickness);
|
|
938
|
+
const waterCausticsHandle = blackboard.get(FrameResources.WaterCaustics);
|
|
636
939
|
const renderDepthAttachment = fg.state.renderDepthAttachment;
|
|
637
940
|
const sssProfileResult = graph.addPass('SSSProfile', (builder)=>{
|
|
638
941
|
builder.read(blackboard.expect(FrameResources.LinearDepth));
|
|
942
|
+
readVirtualTextureToken(builder, blackboard);
|
|
639
943
|
builder.read(depthPassResult.depthFramebufferHandle);
|
|
640
944
|
if (preLightTransmissionDepthToken) {
|
|
641
945
|
builder.read(preLightTransmissionDepthToken);
|
|
@@ -643,6 +947,15 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
643
947
|
if (shadowMaskHandle) {
|
|
644
948
|
builder.read(shadowMaskHandle);
|
|
645
949
|
}
|
|
950
|
+
if (thicknessHandle) {
|
|
951
|
+
builder.read(thicknessHandle);
|
|
952
|
+
}
|
|
953
|
+
if (waterCausticsHandle) {
|
|
954
|
+
// Same reason as the shadow mask: the map reaches the shader through
|
|
955
|
+
// ctx.waterCausticTexture, so only a declared read stops the executor
|
|
956
|
+
// from recycling it while this pass is still sampling it.
|
|
957
|
+
builder.read(waterCausticsHandle);
|
|
958
|
+
}
|
|
646
959
|
const profileHandle = builder.createTexture({
|
|
647
960
|
format: 'rgba16f',
|
|
648
961
|
label: 'sssProfile',
|
|
@@ -668,6 +981,7 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
668
981
|
});
|
|
669
982
|
builder.setExecute((rgCtx)=>{
|
|
670
983
|
ctx.shadowMaskTexture = shadowMaskHandle ? rgCtx.getTexture(shadowMaskHandle) : null;
|
|
984
|
+
ctx.transmissionThicknessTexture = thicknessHandle ? rgCtx.getTexture(thicknessHandle) : null;
|
|
671
985
|
renderForwardSSSProfile(frame, rgCtx.getFramebuffer(framebufferHandle), rgCtx.getTexture(profileHandle), rgCtx.getTexture(paramHandle), null);
|
|
672
986
|
});
|
|
673
987
|
return {
|
|
@@ -691,9 +1005,12 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
691
1005
|
const depthPassResult = requireBuildState(fg, 'depth', 'DepthPrepass', 'SceneColorGrab');
|
|
692
1006
|
const preLightTransmissionDepthToken = fg.state.preLightTransmissionDepthToken;
|
|
693
1007
|
const shadowMaskHandle = blackboard.get(FrameResources.ShadowMask);
|
|
1008
|
+
const thicknessHandle = blackboard.get(FrameResources.TransmissionThickness);
|
|
1009
|
+
const waterCausticsHandle = blackboard.get(FrameResources.WaterCaustics);
|
|
694
1010
|
const renderDepthAttachment = fg.state.renderDepthAttachment;
|
|
695
1011
|
const grabResult = graph.addPass('SceneColorGrab', (builder)=>{
|
|
696
1012
|
builder.read(blackboard.expect(FrameResources.LinearDepth));
|
|
1013
|
+
readVirtualTextureToken(builder, blackboard);
|
|
697
1014
|
builder.read(depthPassResult.depthFramebufferHandle);
|
|
698
1015
|
if (preLightTransmissionDepthToken) {
|
|
699
1016
|
builder.read(preLightTransmissionDepthToken);
|
|
@@ -701,9 +1018,37 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
701
1018
|
if (shadowMaskHandle) {
|
|
702
1019
|
builder.read(shadowMaskHandle);
|
|
703
1020
|
}
|
|
1021
|
+
if (thicknessHandle) {
|
|
1022
|
+
builder.read(thicknessHandle);
|
|
1023
|
+
}
|
|
1024
|
+
if (waterCausticsHandle) {
|
|
1025
|
+
// Same reason as the shadow mask: the map reaches the shader through
|
|
1026
|
+
// ctx.waterCausticTexture, so only a declared read stops the executor
|
|
1027
|
+
// from recycling it while this pass is still sampling it.
|
|
1028
|
+
builder.read(waterCausticsHandle);
|
|
1029
|
+
}
|
|
1030
|
+
// A few mip levels, because the refraction background is sampled at a LOD
|
|
1031
|
+
// and not only at mip 0: water picks one from how deep and how turbid the
|
|
1032
|
+
// column is, so a stone under clear shallows stays sharp while the same
|
|
1033
|
+
// stone under metres of silt smears. With a single-level texture the
|
|
1034
|
+
// sampler returned mip 0 whatever LOD was asked for, and the blur did
|
|
1035
|
+
// nothing.
|
|
1036
|
+
//
|
|
1037
|
+
// Nothing here generates the chain - both backends do it when the
|
|
1038
|
+
// framebuffer is unbound, for any colour attachment with more than one
|
|
1039
|
+
// level (WebGL `framebuffer_webgl.ts` unbind, WebGPU `renderpass_webgpu.ts`
|
|
1040
|
+
// end), and `generateMipmaps` defaults to true on colour attachments. The
|
|
1041
|
+
// level count is the whole precondition.
|
|
1042
|
+
//
|
|
1043
|
+
// Not the full chain down to 1x1. The consumer clamps its LOD, and the
|
|
1044
|
+
// coarsest levels of a full chain average across the whole frame - a
|
|
1045
|
+
// sample from one stops being "what is behind the water" and becomes the
|
|
1046
|
+
// mean of the image. Generating them would cost bandwidth every frame to
|
|
1047
|
+
// produce levels nothing may read.
|
|
704
1048
|
const copyHandle = builder.createTexture({
|
|
705
1049
|
format: ctx.colorFormat,
|
|
706
1050
|
label: 'sceneColorCopy',
|
|
1051
|
+
mipLevels: getSceneColorMipLevelCount(ctx.renderWidth, ctx.renderHeight),
|
|
707
1052
|
allocationKey: 'ForwardPlus.SceneColorCopy'
|
|
708
1053
|
});
|
|
709
1054
|
// Isolate depth when SSR inserts transmission depth before LightPass.
|
|
@@ -717,6 +1062,7 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
717
1062
|
}) : undefined;
|
|
718
1063
|
builder.setExecute((rgCtx)=>{
|
|
719
1064
|
ctx.shadowMaskTexture = shadowMaskHandle ? rgCtx.getTexture(shadowMaskHandle) : null;
|
|
1065
|
+
ctx.transmissionThicknessTexture = thicknessHandle ? rgCtx.getTexture(thicknessHandle) : null;
|
|
720
1066
|
renderSceneColorGrab(frame, rgCtx, copyHandle, copyFramebufferHandle);
|
|
721
1067
|
});
|
|
722
1068
|
return {
|
|
@@ -735,7 +1081,7 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
735
1081
|
FrameResources.SceneNormal,
|
|
736
1082
|
FrameResources.SSSDiffuse,
|
|
737
1083
|
FrameResources.SSSTransmission,
|
|
738
|
-
FrameResources.
|
|
1084
|
+
FrameResources.PostSSS
|
|
739
1085
|
],
|
|
740
1086
|
prepare: ()=>({
|
|
741
1087
|
enabled: true
|
|
@@ -744,10 +1090,13 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
744
1090
|
const { graph, ctx, frame, blackboard, options, backbuffer } = fg;
|
|
745
1091
|
const depthPassResult = requireBuildState(fg, 'depth', 'DepthPrepass', 'LightPass');
|
|
746
1092
|
const shadowMaskHandle = blackboard.get(FrameResources.ShadowMask);
|
|
1093
|
+
const thicknessHandle = blackboard.get(FrameResources.TransmissionThickness);
|
|
1094
|
+
const waterCausticsHandle = blackboard.get(FrameResources.WaterCaustics);
|
|
747
1095
|
const preLightTransmissionDepthToken = fg.state.preLightTransmissionDepthToken;
|
|
748
1096
|
const hiZHandle = blackboard.get(FrameResources.HiZ);
|
|
749
1097
|
const sssProfileHandle = blackboard.get(FrameResources.SSSProfile);
|
|
750
1098
|
const sssParamHandle = blackboard.get(FrameResources.SSSParam);
|
|
1099
|
+
const sssProfileIdHandle = blackboard.get(FrameResources.SSSProfileId);
|
|
751
1100
|
const sceneColorCopyHandle = blackboard.get(FrameResources.SceneColorCopy);
|
|
752
1101
|
const useFinalFramebufferAsIntermediate = fg.state.useFinalFramebufferAsIntermediate;
|
|
753
1102
|
const renderDepthAttachment = fg.state.renderDepthAttachment;
|
|
@@ -810,10 +1159,20 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
810
1159
|
}
|
|
811
1160
|
const opaquePassResult = graph.addPass('LightPass', (builder)=>{
|
|
812
1161
|
builder.read(blackboard.expect(FrameResources.LinearDepth));
|
|
1162
|
+
readVirtualTextureToken(builder, blackboard);
|
|
813
1163
|
builder.read(depthPassResult.depthFramebufferHandle);
|
|
814
1164
|
if (shadowMaskHandle) {
|
|
815
1165
|
builder.read(shadowMaskHandle);
|
|
816
1166
|
}
|
|
1167
|
+
if (thicknessHandle) {
|
|
1168
|
+
builder.read(thicknessHandle);
|
|
1169
|
+
}
|
|
1170
|
+
if (waterCausticsHandle) {
|
|
1171
|
+
// Same reason as the shadow mask: the map reaches the shader through
|
|
1172
|
+
// ctx.waterCausticTexture, so only a declared read stops the executor
|
|
1173
|
+
// from recycling it while this pass is still sampling it.
|
|
1174
|
+
builder.read(waterCausticsHandle);
|
|
1175
|
+
}
|
|
817
1176
|
if (preLightTransmissionDepthToken) {
|
|
818
1177
|
builder.read(preLightTransmissionDepthToken);
|
|
819
1178
|
}
|
|
@@ -838,6 +1197,11 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
838
1197
|
if (sssParamHandle) {
|
|
839
1198
|
builder.read(sssParamHandle);
|
|
840
1199
|
}
|
|
1200
|
+
// Reached through ctx.SSSProfileIdTexture, so the read must be declared or
|
|
1201
|
+
// the executor may recycle the prepass target before the effect samples it.
|
|
1202
|
+
if (sssProfileIdHandle) {
|
|
1203
|
+
builder.read(sssProfileIdHandle);
|
|
1204
|
+
}
|
|
841
1205
|
const surfaceAttachmentCount = Number(options.sceneRoughness) + Number(options.sceneNormal);
|
|
842
1206
|
// Surface MRT products are exposed through blackboard handles.
|
|
843
1207
|
const sceneRoughnessHandle = options.sceneRoughness ? builder.createTexture({
|
|
@@ -855,8 +1219,8 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
855
1219
|
if (writeSSSDiffuse && writeSSSTransmission && surfaceAttachmentCount > 0 && getSSSLightingTextureFormat(ctx, 2, surfaceAttachmentCount) !== ctx.colorFormat) {
|
|
856
1220
|
writeSSSTransmission = false;
|
|
857
1221
|
}
|
|
858
|
-
const
|
|
859
|
-
const sssLightingAttachmentCount = (writeSSSDiffuse ? 1 : 0) + (writeSSSTransmission ? 1 : 0) + (
|
|
1222
|
+
const writePostSSS = options.postSSS;
|
|
1223
|
+
const sssLightingAttachmentCount = (writeSSSDiffuse ? 1 : 0) + (writeSSSTransmission ? 1 : 0) + (writePostSSS ? 1 : 0);
|
|
860
1224
|
const sssLightingFormat = getSSSLightingTextureFormat(ctx, sssLightingAttachmentCount, surfaceAttachmentCount);
|
|
861
1225
|
const sssDiffuseHandle = writeSSSDiffuse ? builder.createTexture({
|
|
862
1226
|
format: sssLightingFormat,
|
|
@@ -868,10 +1232,10 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
868
1232
|
label: 'sssTransmission',
|
|
869
1233
|
allocationKey: 'ForwardPlus.SSSTransmission'
|
|
870
1234
|
}) : undefined;
|
|
871
|
-
const
|
|
1235
|
+
const postSSSHandle = writePostSSS ? builder.createTexture({
|
|
872
1236
|
format: sssLightingFormat,
|
|
873
|
-
label: '
|
|
874
|
-
allocationKey: 'ForwardPlus.
|
|
1237
|
+
label: 'postSSS',
|
|
1238
|
+
allocationKey: 'ForwardPlus.PostSSS'
|
|
875
1239
|
}) : undefined;
|
|
876
1240
|
const sceneColorFramebufferHandle = useFinalFramebufferAsIntermediate ? undefined : builder.createFramebuffer({
|
|
877
1241
|
label: 'SceneColorFramebuffer',
|
|
@@ -885,6 +1249,7 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
885
1249
|
const sceneColorCopyTex = sceneColorCopyHandle ? rgCtx.getTexture(sceneColorCopyHandle) : null;
|
|
886
1250
|
// Legacy effects still read resolved MRT textures from DrawContext.
|
|
887
1251
|
ctx.shadowMaskTexture = shadowMaskHandle ? rgCtx.getTexture(shadowMaskHandle) : null;
|
|
1252
|
+
ctx.transmissionThicknessTexture = thicknessHandle ? rgCtx.getTexture(thicknessHandle) : null;
|
|
888
1253
|
ctx.HiZTexture = hiZHandle ? rgCtx.getTexture(hiZHandle) : null;
|
|
889
1254
|
ctx.SceneRoughnessTexture = sceneRoughnessHandle ? rgCtx.getTexture(sceneRoughnessHandle) : null;
|
|
890
1255
|
ctx.SceneNormalTexture = sceneNormalHandle ? rgCtx.getTexture(sceneNormalHandle) : null;
|
|
@@ -892,7 +1257,8 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
892
1257
|
ctx.SSSParamTexture = sssParamHandle ? rgCtx.getTexture(sssParamHandle) : null;
|
|
893
1258
|
ctx.SSSDiffuseTexture = sssDiffuseHandle ? rgCtx.getTexture(sssDiffuseHandle) : null;
|
|
894
1259
|
ctx.SSSTransmissionTexture = sssTransmissionHandle ? rgCtx.getTexture(sssTransmissionHandle) : null;
|
|
895
|
-
ctx.
|
|
1260
|
+
ctx.SSSMaskTexture = postSSSHandle ? rgCtx.getTexture(postSSSHandle) : null;
|
|
1261
|
+
ctx.SSSProfileIdTexture = sssProfileIdHandle ? rgCtx.getTexture(sssProfileIdHandle) : null;
|
|
896
1262
|
const renderLightPass = ()=>renderOpaqueScenePass(frame, sceneColorTex, sceneColorCopyTex, rgCtx, sceneColorFramebufferHandle);
|
|
897
1263
|
if (historyManager && lightHistoryReadBindings.length > 0) {
|
|
898
1264
|
const ssgiIrradianceBinding = lightHistoryReadBindings.find((binding)=>binding.name === RGHistoryResources.SSGI_IRRADIANCE);
|
|
@@ -921,7 +1287,7 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
921
1287
|
sceneNormalHandle,
|
|
922
1288
|
sssDiffuseHandle,
|
|
923
1289
|
sssTransmissionHandle,
|
|
924
|
-
|
|
1290
|
+
postSSSHandle: postSSSHandle
|
|
925
1291
|
};
|
|
926
1292
|
});
|
|
927
1293
|
const lightPassResult = opaquePassResult;
|
|
@@ -939,8 +1305,8 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
939
1305
|
if (lightPassResult.sssTransmissionHandle) {
|
|
940
1306
|
blackboard.set(FrameResources.SSSTransmission, lightPassResult.sssTransmissionHandle);
|
|
941
1307
|
}
|
|
942
|
-
if (lightPassResult.
|
|
943
|
-
blackboard.set(FrameResources.
|
|
1308
|
+
if (lightPassResult.postSSSHandle) {
|
|
1309
|
+
blackboard.set(FrameResources.PostSSS, lightPassResult.postSSSHandle);
|
|
944
1310
|
}
|
|
945
1311
|
}
|
|
946
1312
|
};
|
|
@@ -1001,6 +1367,68 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
1001
1367
|
}
|
|
1002
1368
|
}
|
|
1003
1369
|
};
|
|
1370
|
+
/** @internal */ const UnderwaterModule = {
|
|
1371
|
+
type: 'Underwater',
|
|
1372
|
+
reads: [
|
|
1373
|
+
{
|
|
1374
|
+
resource: FrameResources.SceneColor,
|
|
1375
|
+
version: 'current'
|
|
1376
|
+
}
|
|
1377
|
+
],
|
|
1378
|
+
writes: [
|
|
1379
|
+
FrameResources.SceneColor
|
|
1380
|
+
],
|
|
1381
|
+
prepare: ({ ctx })=>({
|
|
1382
|
+
enabled: !!ctx.underwater
|
|
1383
|
+
}),
|
|
1384
|
+
setup (fg) {
|
|
1385
|
+
const { graph, ctx, frame, blackboard } = fg;
|
|
1386
|
+
const depthPassResult = requireBuildState(fg, 'depth', 'DepthPrepass', 'Underwater');
|
|
1387
|
+
const lightPassResult = requireBuildState(fg, 'lightPass', 'LightPass', 'Underwater');
|
|
1388
|
+
const sceneColorHandle = blackboard.expect(FrameResources.SceneColor);
|
|
1389
|
+
const waterCausticsHandle = blackboard.get(FrameResources.WaterCaustics);
|
|
1390
|
+
const state = ctx.underwater;
|
|
1391
|
+
// After the sky, before the transparent pass: the medium has to cover the
|
|
1392
|
+
// sky - that is what the horizon fades into underwater - and the opaque
|
|
1393
|
+
// scene is the only thing the depth buffer describes, so it is also the only
|
|
1394
|
+
// thing this can reach. Transparent geometry is on its own, exactly as it is
|
|
1395
|
+
// for atmospheric fog.
|
|
1396
|
+
const underwaterColorHandle = graph.addPass('Underwater', (builder)=>{
|
|
1397
|
+
builder.read(sceneColorHandle);
|
|
1398
|
+
builder.read(depthPassResult.depthFramebufferHandle);
|
|
1399
|
+
if (lightPassResult.sceneColorFramebufferHandle) {
|
|
1400
|
+
builder.read(lightPassResult.sceneColorFramebufferHandle);
|
|
1401
|
+
}
|
|
1402
|
+
if (waterCausticsHandle) {
|
|
1403
|
+
// The light shafts sample the caustic map, which reaches the shader
|
|
1404
|
+
// through ctx.waterCausticTexture rather than through a handle. Same
|
|
1405
|
+
// lifetime argument as the shadow mask in CompositeTail: only a declared
|
|
1406
|
+
// read stops the executor returning it to the pool after LightPass.
|
|
1407
|
+
builder.read(waterCausticsHandle);
|
|
1408
|
+
}
|
|
1409
|
+
const out = builder.write(sceneColorHandle);
|
|
1410
|
+
builder.setExecute((rgCtx)=>{
|
|
1411
|
+
const colorTexture = rgCtx.getTexture(sceneColorHandle);
|
|
1412
|
+
const depthTexture = frame.depthFramebuffer?.getDepthAttachment();
|
|
1413
|
+
if (!colorTexture || !depthTexture) {
|
|
1414
|
+
return;
|
|
1415
|
+
}
|
|
1416
|
+
// Color only. The scene depth is sampled by this pass, which it cannot
|
|
1417
|
+
// be while it is still attached to the bound framebuffer.
|
|
1418
|
+
const colorFramebuffer = rgCtx.createFramebuffer({
|
|
1419
|
+
width: colorTexture.width,
|
|
1420
|
+
height: colorTexture.height,
|
|
1421
|
+
colorAttachments: colorTexture,
|
|
1422
|
+
depthAttachment: null
|
|
1423
|
+
});
|
|
1424
|
+
_underwaterRenderer.render(ctx, state, colorFramebuffer, depthTexture);
|
|
1425
|
+
});
|
|
1426
|
+
return out;
|
|
1427
|
+
});
|
|
1428
|
+
lightPassResult.sceneColorHandle = underwaterColorHandle;
|
|
1429
|
+
blackboard.set(FrameResources.SceneColor, underwaterColorHandle);
|
|
1430
|
+
}
|
|
1431
|
+
};
|
|
1004
1432
|
/** @internal */ const CompositeTailModule = {
|
|
1005
1433
|
type: 'CompositeTail',
|
|
1006
1434
|
writes: [
|
|
@@ -1017,6 +1445,8 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
1017
1445
|
const hiZHandle = blackboard.get(FrameResources.HiZ);
|
|
1018
1446
|
const sceneColorCopyHandle = blackboard.get(FrameResources.SceneColorCopy);
|
|
1019
1447
|
const shadowMaskHandle = blackboard.get(FrameResources.ShadowMask);
|
|
1448
|
+
const thicknessHandle = blackboard.get(FrameResources.TransmissionThickness);
|
|
1449
|
+
const waterCausticsHandle = blackboard.get(FrameResources.WaterCaustics);
|
|
1020
1450
|
const lightPassResult = requireBuildState(fg, 'lightPass', 'LightPass', 'CompositeTail');
|
|
1021
1451
|
const renderDepthAttachment = fg.state.renderDepthAttachment;
|
|
1022
1452
|
const useFinalFramebufferAsIntermediate = fg.state.useFinalFramebufferAsIntermediate;
|
|
@@ -1044,7 +1474,10 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
1044
1474
|
blackboard.get(FrameResources.SceneNormal),
|
|
1045
1475
|
blackboard.get(FrameResources.SSSDiffuse),
|
|
1046
1476
|
blackboard.get(FrameResources.SSSTransmission),
|
|
1047
|
-
blackboard.get(FrameResources.
|
|
1477
|
+
blackboard.get(FrameResources.PostSSS),
|
|
1478
|
+
// Reached through ctx.SSSProfileIdTexture: without the dependency the
|
|
1479
|
+
// allocator may hand the prepass target to a later pass.
|
|
1480
|
+
blackboard.get(FrameResources.SSSProfileId)
|
|
1048
1481
|
]){
|
|
1049
1482
|
if (handle) {
|
|
1050
1483
|
opaqueChainDeps.push(handle);
|
|
@@ -1069,6 +1502,7 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
1069
1502
|
// Transparent geometry writes a new version of the opaque-chain output.
|
|
1070
1503
|
const sceneColorHandle = graph.addPass('TransparentPass', (builder)=>{
|
|
1071
1504
|
builder.read(blackboard.expect(FrameResources.LinearDepth));
|
|
1505
|
+
readVirtualTextureToken(builder, blackboard);
|
|
1072
1506
|
builder.read(depthPassResult.depthFramebufferHandle);
|
|
1073
1507
|
if (hiZHandle) {
|
|
1074
1508
|
// Transparent materials may ray-march HiZ.
|
|
@@ -1090,6 +1524,16 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
1090
1524
|
// after LightPass while this pass is still sampling it.
|
|
1091
1525
|
builder.read(shadowMaskHandle);
|
|
1092
1526
|
}
|
|
1527
|
+
if (thicknessHandle) {
|
|
1528
|
+
// Same reasoning as the shadow mask above: the thickness texture is
|
|
1529
|
+
// bound by the shared clustered-light bind group, so its lifetime has to
|
|
1530
|
+
// reach this pass too.
|
|
1531
|
+
builder.read(thicknessHandle);
|
|
1532
|
+
}
|
|
1533
|
+
if (waterCausticsHandle) {
|
|
1534
|
+
// Same lifetime argument as the shadow mask above.
|
|
1535
|
+
builder.read(waterCausticsHandle);
|
|
1536
|
+
}
|
|
1093
1537
|
builder.read(opaqueChainResult.color);
|
|
1094
1538
|
const out = builder.write(opaqueChainResult.color);
|
|
1095
1539
|
builder.setExecute((rgCtx)=>{
|
|
@@ -1097,6 +1541,7 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
1097
1541
|
// is the same, but relying on the leftover would make this pass depend
|
|
1098
1542
|
// on execution order instead of on its own declared inputs.
|
|
1099
1543
|
ctx.shadowMaskTexture = shadowMaskHandle ? rgCtx.getTexture(shadowMaskHandle) : null;
|
|
1544
|
+
ctx.transmissionThicknessTexture = thicknessHandle ? rgCtx.getTexture(thicknessHandle) : null;
|
|
1100
1545
|
renderTransparentScenePass(frame, rgCtx, opaqueChainRan ? opaqueChainResult.color : null, lightPassResult.sceneColorFramebufferHandle);
|
|
1101
1546
|
});
|
|
1102
1547
|
return out;
|
|
@@ -1195,6 +1640,28 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
1195
1640
|
return outputBackbuffer;
|
|
1196
1641
|
});
|
|
1197
1642
|
}
|
|
1643
|
+
// Debug views of raw data go on the presented image, past tone mapping and exposure
|
|
1644
|
+
const sky = ctx.scene.env.sky;
|
|
1645
|
+
if (sky.cloudDebugViewActive) {
|
|
1646
|
+
const presentedBeforeDebug = presentedBackbuffer;
|
|
1647
|
+
presentedBackbuffer = graph.addPass('VolumetricCloudDebug', (builder)=>{
|
|
1648
|
+
const out = builder.write(presentedBeforeDebug);
|
|
1649
|
+
builder.sideEffect();
|
|
1650
|
+
builder.setExecute(()=>{
|
|
1651
|
+
const device = ctx.device;
|
|
1652
|
+
device.pushDeviceStates();
|
|
1653
|
+
try {
|
|
1654
|
+
device.setFramebuffer(ctx.finalFramebuffer ?? null);
|
|
1655
|
+
device.setViewport(null);
|
|
1656
|
+
device.setScissor(null);
|
|
1657
|
+
sky.drawCloudDebugView();
|
|
1658
|
+
} finally{
|
|
1659
|
+
device.popDeviceStates();
|
|
1660
|
+
}
|
|
1661
|
+
});
|
|
1662
|
+
return out;
|
|
1663
|
+
});
|
|
1664
|
+
}
|
|
1198
1665
|
fg.state.presentedBackbuffer = presentedBackbuffer;
|
|
1199
1666
|
// Downstream modules may replace this sink registration.
|
|
1200
1667
|
blackboard.set(FrameResources.PresentedColor, presentedBackbuffer);
|
|
@@ -1205,14 +1672,18 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
1205
1672
|
ClusterLights: ClusterLightsModule,
|
|
1206
1673
|
GPUPicking: GPUPickingModule,
|
|
1207
1674
|
ShadowMaps: ShadowMapsModule,
|
|
1675
|
+
WaterCaustics: WaterCausticsModule,
|
|
1208
1676
|
DepthPrepass: DepthPrepassModule,
|
|
1677
|
+
VirtualTextureUpdate: VirtualTextureModule,
|
|
1209
1678
|
ShadowMask: ShadowMaskModule,
|
|
1679
|
+
TransmissionThickness: TransmissionThicknessModule,
|
|
1210
1680
|
TransmissionDepthForSSR: TransmissionDepthForSSRModule,
|
|
1211
1681
|
HiZ: HiZModule,
|
|
1212
1682
|
SSSProfile: SSSProfileModule,
|
|
1213
1683
|
SceneColorGrab: SceneColorGrabModule,
|
|
1214
1684
|
LightPass: LightPassModule,
|
|
1215
1685
|
SkyPass: SkyPassModule,
|
|
1686
|
+
Underwater: UnderwaterModule,
|
|
1216
1687
|
CompositeTail: CompositeTailModule
|
|
1217
1688
|
};
|
|
1218
1689
|
/** The default module order assembled by {@link createForwardPlusPipeline}. */ const DEFAULT_FORWARD_PLUS_MODULES = [
|
|
@@ -1220,14 +1691,18 @@ function validateProducedFrameResources(blackboard, options, renderQueue) {
|
|
|
1220
1691
|
ClusterLightsModule,
|
|
1221
1692
|
GPUPickingModule,
|
|
1222
1693
|
ShadowMapsModule,
|
|
1694
|
+
WaterCausticsModule,
|
|
1223
1695
|
DepthPrepassModule,
|
|
1696
|
+
VirtualTextureModule,
|
|
1224
1697
|
ShadowMaskModule,
|
|
1698
|
+
TransmissionThicknessModule,
|
|
1225
1699
|
TransmissionDepthForSSRModule,
|
|
1226
1700
|
HiZModule,
|
|
1227
1701
|
SSSProfileModule,
|
|
1228
1702
|
SceneColorGrabModule,
|
|
1229
1703
|
LightPassModule,
|
|
1230
1704
|
SkyPassModule,
|
|
1705
|
+
UnderwaterModule,
|
|
1231
1706
|
CompositeTailModule
|
|
1232
1707
|
];
|
|
1233
1708
|
/** @internal */ let _defaultForwardPlusPipeline = null;
|
|
@@ -1250,11 +1725,24 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
|
|
|
1250
1725
|
options.ssgi &&= supportsSSGIRenderTargets(ctx);
|
|
1251
1726
|
ctx.SSS = !!options.sss;
|
|
1252
1727
|
ctx.SSGI = !!options.ssgi;
|
|
1728
|
+
ctx.postSSS = !!options.postSSS;
|
|
1729
|
+
// Before the compositor is consulted below: it collects requirements from,
|
|
1730
|
+
// and builds passes for, only the effects that are enabled at that moment.
|
|
1731
|
+
ctx.camera?.setPostSSSActive(ctx.postSSS);
|
|
1253
1732
|
ctx.SSGIIrradianceHistoryTexture = null;
|
|
1254
1733
|
ctx.SSGISurfaceHistoryTexture = null;
|
|
1255
|
-
ctx.
|
|
1734
|
+
ctx.SSSMaskTexture = null;
|
|
1735
|
+
ctx.SSSProfileIdTexture = null;
|
|
1256
1736
|
// ShadowMask sets this only when it produces a texture.
|
|
1257
1737
|
ctx.shadowMaskTexture = null;
|
|
1738
|
+
ctx.transmissionThicknessTexture = null;
|
|
1739
|
+
// WaterCaustics turns these on together once it has produced a map; they are
|
|
1740
|
+
// read by every light pass, so they must start cleared each frame.
|
|
1741
|
+
ctx.waterCaustics = false;
|
|
1742
|
+
ctx.waterCausticLight = null;
|
|
1743
|
+
ctx.waterCausticTexture = null;
|
|
1744
|
+
ctx.waterCausticHeightTexture = null;
|
|
1745
|
+
ctx.waterCausticUniforms = null;
|
|
1258
1746
|
const blackboard = new RGBlackboard();
|
|
1259
1747
|
const frame = {
|
|
1260
1748
|
ctx,
|
|
@@ -1288,6 +1776,10 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
|
|
|
1288
1776
|
const requirements = ctx.compositor?.collectRequirements(ctx) ?? {};
|
|
1289
1777
|
mergeFrameResourceRequirements(requirements, pipeline.collectRequirements(fg));
|
|
1290
1778
|
resolveFrameResourceRequirements(ctx, options, requirements);
|
|
1779
|
+
// Must agree with TransmissionThicknessModule.prepare: the flag decides
|
|
1780
|
+
// whether the light pass declares and binds the thickness texture, so a
|
|
1781
|
+
// mismatch would leave the shader layout disagreeing with the bind group.
|
|
1782
|
+
ctx.transmissionThickness = options.postSSS && renderQueue.shadowedLights.some((light)=>light.transmission);
|
|
1291
1783
|
pipeline.build(fg);
|
|
1292
1784
|
validateProducedFrameResources(blackboard, options, renderQueue);
|
|
1293
1785
|
const presented = blackboard.get(FrameResources.PresentedColor) ?? fg.state.presentedBackbuffer;
|
|
@@ -1384,7 +1876,7 @@ function disposeRenderQueue(frame) {
|
|
|
1384
1876
|
}
|
|
1385
1877
|
function releaseClusteredLight(frame) {
|
|
1386
1878
|
if (!frame.clusteredLightReleased && frame.ctx.clusteredLight) {
|
|
1387
|
-
|
|
1879
|
+
ClusteredLight.release(frame.ctx.clusteredLight);
|
|
1388
1880
|
frame.ctx.clusteredLight = undefined;
|
|
1389
1881
|
frame.clusteredLightReleased = true;
|
|
1390
1882
|
}
|
|
@@ -1418,7 +1910,7 @@ function cleanupFrame(frame) {
|
|
|
1418
1910
|
console.assert(!!existingDepthFb, 'renderSceneDepth: called without an existing depth framebuffer; the ' + 'self-allocating fallback path is untested in the forward+ pipeline.');
|
|
1419
1911
|
if (!depthFramebuffer) {
|
|
1420
1912
|
{
|
|
1421
|
-
const format = ctx.
|
|
1913
|
+
const format = ctx.SSRCalcThickness ? 'rg32f' : 'r32f';
|
|
1422
1914
|
const mvFormat = 'rgba16f';
|
|
1423
1915
|
if (!ctx.finalFramebuffer) {
|
|
1424
1916
|
depthFramebuffer = rgCtx.createFramebuffer({
|
|
@@ -1467,6 +1959,27 @@ function cleanupFrame(frame) {
|
|
|
1467
1959
|
ctx.device.setFramebuffer(depthFramebuffer);
|
|
1468
1960
|
_depthPass.encodeDepth = depthFramebuffer.getColorAttachments()[0].format === 'rgba8unorm';
|
|
1469
1961
|
_depthPass.clearColor = transmission ? null : _depthPass.encodeDepth ? new Vector4(0, 0, 0, 1) : new Vector4(1, 1, 1, 1);
|
|
1962
|
+
// That clear value is chosen for the linear depth target, where 1 is the far
|
|
1963
|
+
// plane. The profile id target needs the opposite: 0 means "not skin", and
|
|
1964
|
+
// every other value addresses a real row of the profile table. Clearing it
|
|
1965
|
+
// alongside the depth made each background texel read as profile 255, so a
|
|
1966
|
+
// sampling disc wide enough to reach past the silhouette accepted those taps
|
|
1967
|
+
// and averaged the background into the skin instead of rejecting it — as
|
|
1968
|
+
// dark speckle, since only the pixels whose sequence happened to draw a
|
|
1969
|
+
// long radius reached that far.
|
|
1970
|
+
//
|
|
1971
|
+
// Assigning clearColor above resets the per-target list, so this has to come
|
|
1972
|
+
// after it; that also means a frame without the target cannot inherit a
|
|
1973
|
+
// stale list.
|
|
1974
|
+
if (!transmission && ctx.sssProfileId) {
|
|
1975
|
+
const attachmentCount = depthFramebuffer.getColorAttachments().length;
|
|
1976
|
+
if (attachmentCount > 1) {
|
|
1977
|
+
// The id is appended last; see DepthPrepassModule.
|
|
1978
|
+
const clearColors = new Array(attachmentCount).fill(_depthPass.clearColor);
|
|
1979
|
+
clearColors[attachmentCount - 1] = SKIN_PROFILE_ID_CLEAR;
|
|
1980
|
+
_depthPass.clearColors = clearColors;
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1470
1983
|
_depthPass.clearDepth = transmission ? null : DEPTH_CLEAR_VALUE;
|
|
1471
1984
|
_depthPass.clearStencil = null;
|
|
1472
1985
|
_depthPass.transmission = transmission;
|
|
@@ -1674,7 +2187,7 @@ function blitToCurrentColorAttachment(ctx, source) {
|
|
|
1674
2187
|
ctx.materialFlags |= MaterialVaryingFlags.SSS_STORE_TRANSMISSION;
|
|
1675
2188
|
}
|
|
1676
2189
|
}
|
|
1677
|
-
if (ctx.
|
|
2190
|
+
if (ctx.SSSMaskTexture) {
|
|
1678
2191
|
ctx.materialFlags |= MaterialVaryingFlags.SKIN_SSS_STORE;
|
|
1679
2192
|
}
|
|
1680
2193
|
// Prefer the graph framebuffer; final-target mode has no graph framebuffer.
|
|
@@ -1740,7 +2253,11 @@ function blitToCurrentColorAttachment(ctx, source) {
|
|
|
1740
2253
|
device.setViewport(null);
|
|
1741
2254
|
device.setScissor(null);
|
|
1742
2255
|
ctx.scene.env.sky.renderSky(ctx);
|
|
1743
|
-
|
|
2256
|
+
// Not while submerged: the medium between the eye and the scene is water,
|
|
2257
|
+
// and the Underwater module composites it. The sky itself is still drawn -
|
|
2258
|
+
// that pass then extinguishes it along with everything else at the far
|
|
2259
|
+
// plane, which is what makes the horizon read as water rather than as air.
|
|
2260
|
+
if (ctx.scene.env.sky.fogPresents && !ctx.underwater) {
|
|
1744
2261
|
// Snapshot between sky and fog. Sky belongs in the copy - a ray that hits
|
|
1745
2262
|
// the sky should read the sky - while fog does not, because the fog a
|
|
1746
2263
|
// screen-space pass would sample lies along the camera ray rather than the
|
|
@@ -1822,8 +2339,17 @@ function blitToCurrentColorAttachment(ctx, source) {
|
|
|
1822
2339
|
let motionVectorFrameCommitted = false;
|
|
1823
2340
|
try {
|
|
1824
2341
|
renderQueue = _scenePass.cullScene(ctx, ctx.camera);
|
|
2342
|
+
// Before the options are derived, because being submerged changes them: the
|
|
2343
|
+
// atmosphere is not the medium the camera is looking through, so its fog is
|
|
2344
|
+
// suppressed for the frame.
|
|
2345
|
+
ctx.underwater = selectUnderwaterSource(ctx);
|
|
1825
2346
|
const options = deriveForwardPlusOptions(ctx.scene, ctx.camera, device.type, renderQueue);
|
|
1826
2347
|
options.ssgi &&= supportsSSGIRenderTargets(ctx);
|
|
2348
|
+
// Atmospheric fog describes a column of air, which is not what a submerged
|
|
2349
|
+
// camera is looking through. The Underwater module owns the medium instead.
|
|
2350
|
+
// Clearing the flag here also stops SkyPass reserving a fog-free snapshot
|
|
2351
|
+
// for a frame that composites no fog.
|
|
2352
|
+
options.fogPresents &&= !ctx.underwater;
|
|
1827
2353
|
ctx.SSS = options.sss;
|
|
1828
2354
|
historyManager = ctx.camera.getHistoryResourceManager();
|
|
1829
2355
|
if (!historyManager) {
|