@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
|
@@ -0,0 +1,1334 @@
|
|
|
1
|
+
import { Vector4, PRNG, half2float } from '@zephyr3d/base';
|
|
2
|
+
import { getDevice } from '../../app/api.js';
|
|
3
|
+
import { buildSDFProgram } from '../sdf/sdf_programs.js';
|
|
4
|
+
import { drawFullscreenQuad } from '../fullscreenquad.js';
|
|
5
|
+
import { fetchSampler } from '../../utility/misc.js';
|
|
6
|
+
import { getGICacheExposure } from '../../utility/physical.js';
|
|
7
|
+
import { RENDER_PASS_TYPE_LIGHT } from '../../values.js';
|
|
8
|
+
import { RenderGraphExecutor } from '../rendergraph/executor.js';
|
|
9
|
+
import { declareGlobalSDFUniforms, declareDynamicSDFUniforms, GLOBAL_SDF_LEVELS, sampleSceneSDF, sampleGlobalSDF, sampleSceneSDFHinted, sdfSurfaceExpand, sdfRefineHit, sampleGlobalSDFLevel } from '../../shaders/sdf.js';
|
|
10
|
+
import { VOXEL_SCENE_ALBEDO, VOXEL_SCENE_EMISSIVE, VOXEL_SCENE_RADIANCE, VOXEL_SCENE_ANISO0, VOXEL_SCENE_ANISO1, declareVoxelSceneUniforms, sampleVoxelSceneRadiance } from '../../shaders/gi.js';
|
|
11
|
+
import { PROBE_MAX_LEVELS, PROBE_PARAM_VEC4S, PROBE_IRRADIANCE, PROBE_VISIBILITY, PROBE_DATA, PROBE_PARAMS, PROBE_STATE_INACTIVE, probeTexelFromIndex, probeWorldFromTexel, probeLevelOrigin, probePosition, probeLevelGrid, probeTileIndex, probeTexelFromWorld, probeActive, probeTileOrigin, quatRotate, sphericalFibonacci, octDecode } from '../../shaders/probes.js';
|
|
12
|
+
|
|
13
|
+
/** Texels of an irradiance tile, border included. */ const IRRADIANCE_TEXELS = 8;
|
|
14
|
+
/** Texels of a visibility tile, border included. */ const VISIBILITY_TEXELS = 16;
|
|
15
|
+
/** Tiles per row of the blend outputs, which hold the tiles of one update by selection slot. */ const STAGING_COLS = 64;
|
|
16
|
+
/** Rays a blend workgroup stages in shared memory at a time. */ const BLEND_BATCH = 128;
|
|
17
|
+
/** Sphere tracing steps of a probe ray. */ const TRACE_STEPS = 48;
|
|
18
|
+
/** Visibility distance clamp, in probe spacings: just enough to separate blockers between neighbouring probes. */ const VISIBILITY_RANGE = 2.6;
|
|
19
|
+
/** Frames between relocations of an updated probe. */ const RELOCATE_EVERY = 8;
|
|
20
|
+
/** Bits of a selection entry: tile index, level, first-update flag. */ const SELECT_INDEX_MASK = 0x00ffffff;
|
|
21
|
+
const SELECT_LEVEL_SHIFT = 24;
|
|
22
|
+
const SELECT_FIRST_BIT = 0x80000000;
|
|
23
|
+
/** Magic and version of a saved probe state. */ const STATE_MAGIC = 0x50494a5a; // 'ZJIP'
|
|
24
|
+
/** Bump when the saved layout or meaning changes. */ const STATE_VERSION = 5;
|
|
25
|
+
/** Hit distance written by a sky-escaping ray: beyond any trace length, within half float range. */ const SKY_ESCAPE_DISTANCE = 16384;
|
|
26
|
+
/**
|
|
27
|
+
* The probe volume read by the draw, or null outside the light pass, with the
|
|
28
|
+
* environment light off, or with GI disabled or not yet built.
|
|
29
|
+
* @internal
|
|
30
|
+
*/ function getProbeVolumeForDraw(ctx) {
|
|
31
|
+
if (!ctx || ctx.renderPass?.type !== RENDER_PASS_TYPE_LIGHT || !ctx.drawEnvLight || !ctx.scene?.gi.enabled) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
const probes = ctx.scene.gi.probes;
|
|
35
|
+
return probes?.ready ? probes : null;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The scene's built probe volume when GI is on, for passes other than the light pass.
|
|
39
|
+
* @internal
|
|
40
|
+
*/ function getActiveProbeVolume(scene) {
|
|
41
|
+
if (!scene.gi.enabled) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
const probes = scene.gi.probes;
|
|
45
|
+
return probes?.ready ? probes : null;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A camera-centred clipmap of DDGI light probes (Majercik et al. JCGT 2019/2021)
|
|
49
|
+
* holding octahedral irradiance and visibility maps. Each frame a budget of probes
|
|
50
|
+
* traces rays through the scene SDF, shading hits from the voxel scene and misses
|
|
51
|
+
* from the sky. Windows scroll toroidally; entering probes are seeded from the level
|
|
52
|
+
* above.
|
|
53
|
+
* Read via {@link sampleProbeIrradiance}; state can be saved with
|
|
54
|
+
* {@link ProbeVolume.exportState}. WebGPU only.
|
|
55
|
+
*
|
|
56
|
+
* @public
|
|
57
|
+
*/ class ProbeVolume {
|
|
58
|
+
_scene;
|
|
59
|
+
_options;
|
|
60
|
+
_levels;
|
|
61
|
+
_count;
|
|
62
|
+
_tileCols;
|
|
63
|
+
_rays;
|
|
64
|
+
_budget;
|
|
65
|
+
_irradiance;
|
|
66
|
+
_irradianceNext;
|
|
67
|
+
/** Irradiance seeds of one update's probes starting over, by selection slot like the blend outputs. */ _irradianceSeed;
|
|
68
|
+
_visibility;
|
|
69
|
+
_visibilityNext;
|
|
70
|
+
/** Two distance moments; rg16f where storage-writable (texture-formats-tier1), else rgba16f. */ _visibilityFormat;
|
|
71
|
+
_rayTexture;
|
|
72
|
+
_rayFramebuffer;
|
|
73
|
+
_data;
|
|
74
|
+
_selected;
|
|
75
|
+
_selection;
|
|
76
|
+
_dataReset;
|
|
77
|
+
_frame;
|
|
78
|
+
_updates;
|
|
79
|
+
_lastUpdated;
|
|
80
|
+
_params;
|
|
81
|
+
/** Live camera exposure over the cache exposure, see {@link ProbeVolume.setReadExposure}. */ _exposureScale;
|
|
82
|
+
_rotation;
|
|
83
|
+
_prng;
|
|
84
|
+
_relocateProgram;
|
|
85
|
+
_relocateBindGroup;
|
|
86
|
+
_seedProgram;
|
|
87
|
+
_seedBindGroup;
|
|
88
|
+
_traceProgram;
|
|
89
|
+
_traceBindGroup;
|
|
90
|
+
_traceKey;
|
|
91
|
+
_blendIrrProgram;
|
|
92
|
+
_blendIrrBindGroup;
|
|
93
|
+
_blendVisProgram;
|
|
94
|
+
_blendVisBindGroup;
|
|
95
|
+
_commitProgram;
|
|
96
|
+
_commitBindGroup;
|
|
97
|
+
_renderStates;
|
|
98
|
+
_scratch;
|
|
99
|
+
constructor(scene, options){
|
|
100
|
+
this._scene = scene;
|
|
101
|
+
this._options = options;
|
|
102
|
+
const levels = Math.max(1, Math.min(PROBE_MAX_LEVELS, Math.floor(options.levels)));
|
|
103
|
+
const scale = Math.max(1.5, options.levelScale);
|
|
104
|
+
const grid = options.grid.map((v)=>Math.max(2, Math.floor(v)));
|
|
105
|
+
this._levels = [];
|
|
106
|
+
let tileBase = 0;
|
|
107
|
+
for(let l = 0; l < levels; l++){
|
|
108
|
+
const count = grid[0] * grid[1] * grid[2];
|
|
109
|
+
this._levels.push({
|
|
110
|
+
grid: [
|
|
111
|
+
...grid
|
|
112
|
+
],
|
|
113
|
+
spacing: options.spacing * scale ** l,
|
|
114
|
+
count,
|
|
115
|
+
tileBase,
|
|
116
|
+
origin: null,
|
|
117
|
+
state: new Uint8Array(count),
|
|
118
|
+
lastRelocated: new Int32Array(count).fill(-1e3),
|
|
119
|
+
lastSelected: new Int32Array(count).fill(-1),
|
|
120
|
+
cursor: 0
|
|
121
|
+
});
|
|
122
|
+
tileBase += count;
|
|
123
|
+
}
|
|
124
|
+
this._count = tileBase;
|
|
125
|
+
this._tileCols = Math.ceil(Math.sqrt(this._count));
|
|
126
|
+
this._rays = Math.max(16, Math.min(1024, Math.floor(options.raysPerProbe)));
|
|
127
|
+
this._budget = Math.max(1, Math.min(this._count, Math.floor(options.budget)));
|
|
128
|
+
this._irradiance = null;
|
|
129
|
+
this._irradianceNext = null;
|
|
130
|
+
this._irradianceSeed = null;
|
|
131
|
+
this._visibility = null;
|
|
132
|
+
this._visibilityNext = null;
|
|
133
|
+
this._visibilityFormat = getDevice().getDeviceCaps().textureCaps.getTextureFormatInfo('rg16f')?.writable ? 'rg16f' : 'rgba16f';
|
|
134
|
+
this._rayTexture = null;
|
|
135
|
+
this._rayFramebuffer = null;
|
|
136
|
+
this._data = null;
|
|
137
|
+
this._selected = null;
|
|
138
|
+
this._selection = new Uint32Array(new ArrayBuffer(this._budget * 4));
|
|
139
|
+
this._dataReset = new Float32Array(new ArrayBuffer(16));
|
|
140
|
+
this._frame = 0;
|
|
141
|
+
this._updates = 0;
|
|
142
|
+
this._lastUpdated = 0;
|
|
143
|
+
this._params = new Float32Array(new ArrayBuffer(PROBE_PARAM_VEC4S * 16));
|
|
144
|
+
this._exposureScale = 1;
|
|
145
|
+
this._rotation = new Vector4(0, 0, 0, 1);
|
|
146
|
+
this._prng = new PRNG(1234);
|
|
147
|
+
this._relocateProgram = null;
|
|
148
|
+
this._relocateBindGroup = null;
|
|
149
|
+
this._seedProgram = null;
|
|
150
|
+
this._seedBindGroup = null;
|
|
151
|
+
this._traceProgram = null;
|
|
152
|
+
this._traceBindGroup = null;
|
|
153
|
+
this._traceKey = '';
|
|
154
|
+
this._blendIrrProgram = null;
|
|
155
|
+
this._blendIrrBindGroup = null;
|
|
156
|
+
this._blendVisProgram = null;
|
|
157
|
+
this._blendVisBindGroup = null;
|
|
158
|
+
this._commitProgram = null;
|
|
159
|
+
this._commitBindGroup = null;
|
|
160
|
+
this._renderStates = null;
|
|
161
|
+
this._scratch = new Map();
|
|
162
|
+
}
|
|
163
|
+
/** Clipmap levels. */ get levels() {
|
|
164
|
+
return this._levels.length;
|
|
165
|
+
}
|
|
166
|
+
/** Probes of every level together. */ get count() {
|
|
167
|
+
return this._count;
|
|
168
|
+
}
|
|
169
|
+
/** Spacing of the finest level. */ get spacing() {
|
|
170
|
+
return this._options.spacing;
|
|
171
|
+
}
|
|
172
|
+
/** True once the volume has been placed and updated, or a state restored. */ get ready() {
|
|
173
|
+
return !!this._levels[0].origin && this._updates > 0;
|
|
174
|
+
}
|
|
175
|
+
getStats() {
|
|
176
|
+
let uninitialized = 0;
|
|
177
|
+
for (const level of this._levels){
|
|
178
|
+
for(let i = 0; i < level.count; i++){
|
|
179
|
+
uninitialized += level.state[i] ? 0 : 1;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
levels: this._levels.length,
|
|
184
|
+
probes: this._count,
|
|
185
|
+
uninitialized,
|
|
186
|
+
lastUpdated: this._lastUpdated,
|
|
187
|
+
updates: this._updates,
|
|
188
|
+
levelInfo: this._levels.map((level)=>({
|
|
189
|
+
grid: [
|
|
190
|
+
...level.grid
|
|
191
|
+
],
|
|
192
|
+
spacing: level.spacing,
|
|
193
|
+
origin: level.origin ? [
|
|
194
|
+
...level.origin
|
|
195
|
+
] : [
|
|
196
|
+
0,
|
|
197
|
+
0,
|
|
198
|
+
0
|
|
199
|
+
]
|
|
200
|
+
}))
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
/** Binds the volume for {@link sampleProbeIrradiance} in a program that declared it. */ applyBindGroup(bg) {
|
|
204
|
+
if (!this._irradiance || !this._visibility || !this._data) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
bg.setTexture(PROBE_IRRADIANCE, this._irradiance, fetchSampler('clamp_linear_nomip'));
|
|
208
|
+
bg.setTexture(PROBE_VISIBILITY, this._visibility, fetchSampler('clamp_linear_nomip'));
|
|
209
|
+
bg.setBuffer(PROBE_DATA, this._data);
|
|
210
|
+
bg.setValue(PROBE_PARAMS, this._params);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Rescales probe reads from the fixed cache exposure to the camera's. Call
|
|
214
|
+
* every frame, also while frozen, so params follow exposure and SDF windows.
|
|
215
|
+
*/ setReadExposure(camera) {
|
|
216
|
+
const scene = this._scene;
|
|
217
|
+
const live = scene.lightingMode === 'physical' ? camera.exposure : 1;
|
|
218
|
+
this._exposureScale = live / getGICacheExposure(scene.lightingMode);
|
|
219
|
+
// Before the first update there are no maps; that update writes the params
|
|
220
|
+
if (this._irradiance && this._visibility) {
|
|
221
|
+
this.updateParams();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Follows the camera and updates a budget of probes. Called by the scene
|
|
226
|
+
* once a frame after the voxel scene is lit, which the rays read.
|
|
227
|
+
*/ update(sdf, voxels, camera) {
|
|
228
|
+
const global = sdf.enabled ? sdf.global : null;
|
|
229
|
+
if (!global || !voxels.lit) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
this.ensureResources();
|
|
233
|
+
const cameraPos = camera.getWorldPosition();
|
|
234
|
+
for (const level of this._levels){
|
|
235
|
+
this.moveWindow(level, cameraPos);
|
|
236
|
+
}
|
|
237
|
+
this._frame++;
|
|
238
|
+
const selected = this.select();
|
|
239
|
+
this.updateParams();
|
|
240
|
+
if (selected === 0) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
this._selected.bufferSubData(0, this._selection);
|
|
244
|
+
// A fresh random rotation of the ray set each update
|
|
245
|
+
this.randomRotation();
|
|
246
|
+
RenderGraphExecutor.profileScope('ProbeRelocate', ()=>this.runRelocate(global, selected));
|
|
247
|
+
// After the relocation, which decides which probes start over, and before
|
|
248
|
+
// the blend, which reads the seeds
|
|
249
|
+
RenderGraphExecutor.profileScope('ProbeSeed', ()=>this.runSeed(selected));
|
|
250
|
+
RenderGraphExecutor.profileScope('ProbeTrace', ()=>this.runTrace(global, voxels, camera, selected));
|
|
251
|
+
this.runBlend(selected);
|
|
252
|
+
RenderGraphExecutor.profileScope('ProbeCommit', ()=>this.runCommit(selected));
|
|
253
|
+
this._updates++;
|
|
254
|
+
this._lastUpdated = selected;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Saves the volume: its maps, probe data and windows, to restore with
|
|
258
|
+
* {@link ProbeVolume.importState} on a volume built with the same settings.
|
|
259
|
+
* Resolves once the GPU has handed the maps back.
|
|
260
|
+
*/ async exportState() {
|
|
261
|
+
if (!this.ready || !this._irradiance || !this._visibility || !this._data) {
|
|
262
|
+
throw new Error('ProbeVolume.exportState(): nothing to save yet');
|
|
263
|
+
}
|
|
264
|
+
const irr = new Uint16Array(new ArrayBuffer(this._irradiance.width * this._irradiance.height * 8));
|
|
265
|
+
const visTexels = this._visibility.width * this._visibility.height;
|
|
266
|
+
const visChannels = this._visibilityFormat === 'rg16f' ? 2 : 4;
|
|
267
|
+
const visRead = new Uint16Array(new ArrayBuffer(visTexels * visChannels * 2));
|
|
268
|
+
const data = new Float32Array(new ArrayBuffer(this._count * 16));
|
|
269
|
+
// Issued together so the three readbacks wait for the GPU once, not three times
|
|
270
|
+
await Promise.all([
|
|
271
|
+
this._irradiance.readPixels(0, 0, this._irradiance.width, this._irradiance.height, 0, 0, irr),
|
|
272
|
+
this._visibility.readPixels(0, 0, this._visibility.width, this._visibility.height, 0, 0, visRead),
|
|
273
|
+
this._data.getBufferSubData(new Uint8Array(data.buffer), 0, data.byteLength)
|
|
274
|
+
]);
|
|
275
|
+
// Saved as rgba16f whatever the device keeps, so a state moves between devices
|
|
276
|
+
const vis = visChannels === 4 ? visRead : expandVisibility(visRead, visTexels);
|
|
277
|
+
const header = JSON.stringify({
|
|
278
|
+
grid: this._options.grid,
|
|
279
|
+
spacing: this._options.spacing,
|
|
280
|
+
levels: this._levels.length,
|
|
281
|
+
levelScale: this._options.levelScale,
|
|
282
|
+
origins: this._levels.map((l)=>l.origin),
|
|
283
|
+
irradiance: [
|
|
284
|
+
this._irradiance.width,
|
|
285
|
+
this._irradiance.height
|
|
286
|
+
],
|
|
287
|
+
visibility: [
|
|
288
|
+
this._visibility.width,
|
|
289
|
+
this._visibility.height
|
|
290
|
+
]
|
|
291
|
+
});
|
|
292
|
+
const headerBytes = new TextEncoder().encode(header);
|
|
293
|
+
const headerLength = headerBytes.byteLength + 3 & -4;
|
|
294
|
+
const out = new Uint8Array(12 + headerLength + irr.byteLength + vis.byteLength + data.byteLength);
|
|
295
|
+
const view = new DataView(out.buffer);
|
|
296
|
+
view.setUint32(0, STATE_MAGIC, true);
|
|
297
|
+
view.setUint32(4, STATE_VERSION, true);
|
|
298
|
+
view.setUint32(8, headerLength, true);
|
|
299
|
+
out.fill(0x20, 12, 12 + headerLength);
|
|
300
|
+
out.set(headerBytes, 12);
|
|
301
|
+
let offset = 12 + headerLength;
|
|
302
|
+
out.set(new Uint8Array(irr.buffer), offset);
|
|
303
|
+
offset += irr.byteLength;
|
|
304
|
+
out.set(new Uint8Array(vis.buffer), offset);
|
|
305
|
+
offset += vis.byteLength;
|
|
306
|
+
out.set(new Uint8Array(data.buffer), offset);
|
|
307
|
+
return out.buffer;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Reads back up to `limit` probes of a level inside a world box, nearest the
|
|
311
|
+
* centre first, for inspection: position, update/inactive state, mean
|
|
312
|
+
* irradiance and sky visibility, and min/mean visibility hit distance.
|
|
313
|
+
*/ async queryProbes(level, min, max, limit = 512) {
|
|
314
|
+
const l = this._levels[level];
|
|
315
|
+
if (!l || !l.origin || !this._irradiance || !this._visibility || !this._data) {
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
const s = l.spacing;
|
|
319
|
+
const lo = [
|
|
320
|
+
Math.ceil(min.x / s),
|
|
321
|
+
Math.ceil(min.y / s),
|
|
322
|
+
Math.ceil(min.z / s)
|
|
323
|
+
];
|
|
324
|
+
const hi = [
|
|
325
|
+
Math.floor(max.x / s),
|
|
326
|
+
Math.floor(max.y / s),
|
|
327
|
+
Math.floor(max.z / s)
|
|
328
|
+
];
|
|
329
|
+
const centre = [
|
|
330
|
+
(min.x + max.x) / 2,
|
|
331
|
+
(min.y + max.y) / 2,
|
|
332
|
+
(min.z + max.z) / 2
|
|
333
|
+
];
|
|
334
|
+
const wrap = (v, m)=>(v % m + m) % m;
|
|
335
|
+
const picked = [];
|
|
336
|
+
for(let z = lo[2]; z <= hi[2]; z++){
|
|
337
|
+
for(let y = lo[1]; y <= hi[1]; y++){
|
|
338
|
+
for(let x = lo[0]; x <= hi[0]; x++){
|
|
339
|
+
const w = [
|
|
340
|
+
x,
|
|
341
|
+
y,
|
|
342
|
+
z
|
|
343
|
+
];
|
|
344
|
+
if (w.some((v, a)=>v < l.origin[a] || v >= l.origin[a] + l.grid[a])) {
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
const local = wrap(x, l.grid[0]) + l.grid[0] * (wrap(y, l.grid[1]) + l.grid[1] * wrap(z, l.grid[2]));
|
|
348
|
+
picked.push({
|
|
349
|
+
world: w,
|
|
350
|
+
index: local
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
const dist2 = (w)=>w.reduce((acc, v, a)=>acc + (v * s - centre[a]) ** 2, 0);
|
|
356
|
+
picked.sort((a, b)=>dist2(a.world) - dist2(b.world));
|
|
357
|
+
picked.length = Math.min(picked.length, limit);
|
|
358
|
+
const irr = new Uint16Array(new ArrayBuffer(this._irradiance.width * this._irradiance.height * 8));
|
|
359
|
+
const visChannels = this._visibilityFormat === 'rg16f' ? 2 : 4;
|
|
360
|
+
const vis = new Uint16Array(new ArrayBuffer(this._visibility.width * this._visibility.height * visChannels * 2));
|
|
361
|
+
const data = new Float32Array(new ArrayBuffer(this._count * 16));
|
|
362
|
+
await Promise.all([
|
|
363
|
+
this._irradiance.readPixels(0, 0, this._irradiance.width, this._irradiance.height, 0, 0, irr),
|
|
364
|
+
this._visibility.readPixels(0, 0, this._visibility.width, this._visibility.height, 0, 0, vis),
|
|
365
|
+
this._data.getBufferSubData(new Uint8Array(data.buffer), 0, data.byteLength)
|
|
366
|
+
]);
|
|
367
|
+
const tileMean = (texels, width, channels, raw, index, reduce)=>{
|
|
368
|
+
const ox = index % this._tileCols * texels;
|
|
369
|
+
const oy = Math.floor(index / this._tileCols) * texels;
|
|
370
|
+
// Interior texels only; the border repeats them
|
|
371
|
+
for(let y = 1; y < texels - 1; y++){
|
|
372
|
+
for(let x = 1; x < texels - 1; x++){
|
|
373
|
+
const o = ((oy + y) * width + ox + x) * channels;
|
|
374
|
+
const t = [];
|
|
375
|
+
for(let c = 0; c < channels; c++){
|
|
376
|
+
t.push(half2float(raw[o + c]));
|
|
377
|
+
}
|
|
378
|
+
reduce(t);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
const probes = picked.map(({ world, index })=>{
|
|
383
|
+
const tile = l.tileBase + index;
|
|
384
|
+
const d = data.subarray(tile * 4, tile * 4 + 4);
|
|
385
|
+
const irrSum = [
|
|
386
|
+
0,
|
|
387
|
+
0,
|
|
388
|
+
0,
|
|
389
|
+
0
|
|
390
|
+
];
|
|
391
|
+
let n = 0;
|
|
392
|
+
tileMean(IRRADIANCE_TEXELS, this._irradiance.width, 4, irr, tile, (t)=>{
|
|
393
|
+
for(let c = 0; c < 4; c++){
|
|
394
|
+
irrSum[c] += t[c];
|
|
395
|
+
}
|
|
396
|
+
n++;
|
|
397
|
+
});
|
|
398
|
+
let visMin = Infinity;
|
|
399
|
+
let visSum = 0;
|
|
400
|
+
let m = 0;
|
|
401
|
+
tileMean(VISIBILITY_TEXELS, this._visibility.width, visChannels, vis, tile, (t)=>{
|
|
402
|
+
visMin = Math.min(visMin, t[0]);
|
|
403
|
+
visSum += t[0];
|
|
404
|
+
m++;
|
|
405
|
+
});
|
|
406
|
+
const r = (v)=>Math.round(v * 1000) / 1000;
|
|
407
|
+
return {
|
|
408
|
+
world,
|
|
409
|
+
position: [
|
|
410
|
+
world[0] * s + d[0],
|
|
411
|
+
world[1] * s + d[1],
|
|
412
|
+
world[2] * s + d[2]
|
|
413
|
+
].map(r),
|
|
414
|
+
offset: [
|
|
415
|
+
d[0],
|
|
416
|
+
d[1],
|
|
417
|
+
d[2]
|
|
418
|
+
].map(r),
|
|
419
|
+
updated: d[3] > 0.5,
|
|
420
|
+
inactive: d[3] > PROBE_STATE_INACTIVE - 0.5,
|
|
421
|
+
irradiance: irrSum.slice(0, 3).map((v)=>r(v / n)),
|
|
422
|
+
sky: r(irrSum[3] / n),
|
|
423
|
+
hitMin: r(visMin),
|
|
424
|
+
hitMean: r(visSum / m)
|
|
425
|
+
};
|
|
426
|
+
});
|
|
427
|
+
return {
|
|
428
|
+
level,
|
|
429
|
+
spacing: s,
|
|
430
|
+
origin: [
|
|
431
|
+
...l.origin
|
|
432
|
+
],
|
|
433
|
+
grid: [
|
|
434
|
+
...l.grid
|
|
435
|
+
],
|
|
436
|
+
probes
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Restores a state saved by {@link ProbeVolume.exportState} with matching
|
|
441
|
+
* settings; freeze the lighting afterwards or the windows follow the camera.
|
|
442
|
+
*/ importState(buffer) {
|
|
443
|
+
const view = new DataView(buffer);
|
|
444
|
+
if (view.getUint32(0, true) !== STATE_MAGIC || view.getUint32(4, true) !== STATE_VERSION) {
|
|
445
|
+
throw new Error('ProbeVolume.importState(): not a probe state');
|
|
446
|
+
}
|
|
447
|
+
const headerLength = view.getUint32(8, true);
|
|
448
|
+
const header = JSON.parse(new TextDecoder().decode(new Uint8Array(buffer, 12, headerLength)));
|
|
449
|
+
if (header.levels !== this._levels.length || header.spacing !== this._options.spacing || header.levelScale !== this._options.levelScale || header.grid.some((v, i)=>v !== this._options.grid[i])) {
|
|
450
|
+
throw new Error('ProbeVolume.importState(): the state was saved with other settings');
|
|
451
|
+
}
|
|
452
|
+
this.ensureResources();
|
|
453
|
+
const irrSize = this._irradiance.width * this._irradiance.height * 4;
|
|
454
|
+
const visSize = this._visibility.width * this._visibility.height * 4;
|
|
455
|
+
let offset = 12 + headerLength;
|
|
456
|
+
const irr = new Uint16Array(buffer, offset, irrSize);
|
|
457
|
+
offset += irr.byteLength;
|
|
458
|
+
const vis = new Uint16Array(buffer, offset, visSize);
|
|
459
|
+
offset += vis.byteLength;
|
|
460
|
+
const visTexels = this._visibility.width * this._visibility.height;
|
|
461
|
+
const data = new Float32Array(buffer, offset, this._count * 4);
|
|
462
|
+
this._irradiance.update(irr, 0, 0, this._irradiance.width, this._irradiance.height);
|
|
463
|
+
this._visibility.update(this._visibilityFormat === 'rg16f' ? compactVisibility(vis, visTexels) : vis, 0, 0, this._visibility.width, this._visibility.height);
|
|
464
|
+
this._data.bufferSubData(0, data);
|
|
465
|
+
this._levels.forEach((level, l)=>{
|
|
466
|
+
level.origin = header.origins[l];
|
|
467
|
+
for(let i = 0; i < level.count; i++){
|
|
468
|
+
level.state[i] = data[(level.tileBase + i) * 4 + 3] > 0.5 ? 1 : 0;
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
this.updateParams();
|
|
472
|
+
this._updates = Math.max(this._updates, 1);
|
|
473
|
+
}
|
|
474
|
+
dispose() {
|
|
475
|
+
for (const tex of [
|
|
476
|
+
this._irradiance,
|
|
477
|
+
this._irradianceNext,
|
|
478
|
+
this._irradianceSeed,
|
|
479
|
+
this._visibility,
|
|
480
|
+
this._visibilityNext,
|
|
481
|
+
this._rayTexture
|
|
482
|
+
]){
|
|
483
|
+
tex?.dispose();
|
|
484
|
+
}
|
|
485
|
+
this._irradiance = this._irradianceNext = this._irradianceSeed = this._visibility = this._visibilityNext = this._rayTexture = null;
|
|
486
|
+
this._rayFramebuffer?.dispose();
|
|
487
|
+
this._rayFramebuffer = null;
|
|
488
|
+
this._data?.dispose();
|
|
489
|
+
this._selected?.dispose();
|
|
490
|
+
this._data = this._selected = null;
|
|
491
|
+
for (const buffer of this._scratch.values()){
|
|
492
|
+
buffer.dispose();
|
|
493
|
+
}
|
|
494
|
+
this._scratch.clear();
|
|
495
|
+
for (const bg of [
|
|
496
|
+
this._relocateBindGroup,
|
|
497
|
+
this._seedBindGroup,
|
|
498
|
+
this._traceBindGroup,
|
|
499
|
+
this._blendIrrBindGroup,
|
|
500
|
+
this._blendVisBindGroup,
|
|
501
|
+
this._commitBindGroup
|
|
502
|
+
]){
|
|
503
|
+
bg?.dispose();
|
|
504
|
+
}
|
|
505
|
+
for (const prog of [
|
|
506
|
+
this._relocateProgram,
|
|
507
|
+
this._seedProgram,
|
|
508
|
+
this._traceProgram,
|
|
509
|
+
this._blendIrrProgram,
|
|
510
|
+
this._blendVisProgram,
|
|
511
|
+
this._commitProgram
|
|
512
|
+
]){
|
|
513
|
+
prog?.dispose();
|
|
514
|
+
}
|
|
515
|
+
this._relocateBindGroup = this._traceBindGroup = this._blendIrrBindGroup = this._blendVisBindGroup = null;
|
|
516
|
+
this._relocateProgram = this._traceProgram = this._blendIrrProgram = this._blendVisProgram = null;
|
|
517
|
+
this._seedProgram = this._seedBindGroup = null;
|
|
518
|
+
this._commitProgram = this._commitBindGroup = null;
|
|
519
|
+
for (const level of this._levels){
|
|
520
|
+
level.origin = null;
|
|
521
|
+
}
|
|
522
|
+
this._updates = 0;
|
|
523
|
+
}
|
|
524
|
+
/** @internal */ ensureResources() {
|
|
525
|
+
if (this._irradiance) {
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
const device = getDevice();
|
|
529
|
+
const rows = Math.ceil(this._count / this._tileCols);
|
|
530
|
+
const make = (texels, name, cols = this._tileCols, r = rows, format = 'rgba16f')=>{
|
|
531
|
+
const tex = device.createTexture2D(format, cols * texels, r * texels, {
|
|
532
|
+
mipmapping: false,
|
|
533
|
+
writable: true
|
|
534
|
+
});
|
|
535
|
+
tex.name = name;
|
|
536
|
+
return tex;
|
|
537
|
+
};
|
|
538
|
+
this._irradiance = make(IRRADIANCE_TEXELS, '@ProbeIrradiance');
|
|
539
|
+
// The blend outputs hold one update's tiles, committed to the maps after
|
|
540
|
+
const stagingRows = Math.ceil(this._budget / STAGING_COLS);
|
|
541
|
+
this._irradianceNext = make(IRRADIANCE_TEXELS, '@ProbeIrradianceNext', STAGING_COLS, stagingRows);
|
|
542
|
+
this._irradianceSeed = make(IRRADIANCE_TEXELS, '@ProbeIrradianceSeed', STAGING_COLS, stagingRows);
|
|
543
|
+
this._visibility = make(VISIBILITY_TEXELS, '@ProbeVisibility', this._tileCols, rows, this._visibilityFormat);
|
|
544
|
+
this._visibilityNext = make(VISIBILITY_TEXELS, '@ProbeVisibilityNext', STAGING_COLS, stagingRows, this._visibilityFormat);
|
|
545
|
+
this._rayTexture = device.createTexture2D('rgba16f', this._rays, this._budget, {
|
|
546
|
+
mipmapping: false
|
|
547
|
+
});
|
|
548
|
+
this._rayTexture.name = '@ProbeRays';
|
|
549
|
+
this._rayFramebuffer = device.createFrameBuffer([
|
|
550
|
+
this._rayTexture
|
|
551
|
+
], null);
|
|
552
|
+
this._data = device.createBuffer(this._count * 16, {
|
|
553
|
+
usage: 'uniform',
|
|
554
|
+
storage: true
|
|
555
|
+
});
|
|
556
|
+
this._data.bufferSubData(0, new Float32Array(new ArrayBuffer(this._count * 16)));
|
|
557
|
+
this._selected = device.createBuffer(this._budget * 4, {
|
|
558
|
+
usage: 'uniform',
|
|
559
|
+
storage: true
|
|
560
|
+
});
|
|
561
|
+
for (const level of this._levels){
|
|
562
|
+
level.state.fill(0);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
/** Re-centres a level's window on the camera, a spacing at a time; probes that entered start over. @internal */ moveWindow(level, cameraPos) {
|
|
566
|
+
const s = level.spacing;
|
|
567
|
+
const n = level.grid;
|
|
568
|
+
const next = [
|
|
569
|
+
Math.floor(cameraPos.x / s) - (n[0] >> 1),
|
|
570
|
+
Math.floor(cameraPos.y / s) - (n[1] >> 1),
|
|
571
|
+
Math.floor(cameraPos.z / s) - (n[2] >> 1)
|
|
572
|
+
];
|
|
573
|
+
const prev = level.origin;
|
|
574
|
+
if (prev && prev[0] === next[0] && prev[1] === next[1] && prev[2] === next[2]) {
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
level.origin = next;
|
|
578
|
+
const wrap = (v, m)=>(v % m + m) % m;
|
|
579
|
+
for(let z = 0; z < n[2]; z++){
|
|
580
|
+
for(let y = 0; y < n[1]; y++){
|
|
581
|
+
for(let x = 0; x < n[0]; x++){
|
|
582
|
+
const wx = next[0] + x;
|
|
583
|
+
const wy = next[1] + y;
|
|
584
|
+
const wz = next[2] + z;
|
|
585
|
+
const inPrev = !!prev && wx >= prev[0] && wy >= prev[1] && wz >= prev[2] && wx < prev[0] + n[0] && wy < prev[1] + n[1] && wz < prev[2] + n[2];
|
|
586
|
+
if (!inPrev) {
|
|
587
|
+
const index = wrap(wx, n[0]) + n[0] * (wrap(wy, n[1]) + n[1] * wrap(wz, n[2]));
|
|
588
|
+
level.state[index] = 0;
|
|
589
|
+
level.lastRelocated[index] = -1e3;
|
|
590
|
+
this._data.bufferSubData((level.tileBase + index) * 16, this._dataReset);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Picks the probes to update: never-updated ones first (a quarter of the
|
|
598
|
+
* budget to the coarsest level, the rest finest first), then each level by
|
|
599
|
+
* its share. At most once per frame per probe, since two updates would race
|
|
600
|
+
* on the same staged tile.
|
|
601
|
+
* @internal
|
|
602
|
+
*/ select() {
|
|
603
|
+
let count = 0;
|
|
604
|
+
const budget = this._budget;
|
|
605
|
+
const frame = this._frame;
|
|
606
|
+
const takeNew = (l, limit)=>{
|
|
607
|
+
const level = this._levels[l];
|
|
608
|
+
for(let i = 0; i < level.count && count < limit; i++){
|
|
609
|
+
if (!level.state[i]) {
|
|
610
|
+
this._selection[count++] = (level.tileBase + i | l << SELECT_LEVEL_SHIFT | SELECT_FIRST_BIT) >>> 0;
|
|
611
|
+
level.state[i] = 1;
|
|
612
|
+
level.lastSelected[i] = frame;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
if (this._levels.length > 1) {
|
|
617
|
+
takeNew(this._levels.length - 1, budget >> 2);
|
|
618
|
+
}
|
|
619
|
+
for(let l = 0; l < this._levels.length && count < budget; l++){
|
|
620
|
+
takeNew(l, budget);
|
|
621
|
+
}
|
|
622
|
+
const remaining = budget - count;
|
|
623
|
+
for(let l = 0; l < this._levels.length && count < budget; l++){
|
|
624
|
+
const level = this._levels[l];
|
|
625
|
+
let share = Math.max(1, Math.round(remaining * level.count / this._count));
|
|
626
|
+
let scanned = 0;
|
|
627
|
+
while(share > 0 && count < budget && scanned < level.count){
|
|
628
|
+
const i = level.cursor;
|
|
629
|
+
level.cursor = (level.cursor + 1) % level.count;
|
|
630
|
+
scanned++;
|
|
631
|
+
if (level.state[i] && level.lastSelected[i] !== frame) {
|
|
632
|
+
this._selection[count++] = (level.tileBase + i | l << SELECT_LEVEL_SHIFT) >>> 0;
|
|
633
|
+
level.lastSelected[i] = frame;
|
|
634
|
+
share--;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
for(let i = count; i < budget; i++){
|
|
639
|
+
this._selection[i] = 0xffffffff;
|
|
640
|
+
}
|
|
641
|
+
return count;
|
|
642
|
+
}
|
|
643
|
+
/** @internal */ randomRotation() {
|
|
644
|
+
// Uniform random unit quaternion (Shoemake)
|
|
645
|
+
const u1 = this._prng.get();
|
|
646
|
+
const u2 = this._prng.get() * 2 * Math.PI;
|
|
647
|
+
const u3 = this._prng.get() * 2 * Math.PI;
|
|
648
|
+
const a = Math.sqrt(1 - u1);
|
|
649
|
+
const b = Math.sqrt(u1);
|
|
650
|
+
this._rotation.setXYZW(a * Math.sin(u2), a * Math.cos(u2), b * Math.sin(u3), b * Math.cos(u3));
|
|
651
|
+
}
|
|
652
|
+
/** @internal */ updateParams() {
|
|
653
|
+
const o = this._options;
|
|
654
|
+
const p = this._params;
|
|
655
|
+
p.set([
|
|
656
|
+
this._levels.length,
|
|
657
|
+
this._tileCols,
|
|
658
|
+
IRRADIANCE_TEXELS,
|
|
659
|
+
VISIBILITY_TEXELS
|
|
660
|
+
], 0);
|
|
661
|
+
p.set([
|
|
662
|
+
this._irradiance.width,
|
|
663
|
+
this._irradiance.height,
|
|
664
|
+
this._visibility.width,
|
|
665
|
+
this._visibility.height
|
|
666
|
+
], 4);
|
|
667
|
+
p.set([
|
|
668
|
+
o.intensity * this._exposureScale,
|
|
669
|
+
o.normalBias,
|
|
670
|
+
o.viewBias,
|
|
671
|
+
o.maxRayDistance
|
|
672
|
+
], 8);
|
|
673
|
+
this._levels.forEach((level, l)=>{
|
|
674
|
+
const origin = level.origin ?? [
|
|
675
|
+
0,
|
|
676
|
+
0,
|
|
677
|
+
0
|
|
678
|
+
];
|
|
679
|
+
p.set([
|
|
680
|
+
origin[0],
|
|
681
|
+
origin[1],
|
|
682
|
+
origin[2],
|
|
683
|
+
level.spacing
|
|
684
|
+
], 12 + l * 8);
|
|
685
|
+
p.set([
|
|
686
|
+
level.grid[0],
|
|
687
|
+
level.grid[1],
|
|
688
|
+
level.grid[2],
|
|
689
|
+
level.tileBase
|
|
690
|
+
], 16 + l * 8);
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
/** @internal */ bindCommon(bg, global) {
|
|
694
|
+
global.applyBindGroup(bg);
|
|
695
|
+
bg.setBuffer(PROBE_DATA, this._data);
|
|
696
|
+
bg.setValue(PROBE_PARAMS, this._params);
|
|
697
|
+
bg.setBuffer('selected', this._selected);
|
|
698
|
+
bg.setValue('rotation', this._rotation);
|
|
699
|
+
bg.setValue('rayInfo', new Vector4(this._rays, this._budget, this._frame, this._options.hysteresis));
|
|
700
|
+
}
|
|
701
|
+
/** @internal */ scratchBuffer(name, bytes) {
|
|
702
|
+
let buffer = this._scratch.get(name);
|
|
703
|
+
if (!buffer || buffer.byteLength < bytes) {
|
|
704
|
+
buffer?.dispose();
|
|
705
|
+
buffer = getDevice().createBuffer(Math.max(16, bytes), {
|
|
706
|
+
usage: 'uniform',
|
|
707
|
+
storage: true
|
|
708
|
+
});
|
|
709
|
+
this._scratch.set(name, buffer);
|
|
710
|
+
}
|
|
711
|
+
return buffer;
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Pushes selected probes out of nearby geometry along the SDF gradient,
|
|
715
|
+
* never through a surface; on first update and every few frames after.
|
|
716
|
+
* @internal
|
|
717
|
+
*/ runRelocate(global, selected) {
|
|
718
|
+
const device = getDevice();
|
|
719
|
+
const levels = global.levels;
|
|
720
|
+
if (!this._relocateProgram) {
|
|
721
|
+
this._relocateProgram = buildSDFProgram({
|
|
722
|
+
label: 'ProbeRelocate',
|
|
723
|
+
workgroupSize: [
|
|
724
|
+
64,
|
|
725
|
+
1,
|
|
726
|
+
1
|
|
727
|
+
],
|
|
728
|
+
compute (pb) {
|
|
729
|
+
declareGlobalSDFUniforms(this, 0, levels);
|
|
730
|
+
declareDynamicSDFUniforms(this, 0);
|
|
731
|
+
this[PROBE_DATA] = pb.vec4[0]().storageBuffer(0);
|
|
732
|
+
this[PROBE_PARAMS] = pb.vec4[PROBE_PARAM_VEC4S]().uniform(0);
|
|
733
|
+
this.selected = pb.uint[0]().storageBufferReadonly(0);
|
|
734
|
+
this.rotation = pb.vec4().uniform(0);
|
|
735
|
+
// (rays per probe, probes selected, frame, hysteresis)
|
|
736
|
+
this.rayInfo = pb.vec4().uniform(0);
|
|
737
|
+
this.relocate = pb.uint[0]().storageBufferReadonly(0);
|
|
738
|
+
pb.main(function() {
|
|
739
|
+
this.$l.slot = this.$builtins.globalInvocationId.x;
|
|
740
|
+
this.$if(pb.greaterThanEqual(this.slot, pb.uint(this.rayInfo.y)), function() {
|
|
741
|
+
this.$return();
|
|
742
|
+
});
|
|
743
|
+
this.$if(pb.equal(this.relocate.at(this.slot), 0), function() {
|
|
744
|
+
this.$return();
|
|
745
|
+
});
|
|
746
|
+
this.$l.sel = this.selected.at(this.slot);
|
|
747
|
+
this.$l.index = pb.int(pb.compAnd(this.sel, pb.uint(SELECT_INDEX_MASK)));
|
|
748
|
+
this.$l.level = pb.int(pb.compAnd(pb.sar(this.sel, SELECT_LEVEL_SHIFT), pb.uint(0x7f)));
|
|
749
|
+
this.$l.texel = probeTexelFromIndex(this, this.level, this.index);
|
|
750
|
+
this.$l.world = probeWorldFromTexel(this, this.level, this.texel);
|
|
751
|
+
this.$l.data = this[PROBE_DATA].at(this.index);
|
|
752
|
+
this.$l.spacing = probeLevelOrigin(this, this.level).w;
|
|
753
|
+
this.$l.grid = pb.mul(pb.vec3(this.world), this.spacing);
|
|
754
|
+
// Keep at least this far from surfaces, moving at most this far from the grid point
|
|
755
|
+
this.$l.keep = pb.mul(this.spacing, 0.3);
|
|
756
|
+
this.$l.limit = pb.mul(this.spacing, 0.45);
|
|
757
|
+
// Central differences at least a finest field voxel apart, so sub-voxel walls give a gradient
|
|
758
|
+
this.$l.h = pb.max(pb.mul(this.spacing, 0.05), this[GLOBAL_SDF_LEVELS].at(0).w);
|
|
759
|
+
// Crossing check every quarter voxel: thin walls dip only slightly below their face distance
|
|
760
|
+
this.$l.probeStep = pb.mul(this[GLOBAL_SDF_LEVELS].at(0).w, 0.25);
|
|
761
|
+
// Always from the grid point, so the result depends on the field alone and never drifts
|
|
762
|
+
this.$l.pos = this.grid;
|
|
763
|
+
this.$for(pb.int('iter'), 0, 3, function() {
|
|
764
|
+
this.$l.d = sampleSceneSDF(this, this.pos, levels);
|
|
765
|
+
this.$if(pb.greaterThanEqual(this.d, this.keep), function() {
|
|
766
|
+
this.$break();
|
|
767
|
+
});
|
|
768
|
+
this.$l.g = pb.vec3(pb.sub(sampleSceneSDF(this, pb.add(this.pos, pb.vec3(this.h, 0, 0)), levels), sampleSceneSDF(this, pb.sub(this.pos, pb.vec3(this.h, 0, 0)), levels)), pb.sub(sampleSceneSDF(this, pb.add(this.pos, pb.vec3(0, this.h, 0)), levels), sampleSceneSDF(this, pb.sub(this.pos, pb.vec3(0, this.h, 0)), levels)), pb.sub(sampleSceneSDF(this, pb.add(this.pos, pb.vec3(0, 0, this.h)), levels), sampleSceneSDF(this, pb.sub(this.pos, pb.vec3(0, 0, this.h)), levels)));
|
|
769
|
+
this.$l.n = pb.div(this.g, pb.max(pb.length(this.g), 1e-6));
|
|
770
|
+
this.$l.next = pb.sub(pb.add(this.pos, pb.mul(this.n, pb.sub(this.keep, this.d))), this.grid);
|
|
771
|
+
this.$l.len = pb.length(this.next);
|
|
772
|
+
this.$if(pb.greaterThan(this.len, this.limit), function() {
|
|
773
|
+
this.next = pb.mul(this.next, pb.div(this.limit, this.len));
|
|
774
|
+
});
|
|
775
|
+
this.next = pb.add(this.grid, this.next);
|
|
776
|
+
// A distance dip along the move means it passes through geometry: stop here
|
|
777
|
+
this.$l.moveLen = pb.length(pb.sub(this.next, this.pos));
|
|
778
|
+
this.$l.samples = pb.int(pb.min(pb.ceil(pb.div(this.moveLen, this.probeStep)), 64));
|
|
779
|
+
this.$l.through = false;
|
|
780
|
+
this.$for(pb.int('k'), 1, this.samples, function() {
|
|
781
|
+
this.$l.q = pb.mix(this.pos, this.next, pb.div(pb.float(this.k), pb.float(this.samples)));
|
|
782
|
+
this.$if(pb.lessThan(sampleSceneSDF(this, this.q, levels), pb.sub(this.d, this.probeStep)), function() {
|
|
783
|
+
this.through = true;
|
|
784
|
+
this.$break();
|
|
785
|
+
});
|
|
786
|
+
});
|
|
787
|
+
this.$if(this.through, function() {
|
|
788
|
+
this.$break();
|
|
789
|
+
});
|
|
790
|
+
this.pos = this.next;
|
|
791
|
+
});
|
|
792
|
+
this.$l.offset = pb.sub(this.pos, this.grid);
|
|
793
|
+
// Classification (Majercik et al. 2021, §5): a probe inside geometry is
|
|
794
|
+
// inactive; one out again starts over. Static field only, so dynamic instances turn none off
|
|
795
|
+
this.$l.probeState = this.data.w;
|
|
796
|
+
this.$if(pb.lessThan(sampleGlobalSDF(this, this.pos, levels), 0), function() {
|
|
797
|
+
this.probeState = pb.float(PROBE_STATE_INACTIVE);
|
|
798
|
+
}).$elseif(pb.greaterThan(this.probeState, PROBE_STATE_INACTIVE - 0.5), function() {
|
|
799
|
+
this.probeState = pb.float(0);
|
|
800
|
+
});
|
|
801
|
+
this[PROBE_DATA].setAt(this.index, pb.vec4(this.offset, this.probeState));
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
});
|
|
805
|
+
this._relocateBindGroup = device.createBindGroup(this._relocateProgram.bindGroupLayouts[0]);
|
|
806
|
+
}
|
|
807
|
+
// Which selected probes relocate this frame
|
|
808
|
+
const flags = new Uint32Array(new ArrayBuffer(this._budget * 4));
|
|
809
|
+
for(let i = 0; i < selected; i++){
|
|
810
|
+
const sel = this._selection[i];
|
|
811
|
+
const level = this._levels[sel >>> SELECT_LEVEL_SHIFT & 0x7f];
|
|
812
|
+
const index = (sel & SELECT_INDEX_MASK) - level.tileBase;
|
|
813
|
+
if (this._frame - level.lastRelocated[index] >= RELOCATE_EVERY) {
|
|
814
|
+
flags[i] = 1;
|
|
815
|
+
level.lastRelocated[index] = this._frame;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
const relocateBuffer = this.scratchBuffer('relocate', flags.byteLength);
|
|
819
|
+
relocateBuffer.bufferSubData(0, flags);
|
|
820
|
+
const bg = this._relocateBindGroup;
|
|
821
|
+
this.bindCommon(bg, global);
|
|
822
|
+
bg.setBuffer('relocate', relocateBuffer);
|
|
823
|
+
device.setProgram(this._relocateProgram);
|
|
824
|
+
device.setBindGroup(0, bg);
|
|
825
|
+
device.compute(Math.ceil(selected / 64), 1, 1);
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Seeds the irradiance of probes starting over by trilinearly blending the
|
|
829
|
+
* eight surrounding active probes of the level above; the first update then
|
|
830
|
+
* blends into the seed with hysteresis. Without a seed, alpha is -1. One
|
|
831
|
+
* workgroup per selected probe, written by selection slot.
|
|
832
|
+
* @internal
|
|
833
|
+
*/ runSeed(selected) {
|
|
834
|
+
const device = getDevice();
|
|
835
|
+
if (!this._seedProgram) {
|
|
836
|
+
this._seedProgram = buildSDFProgram({
|
|
837
|
+
label: 'ProbeSeed',
|
|
838
|
+
workgroupSize: [
|
|
839
|
+
IRRADIANCE_TEXELS,
|
|
840
|
+
IRRADIANCE_TEXELS,
|
|
841
|
+
1
|
|
842
|
+
],
|
|
843
|
+
compute (pb) {
|
|
844
|
+
this[PROBE_DATA] = pb.vec4[0]().storageBufferReadonly(0);
|
|
845
|
+
this[PROBE_PARAMS] = pb.vec4[PROBE_PARAM_VEC4S]().uniform(0);
|
|
846
|
+
this.selected = pb.uint[0]().storageBufferReadonly(0);
|
|
847
|
+
this.irradiance = pb.tex2D().sampleType('unfilterable-float').uniform(0);
|
|
848
|
+
this.seedOut = pb.texStorage2D.rgba16float().storage(0);
|
|
849
|
+
pb.main(function() {
|
|
850
|
+
this.$l.slot = this.$builtins.workGroupId.x;
|
|
851
|
+
this.$l.sel = this.selected.at(this.slot);
|
|
852
|
+
this.$l.index = pb.int(pb.compAnd(this.sel, pb.uint(SELECT_INDEX_MASK)));
|
|
853
|
+
this.$l.level = pb.int(pb.compAnd(pb.sar(this.sel, SELECT_LEVEL_SHIFT), pb.uint(0x7f)));
|
|
854
|
+
this.$l.uv = pb.ivec2(this.$builtins.localInvocationId.xy);
|
|
855
|
+
this.$l.data = this[PROBE_DATA].at(this.index);
|
|
856
|
+
// Starting over as the blend tells it
|
|
857
|
+
this.$l.first = pb.or(pb.greaterThan(pb.compAnd(this.sel, pb.uint(SELECT_FIRST_BIT)), pb.uint(0)), pb.lessThan(this.data.w, 0.5));
|
|
858
|
+
this.$l.parent = pb.add(this.level, 1);
|
|
859
|
+
this.$l.sum = pb.vec4(0);
|
|
860
|
+
this.$l.wsum = pb.float(0);
|
|
861
|
+
this.$if(pb.and(this.first, pb.lessThan(this.parent, pb.int(this[PROBE_PARAMS].at(0).x))), function() {
|
|
862
|
+
this.$l.texel = probeTexelFromIndex(this, this.level, this.index);
|
|
863
|
+
this.$l.pos = probePosition(this, this.level, probeWorldFromTexel(this, this.level, this.texel), this.data);
|
|
864
|
+
this.$l.lo = probeLevelOrigin(this, this.parent);
|
|
865
|
+
this.$l.n3 = pb.vec3(probeLevelGrid(this, this.parent).xyz);
|
|
866
|
+
// In probes of the level above, its grid points at integers
|
|
867
|
+
this.$l.g = pb.sub(pb.div(this.pos, this.lo.w), this.lo.xyz);
|
|
868
|
+
this.$if(pb.and(pb.all(pb.greaterThanEqual(this.g, pb.vec3(0))), pb.all(pb.lessThanEqual(this.g, pb.sub(this.n3, pb.vec3(1))))), function() {
|
|
869
|
+
this.$l.base = pb.clamp(pb.floor(this.g), pb.vec3(0), pb.sub(this.n3, pb.vec3(2)));
|
|
870
|
+
this.$l.alpha = pb.clamp(pb.sub(this.g, this.base), pb.vec3(0), pb.vec3(1));
|
|
871
|
+
for(let i = 0; i < 8; i++){
|
|
872
|
+
const off = [
|
|
873
|
+
i & 1,
|
|
874
|
+
i >> 1 & 1,
|
|
875
|
+
i >> 2 & 1
|
|
876
|
+
];
|
|
877
|
+
this.$l[`pw${i}`] = pb.add(pb.ivec3(this.lo.xyz), pb.ivec3(this.base), pb.ivec3(off[0], off[1], off[2]));
|
|
878
|
+
this.$l[`pi${i}`] = probeTileIndex(this, this.parent, probeTexelFromWorld(this, this.parent, this[`pw${i}`]));
|
|
879
|
+
this.$if(probeActive(this, this[PROBE_DATA].at(this[`pi${i}`])), function() {
|
|
880
|
+
this.$l.tri = pb.mul(off[0] ? this.alpha.x : pb.sub(1, this.alpha.x), off[1] ? this.alpha.y : pb.sub(1, this.alpha.y), off[2] ? this.alpha.z : pb.sub(1, this.alpha.z));
|
|
881
|
+
this.sum = pb.add(this.sum, pb.mul(pb.textureLoad(this.irradiance, pb.add(probeTileOrigin(this, this[`pi${i}`], pb.float(IRRADIANCE_TEXELS)), this.uv), 0), this.tri));
|
|
882
|
+
this.wsum = pb.add(this.wsum, this.tri);
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
});
|
|
886
|
+
});
|
|
887
|
+
pb.textureStore(this.seedOut, pb.uvec2(pb.add(stagingOrigin(this, this.slot, IRRADIANCE_TEXELS), this.uv)), this.$choice(pb.greaterThan(this.wsum, 1e-4), pb.div(this.sum, pb.max(this.wsum, 1e-4)), pb.vec4(0, 0, 0, -1)));
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
});
|
|
891
|
+
this._seedBindGroup = device.createBindGroup(this._seedProgram.bindGroupLayouts[0]);
|
|
892
|
+
}
|
|
893
|
+
const bg = this._seedBindGroup;
|
|
894
|
+
bg.setBuffer(PROBE_DATA, this._data);
|
|
895
|
+
bg.setValue(PROBE_PARAMS, this._params);
|
|
896
|
+
bg.setBuffer('selected', this._selected);
|
|
897
|
+
bg.setTexture('irradiance', this._irradiance, fetchSampler('clamp_nearest_nomip'));
|
|
898
|
+
bg.setTexture('seedOut', this._irradianceSeed);
|
|
899
|
+
device.setProgram(this._seedProgram);
|
|
900
|
+
device.setBindGroup(0, bg);
|
|
901
|
+
device.compute(selected, 1, 1);
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* Traces the rays of the selected probes: one fragment per ray, the hit's
|
|
905
|
+
* voxel radiance or the sky, and the hit distance (negative when the ray
|
|
906
|
+
* started inside geometry, {@link SKY_ESCAPE_DISTANCE} when it escaped to
|
|
907
|
+
* the sky), into the ray texture.
|
|
908
|
+
* @internal
|
|
909
|
+
*/ runTrace(global, voxels, camera, selected) {
|
|
910
|
+
const device = getDevice();
|
|
911
|
+
const scene = this._scene;
|
|
912
|
+
const envLight = scene.env.light.envLight;
|
|
913
|
+
// Voxel radiance and sky bake share the cache exposure; the sky takes the strength alone
|
|
914
|
+
const envStrength = scene.env.light.strength ?? 0;
|
|
915
|
+
// No sky without an environment light (its stand-in carries only the probes)
|
|
916
|
+
const skyMode = scene.env.light.type === 'none' ? 'none' : envLight.hasRadiance() ? 'radiance' : envLight.hasIrradiance() ? 'irradiance' : 'none';
|
|
917
|
+
const envVariant = skyMode !== 'none' ? `${envLight.getType()}:${envLight.irradianceSHFB ? 'fb' : envLight.irradianceSH ? 'buf' : ''}` : '';
|
|
918
|
+
const key = `${global.levels}:${skyMode}:${envVariant}`;
|
|
919
|
+
if (!this._traceProgram || this._traceKey !== key) {
|
|
920
|
+
this._traceBindGroup?.dispose();
|
|
921
|
+
this._traceProgram?.dispose();
|
|
922
|
+
const levels = global.levels;
|
|
923
|
+
this._traceProgram = buildSDFProgram({
|
|
924
|
+
label: 'ProbeTrace',
|
|
925
|
+
vertex (pb) {
|
|
926
|
+
this.$inputs.pos = pb.vec2().attrib('position');
|
|
927
|
+
pb.main(function() {
|
|
928
|
+
this.$builtins.position = pb.vec4(this.$inputs.pos, 0, 1);
|
|
929
|
+
});
|
|
930
|
+
},
|
|
931
|
+
fragment (pb) {
|
|
932
|
+
this.$outputs.color = pb.vec4();
|
|
933
|
+
declareGlobalSDFUniforms(this, 0, levels);
|
|
934
|
+
declareDynamicSDFUniforms(this, 0);
|
|
935
|
+
declareVoxelSceneUniforms(this, 0);
|
|
936
|
+
this[PROBE_DATA] = pb.vec4[0]().storageBufferReadonly(0);
|
|
937
|
+
this[PROBE_PARAMS] = pb.vec4[PROBE_PARAM_VEC4S]().uniform(0);
|
|
938
|
+
this.selected = pb.uint[0]().storageBufferReadonly(0);
|
|
939
|
+
this.rotation = pb.vec4().uniform(0);
|
|
940
|
+
this.rayInfo = pb.vec4().uniform(0);
|
|
941
|
+
// (environment strength, 0, 0, 0)
|
|
942
|
+
this.skyInfo = pb.vec4().uniform(0);
|
|
943
|
+
if (skyMode !== 'none') {
|
|
944
|
+
envLight.initShaderBindings(pb);
|
|
945
|
+
}
|
|
946
|
+
pb.main(function() {
|
|
947
|
+
this.$l.fc = pb.ivec2(pb.floor(this.$builtins.fragCoord.xy));
|
|
948
|
+
this.$l.slot = pb.uint(this.fc.y);
|
|
949
|
+
this.$l.sel = this.selected.at(this.slot);
|
|
950
|
+
this.$l.index = pb.int(pb.compAnd(this.sel, pb.uint(SELECT_INDEX_MASK)));
|
|
951
|
+
this.$l.level = pb.int(pb.compAnd(pb.sar(this.sel, SELECT_LEVEL_SHIFT), pb.uint(0x7f)));
|
|
952
|
+
this.$l.texel = probeTexelFromIndex(this, this.level, this.index);
|
|
953
|
+
this.$l.world = probeWorldFromTexel(this, this.level, this.texel);
|
|
954
|
+
this.$l.data = this[PROBE_DATA].at(this.index);
|
|
955
|
+
this.$l.pos = probePosition(this, this.level, this.world, this.data);
|
|
956
|
+
this.$l.dir = quatRotate(this, this.rotation, sphericalFibonacci(this, pb.float(this.fc.x), this.rayInfo.x));
|
|
957
|
+
// Coarser levels look proportionally farther
|
|
958
|
+
this.$l.spacing = probeLevelOrigin(this, this.level).w;
|
|
959
|
+
this.$l.maxDist = pb.mul(this[PROBE_PARAMS].at(2).w, pb.div(this.spacing, probeLevelOrigin(this, 0).w));
|
|
960
|
+
// Past the visibility range, start at the field level matching the probe level;
|
|
961
|
+
// within it read every level, since coarse
|
|
962
|
+
// levels close thin gaps and would shut probes in
|
|
963
|
+
this.$l.fieldScale = pb.div(this[GLOBAL_SDF_LEVELS].at(0).w, probeLevelOrigin(this, 0).w);
|
|
964
|
+
this.$l.farLevel = pb.int(0);
|
|
965
|
+
for(let i = 1; i < levels; i++){
|
|
966
|
+
this.$if(pb.lessThanEqual(this[GLOBAL_SDF_LEVELS].at(i).w, pb.mul(this.spacing, this.fieldScale, 1.01)), function() {
|
|
967
|
+
this.farLevel = pb.int(i);
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
this.$l.fineRange = pb.mul(this.spacing, VISIBILITY_RANGE);
|
|
971
|
+
this.$l.radiance = pb.vec3(0);
|
|
972
|
+
this.$l.dist = this.maxDist;
|
|
973
|
+
this.$l.d0 = sampleSceneSDF(this, this.pos, levels);
|
|
974
|
+
this.$if(pb.lessThan(this.d0, 0), function() {
|
|
975
|
+
// Started inside geometry: a backface hit at no distance
|
|
976
|
+
this.dist = pb.float(-0.01);
|
|
977
|
+
}).$else(function() {
|
|
978
|
+
this.$l.t = pb.float(0);
|
|
979
|
+
this.$l.hit = false;
|
|
980
|
+
this.$l.prevD = pb.float(0);
|
|
981
|
+
// Distance read at the hit
|
|
982
|
+
this.$l.hitD = pb.float(0);
|
|
983
|
+
// Voxel size of the level that answered at the hit, 0 for a dynamic instance
|
|
984
|
+
this.$l.hitVoxel = pb.float(0);
|
|
985
|
+
// Finest field level the step that hit could read
|
|
986
|
+
this.$l.hitMin = pb.int(0);
|
|
987
|
+
this.$for(pb.int('i'), 0, TRACE_STEPS, function() {
|
|
988
|
+
this.$l.sp = pb.add(this.pos, pb.mul(this.dir, this.t));
|
|
989
|
+
this.$l.stepMin = this.$choice(pb.lessThan(this.t, this.fineRange), pb.int(0), this.farLevel);
|
|
990
|
+
this.$l.s = sampleSceneSDFHinted(this, this.sp, this.prevD, levels, this.stepMin);
|
|
991
|
+
this.$l.d = this.s.x;
|
|
992
|
+
this.prevD = this.d;
|
|
993
|
+
this.$l.expand = sdfSurfaceExpand(this, this.sp, this.s, this.t, levels);
|
|
994
|
+
this.$if(pb.lessThan(this.d, this.expand), function() {
|
|
995
|
+
// Swollen surface decides the hit so sub-voxel walls stop the ray
|
|
996
|
+
this.hitD = this.d;
|
|
997
|
+
this.hit = true;
|
|
998
|
+
this.hitVoxel = this.s.y;
|
|
999
|
+
this.hitMin = this.stepMin;
|
|
1000
|
+
this.$break();
|
|
1001
|
+
});
|
|
1002
|
+
this.t = pb.add(this.t, pb.max(this.d, 0.02));
|
|
1003
|
+
this.$if(pb.greaterThan(this.t, this.maxDist), function() {
|
|
1004
|
+
this.$break();
|
|
1005
|
+
});
|
|
1006
|
+
});
|
|
1007
|
+
this.$if(this.hit, function() {
|
|
1008
|
+
// Keep the distance to the unswollen surface, else concave corners read as hidden
|
|
1009
|
+
this.dist = sdfRefineHit(this, this.pos, this.dir, this.t, this.hitD, this.hitVoxel, this.hitMin, levels);
|
|
1010
|
+
this.$l.hp = pb.add(this.pos, pb.mul(this.dir, this.t));
|
|
1011
|
+
// Hit normal from the SDF gradient
|
|
1012
|
+
this.$l.hg = pb.vec3(0);
|
|
1013
|
+
this.$if(pb.greaterThan(this.hitVoxel, 0), function() {
|
|
1014
|
+
// Central differences over half a voxel of the answering level
|
|
1015
|
+
// (ComputeGlobalDistanceFieldNormal); level blends take the nearer level
|
|
1016
|
+
this.$l.hl = this.hitMin;
|
|
1017
|
+
for(let i = 1; i < levels; i++){
|
|
1018
|
+
this.$if(pb.and(pb.greaterThanEqual(pb.int(i), this.hitMin), pb.greaterThan(this.hitVoxel, pb.mul(this[GLOBAL_SDF_LEVELS].at(i - 1).w, Math.SQRT2))), function() {
|
|
1019
|
+
this.hl = pb.int(i);
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
this.$l.he = pb.mul(this[GLOBAL_SDF_LEVELS].at(this.hl).w, 0.5);
|
|
1023
|
+
this.hg = pb.vec3(pb.sub(sampleGlobalSDFLevel(this, pb.add(this.hp, pb.vec3(this.he, 0, 0)), this.hl, levels), sampleGlobalSDFLevel(this, pb.sub(this.hp, pb.vec3(this.he, 0, 0)), this.hl, levels)), pb.sub(sampleGlobalSDFLevel(this, pb.add(this.hp, pb.vec3(0, this.he, 0)), this.hl, levels), sampleGlobalSDFLevel(this, pb.sub(this.hp, pb.vec3(0, this.he, 0)), this.hl, levels)), pb.sub(sampleGlobalSDFLevel(this, pb.add(this.hp, pb.vec3(0, 0, this.he)), this.hl, levels), sampleGlobalSDFLevel(this, pb.sub(this.hp, pb.vec3(0, 0, this.he)), this.hl, levels)));
|
|
1024
|
+
}).$else(function() {
|
|
1025
|
+
// Dynamic instance: forward differences of the merged field over the finest voxel
|
|
1026
|
+
this.$l.he = this[GLOBAL_SDF_LEVELS].at(0).w;
|
|
1027
|
+
this.$l.hd = sampleSceneSDF(this, this.hp, levels);
|
|
1028
|
+
this.hg = pb.vec3(pb.sub(sampleSceneSDF(this, pb.add(this.hp, pb.vec3(this.he, 0, 0)), levels), this.hd), pb.sub(sampleSceneSDF(this, pb.add(this.hp, pb.vec3(0, this.he, 0)), levels), this.hd), pb.sub(sampleSceneSDF(this, pb.add(this.hp, pb.vec3(0, 0, this.he)), levels), this.hd));
|
|
1029
|
+
});
|
|
1030
|
+
this.$l.hn = this.$choice(pb.greaterThan(pb.dot(this.hg, this.hg), 1e-12), pb.normalize(this.hg), pb.neg(this.dir));
|
|
1031
|
+
this.radiance = sampleVoxelSceneRadiance(this, this.hp, this.hn, levels);
|
|
1032
|
+
}).$else(function() {
|
|
1033
|
+
this.dist = pb.float(SKY_ESCAPE_DISTANCE);
|
|
1034
|
+
if (skyMode === 'radiance') {
|
|
1035
|
+
this.radiance = pb.mul(envLight.getRadiance(this, this.dir, pb.float(0)).rgb, this.skyInfo.x);
|
|
1036
|
+
} else if (skyMode === 'irradiance') {
|
|
1037
|
+
this.radiance = pb.mul(envLight.getIrradiance(this, this.dir).rgb, this.skyInfo.x);
|
|
1038
|
+
}
|
|
1039
|
+
});
|
|
1040
|
+
});
|
|
1041
|
+
this.$outputs.color = pb.vec4(this.radiance, this.dist);
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
});
|
|
1045
|
+
this._traceKey = key;
|
|
1046
|
+
this._traceBindGroup = device.createBindGroup(this._traceProgram.bindGroupLayouts[0]);
|
|
1047
|
+
}
|
|
1048
|
+
const bg = this._traceBindGroup;
|
|
1049
|
+
this.bindCommon(bg, global);
|
|
1050
|
+
bg.setTexture(VOXEL_SCENE_ALBEDO, voxels.albedo, fetchSampler('clamp_nearest_nomip'));
|
|
1051
|
+
bg.setTexture(VOXEL_SCENE_EMISSIVE, voxels.emissive, fetchSampler('clamp_nearest_nomip'));
|
|
1052
|
+
bg.setTexture(VOXEL_SCENE_RADIANCE, voxels.radiance, fetchSampler('clamp_nearest_nomip'));
|
|
1053
|
+
bg.setTexture(VOXEL_SCENE_ANISO0, voxels.radianceAniso0, fetchSampler('clamp_nearest_nomip'));
|
|
1054
|
+
bg.setTexture(VOXEL_SCENE_ANISO1, voxels.radianceAniso1, fetchSampler('clamp_nearest_nomip'));
|
|
1055
|
+
bg.setValue('skyInfo', new Vector4(envStrength, 0, 0, 0));
|
|
1056
|
+
if (skyMode !== 'none') {
|
|
1057
|
+
envLight.updateBindGroup(bg);
|
|
1058
|
+
}
|
|
1059
|
+
if (!this._renderStates) {
|
|
1060
|
+
this._renderStates = device.createRenderStateSet();
|
|
1061
|
+
this._renderStates.useDepthState().enableTest(false).enableWrite(false);
|
|
1062
|
+
this._renderStates.useRasterizerState().setCullMode('none');
|
|
1063
|
+
}
|
|
1064
|
+
device.pushDeviceStates();
|
|
1065
|
+
try {
|
|
1066
|
+
device.setFramebuffer(this._rayFramebuffer);
|
|
1067
|
+
device.setViewport([
|
|
1068
|
+
0,
|
|
1069
|
+
0,
|
|
1070
|
+
this._rays,
|
|
1071
|
+
selected
|
|
1072
|
+
]);
|
|
1073
|
+
device.setScissor([
|
|
1074
|
+
0,
|
|
1075
|
+
0,
|
|
1076
|
+
this._rays,
|
|
1077
|
+
selected
|
|
1078
|
+
]);
|
|
1079
|
+
device.setProgram(this._traceProgram);
|
|
1080
|
+
device.setBindGroup(0, bg);
|
|
1081
|
+
drawFullscreenQuad(this._renderStates);
|
|
1082
|
+
} finally{
|
|
1083
|
+
device.popDeviceStates();
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
/**
|
|
1087
|
+
* Integrates the traced rays into the selected probes' maps, one thread per
|
|
1088
|
+
* texel; border texels use the direction of the interior texel they mirror.
|
|
1089
|
+
* @internal
|
|
1090
|
+
*/ runBlend(selected) {
|
|
1091
|
+
const device = getDevice();
|
|
1092
|
+
if (!this._blendIrrProgram) {
|
|
1093
|
+
this._blendIrrProgram = this.createBlendProgram(IRRADIANCE_TEXELS, false);
|
|
1094
|
+
this._blendIrrBindGroup = device.createBindGroup(this._blendIrrProgram.bindGroupLayouts[0]);
|
|
1095
|
+
this._blendVisProgram = this.createBlendProgram(VISIBILITY_TEXELS, true);
|
|
1096
|
+
this._blendVisBindGroup = device.createBindGroup(this._blendVisProgram.bindGroupLayouts[0]);
|
|
1097
|
+
}
|
|
1098
|
+
// Irradiance last: it marks probes updated, and both passes read the prior state
|
|
1099
|
+
const passes = [
|
|
1100
|
+
[
|
|
1101
|
+
'ProbeBlendVisibility',
|
|
1102
|
+
this._blendVisProgram,
|
|
1103
|
+
this._blendVisBindGroup,
|
|
1104
|
+
this._visibility,
|
|
1105
|
+
this._visibilityNext,
|
|
1106
|
+
null
|
|
1107
|
+
],
|
|
1108
|
+
[
|
|
1109
|
+
'ProbeBlendIrradiance',
|
|
1110
|
+
this._blendIrrProgram,
|
|
1111
|
+
this._blendIrrBindGroup,
|
|
1112
|
+
this._irradiance,
|
|
1113
|
+
this._irradianceNext,
|
|
1114
|
+
this._irradianceSeed
|
|
1115
|
+
]
|
|
1116
|
+
];
|
|
1117
|
+
for (const [scope, program, bg, current, next, seed] of passes){
|
|
1118
|
+
if (seed) {
|
|
1119
|
+
bg.setTexture('seedTex', seed, fetchSampler('clamp_nearest_nomip'));
|
|
1120
|
+
}
|
|
1121
|
+
bg.setBuffer(PROBE_DATA, this._data);
|
|
1122
|
+
bg.setValue(PROBE_PARAMS, this._params);
|
|
1123
|
+
bg.setBuffer('selected', this._selected);
|
|
1124
|
+
bg.setValue('rotation', this._rotation);
|
|
1125
|
+
bg.setValue('rayInfo', new Vector4(this._rays, this._budget, this._frame, this._options.hysteresis));
|
|
1126
|
+
bg.setTexture('rays', this._rayTexture, fetchSampler('clamp_nearest_nomip'));
|
|
1127
|
+
bg.setTexture('current', current, fetchSampler('clamp_nearest_nomip'));
|
|
1128
|
+
bg.setTexture('next', next);
|
|
1129
|
+
RenderGraphExecutor.profileScope(scope, ()=>{
|
|
1130
|
+
device.setProgram(program);
|
|
1131
|
+
device.setBindGroup(0, bg);
|
|
1132
|
+
device.compute(selected, 1, 1);
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
/**
|
|
1137
|
+
* Copies the staged tiles of {@link ProbeVolume.runBlend} into the maps, one
|
|
1138
|
+
* workgroup per updated probe, leaving other tiles untouched.
|
|
1139
|
+
* @internal
|
|
1140
|
+
*/ runCommit(selected) {
|
|
1141
|
+
const device = getDevice();
|
|
1142
|
+
const twoChannel = this._visibilityFormat === 'rg16f';
|
|
1143
|
+
if (!this._commitProgram) {
|
|
1144
|
+
this._commitProgram = buildSDFProgram({
|
|
1145
|
+
label: 'ProbeCommit',
|
|
1146
|
+
workgroupSize: [
|
|
1147
|
+
VISIBILITY_TEXELS,
|
|
1148
|
+
VISIBILITY_TEXELS,
|
|
1149
|
+
1
|
|
1150
|
+
],
|
|
1151
|
+
compute (pb) {
|
|
1152
|
+
this[PROBE_PARAMS] = pb.vec4[PROBE_PARAM_VEC4S]().uniform(0);
|
|
1153
|
+
this.selected = pb.uint[0]().storageBufferReadonly(0);
|
|
1154
|
+
this.stagedIrradiance = pb.tex2D().sampleType('unfilterable-float').uniform(0);
|
|
1155
|
+
this.stagedVisibility = pb.tex2D().sampleType('unfilterable-float').uniform(0);
|
|
1156
|
+
this.irradiance = pb.texStorage2D.rgba16float().storage(0);
|
|
1157
|
+
this.visibility = twoChannel ? pb.texStorage2D.rg16float().storage(0) : pb.texStorage2D.rgba16float().storage(0);
|
|
1158
|
+
pb.main(function() {
|
|
1159
|
+
this.$l.slot = this.$builtins.workGroupId.x;
|
|
1160
|
+
this.$l.index = pb.int(pb.compAnd(this.selected.at(this.slot), pb.uint(SELECT_INDEX_MASK)));
|
|
1161
|
+
this.$l.uv = pb.ivec2(this.$builtins.localInvocationId.xy);
|
|
1162
|
+
pb.textureStore(this.visibility, pb.uvec2(pb.add(probeTileOrigin(this, this.index, pb.float(VISIBILITY_TEXELS)), this.uv)), pb.textureLoad(this.stagedVisibility, pb.add(stagingOrigin(this, this.slot, VISIBILITY_TEXELS), this.uv), 0));
|
|
1163
|
+
this.$if(pb.all(pb.lessThan(this.uv, pb.ivec2(IRRADIANCE_TEXELS))), function() {
|
|
1164
|
+
pb.textureStore(this.irradiance, pb.uvec2(pb.add(probeTileOrigin(this, this.index, pb.float(IRRADIANCE_TEXELS)), this.uv)), pb.textureLoad(this.stagedIrradiance, pb.add(stagingOrigin(this, this.slot, IRRADIANCE_TEXELS), this.uv), 0));
|
|
1165
|
+
});
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
});
|
|
1169
|
+
this._commitBindGroup = device.createBindGroup(this._commitProgram.bindGroupLayouts[0]);
|
|
1170
|
+
}
|
|
1171
|
+
const bg = this._commitBindGroup;
|
|
1172
|
+
bg.setValue(PROBE_PARAMS, this._params);
|
|
1173
|
+
bg.setBuffer('selected', this._selected);
|
|
1174
|
+
bg.setTexture('stagedIrradiance', this._irradianceNext, fetchSampler('clamp_nearest_nomip'));
|
|
1175
|
+
bg.setTexture('stagedVisibility', this._visibilityNext, fetchSampler('clamp_nearest_nomip'));
|
|
1176
|
+
bg.setTexture('irradiance', this._irradiance);
|
|
1177
|
+
bg.setTexture('visibility', this._visibility);
|
|
1178
|
+
device.setProgram(this._commitProgram);
|
|
1179
|
+
device.setBindGroup(0, bg);
|
|
1180
|
+
device.compute(selected, 1, 1);
|
|
1181
|
+
}
|
|
1182
|
+
/** @internal */ createBlendProgram(texels, visibility) {
|
|
1183
|
+
const rays = this._rays;
|
|
1184
|
+
const twoChannel = this._visibilityFormat === 'rg16f';
|
|
1185
|
+
return buildSDFProgram({
|
|
1186
|
+
label: visibility ? 'ProbeBlendVisibility' : 'ProbeBlendIrradiance',
|
|
1187
|
+
workgroupSize: [
|
|
1188
|
+
texels,
|
|
1189
|
+
texels,
|
|
1190
|
+
1
|
|
1191
|
+
],
|
|
1192
|
+
compute (pb) {
|
|
1193
|
+
this[PROBE_DATA] = pb.vec4[0]().storageBuffer(0);
|
|
1194
|
+
this[PROBE_PARAMS] = pb.vec4[PROBE_PARAM_VEC4S]().uniform(0);
|
|
1195
|
+
this.selected = pb.uint[0]().storageBufferReadonly(0);
|
|
1196
|
+
this.rotation = pb.vec4().uniform(0);
|
|
1197
|
+
this.rayInfo = pb.vec4().uniform(0);
|
|
1198
|
+
this.rays = pb.tex2D().sampleType('unfilterable-float').uniform(0);
|
|
1199
|
+
this.current = pb.tex2D().sampleType('unfilterable-float').uniform(0);
|
|
1200
|
+
if (!visibility) {
|
|
1201
|
+
// Seeds of the probes starting over, by selection slot (runSeed)
|
|
1202
|
+
this.seedTex = pb.tex2D().sampleType('unfilterable-float').uniform(0);
|
|
1203
|
+
}
|
|
1204
|
+
this.next = visibility && twoChannel ? pb.texStorage2D.rg16float().storage(0) : pb.texStorage2D.rgba16float().storage(0);
|
|
1205
|
+
// A batch of the probe's rays, direction and traced result, read once per workgroup
|
|
1206
|
+
this.batchDir = pb.vec4[BLEND_BATCH]().workgroup();
|
|
1207
|
+
this.batchRay = pb.vec4[BLEND_BATCH]().workgroup();
|
|
1208
|
+
pb.main(function() {
|
|
1209
|
+
this.$l.slot = this.$builtins.workGroupId.x;
|
|
1210
|
+
this.$l.sel = this.selected.at(this.slot);
|
|
1211
|
+
this.$l.index = pb.int(pb.compAnd(this.sel, pb.uint(SELECT_INDEX_MASK)));
|
|
1212
|
+
this.$l.level = pb.int(pb.compAnd(pb.sar(this.sel, SELECT_LEVEL_SHIFT), pb.uint(0x7f)));
|
|
1213
|
+
// First update: new to its window, or state 0 (e.g. reactivated by relocation)
|
|
1214
|
+
this.$l.probeState = this[PROBE_DATA].at(this.index).w;
|
|
1215
|
+
this.$l.first = pb.or(pb.greaterThan(pb.compAnd(this.sel, pb.uint(SELECT_FIRST_BIT)), pb.uint(0)), pb.lessThan(this.probeState, 0.5));
|
|
1216
|
+
this.$l.uv = pb.ivec2(this.$builtins.localInvocationId.xy);
|
|
1217
|
+
// Border texels mirror the interior: take the direction of the texel they copy
|
|
1218
|
+
const s = texels;
|
|
1219
|
+
this.$l.src = this.uv;
|
|
1220
|
+
this.$l.onX = pb.or(pb.equal(this.uv.x, 0), pb.equal(this.uv.x, s - 1));
|
|
1221
|
+
this.$l.onY = pb.or(pb.equal(this.uv.y, 0), pb.equal(this.uv.y, s - 1));
|
|
1222
|
+
this.$if(pb.and(this.onX, this.onY), function() {
|
|
1223
|
+
this.src = pb.ivec2(this.$choice(pb.equal(this.uv.x, 0), pb.int(s - 2), pb.int(1)), this.$choice(pb.equal(this.uv.y, 0), pb.int(s - 2), pb.int(1)));
|
|
1224
|
+
}).$elseif(this.onX, function() {
|
|
1225
|
+
this.src = pb.ivec2(this.$choice(pb.equal(this.uv.x, 0), pb.int(1), pb.int(s - 2)), pb.sub(s - 1, this.uv.y));
|
|
1226
|
+
}).$elseif(this.onY, function() {
|
|
1227
|
+
this.src = pb.ivec2(pb.sub(s - 1, this.uv.x), this.$choice(pb.equal(this.uv.y, 0), pb.int(1), pb.int(s - 2)));
|
|
1228
|
+
});
|
|
1229
|
+
this.$l.oct = pb.sub(pb.mul(pb.div(pb.add(pb.vec2(this.src), pb.vec2(-1 + 0.5)), s - 2), 2), pb.vec2(1));
|
|
1230
|
+
this.$l.dir = octDecode(this, this.oct);
|
|
1231
|
+
this.$l.sum = pb.vec4(0);
|
|
1232
|
+
this.$l.wsum = pb.float(0);
|
|
1233
|
+
this.$l.maxDist = pb.mul(probeLevelOrigin(this, this.level).w, VISIBILITY_RANGE);
|
|
1234
|
+
// One ray, `ray` its traced result and `c` the cosine to the texel's direction
|
|
1235
|
+
const accumulate = (scope)=>{
|
|
1236
|
+
if (visibility) {
|
|
1237
|
+
// Sharpened (cos^50); below cos 0.8 the weight is under 2e-5, so skip
|
|
1238
|
+
scope.$if(pb.greaterThan(scope.c, 0.8), function() {
|
|
1239
|
+
this.$l.w = pb.pow(this.c, 50);
|
|
1240
|
+
this.$l.hd = pb.abs(this.ray.a);
|
|
1241
|
+
this.$if(pb.lessThan(this.ray.a, 0), function() {
|
|
1242
|
+
this.hd = pb.mul(this.hd, 0.2);
|
|
1243
|
+
});
|
|
1244
|
+
this.hd = pb.min(this.hd, this.maxDist);
|
|
1245
|
+
this.sum = pb.add(this.sum, pb.mul(pb.vec4(this.hd, pb.mul(this.hd, this.hd), 0, 0), this.w));
|
|
1246
|
+
this.wsum = pb.add(this.wsum, this.w);
|
|
1247
|
+
});
|
|
1248
|
+
} else {
|
|
1249
|
+
// Cosine-weighted sky escape share in alpha: sky visibility, as DDGI [Majercik19]
|
|
1250
|
+
scope.$if(pb.greaterThanEqual(scope.ray.a, 0), function() {
|
|
1251
|
+
this.$l.escaped = pb.float(pb.greaterThanEqual(this.ray.a, SKY_ESCAPE_DISTANCE * 0.5));
|
|
1252
|
+
this.sum = pb.add(this.sum, pb.mul(pb.vec4(this.ray.rgb, this.escaped), this.c));
|
|
1253
|
+
this.wsum = pb.add(this.wsum, this.c);
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
};
|
|
1257
|
+
this.$l.thread = pb.int(pb.add(pb.mul(this.$builtins.localInvocationId.y, texels), this.$builtins.localInvocationId.x));
|
|
1258
|
+
for(let b = 0; b < rays; b += BLEND_BATCH){
|
|
1259
|
+
const count = Math.min(BLEND_BATCH, rays - b);
|
|
1260
|
+
// Each thread stages every (texels²)-th ray of the batch
|
|
1261
|
+
this.$for(pb.int('stageRound'), 0, Math.ceil(count / (texels * texels)), function() {
|
|
1262
|
+
this.$l.j = pb.add(this.thread, pb.mul(this.stageRound, texels * texels));
|
|
1263
|
+
this.$if(pb.greaterThanEqual(this.j, count), function() {
|
|
1264
|
+
this.$break();
|
|
1265
|
+
});
|
|
1266
|
+
this.$l.r = pb.add(this.j, b);
|
|
1267
|
+
this.batchRay.setAt(this.j, pb.textureLoad(this.rays, pb.ivec2(this.r, pb.int(this.slot)), 0));
|
|
1268
|
+
this.batchDir.setAt(this.j, pb.vec4(quatRotate(this, this.rotation, sphericalFibonacci(this, pb.float(this.r), pb.float(rays))), 0));
|
|
1269
|
+
});
|
|
1270
|
+
pb.workgroupBarrier();
|
|
1271
|
+
this.$for(pb.int('k'), 0, count, function() {
|
|
1272
|
+
this.$l.ray = this.batchRay.at(this.k);
|
|
1273
|
+
this.$l.c = pb.max(pb.dot(this.dir, this.batchDir.at(this.k).xyz), 0);
|
|
1274
|
+
accumulate(this);
|
|
1275
|
+
});
|
|
1276
|
+
// The next batch overwrites the one still being read
|
|
1277
|
+
pb.workgroupBarrier();
|
|
1278
|
+
}
|
|
1279
|
+
this.$l.result = this.$choice(pb.greaterThan(this.wsum, 1e-5), pb.div(this.sum, this.wsum), visibility ? pb.vec4(this.maxDist, pb.mul(this.maxDist, this.maxDist), 0, 0) : pb.vec4(0));
|
|
1280
|
+
this.$l.origin = probeTileOrigin(this, this.index, pb.float(s));
|
|
1281
|
+
this.$l.texel = pb.add(this.origin, this.uv);
|
|
1282
|
+
this.$l.old = pb.textureLoad(this.current, this.texel, 0);
|
|
1283
|
+
this.$l.h = this.$choice(this.first, pb.float(0), this.rayInfo.w);
|
|
1284
|
+
if (!visibility) {
|
|
1285
|
+
// A probe starting over blends into its seed, if any (alpha -1 = none)
|
|
1286
|
+
this.$if(this.first, function() {
|
|
1287
|
+
this.$l.seedValue = pb.textureLoad(this.seedTex, pb.add(stagingOrigin(this, this.slot, s), this.uv), 0);
|
|
1288
|
+
this.$if(pb.greaterThanEqual(this.seedValue.a, 0), function() {
|
|
1289
|
+
this.old = this.seedValue;
|
|
1290
|
+
this.h = this.rayInfo.w;
|
|
1291
|
+
});
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
this.$l.value = pb.mix(this.result, this.old, this.h);
|
|
1295
|
+
// Staged by selection slot; runCommit copies the tile into the map
|
|
1296
|
+
pb.textureStore(this.next, pb.uvec2(pb.add(stagingOrigin(this, this.slot, s), this.uv)), visibility ? pb.vec4(this.value.xyz, 1) : this.value);
|
|
1297
|
+
if (!visibility) {
|
|
1298
|
+
// Mark the probe updated; an inactive one stays inactive
|
|
1299
|
+
this.$if(pb.equal(this.$builtins.localInvocationId.xy, pb.uvec2(0)), function() {
|
|
1300
|
+
this.$l.data = this[PROBE_DATA].at(this.index);
|
|
1301
|
+
this[PROBE_DATA].setAt(this.index, pb.vec4(this.data.xyz, this.$choice(pb.greaterThan(this.data.w, PROBE_STATE_INACTIVE - 0.5), pb.float(PROBE_STATE_INACTIVE), pb.float(1))));
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1304
|
+
});
|
|
1305
|
+
}
|
|
1306
|
+
});
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
/** First texel of a selection slot's tile in a blend output, {@link STAGING_COLS} tiles a row. */ function stagingOrigin(scope, slot, texels) {
|
|
1310
|
+
const pb = scope.$builder;
|
|
1311
|
+
const s = pb.int(slot);
|
|
1312
|
+
return pb.mul(pb.ivec2(pb.mod(s, STAGING_COLS), pb.div(s, STAGING_COLS)), texels);
|
|
1313
|
+
}
|
|
1314
|
+
/** Visibility texels as saved, rgba16f: the two moments, then 0 and 1. */ function expandVisibility(rg, texels) {
|
|
1315
|
+
const out = new Uint16Array(new ArrayBuffer(texels * 8));
|
|
1316
|
+
for(let i = 0; i < texels; i++){
|
|
1317
|
+
out[i * 4] = rg[i * 2];
|
|
1318
|
+
out[i * 4 + 1] = rg[i * 2 + 1];
|
|
1319
|
+
// Half 1.0, which the blend writes to alpha
|
|
1320
|
+
out[i * 4 + 3] = 0x3c00;
|
|
1321
|
+
}
|
|
1322
|
+
return out;
|
|
1323
|
+
}
|
|
1324
|
+
/** The two moments of saved rgba16f visibility texels, for an rg16f map. */ function compactVisibility(rgba, texels) {
|
|
1325
|
+
const out = new Uint16Array(new ArrayBuffer(texels * 4));
|
|
1326
|
+
for(let i = 0; i < texels; i++){
|
|
1327
|
+
out[i * 2] = rgba[i * 4];
|
|
1328
|
+
out[i * 2 + 1] = rgba[i * 4 + 1];
|
|
1329
|
+
}
|
|
1330
|
+
return out;
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
export { ProbeVolume, getActiveProbeVolume, getProbeVolumeForDraw };
|
|
1334
|
+
//# sourceMappingURL=probe_volume.js.map
|