@woosh/meep-engine 3.58.0 → 3.59.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/editor/particles/effect/ParticleEffectDocument.d.ts +162 -43
- package/editor/particles/effect/ParticleEffectDocument.d.ts.map +1 -1
- package/editor/particles/effect/ParticleEffectDocument.js +450 -43
- package/editor/particles/effect/ParticleReferenceSimulation.d.ts +12 -3
- package/editor/particles/effect/ParticleReferenceSimulation.d.ts.map +1 -1
- package/editor/particles/effect/ParticleReferenceSimulation.js +78 -23
- package/editor/particles/effect/make_starter_particle_effect.js +17 -17
- package/editor/view/particles/effect/ParticleEmitterInspectorView.d.ts +3 -3
- package/editor/view/particles/effect/ParticleEmitterInspectorView.d.ts.map +1 -1
- package/editor/view/particles/effect/ParticleEmitterInspectorView.js +224 -67
- package/editor/view/particles/effect/ParticleGraphEditorView.d.ts.map +1 -1
- package/editor/view/particles/effect/ParticleGraphEditorView.js +44 -11
- package/editor/view/particles/effect/ParticleNodeParametersView.d.ts +2 -1
- package/editor/view/particles/effect/ParticleNodeParametersView.d.ts.map +1 -1
- package/editor/view/particles/effect/ParticleNodeParametersView.js +46 -16
- package/editor/view/particles/effect/ParticlePreviewView.js +2 -2
- package/editor/view/particles/effect/particle-editor.css +77 -68
- package/package.json +1 -1
- package/src/engine/Engine.d.ts.map +1 -1
- package/src/engine/Engine.js +2 -2
- package/src/engine/ecs/EntityManager.d.ts +29 -5
- package/src/engine/ecs/EntityManager.d.ts.map +1 -1
- package/src/engine/ecs/EntityManager.js +37 -1
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +4 -0
- package/src/engine/graphics/ecs/particles/ParticleEffect.d.ts +74 -17
- package/src/engine/graphics/ecs/particles/ParticleEffect.d.ts.map +1 -1
- package/src/engine/graphics/ecs/particles/ParticleEffect.js +156 -25
- package/src/engine/graphics/ecs/particles/ParticleEffectSerializationAdapter.d.ts +13 -3
- package/src/engine/graphics/ecs/particles/ParticleEffectSerializationAdapter.d.ts.map +1 -1
- package/src/engine/graphics/ecs/particles/ParticleEffectSerializationAdapter.js +59 -13
- package/src/engine/graphics/ecs/particles/ParticleEffectSerializationUpgrader_1_2.d.ts +3 -3
- package/src/engine/graphics/ecs/particles/ParticleEffectSerializationUpgrader_1_2.js +9 -9
- package/src/engine/graphics/ecs/particles/ParticleEffectSerializationUpgrader_2_3.d.ts +15 -0
- package/src/engine/graphics/ecs/particles/ParticleEffectSerializationUpgrader_2_3.d.ts.map +1 -0
- package/src/engine/graphics/ecs/particles/ParticleEffectSerializationUpgrader_2_3.js +78 -0
- package/src/engine/graphics/ecs/trail3d/Trail3D.d.ts +2 -0
- package/src/engine/graphics/ecs/trail3d/Trail3D.d.ts.map +1 -1
- package/src/engine/graphics/ecs/trail3d/Trail3D.js +2 -0
- package/src/engine/graphics/ecs/trail3d/seed_trail_stroke.js +1 -1
- package/src/engine/graphics/ecs/trail3d/seed_trail_tube.js +1 -1
- package/src/engine/graphics/trail/tube/TubeX.d.ts +5 -3
- package/src/engine/graphics/trail/tube/TubeX.d.ts.map +1 -1
- package/src/engine/graphics/trail/tube/TubeX.js +5 -3
- package/src/engine/graphics/trail/tube/tube_attributes_spec.d.ts +3 -2
- package/src/engine/graphics/trail/tube/tube_attributes_spec.d.ts.map +1 -1
- package/src/engine/graphics/trail/tube/tube_attributes_spec.js +9 -8
- package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts +65 -4
- package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts.map +1 -1
- package/src/engine/graphics3/GPUParticleEmitterSystem.js +1351 -904
- package/src/engine/graphics3/trail/trail_tube_write_geometry.d.ts +1 -1
- package/src/engine/graphics3/trail/trail_tube_write_geometry.js +13 -13
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/generation/foliage/FOLIAGE_DESIGN.md +26 -5
- package/src/generation/foliage/LOD_REWORK_BRIEF_2026_10_27.md +35 -0
- package/src/generation/foliage/SELF_ORGANIZING_FRAMEWORK_BRIEF_2026_10_26.md +2 -1
- package/src/generation/foliage/TWO_FRONT_ENDS_BRIEF_2026_10_28.md +552 -0
- package/src/generation/foliage/analysis/tree_crown_opacity.d.ts.map +1 -1
- package/src/generation/foliage/analysis/tree_crown_opacity.js +2 -1
- package/src/generation/foliage/analysis/tree_render_cost.d.ts +5 -3
- package/src/generation/foliage/analysis/tree_render_cost.d.ts.map +1 -1
- package/src/generation/foliage/analysis/tree_render_cost.js +8 -7
- package/src/generation/foliage/asset/TreeAsset.d.ts +17 -0
- package/src/generation/foliage/asset/TreeAsset.d.ts.map +1 -1
- package/src/generation/foliage/asset/TreeAsset.js +20 -0
- package/src/generation/foliage/asset/TreeSpeciesCache.d.ts +69 -0
- package/src/generation/foliage/asset/TreeSpeciesCache.d.ts.map +1 -0
- package/src/generation/foliage/asset/TreeSpeciesCache.js +147 -0
- package/src/generation/foliage/asset/generate_tree.d.ts +10 -0
- package/src/generation/foliage/asset/generate_tree.d.ts.map +1 -1
- package/src/generation/foliage/asset/generate_tree.js +57 -29
- package/src/generation/foliage/asset/place_tree.d.ts +2 -1
- package/src/generation/foliage/asset/place_tree.d.ts.map +1 -1
- package/src/generation/foliage/asset/place_tree.js +2 -1
- package/src/generation/foliage/asset/tree_lod_plan.d.ts +4 -2
- package/src/generation/foliage/asset/tree_lod_plan.d.ts.map +1 -1
- package/src/generation/foliage/asset/tree_lod_plan.js +12 -14
- package/src/generation/foliage/asset/tree_shoot_instances.d.ts +119 -0
- package/src/generation/foliage/asset/tree_shoot_instances.d.ts.map +1 -0
- package/src/generation/foliage/asset/tree_shoot_instances.js +410 -0
- package/src/generation/foliage/asset/tree_shoot_prototypes.d.ts +155 -0
- package/src/generation/foliage/asset/tree_shoot_prototypes.d.ts.map +1 -0
- package/src/generation/foliage/asset/tree_shoot_prototypes.js +251 -0
- package/src/generation/foliage/config/TreeGrowth.d.ts +10 -0
- package/src/generation/foliage/config/TreeGrowth.d.ts.map +1 -0
- package/src/generation/foliage/config/TreeGrowth.js +18 -0
- package/src/generation/foliage/config/TreeLODConfig.d.ts +6 -0
- package/src/generation/foliage/config/TreeLODConfig.d.ts.map +1 -1
- package/src/generation/foliage/config/TreeLODConfig.js +7 -0
- package/src/generation/foliage/index.d.ts +3 -0
- package/src/generation/foliage/index.js +3 -0
- package/src/generation/foliage/material/foliage_tile.d.ts +6 -6
- package/src/generation/foliage/material/foliage_tile.js +3 -3
- package/src/generation/foliage/material/tree_cluster_atlas.d.ts +44 -0
- package/src/generation/foliage/material/tree_cluster_atlas.d.ts.map +1 -0
- package/src/generation/foliage/material/tree_cluster_atlas.js +37 -0
- package/src/generation/foliage/mesh/foliage_collect_attachments.js +2 -2
- package/src/generation/foliage/mesh/foliage_scale_spread.d.ts +14 -0
- package/src/generation/foliage/mesh/foliage_scale_spread.d.ts.map +1 -0
- package/src/generation/foliage/mesh/foliage_scale_spread.js +15 -0
- package/src/generation/foliage/mesh/tree_cluster_representatives.d.ts +8 -7
- package/src/generation/foliage/mesh/tree_cluster_representatives.d.ts.map +1 -1
- package/src/generation/foliage/mesh/tree_cluster_representatives.js +85 -40
- package/src/generation/foliage/mesh/tree_clusters.d.ts +26 -5
- package/src/generation/foliage/mesh/tree_clusters.d.ts.map +1 -1
- package/src/generation/foliage/mesh/tree_clusters.js +52 -39
- package/src/generation/foliage/mesh/tree_mesh_build_bark.d.ts.map +1 -1
- package/src/generation/foliage/mesh/tree_mesh_build_bark.js +32 -54
- package/src/generation/foliage/presets/make_tree_species_preset.d.ts +6 -1
- package/src/generation/foliage/presets/make_tree_species_preset.d.ts.map +1 -1
- package/src/generation/foliage/presets/make_tree_species_preset.js +157 -3
- package/src/generation/foliage/skeleton/TreeSkeleton.d.ts +19 -0
- package/src/generation/foliage/skeleton/TreeSkeleton.d.ts.map +1 -1
- package/src/generation/foliage/skeleton/TreeSkeleton.js +41 -0
- package/src/generation/foliage/skeleton/tree_skeleton_compute_joint_mechanics.d.ts +5 -0
- package/src/generation/foliage/skeleton/tree_skeleton_compute_joint_mechanics.d.ts.map +1 -1
- package/src/generation/foliage/skeleton/tree_skeleton_compute_joint_mechanics.js +8 -1
- package/src/generation/foliage/skeleton/tree_skeleton_copy_branches.d.ts +70 -0
- package/src/generation/foliage/skeleton/tree_skeleton_copy_branches.d.ts.map +1 -0
- package/src/generation/foliage/skeleton/tree_skeleton_copy_branches.js +171 -0
- package/src/generation/foliage/skeleton/tree_skeleton_extend_branch.d.ts +17 -0
- package/src/generation/foliage/skeleton/tree_skeleton_extend_branch.d.ts.map +1 -0
- package/src/generation/foliage/skeleton/tree_skeleton_extend_branch.js +79 -0
- package/src/generation/foliage/skeleton/tree_skeleton_fit_crown_frame.d.ts +12 -0
- package/src/generation/foliage/skeleton/tree_skeleton_fit_crown_frame.d.ts.map +1 -0
- package/src/generation/foliage/skeleton/tree_skeleton_fit_crown_frame.js +56 -0
- package/src/generation/foliage/skeleton/tree_skeleton_floor_girth.d.ts +11 -0
- package/src/generation/foliage/skeleton/tree_skeleton_floor_girth.d.ts.map +1 -0
- package/src/generation/foliage/skeleton/tree_skeleton_floor_girth.js +38 -0
- package/src/generation/foliage/skeleton/tree_skeleton_grow.d.ts +4 -11
- package/src/generation/foliage/skeleton/tree_skeleton_grow.d.ts.map +1 -1
- package/src/generation/foliage/skeleton/tree_skeleton_grow.js +8 -2034
- package/src/generation/foliage/skeleton/tree_skeleton_grow_framework.d.ts +22 -0
- package/src/generation/foliage/skeleton/tree_skeleton_grow_framework.d.ts.map +1 -0
- package/src/generation/foliage/skeleton/tree_skeleton_grow_framework.js +2050 -0
- package/src/generation/foliage/skeleton/tree_skeleton_grow_shoots.d.ts +10 -0
- package/src/generation/foliage/skeleton/tree_skeleton_grow_shoots.d.ts.map +1 -1
- package/src/generation/foliage/skeleton/tree_skeleton_grow_shoots.js +27 -56
- package/src/shade/descriptor/pipeline/render/DepthStencilState.d.ts +4 -0
- package/src/shade/descriptor/pipeline/render/DepthStencilState.d.ts.map +1 -1
- package/src/shade/descriptor/pipeline/render/DepthStencilState.js +170 -144
- package/src/shade/descriptor/pipeline/render/MultisampleState.d.ts +4 -0
- package/src/shade/descriptor/pipeline/render/MultisampleState.d.ts.map +1 -1
- package/src/shade/descriptor/pipeline/render/MultisampleState.js +78 -65
- package/src/shade/descriptor/pipeline/render/RenderPipelineDescriptor.d.ts.map +1 -1
- package/src/shade/descriptor/pipeline/render/RenderPipelineDescriptor.js +176 -172
- package/src/shade/descriptor/pipeline/render/StencilFaceState.d.ts +4 -0
- package/src/shade/descriptor/pipeline/render/StencilFaceState.d.ts.map +1 -1
- package/src/shade/descriptor/pipeline/render/StencilFaceState.js +107 -88
- package/src/shade/playground/particle_ecs/README.md +9 -8
- package/src/shade/playground/particle_ecs/bonfire_editor.js +3 -3
- package/src/shade/playground/particle_ecs/bonfire_effects.d.ts +6 -6
- package/src/shade/playground/particle_ecs/bonfire_effects.js +6 -6
- package/src/shade/playground/particle_editor/README.md +14 -11
- package/src/shade/playground/particle_system/particle_prototype.js +18 -18
- package/src/shade/playground/particle_system/particle_scene.js +13 -13
- package/src/shade/playground/probe_clipmap/README.md +42 -0
- package/src/shade/playground/probe_clipmap/index.html +178 -0
- package/src/shade/playground/probe_clipmap/main.d.ts +8 -0
- package/src/shade/playground/probe_clipmap/main.d.ts.map +1 -0
- package/src/shade/playground/probe_clipmap/main.js +360 -0
- package/src/shade/renderer/Renderer.d.ts +3 -1
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +94 -38
- package/src/shade/renderer/extension/ViewTextures.d.ts +2 -1
- package/src/shade/renderer/extension/ViewTextures.d.ts.map +1 -1
- package/src/shade/renderer/extension/ViewTextures.js +2 -1
- package/src/shade/renderer/fluid/GPUFluidCascade.d.ts +3 -3
- package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts +1 -1
- package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts +6 -6
- package/src/shade/renderer/fluid/pressure/GPUFluidPressureSolve.d.ts +1 -1
- package/src/shade/renderer/fluid/query/GPUFluidQueries.d.ts +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts +34 -12
- package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.js +56 -15
- package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.d.ts +2 -2
- package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.js +13 -26
- package/src/shade/renderer/geometry/virtual/runtime/VGeoViewCut.d.ts +25 -2
- package/src/shade/renderer/geometry/virtual/runtime/VGeoViewCut.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/VGeoViewCut.js +54 -32
- package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_VERDICT.d.ts +14 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_VERDICT.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_VERDICT.js +37 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.js +16 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_claim.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_claim.js +2 -6
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_descent.d.ts +131 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_descent.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_descent.js +467 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_instance.d.ts +24 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_instance.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_instance.js +111 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_rule.d.ts +42 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_rule.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_rule.js +404 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_stat.d.ts +16 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_stat.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_stat.js +23 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_want.d.ts +17 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_want.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_want.js +65 -0
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_collect.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_collect.js +14 -70
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.js +13 -773
- package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_cut_descent.d.ts +55 -0
- package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_cut_descent.d.ts.map +1 -0
- package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_cut_descent.js +248 -0
- package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.js +15 -174
- package/src/shade/renderer/global_illumination/brick4/gpu/draw/chunk_brick4_probe_meta_pick2.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/brick4/gpu/draw/chunk_brick4_probe_meta_pick2.js +8 -24
- package/src/shade/renderer/global_illumination/brick4/gpu/draw/chunk_brick4_sample_forward_indirect.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/brick4/gpu/draw/chunk_brick4_sample_forward_indirect.js +1 -25
- package/src/shade/renderer/global_illumination/lpv/deprecated/chunk_lpv_get_visibility_mask.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/lpv/deprecated/chunk_lpv_get_visibility_mask.js +5 -23
- package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts +25 -4
- package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.js +126 -8
- package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_CLIPMAP_PROPOSAL_2026_10_27.md +832 -4
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/BRICK_CLIPMAP.d.ts +7 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/BRICK_CLIPMAP.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/BRICK_CLIPMAP.js +8 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/GPUBrickClipmap.d.ts +16 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/GPUBrickClipmap.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/GPUBrickClipmap.js +9 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/{mesh/chunk_mesh_brick_object.d.ts → chunk_brick_object.d.ts} +8 -8
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_object.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_object.js +42 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_provider.d.ts +45 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_provider.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_provider.js +93 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/MESH_BRICK_INSTANCE_STRUCT.d.ts +0 -7
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/MESH_BRICK_INSTANCE_STRUCT.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/MESH_BRICK_INSTANCE_STRUCT.js +0 -8
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_instance.d.ts +2 -4
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_instance.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_instance.js +5 -8
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/shader_mesh_brick_provider.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/shader_mesh_brick_provider.js +13 -22
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/VGEO_BRICK.d.ts +99 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/VGEO_BRICK.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/VGEO_BRICK.js +139 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/VGeoBrickProvider.d.ts +65 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/VGeoBrickProvider.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/VGeoBrickProvider.js +393 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/chunk_vgeo_brick_box.d.ts +17 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/chunk_vgeo_brick_box.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/chunk_vgeo_brick_box.js +36 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/chunk_vgeo_brick_pair.d.ts +29 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/chunk_vgeo_brick_pair.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/chunk_vgeo_brick_pair.js +108 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/chunk_vgeo_brick_texel.d.ts +31 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/chunk_vgeo_brick_texel.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/chunk_vgeo_brick_texel.js +113 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/chunk_vgeo_brick_triangle.d.ts +36 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/chunk_vgeo_brick_triangle.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/chunk_vgeo_brick_triangle.js +85 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/shader_vgeo_brick_distance.d.ts +23 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/shader_vgeo_brick_distance.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/shader_vgeo_brick_distance.js +273 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/shader_vgeo_brick_gather.d.ts +13 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/shader_vgeo_brick_gather.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/shader_vgeo_brick_gather.js +151 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/shader_vgeo_brick_traverse.d.ts +13 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/shader_vgeo_brick_traverse.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/shader_vgeo_brick_traverse.js +133 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/shader_vgeo_brick_wait.d.ts +22 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/shader_vgeo_brick_wait.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/vgeo/shader_vgeo_brick_wait.js +135 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts +20 -6
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.js +152 -10
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts +7 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.js +8 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts +14 -2
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.js +50 -5
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_cage.d.ts +19 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_cage.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_cage.js +40 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_pick.d.ts +72 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_pick.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_pick.js +343 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.d.ts +29 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.js +188 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_want.d.ts +26 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_want.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_want.js +132 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts +4 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.js +26 -3
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_admit.d.ts +3 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_admit.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_admit.js +8 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_diffuse.d.ts +12 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_diffuse.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_diffuse.js +101 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_due.d.ts +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_due.js +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_indirect_lighting.d.ts +15 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_indirect_lighting.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_indirect_lighting.js +175 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_light.d.ts +26 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_light.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_light.js +210 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_retire.d.ts +3 -3
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_retire.js +3 -3
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.d.ts +2 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.js +5 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_trace.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_trace.js +3 -10
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want.d.ts +5 -9
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want.js +9 -110
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_hits.d.ts +27 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_hits.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_hits.js +110 -0
- package/src/shade/renderer/light/world_cluster/chunk_world_light_cluster_lookup.d.ts +13 -0
- package/src/shade/renderer/light/world_cluster/chunk_world_light_cluster_lookup.d.ts.map +1 -1
- package/src/shade/renderer/light/world_cluster/chunk_world_light_cluster_lookup.js +40 -0
- package/src/shade/renderer/light/world_cluster/graph_build_world_light_clusters.d.ts +11 -6
- package/src/shade/renderer/light/world_cluster/graph_build_world_light_clusters.d.ts.map +1 -1
- package/src/shade/renderer/light/world_cluster/graph_build_world_light_clusters.js +15 -8
- package/src/shade/renderer/light/world_cluster/shader_world_cluster_assign_lights.d.ts +9 -2
- package/src/shade/renderer/light/world_cluster/shader_world_cluster_assign_lights.d.ts.map +1 -1
- package/src/shade/renderer/light/world_cluster/shader_world_cluster_assign_lights.js +25 -11
- package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
- package/src/shade/renderer/particles/DESIGN.md +151 -42
- package/src/shade/renderer/particles/EMITTER_GLOBAL_STATE_PLAN_2026_10_29.md +283 -0
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts +10 -2
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
- package/src/shade/renderer/particles/GPUParticleSystem.js +46 -8
- package/src/shade/renderer/particles/ParticleConstants.d.ts +23 -6
- package/src/shade/renderer/particles/ParticleConstants.d.ts.map +1 -1
- package/src/shade/renderer/particles/ParticleConstants.js +25 -6
- package/src/shade/renderer/particles/bounds/chunk_particle_bounds.js +5 -5
- package/src/shade/renderer/particles/bounds/shader_particle_bounds.js +187 -187
- package/src/shade/renderer/particles/data/PARTICLE_DATABASE_SPEC.d.ts +20 -9
- package/src/shade/renderer/particles/data/PARTICLE_DATABASE_SPEC.d.ts.map +1 -1
- package/src/shade/renderer/particles/data/PARTICLE_DATABASE_SPEC.js +36 -10
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.d.ts +8 -7
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.d.ts.map +1 -1
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.js +25 -16
- package/src/shade/renderer/particles/data/PARTICLE_FIELD_BINDING.d.ts +51 -0
- package/src/shade/renderer/particles/data/PARTICLE_FIELD_BINDING.d.ts.map +1 -0
- package/src/shade/renderer/particles/data/PARTICLE_FIELD_BINDING.js +56 -0
- package/src/shade/renderer/particles/data/particle_emitter_record.d.ts.map +1 -1
- package/src/shade/renderer/particles/data/particle_emitter_record.js +2 -0
- package/src/shade/renderer/particles/graph/ParticleGroupBuilder.d.ts +1 -1
- package/src/shade/renderer/particles/graph/ParticleGroupBuilder.js +1 -1
- package/src/shade/renderer/particles/graph/ParticleNodeDescription.d.ts +22 -11
- package/src/shade/renderer/particles/graph/ParticleNodeDescription.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/ParticleNodeDescription.js +14 -11
- package/src/shade/renderer/particles/graph/ParticleNodeRegistry.d.ts +4 -4
- package/src/shade/renderer/particles/graph/ParticleNodeRegistry.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/ParticleNodeRegistry.js +59 -24
- package/src/shade/renderer/particles/graph/ParticleValueType.d.ts +3 -3
- package/src/shade/renderer/particles/graph/ParticleValueType.js +287 -287
- package/src/shade/renderer/particles/graph/compile_particle_graph.d.ts +13 -6
- package/src/shade/renderer/particles/graph/compile_particle_graph.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/compile_particle_graph.js +46 -27
- package/src/shade/renderer/particles/graph/infer_particle_graph_types.d.ts +8 -2
- package/src/shade/renderer/particles/graph/infer_particle_graph_types.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/infer_particle_graph_types.js +240 -234
- package/src/shade/renderer/particles/graph/make_particle_group.d.ts +1 -1
- package/src/shade/renderer/particles/graph/make_particle_group.js +1 -1
- package/src/shade/renderer/particles/graph/particle_graph_authoring.d.ts +3 -2
- package/src/shade/renderer/particles/graph/particle_graph_authoring.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/particle_graph_authoring.js +3 -2
- package/src/shade/renderer/particles/graph/particle_graph_distributions.js +170 -170
- package/src/shade/renderer/particles/graph/particle_graph_json.d.ts +25 -16
- package/src/shade/renderer/particles/graph/particle_graph_json.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/particle_graph_json.js +16 -2
- package/src/shade/renderer/particles/graph/particle_node_parameters.d.ts +29 -15
- package/src/shade/renderer/particles/graph/particle_node_parameters.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/particle_node_parameters.js +54 -22
- package/src/shade/renderer/particles/graph/particle_node_presentation.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/particle_node_presentation.js +18 -8
- package/src/shade/renderer/particles/graph/validate_particle_graph.d.ts +9 -2
- package/src/shade/renderer/particles/graph/validate_particle_graph.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/validate_particle_graph.js +70 -32
- package/src/shade/renderer/particles/graph_particles.d.ts +9 -4
- package/src/shade/renderer/particles/graph_particles.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph_particles.js +27 -4
- package/src/shade/renderer/particles/isa/InstructionStream.d.ts +7 -6
- package/src/shade/renderer/particles/isa/InstructionStream.d.ts.map +1 -1
- package/src/shade/renderer/particles/isa/InstructionStream.js +17 -16
- package/src/shade/renderer/particles/isa/ParticleAssembler.d.ts +17 -0
- package/src/shade/renderer/particles/isa/ParticleAssembler.d.ts.map +1 -1
- package/src/shade/renderer/particles/isa/ParticleAssembler.js +35 -0
- package/src/shade/renderer/particles/isa/ParticleProgram.d.ts +26 -6
- package/src/shade/renderer/particles/isa/ParticleProgram.d.ts.map +1 -1
- package/src/shade/renderer/particles/isa/ParticleProgram.js +61 -9
- package/src/shade/renderer/particles/isa/ParticleVMISA.d.ts +55 -4
- package/src/shade/renderer/particles/isa/ParticleVMISA.d.ts.map +1 -1
- package/src/shade/renderer/particles/isa/ParticleVMISA.js +79 -10
- package/src/shade/renderer/particles/isa/particle_assembly.d.ts +20 -6
- package/src/shade/renderer/particles/isa/particle_assembly.d.ts.map +1 -1
- package/src/shade/renderer/particles/isa/particle_assembly.js +136 -25
- package/src/shade/renderer/particles/isa/particle_link_global_fields.d.ts +20 -0
- package/src/shade/renderer/particles/isa/particle_link_global_fields.d.ts.map +1 -0
- package/src/shade/renderer/particles/isa/particle_link_global_fields.js +46 -0
- package/src/shade/renderer/particles/isa/validate_particle_program.js +8 -8
- package/src/shade/renderer/particles/layout/ParticleLayout.d.ts +62 -28
- package/src/shade/renderer/particles/layout/ParticleLayout.d.ts.map +1 -1
- package/src/shade/renderer/particles/layout/ParticleLayout.js +102 -62
- package/src/shade/renderer/particles/optimizer/dag/optimize_particle_program_vector.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/dag/optimize_particle_program_vector.js +2 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_groups.js +1 -1
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_lift.js +4 -3
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_lower.js +2 -2
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_pack.d.ts +3 -3
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_pack.js +11 -11
- package/src/shade/renderer/particles/optimizer/optimize_particle_program.d.ts +4 -4
- package/src/shade/renderer/particles/optimizer/optimize_particle_program.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/optimize_particle_program.js +5 -4
- package/src/shade/renderer/particles/optimizer/particle_integer_bounds.d.ts +1 -1
- package/src/shade/renderer/particles/optimizer/particle_integer_bounds.js +1 -1
- package/src/shade/renderer/particles/optimizer/particle_program_equivalence.d.ts +4 -4
- package/src/shade/renderer/particles/optimizer/particle_program_equivalence.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/particle_program_equivalence.js +13 -11
- package/src/shade/renderer/particles/optimizer/particle_program_fuzz.js +7 -7
- package/src/shade/renderer/particles/optimizer/particle_vm_semantics.d.ts +3 -2
- package/src/shade/renderer/particles/optimizer/particle_vm_semantics.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/particle_vm_semantics.js +5 -4
- package/src/shade/renderer/particles/optimizer/pass_allocate_registers.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_dedup_constants.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_eliminate_dead_code.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_eliminate_dead_code.js +51 -51
- package/src/shade/renderer/particles/optimizer/pass_fold_constants.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_fold_constants.js +5 -5
- package/src/shade/renderer/particles/optimizer/pass_inline_constants.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_narrow_widths.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_pack_registers.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_peephole.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_propagate_copies.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_schedule.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_schedule.js +2 -2
- package/src/shade/renderer/particles/optimizer/pass_share_subexpressions.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_share_subexpressions.js +5 -4
- package/src/shade/renderer/particles/runtime/EmitterRegistry.d.ts +93 -22
- package/src/shade/renderer/particles/runtime/EmitterRegistry.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/EmitterRegistry.js +336 -29
- package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.d.ts +43 -3
- package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.js +43 -3
- package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts +56 -26
- package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/ParticleEmitter.js +89 -33
- package/src/shade/renderer/particles/runtime/ParticleGlobalFields.d.ts +85 -0
- package/src/shade/renderer/particles/runtime/ParticleGlobalFields.d.ts.map +1 -0
- package/src/shade/renderer/particles/runtime/ParticleGlobalFields.js +221 -0
- package/src/shade/renderer/particles/runtime/ProgramHeap.d.ts +143 -18
- package/src/shade/renderer/particles/runtime/ProgramHeap.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/ProgramHeap.js +378 -28
- package/src/shade/renderer/particles/runtime/create_particle_effect.d.ts +19 -7
- package/src/shade/renderer/particles/runtime/create_particle_effect.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/create_particle_effect.js +22 -9
- package/src/shade/renderer/particles/shaders/chunk_particle_billboard_vertex.js +8 -8
- package/src/shade/renderer/particles/shaders/chunk_particle_render_math.js +1 -1
- package/src/shade/renderer/particles/shaders/chunk_particle_setup_context.d.ts +3 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_setup_context.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/chunk_particle_setup_context.js +4 -0
- package/src/shade/renderer/particles/shaders/shader_particle_field_bind.d.ts +30 -0
- package/src/shade/renderer/particles/shaders/shader_particle_field_bind.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/shader_particle_field_bind.js +121 -0
- package/src/shade/renderer/particles/vm/PARTICLE_VM_CONTEXT_STRUCT.d.ts.map +1 -1
- package/src/shade/renderer/particles/vm/PARTICLE_VM_CONTEXT_STRUCT.js +3 -1
- package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts +12 -4
- package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts.map +1 -1
- package/src/shade/renderer/particles/vm/ParticleVMReference.js +48 -11
- package/src/shade/renderer/particles/vm/chunk_particle_vm.d.ts.map +1 -1
- package/src/shade/renderer/particles/vm/chunk_particle_vm.js +36 -14
- package/src/shade/renderer/postprocess/PostProcess.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/PostProcess.js +2 -1
- package/src/shade/renderer/postprocess/gtao/GTAO.d.ts +31 -19
- package/src/shade/renderer/postprocess/gtao/GTAO.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/gtao/GTAO.js +44 -30
- package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.js +16 -2
- package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
- package/src/shade/renderer/postprocess/nss/NSS.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/nss/NSS.js +4 -3
- package/src/shade/renderer/postprocess/ssr/SSR.d.ts +7 -1
- package/src/shade/renderer/postprocess/ssr/SSR.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/ssr/SSR.js +16 -0
- package/src/shade/renderer/postprocess/ssr/resolve/chunk_ssr_add_hit_irradiance.d.ts +20 -2
- package/src/shade/renderer/postprocess/ssr/resolve/chunk_ssr_add_hit_irradiance.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/ssr/resolve/chunk_ssr_add_hit_irradiance.js +53 -3
- package/src/shade/renderer/postprocess/ssr/resolve/chunk_ssr_sample_radiance.d.ts +10 -2
- package/src/shade/renderer/postprocess/ssr/resolve/chunk_ssr_sample_radiance.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/ssr/resolve/chunk_ssr_sample_radiance.js +31 -3
- package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader.js +17 -11
- package/src/shade/renderer/postprocess/taa/TAA.d.ts +4 -2
- package/src/shade/renderer/postprocess/taa/TAA.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/taa/TAA.js +6 -1
- package/src/shade/renderer/postprocess/taa/chunk_fsr2_compute_depth_clip.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/taa/chunk_fsr2_compute_depth_clip.js +10 -5
- package/src/shade/renderer/postprocess/taa/shader_taa.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/taa/shader_taa.js +15 -12
- package/src/shade/renderer/postprocess/velocity/graph_add_per_object_velocity.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/velocity/graph_add_per_object_velocity.js +26 -17
- package/src/shade/renderer/rasterize/forward/chunk_forward_sample_indirect.d.ts +4 -3
- package/src/shade/renderer/rasterize/forward/chunk_forward_sample_indirect.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/forward/chunk_forward_sample_indirect.js +6 -4
- package/src/shade/renderer/rasterize/forward/chunk_forward_sample_indirect_probe_clipmap.d.ts +12 -0
- package/src/shade/renderer/rasterize/forward/chunk_forward_sample_indirect_probe_clipmap.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/forward/chunk_forward_sample_indirect_probe_clipmap.js +57 -0
- package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.d.ts +4 -2
- package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.js +4 -2
- package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts +8 -2
- package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.js +28 -15
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_draw.d.ts +2 -1
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_draw.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_draw.js +19 -3
- package/src/shade/renderer/rasterize/native/graph_rasterize_meshes_alpha_tested.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/graph_rasterize_meshes_alpha_tested.js +4 -2
- package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.d.ts +3 -1
- package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.js +27 -14
- package/src/shade/renderer/rasterize/native/oit/shader_oit_resolve_moments.d.ts +2 -1
- package/src/shade/renderer/rasterize/native/oit/shader_oit_resolve_moments.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/oit/shader_oit_resolve_moments.js +19 -3
- package/src/shade/renderer/shader/chunk/geometry/meshlet/chunk_read_meshlet_triangle_bounds.d.ts +2 -8
- package/src/shade/renderer/shader/chunk/geometry/meshlet/chunk_read_meshlet_triangle_bounds.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/geometry/meshlet/chunk_read_meshlet_triangle_bounds.js +25 -52
- package/src/shade/renderer/shader/chunk/geometry/meshlet/chunk_read_meshlet_triangle_positions.d.ts +15 -0
- package/src/shade/renderer/shader/chunk/geometry/meshlet/chunk_read_meshlet_triangle_positions.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/geometry/meshlet/chunk_read_meshlet_triangle_positions.js +52 -0
- package/src/shade/renderer/shader/chunk/geometry/triangle/TRIANGLE_POSITIONS_STRUCT.d.ts +7 -0
- package/src/shade/renderer/shader/chunk/geometry/triangle/TRIANGLE_POSITIONS_STRUCT.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/geometry/triangle/TRIANGLE_POSITIONS_STRUCT.js +12 -0
- package/src/shade/renderer/shader/chunk/grid/chunk_index_to_grid2d_reciprocal.d.ts +22 -0
- package/src/shade/renderer/shader/chunk/grid/chunk_index_to_grid2d_reciprocal.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/grid/chunk_index_to_grid2d_reciprocal.js +37 -0
- package/src/shade/renderer/shader/chunk/light/lpv/depth/chunk_lpv_mask_weights_by_visibility.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/light/lpv/depth/chunk_lpv_mask_weights_by_visibility.js +26 -60
- package/src/shade/renderer/shader/chunk/light/probe/chunk_probe_visibility_weight.d.ts +23 -0
- package/src/shade/renderer/shader/chunk/light/probe/chunk_probe_visibility_weight.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/light/probe/chunk_probe_visibility_weight.js +54 -0
- package/src/shade/renderer/shader/chunk/math/chunk_f16_round_stochastic.d.ts +25 -0
- package/src/shade/renderer/shader/chunk/math/chunk_f16_round_stochastic.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/math/chunk_f16_round_stochastic.js +43 -0
- package/src/shade/renderer/shader/chunk/random/chunk_jitter_from_vectors.d.ts +14 -0
- package/src/shade/renderer/shader/chunk/random/chunk_jitter_from_vectors.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/random/chunk_jitter_from_vectors.js +37 -0
- package/src/shade/renderer/shader/chunk/random/reservoir/chunk_pick2_wrs_mat2x4.d.ts +27 -0
- package/src/shade/renderer/shader/chunk/random/reservoir/chunk_pick2_wrs_mat2x4.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/random/reservoir/chunk_pick2_wrs_mat2x4.js +61 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/l1i/chunk_resolve_irradiance_sh_to_specular.d.ts +21 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/l1i/chunk_resolve_irradiance_sh_to_specular.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/spherical_harmonics/l1i/chunk_resolve_irradiance_sh_to_specular.js +41 -0
- package/src/shade/renderer/texture/util/texture_fill_rectangle.d.ts.map +1 -1
- package/src/shade/renderer/texture/util/texture_fill_rectangle.js +282 -267
- package/src/shade/renderer/volumetrics/GPUViewVolumetrics.d.ts +8 -4
- package/src/shade/renderer/volumetrics/GPUViewVolumetrics.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/GPUViewVolumetrics.js +36 -36
- package/src/shade/renderer/volumetrics/irradiance_clipmap/GPUIrradianceClipmap.d.ts +6 -1
- package/src/shade/renderer/volumetrics/irradiance_clipmap/GPUIrradianceClipmap.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/irradiance_clipmap/GPUIrradianceClipmap.js +498 -484
- package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_gi_sample.d.ts +15 -2
- package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_gi_sample.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_gi_sample.js +57 -3
- package/src/shade/renderer/volumetrics/irradiance_clipmap/shader_irradiance_clipmap_build.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/irradiance_clipmap/shader_irradiance_clipmap_build.js +197 -191
- package/src/shade/renderer/volumetrics/taa/shader_volumetrics_taa.js +4 -4
- package/src/shade/renderer/water/GPUWaterRenderer.d.ts +2 -1
- package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
- package/src/shade/renderer/water/GPUWaterRenderer.js +5 -1
- package/src/shade/renderer/water/chunk_water_gi.d.ts +13 -3
- package/src/shade/renderer/water/chunk_water_gi.d.ts.map +1 -1
- package/src/shade/renderer/water/chunk_water_gi.js +47 -5
- package/src/shade/renderer/water/query/GPUWaterQueries.d.ts +1 -1
- package/src/shade/renderer/water/shader_water.d.ts +3 -2
- package/src/shade/renderer/water/shader_water.d.ts.map +1 -1
- package/src/shade/renderer/water/shader_water.js +9 -3
- package/src/shade/renderer/geometry/sdf/SDF_BUILD_SETTINGS_STRUCT.d.ts +0 -3
- package/src/shade/renderer/geometry/sdf/SDF_BUILD_SETTINGS_STRUCT.d.ts.map +0 -1
- package/src/shade/renderer/geometry/sdf/SFD_BUILD_WORKGROUP_SIZE.d.ts +0 -2
- package/src/shade/renderer/geometry/sdf/SFD_BUILD_WORKGROUP_SIZE.d.ts.map +0 -1
- package/src/shade/renderer/geometry/sdf/chunk_blas_query_sdf.d.ts +0 -8
- package/src/shade/renderer/geometry/sdf/chunk_blas_query_sdf.d.ts.map +0 -1
- package/src/shade/renderer/geometry/sdf/chunk_blas_query_volume_sign.d.ts +0 -3
- package/src/shade/renderer/geometry/sdf/chunk_blas_query_volume_sign.d.ts.map +0 -1
- package/src/shade/renderer/geometry/sdf/shader_build_sdf.d.ts +0 -3
- package/src/shade/renderer/geometry/sdf/shader_build_sdf.d.ts.map +0 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_object.d.ts.map +0 -1
- package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_object.js +0 -42
- package/src/shade/renderer/particles/shaders/shader_particle_init_free_list.d.ts +0 -31
- package/src/shade/renderer/particles/shaders/shader_particle_init_free_list.d.ts.map +0 -1
- package/src/shade/renderer/shadow/vsm/VSMMoverTracker.d.ts +0 -42
- package/src/shade/renderer/shadow/vsm/VSMMoverTracker.d.ts.map +0 -1
- package/src/shade/renderer/shadow/vsm/VSM_MOVER_LAYOUT.d.ts +0 -54
- package/src/shade/renderer/shadow/vsm/VSM_MOVER_LAYOUT.d.ts.map +0 -1
- package/src/shade/renderer/shadow/vsm/shader/chunk_vsm_append_mover.d.ts +0 -14
- package/src/shade/renderer/shadow/vsm/shader/chunk_vsm_append_mover.d.ts.map +0 -1
- package/src/shade/renderer/shadow/vsm/shader/shader_vsm_movers.d.ts +0 -30
- package/src/shade/renderer/shadow/vsm/shader/shader_vsm_movers.d.ts.map +0 -1
- package/src/shade/renderer/shadow/vsm/shader/shader_vsm_skinned_movers.d.ts +0 -23
- package/src/shade/renderer/shadow/vsm/shader/shader_vsm_skinned_movers.d.ts.map +0 -1
package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_CLIPMAP_PROPOSAL_2026_10_27.md
CHANGED
|
@@ -424,7 +424,7 @@ the SDF [49:08].
|
|
|
424
424
|
| moving-average estimator, 5 u32 per texel | exponential blend over a response time in seconds (§8.8) | DDGI's form; one history; a physical knob |
|
|
425
425
|
| SDF shadow ray per hit for the sun | same (§8.6) | shadow maps answer "lit" off screen (§1) |
|
|
426
426
|
| light cache for point lights | the world light clusters, keyed by this clipmap (§8.7) | reuse |
|
|
427
|
-
| world-cache probes at hits |
|
|
427
|
+
| world-cache probes at hits | hits want the probes they read, one step from the screen, the one probe kind (§8.3) | the off-screen bounce measured short at S5a (D31) |
|
|
428
428
|
| stochastic 1-of-8 + reservoirs + denoise | 2 of 8 by Brick4's WRS, resolved by TAA (§9.1, §9.4) | the review; two probe reads, no denoiser |
|
|
429
429
|
| prefiltered radiance levels for specular | the radiance tap blended with GGX-convolved SH2 by roughness (§9.3) | the review: WebGPU's mips; LPV's blend |
|
|
430
430
|
| ambient probes and froxel GI | the existing irradiance clipmap samples this mode's probes (§9.5) | fog has no GI in any mode today |
|
|
@@ -1535,6 +1535,263 @@ no cache. The provider, per taken key:
|
|
|
1535
1535
|
Triangle work here happens per requested cell (scroll-ins and changes), never per frame. It is bounded
|
|
1536
1536
|
by the cut error: at error ½·h_c, a cell's triangles number on the order of its surface in voxels.
|
|
1537
1537
|
|
|
1538
|
+
**What the vgeo runtime gave, read before building (2026-10-29)**. The plan and its choices are the shared
|
|
1539
|
+
note `s7-vgeo-provider-plan-2026-10-29.md`; Alex took them with "make sure we don't introduce more
|
|
1540
|
+
coupling. If you need to refactor how wants work - please do so. Keep performance in mind. [...] DRY and
|
|
1541
|
+
KISS".
|
|
1542
|
+
- vgeo rows are ordinary `meshes` rows in the scene's TLAS with an empty BLAS;
|
|
1543
|
+
`geometries[row.geometry].vg_row` names the geometry's root page and page lookup.
|
|
1544
|
+
- The cut's rule was fused into the camera's traverse kernel with its drawing, claims and subgroup
|
|
1545
|
+
scheduling, so no other kernel could call it.
|
|
1546
|
+
- Wants reach the residency only through the frame's feedback slots, whose reading wrote a view's fields.
|
|
1547
|
+
- No event says a page arrived: counters and lookups only.
|
|
1548
|
+
- A cut of its own a lane a pair would be a per-thread loop of hundreds of steps. The camera cut's wavefront
|
|
1549
|
+
is the engine's answer to that (one item a lane a step, a uniform step bound, spills between dispatches),
|
|
1550
|
+
so the box cut runs it rather than a walk of its own.
|
|
1551
|
+
|
|
1552
|
+
**As built (S7a, 2026-10-29)**: the vgeo runtime, refactored so that a box cut runs the camera cut's rule and
|
|
1553
|
+
descent, the camera's cut unchanged.
|
|
1554
|
+
- `chunk_vgeo_cut_rule`: the cut's items and expansions, and the rule as two verdicts, `vg_cut_node` and
|
|
1555
|
+
`vg_cut_cluster` (`VGEO_CUT_VERDICT`). The importing kernel defines the two culls,
|
|
1556
|
+
`vg_cut_sphere_outside` and `vg_cut_box_outside`.
|
|
1557
|
+
- `chunk_vgeo_cut_descent`: what the descent does with a verdict (counts, wants, claims, the walk up) and the
|
|
1558
|
+
wavefront's scheduling, with one more hook, `vg_cut_take`, for what taking a cluster means. The camera's
|
|
1559
|
+
traverse is that chunk with its frustum culls and `vg_draw`.
|
|
1560
|
+
- `graph_vgeo_cut_descent`: the seam's seed, wavefront and tail, for any cut's instance records and traverse
|
|
1561
|
+
kernel. The seam keeps its collect.
|
|
1562
|
+
- `vg_want` and `vg_stat` are chunks of their own, so a kernel that only counts binds no claim buffer.
|
|
1563
|
+
- A feedback slot is any `VGeoFeedbackConsumer`'s: the loop hands each consumer its slot's counters through
|
|
1564
|
+
`read_feedback`, and whether it shared the slot. `VGeoViewCut` implements it. The runtime fills a slot's
|
|
1565
|
+
own params (`fill_slot_params`) for any cut.
|
|
1566
|
+
- Measured on Zorah (the textured UE rebuild: 1,012 containers, 12,597 instances) from its manifest's camera
|
|
1567
|
+
at 1280×720. Blocks of frames alternated the refactored traverse with the one before S7a, for every cut of
|
|
1568
|
+
the frame, once after the rule moved and again after the descent did.
|
|
1569
|
+
- The frame view's counters are the same between the arms on both GPUs: 32,618 clusters drawn, 163,353
|
|
1570
|
+
items, 10,822 pages, 144,079 clusters tested once settled. The 4090's second run held 118 clamped
|
|
1571
|
+
clusters in both arms.
|
|
1572
|
+
- The traverse's 80 dispatches a frame cost the same: p50 0.96 and 1.00 of before on the RTX 4090
|
|
1573
|
+
(1.93 against 1.92 ms the second time), 0.99 and 0.98 on the AMD iGPU (4.19 against 4.27 ms).
|
|
1574
|
+
- Tier 2: the cut agreement spec runs the traverse on the emulator unchanged. `VGeoFeedbackLoop.spec.js`
|
|
1575
|
+
adds a consumer that is not a view (handed its counters, told when it shared its slot) and a slot's params
|
|
1576
|
+
against the feedback's wire layout, which no spec held before. `graph_vgeo_expand.spec.js` counts the
|
|
1577
|
+
wavefronts a cut records and encodes a cut's graph on the software device, which no spec did either.
|
|
1578
|
+
20 of 20 mutants were killed.
|
|
1579
|
+
|
|
1580
|
+
**As built (S7b, 2026-10-29)**: the gather and the box cut, which leave each taken cell a list of the clusters in
|
|
1581
|
+
its cut. Not registered yet: S7c's distance pass is the lists' reader.
|
|
1582
|
+
- `VGeoBrickProvider`, a `BrickProvider` with a `VGeoViewCut` of its own. In a frame the scene's vgeo runtime has
|
|
1583
|
+
open, it records the gather, the claim widths scanned by `graph_prefix_scan_csdldf`, and `graph_vgeo_cut_descent`
|
|
1584
|
+
with `shader_vgeo_brick_traverse`, then publishes the cut's feedback: its wants reach the residency through its
|
|
1585
|
+
own slot, ranked with the camera's, and its counters come back to its cut. With no runtime, or none open, it
|
|
1586
|
+
records nothing.
|
|
1587
|
+
- `shader_vgeo_brick_gather`: a 64-lane workgroup a taken cell, dispatched by take, running the mesh provider's
|
|
1588
|
+
cooperative TLAS query over the cell ⊕ R.
|
|
1589
|
+
- `vgeo_brick_accept` takes a row in the SDF world whose geometry is virtual and whose box is at least ½ voxel
|
|
1590
|
+
across.
|
|
1591
|
+
- `vgeo_brick_pair` writes a cut's instance record for each. k is σ_max/(½·h_c), the distance weight is 0 and
|
|
1592
|
+
there is no eye, so `vg_project` is the world error over ½·h_c against τ = 1. The cell ⊕ R box, taken into the
|
|
1593
|
+
instance's object space as the bounds of its eight corners, goes in the record's frustum words, and the
|
|
1594
|
+
taken cell in word 30.
|
|
1595
|
+
- A flattened instance makes no pair. Pairs past `budget × 8` records are dropped and counted, as is what the
|
|
1596
|
+
query drops.
|
|
1597
|
+
- `shader_vgeo_brick_traverse`: `chunk_vgeo_cut_descent`, culled by the box's six faces, a sphere as its cube.
|
|
1598
|
+
The take puts a cluster onto its cell's list as its pair and its meshlet row, up to 64. A list counts past what
|
|
1599
|
+
it holds, and the clusters past it are counted dropped (`MESHLETS_DROPPED`).
|
|
1600
|
+
- Shared rather than copied: `chunk_vgeo_cut_instance` (what any collect writes of an instance record, now under
|
|
1601
|
+
the camera's collect too) and `chunk_brick_provider` (a taken cell's key, window, voxel and band, and the
|
|
1602
|
+
resolution test, now under the mesh provider too).
|
|
1603
|
+
- Tier 2, on the emulator:
|
|
1604
|
+
- `vgeo_brick_traverse.spec.js` runs the seed, prepare, the box traverse and the remainder over a built torus
|
|
1605
|
+
of 26 pages. Each list is the CPU oracle's cut at the pair's error with the pair's box as its planes
|
|
1606
|
+
(`VGeoCutView.plane_count`, new: a camera tests five).
|
|
1607
|
+
- Six fine cells around the ring and over and under the tube take 8 to 10 clusters each, and the whole ring
|
|
1608
|
+
at a coarse error 7 over 3 pages.
|
|
1609
|
+
- With a page withheld, the cell takes the oracle's stand-in, counts the clamp, and wants the page through
|
|
1610
|
+
the cut's slot.
|
|
1611
|
+
- Ten instances in one cell take 70 clusters: the list holds 64, counts 70, and 6 are counted dropped.
|
|
1612
|
+
- `vgeo_brick_pair.spec.js` covers the accept and the record under a skew turn and a non-uniform scale:
|
|
1613
|
+
k, w, the eye in a buffer of stale NaNs, the box, the cell, and the cut's own fields. It also covers the
|
|
1614
|
+
drops.
|
|
1615
|
+
- Tier 3: `VGeoBrickProvider.spec.js` on the software device.
|
|
1616
|
+
- Nothing is recorded without an open runtime.
|
|
1617
|
+
- The gather runs through take, then the cut's wavefronts, between clear and free.
|
|
1618
|
+
- τ is 1 with the camera's at 3; the cut has a slot of its own; its tail publishes the frame's feedback;
|
|
1619
|
+
destroy gives back what it made.
|
|
1620
|
+
- 37 of 38 mutants were killed. The survivor never culls by a sphere, which changes nothing on the torus: its
|
|
1621
|
+
pages hold a node each. A page's sphere lies outside the box only if the cluster refining into it does, so
|
|
1622
|
+
the sphere decides at a page's inner nodes alone.
|
|
1623
|
+
- The gather is a barrier kernel the emulator cannot run, as the mesh provider's is. Its glue is checked on the
|
|
1624
|
+
device in S7c, once the lists have a reader.
|
|
1625
|
+
|
|
1626
|
+
**As built (S7c, 2026-10-29)**: the distances, and the provider registered: the GI runs it after the mesh
|
|
1627
|
+
provider, before the plugins.
|
|
1628
|
+
- `shader_vgeo_brick_distance` runs a 256-lane workgroup for each taken cell that the box cut put a cluster in.
|
|
1629
|
+
The traverse lists a cell at its first take (`vgeo_brick_filled`), and `graph_build_indirect_dispatch_command`
|
|
1630
|
+
turns the count into the dispatch. The plan's two passes are kept as the cut and this one: the triangles are
|
|
1631
|
+
culled at the start of this pass, so the frame carries each cell's 516-byte cluster list rather than a triangle
|
|
1632
|
+
list (about 42 MB at 4,096 cells).
|
|
1633
|
+
- Triangles: a lane a triangle, two of the list's clusters at a time. A triangle whose box misses its pair's
|
|
1634
|
+
(the cell ⊕ R, in the instance's space) lies past the band from every texel and is skipped. The rest go into
|
|
1635
|
+
workgroup memory, up to 256 (§7.8's choice), past it dropped and counted. They are held in the cell's own
|
|
1636
|
+
space, the translation taken less the cell's origin first, and wound out of the surface their instance
|
|
1637
|
+
draws: `triangle_winding_is_reversed` swaps b and c.
|
|
1638
|
+
- Texels, two a lane, `vgeo_brick_texel`: the nearest triangle within the band, signed by its side. Of
|
|
1639
|
+
triangles as near, the one whose normal lies most along the texel's offset decides; "as near" is a tolerance
|
|
1640
|
+
derived from the closest points' rounding in the cell's space, 2.0·10⁻³ of the squared distance. A material
|
|
1641
|
+
drawn on both sides signs positive, as UE's two-sided fields do. Past the band a texel is unsigned, so
|
|
1642
|
+
positive.
|
|
1643
|
+
- The merge: the bytes meet in workgroup memory. A lane a staged word takes each byte's least (contracts 1 and
|
|
1644
|
+
2). A lane an object cell writes the vgeo row where its nearest texel is nearer than the staged minimum from
|
|
1645
|
+
before the merge (contract 3).
|
|
1646
|
+
- Shared rather than copied:
|
|
1647
|
+
- `chunk_brick_object`: the encoding and the object bids, out of the mesh provider, renamed `brick_*`;
|
|
1648
|
+
- `chunk_read_meshlet_triangle_positions`, which `read_meshlet_triangle_bounds` is now built on;
|
|
1649
|
+
- `chunk_brick_provider_cell`, split out of `chunk_brick_provider`;
|
|
1650
|
+
- `chunk_vgeo_brick_box`, the traverse's too.
|
|
1651
|
+
|
|
1652
|
+
A cell's list carries its key. `graph_vgeo_cut_descent` returns the records at the seed's version, their claim
|
|
1653
|
+
bases in them.
|
|
1654
|
+
- Tier 2, `vgeo_brick_distance.spec.js`:
|
|
1655
|
+
- the cull, past each of the six faces;
|
|
1656
|
+
- the placement: a turn, a scale, an offset origin, reversed, the reach, sided;
|
|
1657
|
+
- the texel over an octahedron against a brute force of the closest points: the distance to 5 places, the
|
|
1658
|
+
sign, the row's triangle as near as the nearest, and none past the band;
|
|
1659
|
+
- two faces meeting at 15°, whose planes disagree in front of their edge: the face the texel stands over
|
|
1660
|
+
decides, in either order;
|
|
1661
|
+
- a two-sided surface, positive on both sides.
|
|
1662
|
+
|
|
1663
|
+
`vgeo_brick_traverse.spec.js` holds the filled list: each cell that took a cluster, once. Tier 3: the
|
|
1664
|
+
distance pass between the cut and free; the GI's order with the scene's vgeo open. 30 of 30 mutants were
|
|
1665
|
+
killed.
|
|
1666
|
+
- Device, on both GPUs: two tori built in the page (128×48, 40 pages), one of them mirrored. The mode was entered
|
|
1667
|
+
again until no fill clamped, which was the second fill (S7d is the re-queue), and every brick texel was held to
|
|
1668
|
+
the analytic distance.
|
|
1669
|
+
- The mean error is 1.9–2.4 bytes and p99 is at most 8 bytes (a quarter voxel), against the half-voxel bound
|
|
1670
|
+
of 17. No cell the surface crosses is without a brick.
|
|
1671
|
+
- 65 texels at cascade 0 (of 180,751 within the band) have the wrong sign. The vgeo builder's simplification
|
|
1672
|
+
flipped 2 of the torus's triangles, at its inner top (cos −0.99 and −0.66 against the analytic normal), and
|
|
1673
|
+
the nearest triangle's side follows them.
|
|
1674
|
+
- The 256-triangle cap: 538 triangles were dropped, in cascade-1 cells whose 3.75 m box takes in a quarter of a
|
|
1675
|
+
torus. 159 texels there are over the bound on the 4090, 178 on the iGPU, since atomic order picks the
|
|
1676
|
+
triangles dropped. On a coarser torus (64×24) 275 are dropped and cascade 1 is clean, while three 15 m
|
|
1677
|
+
cascade-3 cells over both tori drop instead.
|
|
1678
|
+
- Inside the tube past the band (1.05 m against cascade 0's 0.5 m band), 24,314 texels are unsigned, so
|
|
1679
|
+
positive, as §7.8 writes them.
|
|
1680
|
+
- A fill frame, p50 / p90 / max:
|
|
1681
|
+
|
|
1682
|
+
| | gather | cut | distances |
|
|
1683
|
+
|---|---|---|---|
|
|
1684
|
+
| 4090 | 10 / 12 / 12 µs | 39 / 138 / 881 µs | 58 / 91 / 399 µs |
|
|
1685
|
+
| iGPU | 605 / 619 / 728 µs | 20 / 63 / 248 µs | 1.3 µs / 1.04 / 6.49 ms |
|
|
1686
|
+
|
|
1687
|
+
The gather is a workgroup a taken cell, whose launch is the iGPU's 605 µs; the mesh provider's 1.07 ms here
|
|
1688
|
+
is the same floor. With no vgeo in any cell, the cut's empty wavefronts cost 22 µs (4090) and 20 µs (iGPU).
|
|
1689
|
+
- Dispatching the distances by the taken cells instead cost 1.73 ms on the iGPU with no vgeo in any cell, all
|
|
1690
|
+
of it launch. By the filled count it costs 1.3 µs.
|
|
1691
|
+
- Sponza's census is the same on the tree before S7b and after, so both refactors left the mesh provider as it
|
|
1692
|
+
was: 7,171 bricks in every census (progressive, again, fresh), every cascade's counts, and the instances
|
|
1693
|
+
dropped past 64 (402, 460, 58).
|
|
1694
|
+
|
|
1695
|
+
**As built (S7d, 2026-10-29)**: a cell built with a stand-in waits, and is built again once what it waited on
|
|
1696
|
+
arrives (contract 6).
|
|
1697
|
+
- The box traverse records a cell's first stand-in in its list header (`VGEO_BRICK_CELL_OFFSET`): the page it
|
|
1698
|
+
stood in for, as its lookup word, and the group in that page. The first claimant writes them, decided by an add
|
|
1699
|
+
on the header's count of stand-ins, so no compare-and-swap is needed. A clamp (the page not resident) and a held
|
|
1700
|
+
group (resident, not selectable) wait alike, until the page is resident and the group selectable.
|
|
1701
|
+
- The distance pass puts each cell with a stand-in on a waiting list. The list holds budget × 4 cells; those past
|
|
1702
|
+
it are dropped and counted.
|
|
1703
|
+
- `shader_vgeo_brick_wait` heads the provider's passes each frame, a thread a cell the last frame left waiting.
|
|
1704
|
+
A cell whose page is resident and whose group is selectable is queued with `brick_clipmap_enqueue`, so exactly
|
|
1705
|
+
the cells that stood something in are built again, once what they waited on is there. The rest wait on, onto
|
|
1706
|
+
the list this frame's distances add to; the two lists trade places each frame.
|
|
1707
|
+
- Contract 6's seam on the device: the provider context hands a provider the clipmap's queue, as `state`, `ring`
|
|
1708
|
+
and the new `pending` bits. A pass that queues cells writes `state` and hands its version on.
|
|
1709
|
+
- `vg_group_row` and `vg_group_selectable` move into `chunk_vgeo_cut_arena`, so a pass that asks whether a group
|
|
1710
|
+
is drawable binds only the arena.
|
|
1711
|
+
- Tier 2, `vgeo_brick_wait.spec.js`, over the runtime's tables with a page withheld:
|
|
1712
|
+
- a cell is queued, while one waits on the missing page and one on a group that page holds back;
|
|
1713
|
+
- installing the page queues those two and no other;
|
|
1714
|
+
- a key already pending is not queued twice;
|
|
1715
|
+
- a list whose count runs past its capacity is read only as far as it holds.
|
|
1716
|
+
|
|
1717
|
+
`vgeo_brick_traverse.spec.js` checks that a clamp records its page and group, and that nothing is recorded when
|
|
1718
|
+
all is resident; the torus's lookup now starts past word 0. Tier 3: the wait runs before the gather, indirect;
|
|
1719
|
+
the distances bind the list the wait keeps onto; the lists trade places; destroy gives them back. 16 of 17
|
|
1720
|
+
mutants were killed. The survivor drops the gather's reset of a cell's stand-ins, inside a barrier kernel that
|
|
1721
|
+
only the device runs.
|
|
1722
|
+
- Device, on both GPUs: the tori of S7c in one fill, never left.
|
|
1723
|
+
- The 4090 had 187 cells waiting at frame 10 and none at frame 20, by when all 15 pages were resident; nothing
|
|
1724
|
+
clamped after frame 20. On the iGPU none waited at frame 10, and nothing clamped after it.
|
|
1725
|
+
- The bricks are those S7c built by entering the mode again until no fill clamped. At cascade 0 the mean error
|
|
1726
|
+
is 1.865 bytes on the 4090 and 1.862 on the iGPU, p99 6, with the same 66 and 64 texels of the wrong sign.
|
|
1727
|
+
Cascades 2 to 5 have the same counts and errors; cascade 1 differs only in the texels the 256-triangle cap
|
|
1728
|
+
leaves over the bound, by atomic order. No cell the surface crosses is without a brick.
|
|
1729
|
+
- The wait costs 4.1 µs a frame at p50 on the 4090 (11 µs at worst) and 1.8 µs on the iGPU (6.2 µs).
|
|
1730
|
+
- The iGPU's distances reached 13.1 ms in one frame, against a p90 of 1.4 µs. Zorah (S7e) is where that is
|
|
1731
|
+
measured on a real scene.
|
|
1732
|
+
|
|
1733
|
+
**As built (S7e, 2026-10-29)**: Zorah lit by its own geometry, measured; and the wait keeps its cells' wants.
|
|
1734
|
+
- Zorah, the textured UE rebuild (1,012 containers, 12,597 instances, 332,613 pages), one grey material, the
|
|
1735
|
+
manifest's sun, 1280×720. Zorah has no other geometry, so all the probes see of it is the vgeo provider's bricks.
|
|
1736
|
+
- At ground level, in an arcade, IBL leaves the frame black (mean luma 6.3 of 255; Zorah's IBL sky is black).
|
|
1737
|
+
ProbeClipmap lights it from the sunlit floor: 19 to 37 over five runs, the probes 240 frames after the fill.
|
|
1738
|
+
- Of the probes' rays, 70% hit Zorah's bricks at the manifest camera, 76% at ground level and 48% from the air.
|
|
1739
|
+
- Timings, p50 of a settled frame.
|
|
1740
|
+
- The AMD iGPU: IBL 39.5 ms, ProbeClipmap 62.8 ms (+23.3 ms). That is the trace's 11.0 ms (8 ms on Sponza: Zorah's
|
|
1741
|
+
bricks are denser), the fused indirect pass's 9.3 ms (as S6c2 measured it), light 2.1 ms, and every other pass
|
|
1742
|
+
under 0.6 ms. The provider at rest costs ~20 µs: wait 3.5, gather 1.6, cut 11.1, distances 1.4.
|
|
1743
|
+
- The 4090, sharing its GPU with another process (its medians stand, its maxima do not): 6.9 → 8.3 ms. The trace is
|
|
1744
|
+
1.2 ms and the fused pass 81 µs.
|
|
1745
|
+
- The fill on the iGPU (196,608 cells at 4,096 a frame, and the cells queued again): 62 ms a frame at p50, 69 ms
|
|
1746
|
+
at p99 and 132 ms at worst. The distances take 56.6 ms in their worst frame, the cut 6.6 ms and the gather 3.6 ms.
|
|
1747
|
+
- **§7.8's bound does not hold on Zorah: its caps bind as budgets.** Over one fill on the iGPU (the 4090 is within
|
|
1748
|
+
10%):
|
|
1749
|
+
- 46% of the clusters the box cut took are past a cell's 64 (1.44M of 3.13M): 39% in cascade 0, rising to 79% in
|
|
1750
|
+
cascade 5;
|
|
1751
|
+
- 18–19.5M triangles are past a cell's 256: 9.8M in cascade 0, 6.4M in 1, 2.0M in 2;
|
|
1752
|
+
- 80–150k pairs are past the records (103k in cascade 1), and the TLAS query drops 182–224k candidates and 48k
|
|
1753
|
+
nodes.
|
|
1754
|
+
|
|
1755
|
+
A cell ⊕ R box in kitbashed content holds many overlapping instances and layered surfaces. The cut's error bounds
|
|
1756
|
+
how big a triangle is, not how many surfaces cross a box.
|
|
1757
|
+
- What the image measures, and what it does not come from.
|
|
1758
|
+
- Holes: from the air the ground plane's bricks are missing beside the greenhouse, and cells of the ground, the
|
|
1759
|
+
greenhouse and the tower read inside out.
|
|
1760
|
+
- Of the probes, 17–48% are dead and 8–16% of rays meet back faces. Sponza on the mesh caches has 1.6–4% dead and
|
|
1761
|
+
2% back-face rays.
|
|
1762
|
+
- With the provider's cut 4× and 16× coarser, the drops fall about threefold (triangles 19.3M → 6.2M → 5.6M,
|
|
1763
|
+
clusters past a list 46% → 26% → 29%). The back faces, the dead probes and the holes stay. **The caps are not
|
|
1764
|
+
what the image suffers from.**
|
|
1765
|
+
- Each texel the least over its instances of that instance's own signed distance, in place of the nearest
|
|
1766
|
+
triangle of all of them, is no better: back faces 8–18% and dead probes 18–54%. The nearest triangle's side is
|
|
1767
|
+
the open question on content made of single-sided, interpenetrating pieces.
|
|
1768
|
+
- The two tori of S7c, as vgeo and as plain meshes: of the probes deep inside a tube, vgeo leaves 4 alive and 36
|
|
1769
|
+
dead, while the meshes' signed fields relocate 26 out alive and leave 14 dead. Past the band a vgeo interior is
|
|
1770
|
+
unsigned (§7.8), so its probes die where a mesh's move out.
|
|
1771
|
+
- **The wait keeps its cells' wants.** On Zorah, 826 cells (iGPU) and 888 (4090) waited for ever, every one on a
|
|
1772
|
+
page not resident.
|
|
1773
|
+
- A want lasts the reading it is in: past its 64 fetches a reading, `VGeoResidency.apply` drops what it defers.
|
|
1774
|
+
The camera's cut wants its pages again every frame; the box cut wants only while it cuts.
|
|
1775
|
+
- Now the traverse records what a cell's first stand-in's want is made of, in the cell's header
|
|
1776
|
+
(`VGEO_BRICK_WAIT_OFFSET`): the page, the group, the row the stand-in was found in and that page's own index, the
|
|
1777
|
+
child-table entry, and the error. The distances copy it onto the waiting list whole.
|
|
1778
|
+
- Each frame, the wait wants an absent page again through the provider's slot, while that row still holds that page
|
|
1779
|
+
(`vg_lookup[lookup + source_page] == source`). Once the row holds another page, the cell is queued again instead.
|
|
1780
|
+
The wait writes the frame's feedback ahead of the gather and hands its version on.
|
|
1781
|
+
- A group held back carries no want, as in the cut.
|
|
1782
|
+
- On Zorah no cell is left waiting on an absent page (827 → 0). 7 to 47 wait at rest, every one on a resident page
|
|
1783
|
+
whose group the activation rule holds back. That is the runtime's own gap, which the 4090's camera cut shows
|
|
1784
|
+
too: 1,539 clusters held for good, where S7a saw 118.
|
|
1785
|
+
- Tier 2 (`vgeo_brick_wait.spec.js`, over the runtime's tables with a page withheld): a cell waiting on an absent page
|
|
1786
|
+
wants it again each frame, through its source, at its error; one whose source row holds another page is queued
|
|
1787
|
+
again and wants nothing; a held group wants nothing; the cells kept go onto the list whole. The traverse spec holds a
|
|
1788
|
+
clamp's want words to what the residency needs: the source row holds the page named, whose child table names the
|
|
1789
|
+
page withheld at the entry recorded, and the error is above τ. Tier 3: the wait binds the frame's feedback and the
|
|
1790
|
+
want stamps. 17 of 18 mutants were killed. The survivor shortens the distances' copy onto the list, inside a barrier
|
|
1791
|
+
kernel that only the device runs.
|
|
1792
|
+
- Open, measured, cause not established: on both GPUs, the lit frame at the manifest camera has dark blotches on a
|
|
1793
|
+
lattice over a column whose bricks are clean.
|
|
1794
|
+
|
|
1538
1795
|
### 7.9 Tracing
|
|
1539
1796
|
|
|
1540
1797
|
`brick_clipmap_trace(origin, direction, t_min, t_max)` is a pure chunk over seam readers, so the
|
|
@@ -1758,10 +2015,20 @@ Per frame, in this order:
|
|
|
1758
2015
|
- At a cascade's outer edge a probe cell projects to `H / (N_p·tan(fov/2))` pixels: 58 px at
|
|
1759
2016
|
1080p and 60°, 19 px at 120°. A tile size of 8 therefore samples every cell several times a
|
|
1760
2017
|
frame.
|
|
1761
|
-
3. **
|
|
2018
|
+
3. **want hits** (thread per ray of the last frame's updates; S5a, D31 reversed).
|
|
2019
|
+
- A front-face hit that bounces anything wants the cages its pick reads, as a pixel does, since the
|
|
2020
|
+
next bounce reads those probes (§8.7). So a probe a hit needs is admitted where nothing on screen
|
|
2021
|
+
wanted one, and kept while hits read it.
|
|
2022
|
+
- Only a probe a pixel wanted has hits that want: the chain stops after one step, as Enshrouded's
|
|
2023
|
+
world-cache probes do. Unstopped, every surface the windows hold would come to want a probe, the
|
|
2024
|
+
dense lattice D31 turned down.
|
|
2025
|
+
- It reads the last frame's rays before this frame's trace writes over them.
|
|
2026
|
+
4. **admit** (thread per wanted cell).
|
|
1762
2027
|
- An EMPTY entry pops a slot (a failure is counted), writes a NEW state, a record and the
|
|
1763
2028
|
indirection entry, and appends to the active list.
|
|
1764
2029
|
- An existing probe refreshes its wanted frame.
|
|
2030
|
+
- Either way the record is marked the screen's when a pixel wanted the point, and the hits'
|
|
2031
|
+
otherwise: what the next frame's hits read to stop the chain.
|
|
1765
2032
|
|
|
1766
2033
|
Consumers weight a NEW probe at zero until its first update.
|
|
1767
2034
|
|
|
@@ -1820,6 +2087,31 @@ records the frame's lifetime as one graph pass: retire, want, admit's command, a
|
|
|
1820
2087
|
- Tier 3, `GPUProbeClipmap.spec.js`: the first frame's fills and the passes' order, sizes and commands;
|
|
1821
2088
|
the cage windows; the clock; the capacity; growth; destroy.
|
|
1822
2089
|
|
|
2090
|
+
**As built (S5a, 2026-10-28), the hits' wants**: `probe/shader_probe_clipmap_want_hits.js`, between want
|
|
2091
|
+
and admit's command, through the launch the last frame's rays had, `PROBE_DISPATCH_RAYS`.
|
|
2092
|
+
- **Want's cages are one chunk**, `probe/chunk_probe_clipmap_want.js`: the pick's cages, the claims and
|
|
2093
|
+
the appends. Want and the hits' wants both call it, so a hit wants what its pick reads.
|
|
2094
|
+
- **A wanted list entry's top bit says a pixel wanted the point**, `PROBE_WANTED_SCREEN`. Want runs
|
|
2095
|
+
first, so a point both want is listed once, as the screen's. Admit copies the bit into the record's
|
|
2096
|
+
`screen` word, which sits in the padding the record had: still 64 B.
|
|
2097
|
+
- **The row says which rays want**: shade leaves a front-face hit's albedo there and zero in every other
|
|
2098
|
+
ray's, so a ray that takes no bounce wants nothing.
|
|
2099
|
+
- **The trace list is cleared after the hits' wants**, which read the last frame's list, origins and
|
|
2100
|
+
rays, told that frame's U and R by the host.
|
|
2101
|
+
- Tier 2, `probe_clipmap_lifetime.spec.js`:
|
|
2102
|
+
- a pixel's points are listed as the screen's, and so are their probes;
|
|
2103
|
+
- a front-face hit of a screen probe wants its pick's cage where its ray ends, and admit gives the
|
|
2104
|
+
points NEW probes, the hits';
|
|
2105
|
+
- a probe only hits wanted has hits that want nothing;
|
|
2106
|
+
- misses, unresolved rays, back faces, black hits, and updates past the list's count or bound want
|
|
2107
|
+
nothing;
|
|
2108
|
+
- a point a pixel and a hit both want is listed once, as the screen's;
|
|
2109
|
+
- a probe the screen wanted and then only hits did becomes the hits', and the screen's again; a slot a
|
|
2110
|
+
screen probe gave back is the hits' when a hit's point takes it;
|
|
2111
|
+
- the cage viewed back along the ray, and the next cascade's in the skirt.
|
|
2112
|
+
- Tier 3, `GPUProbeClipmap.spec.js`: the pass's place, its command, and the last frame's U and R.
|
|
2113
|
+
- Mutants: 24 of 24 killed.
|
|
2114
|
+
|
|
1823
2115
|
### 8.4 Relocation and classification
|
|
1824
2116
|
|
|
1825
2117
|
**Relocation**, at the start of every update:
|
|
@@ -1992,6 +2284,7 @@ The three irradiance terms of a HIT:
|
|
|
1992
2284
|
- Its pick takes the ray's own STBN sample as noise; the probe blend averages it.
|
|
1993
2285
|
- Where no probe exists, the bounce is **zero**, not the environment. A hit behind the camera in a
|
|
1994
2286
|
cave must not bring daylight in.
|
|
2287
|
+
- The hit wants its cage (§8.3), so its probes stand there a frame or two later.
|
|
1995
2288
|
- LPV's 0.97 factor is dropped: albedo below 1 already bounds the series.
|
|
1996
2289
|
|
|
1997
2290
|
**Hits are diffuse only**, as Lumen's surface cache lights diffusely (D7).
|
|
@@ -2041,6 +2334,97 @@ through the same command:
|
|
|
2041
2334
|
cannot see on the texture build's own encoder; the device check reads the means back against the
|
|
2042
2335
|
atlas.
|
|
2043
2336
|
|
|
2337
|
+
**As built (S5a, 2026-10-28)**, E_ind: `probe/shader_probe_clipmap_light.js`, a pass of its own after
|
|
2338
|
+
shade, through shade's own launch.
|
|
2339
|
+
- **Why its own pass.** Shade binds nine of a stage's ten storage buffers, and the pick takes two more.
|
|
2340
|
+
The light pass binds five.
|
|
2341
|
+
- **The albedo.** Shade leaves a front-face hit's diffuse albedo in the ray's `row`, `pack4x8unorm`, and
|
|
2342
|
+
zero in every other ray's. Nothing after shade reads `row`.
|
|
2343
|
+
- **The pick** is made at the hit, along its normal, with the view back along the ray.
|
|
2344
|
+
- Its noise is the ray's own jitter, `probe_clipmap_ray_jitter`, extracted from the trace into
|
|
2345
|
+
`chunk_probe_clipmap.js`; the trace aims its ray with it.
|
|
2346
|
+
- It reads last frame's SH, since the projection writes the records later in the frame.
|
|
2347
|
+
- Where nothing is found the bounce is zero, as above.
|
|
2348
|
+
- **Tier 2.**
|
|
2349
|
+
- `probe_clipmap_light.spec.js` checks:
|
|
2350
|
+
- a hit takes its albedo by its probes' SH;
|
|
2351
|
+
- misses, unresolved rays, back faces and black hits keep their light, and nothing is found nowhere;
|
|
2352
|
+
- the pick is made at the hit, viewed back along the ray, with the ray's own jitter, across two
|
|
2353
|
+
probes, against the chunks themselves;
|
|
2354
|
+
- the half-float hold;
|
|
2355
|
+
- a fresh jitter each update and round.
|
|
2356
|
+
- Shade's stash is checked in `probe_clipmap_shade.spec.js`.
|
|
2357
|
+
- Mutants: 18 of 18 killed, six by tests added for them.
|
|
2358
|
+
- **Measured, the multi-bounce furnace**: a closed room of ρ = 0.5 glowing L_e, no light, the clock held
|
|
2359
|
+
at 1/60 s, 900 frames. On the RTX 4090:
|
|
2360
|
+
- With the rule above, E reaches **0.836 of πL_e/(1 − ρ)** at p50, p01 0.718.
|
|
2361
|
+
- 12% of the front-face hits find no probe. Every one lies on a wall the camera does not see, behind it
|
|
2362
|
+
or beside it, since probes stand only where pixels want them, and the rule makes their bounce zero.
|
|
2363
|
+
- A twin that falls back to the tracing probe's own SH where nothing is found reaches 0.994 (p01
|
|
2364
|
+
0.988), but only because a uniform room makes every probe's irradiance the right one.
|
|
2365
|
+
- **Alex, 2026-10-28: the hits want their probes (D31 reversed in part), and nothing stands in.** "If we
|
|
2366
|
+
get no indirect bounce, it is better to lose energy than to invent energy." A hit whose cage has no
|
|
2367
|
+
probe wants it (§8.3), and its bounce is zero until that probe's first update, a frame or two later.
|
|
2368
|
+
- **Measured with the hits' wants**, same room and clock:
|
|
2369
|
+
- E reaches **0.994** at p50 (p01 0.988) on the RTX 4090 and on the AMD iGPU, against 0.836 without.
|
|
2370
|
+
No front-face hit finds nothing, against 12%. At ρ = 0.8 it reaches 0.992 after 1,800 frames.
|
|
2371
|
+
- The room holds 418 probes, against 228: the walls the camera does not see.
|
|
2372
|
+
- **On Sponza in the sun**, the camera still at 1280×720, the defaults (512 probes a frame), with the
|
|
2373
|
+
hits' wants and without:
|
|
2374
|
+
|
|
2375
|
+
| | nave view | toward a side wall |
|
|
2376
|
+
|---|---|---|
|
|
2377
|
+
| irradiance before AO, mean of the frame, 4090 | 0.108 vs 0.110 | **0.054 vs 0.050** |
|
|
2378
|
+
| the same, iGPU | 0.112 vs 0.110 | **0.055 vs 0.050** |
|
|
2379
|
+
| points wanted a frame | 2,600–3,200 vs 640 | 2,000–2,100 vs 160 |
|
|
2380
|
+
| live probes | 4,600 vs 770 | 3,500 vs 180 |
|
|
2381
|
+
| period T | 6–7 vs 2 frames | 4–5 vs 1 |
|
|
2382
|
+
| the hits' wants pass | 25 µs 4090, 0.44 ms iGPU | 17 µs, 0.42 ms |
|
|
2383
|
+
| trace, iGPU | 6.6 vs 4.9 ms | 7.6 vs 2.2 ms |
|
|
2384
|
+
|
|
2385
|
+
- Toward the wall, where most of the building is off screen, the hits bring 7–9% more light. In the
|
|
2386
|
+
nave view the frame's mean moves less than its own frame-to-frame swing.
|
|
2387
|
+
- **Stability is unchanged** by the longer period: S4g's measure, the change of the diffuse from frame
|
|
2388
|
+
to frame at reprojected points, p50 on the 4090, still 16.3% against 16.3%, orbiting 22.3% against
|
|
2389
|
+
25.1%. A probe the hits keep around the camera is there when the orbit brings its surface on screen.
|
|
2390
|
+
- **The cost is the budget**: with the hits' probes wanted, each frame's updates reach U where the
|
|
2391
|
+
screen alone left them under it, so the trace costs what the ray budget allows (§8.5). On the iGPU
|
|
2392
|
+
that is most of the GI's growth.
|
|
2393
|
+
|
|
2394
|
+
**As built (S5b, 2026-10-28)**, E_loc, in the light pass beside E_ind:
|
|
2395
|
+
- **The world light clusters take any clipmap's windows.** `graph_build_world_light_clusters` binds a
|
|
2396
|
+
uniform that starts with a `CLIPMAP_LEVEL_ARRAY`, and is told the cascade count and a border of the
|
|
2397
|
+
clipmap's own cells. The irradiance clipmap passes its uniform and its tiles' skirt, as it always had
|
|
2398
|
+
them; the probe clipmap the bricks' levels and no border, so every hit has a cell.
|
|
2399
|
+
- **A cell by position**, `chunk_world_light_cluster_lookup_at`: the cell of a cascade holding a world
|
|
2400
|
+
point, the nearest past the cells.
|
|
2401
|
+
- **At a front-face hit**: the cell of the hit's brick cascade, at most `IRRADIANCE_CLIPMAP_LIGHT_BUDGET`
|
|
2402
|
+
point lights and as many spots, each the irradiance clipmap's at the footprint of a voxel of that
|
|
2403
|
+
cascade, its cosine at the normal, its map's nearest texel from a voxel off the surface. The light pass
|
|
2404
|
+
binds 8 storage buffers. The renderer hands the GI the frame's shadow map atlas.
|
|
2405
|
+
- **Measured on the device**, a closed room of albedo 0.5, nothing glowing, one point light inside: one
|
|
2406
|
+
frame through a twin that adds E_loc alone, every front-face hit facing the light against I·cosθ/d²
|
|
2407
|
+
under the range window.
|
|
2408
|
+
- 0.9958 at p50 on both GPUs, and 1.0039 above p99: the albedo the hits read, 127/255 from the cards
|
|
2409
|
+
and 128/255 from the material mean, against the 0.5 the check assumed. E_loc is exact.
|
|
2410
|
+
- 0.27% of those hits read shadowed, all within a few centimetres of the floor's far corner 7.6 m from
|
|
2411
|
+
the light, at a grazing angle, their points 0.12 m into the slab: acne a voxel's lift does not clear
|
|
2412
|
+
there. None do without the light's shadow.
|
|
2413
|
+
- **Its cost**, paired on Sponza (the light pass with E_loc and a twin without it, alternating frames),
|
|
2414
|
+
p50:
|
|
2415
|
+
|
|
2416
|
+
| | 4090 | iGPU |
|
|
2417
|
+
|---|---:|---:|
|
|
2418
|
+
| no local light | 28.7 against 27.6 µs | 1.70 against 1.64 ms |
|
|
2419
|
+
| 8 shadowed point lights along the nave | 34.8 against 27.6 µs | 2.32 against 1.58 ms |
|
|
2420
|
+
|
|
2421
|
+
- Tier 2: `probe_clipmap_light.spec.js`, against the light chunks themselves; `world_light_clusters.spec.js`
|
|
2422
|
+
for assign with one light, which the emulator's lane-serial run serves exactly, since lane 0 loads the
|
|
2423
|
+
batch before any lane reads it, and for the lookup.
|
|
2424
|
+
- Mutants: 27 of 29 killed. The two left: the cascade bound in assign, which the launch sized by the
|
|
2425
|
+
cascade count makes redundant; the cull box's far corner, which the host spec has no light to cull
|
|
2426
|
+
against, and which the device check shows holding its light.
|
|
2427
|
+
|
|
2044
2428
|
### 8.8 Update
|
|
2045
2429
|
|
|
2046
2430
|
**accumulate**: one workgroup per traced probe.
|
|
@@ -2117,6 +2501,30 @@ a per-invocation function instead. Each is tier 2 whole, and no thread's loop ru
|
|
|
2117
2501
|
texel's solid angle against one integrated from the decode, and the three launches the update's
|
|
2118
2502
|
command adds.
|
|
2119
2503
|
|
|
2504
|
+
**Measured (S4g, 2026-10-28): the stores round toward zero, so accumulate rounds them itself.**
|
|
2505
|
+
- Both GPUs gave the half toward zero for all 131,072 values tried, on each of three paths:
|
|
2506
|
+
`pack2x16float`, `textureStore` into `rgba16float`, and a render target's write. The stall above is
|
|
2507
|
+
therefore a whole ULP/α, and one-sided: below the target.
|
|
2508
|
+
- α follows the frame rate, since the GI's clock is the caller's (`performance.now()` in `Renderer`).
|
|
2509
|
+
- On §12's closed room glowing L, after 900 frames to settle, E/πL was:
|
|
2510
|
+
|
|
2511
|
+
| | 60 Hz | 240 Hz |
|
|
2512
|
+
|---|---:|---:|
|
|
2513
|
+
| rounded by the store, toward zero | 0.985 | 0.941 |
|
|
2514
|
+
| rounded to nearest before the store | 0.992 | 0.970 |
|
|
2515
|
+
| rounded stochastically (built) | 0.999 | 0.999 |
|
|
2516
|
+
|
|
2517
|
+
The first row is the 1.5% and 5.9% that ULP/α predicts.
|
|
2518
|
+
- **Accumulate now rounds every stored component stochastically onto a half itself**, for radiance and
|
|
2519
|
+
for depth: `shader/chunk/math/chunk_f16_round_stochastic.js`, with noise from `pcg3d(slot, texel,
|
|
2520
|
+
updates)`.
|
|
2521
|
+
- The value it hands the store is a half already, so the device's own rounding never applies.
|
|
2522
|
+
- The blend moves in expectation however small its step.
|
|
2523
|
+
- UE 5.4's TSR stores its history through `QuantizeForFloatRenderTarget` for the same reason.
|
|
2524
|
+
- The emulator's `pack2x16float` rounds to nearest even, so tier 2 sees half the devices' stall, and on
|
|
2525
|
+
both sides. `probe_clipmap_accumulate.spec.js` pins convergence from either side at α = 1/64; it was
|
|
2526
|
+
red before the change.
|
|
2527
|
+
|
|
2120
2528
|
## 9. Sampling
|
|
2121
2529
|
|
|
2122
2530
|
### 9.1 The pick: `probe_clipmap_pick(position, normal, view_direction, ξ) -> pick`
|
|
@@ -2152,6 +2560,45 @@ Per pixel and cascade the visibility pass makes 8 indirection loads, 8 state wor
|
|
|
2152
2560
|
depth taps. Then the pick reads two probes: two SH records and, for specular, two radiance taps. (a)
|
|
2153
2561
|
read eight 64 B records and resolved eight SH.
|
|
2154
2562
|
|
|
2563
|
+
**As built (S4f, 2026-10-28)**, `probe/chunk_probe_clipmap_pick.js`:
|
|
2564
|
+
- **The cage is want's.** `probe_clipmap_cage` and `probe_clipmap_biased` live in
|
|
2565
|
+
`probe/chunk_probe_clipmap_cage.js`, and want calls them too, so a pixel reads the cage it wanted.
|
|
2566
|
+
- **A corner's weight**, `probe_clipmap_corner_weight`:
|
|
2567
|
+
- No slot, or a state that is not ALIVE, weighs 0.
|
|
2568
|
+
- The probe sits at its lattice point plus the relocation offset in its state's second word.
|
|
2569
|
+
- The tap is toward the biased point, at its distance in the cascade's spacings, which is how
|
|
2570
|
+
publish writes depth (§8.8). The wrap back-face term is taken from the point itself.
|
|
2571
|
+
- **The weight is LPV's, extracted**: `probe_visibility_weight` and `probe_visibility_chebyshev` in
|
|
2572
|
+
`shader/chunk/light/probe/chunk_probe_visibility_weight.js`. `chunk_lpv_mask_weights_by_visibility`
|
|
2573
|
+
and `chunk_lpv_get_visibility_mask` call it, unchanged.
|
|
2574
|
+
- **The pick is Brick4's, extracted**: `pick2_wrs_mat2x4` in
|
|
2575
|
+
`shader/chunk/random/reservoir/chunk_pick2_wrs_mat2x4.js`, which `chunk_brick4_probe_meta_pick2`
|
|
2576
|
+
now calls.
|
|
2577
|
+
- It reads and clears the drawn weights through masks, because indexing the matrix by a value puts
|
|
2578
|
+
it in local memory. That cost 3% of the diffuse on the iGPU: 7.29 → 7.06 ms, A/B in one frame
|
|
2579
|
+
with the order alternating.
|
|
2580
|
+
- Extracting it found a defect in the draw it is built on: `sample_discrete_wrs_mat2x4` and
|
|
2581
|
+
`_vec4` drew NaN after leading zero weights, 0/0 in the first step. It is fixed and pinned in
|
|
2582
|
+
`5a3c8e968`.
|
|
2583
|
+
- **Reads** per cascade: 8 indirection loads, 8 state words and 8 depth taps. The pair's two slots
|
|
2584
|
+
are loaded again by index rather than kept, so no array is indexed by a value.
|
|
2585
|
+
- **A depth tile's place takes no integer division** since S4g: `index_to_grid2d_reciprocal`, through
|
|
2586
|
+
the reciprocal the sampling uniform carries. RDNA2 emulates integer division. The iGPU's diffuse went
|
|
2587
|
+
from 7.08 to 6.65 ms, paired, with the image the same to the bit on both GPUs.
|
|
2588
|
+
- **The clipmap's shape is a uniform**, `PROBE_SAMPLING_STRUCT`: C_p, N_p, n_d, the depth atlas's
|
|
2589
|
+
tiles a row and its texel size. `GPUProbeClipmap.write_sampling_bindings` puts it and the six
|
|
2590
|
+
resources into a reader's bindings.
|
|
2591
|
+
- **Tier 2**, `probe_clipmap_pick.spec.js`:
|
|
2592
|
+
- picks as often as the weights, and dead, new and missing corners passed over;
|
|
2593
|
+
- the depth read toward the point, at its direction's own texel, in the cascade's spacings;
|
|
2594
|
+
- a relocated probe weighed from where it moved;
|
|
2595
|
+
- the fallback cascade, the skirt's share, nothing found;
|
|
2596
|
+
- two weighing corners blended in every draw;
|
|
2597
|
+
- the SH mixed by the weights.
|
|
2598
|
+
|
|
2599
|
+
`chunk_probe_visibility_weight.spec.js` and `chunk_pick2_wrs_mat2x4.spec.js` cover the extracted
|
|
2600
|
+
pieces, and LPV's and Brick4's specs pass unchanged.
|
|
2601
|
+
|
|
2155
2602
|
### 9.2 Diffuse: `probe_clipmap_sample_irradiance(position, normal, view_direction, ξ) -> E`
|
|
2156
2603
|
|
|
2157
2604
|
- The two picked records' Metro L1 structs are mixed by t, then resolved once with
|
|
@@ -2159,6 +2606,12 @@ read eight 64 B records and resolved eight SH.
|
|
|
2159
2606
|
- The resolve returns E/π by its convention. The pass multiplies by π, because the resolve pass takes
|
|
2160
2607
|
irradiance E.
|
|
2161
2608
|
|
|
2609
|
+
**As built (S4f)**: there is no `probe_clipmap_sample_irradiance`. `probe_clipmap_sample_sh` returns the
|
|
2610
|
+
mixed Metro L1 struct and whether anything was found, and the caller resolves at the normal it
|
|
2611
|
+
chooses. The deferred pass uses two normals where this section has one n:
|
|
2612
|
+
- it picks with the surface's own normal, as want chose its cage;
|
|
2613
|
+
- it resolves at the bent normal, as IBL's pass does.
|
|
2614
|
+
|
|
2162
2615
|
### 9.3 Specular: `probe_clipmap_sample_radiance(position, normal, view_direction, roughness, ξ) -> L`
|
|
2163
2616
|
|
|
2164
2617
|
The two probes of the same pick are used. Per Brick4's fused pass, the pair's blend is re-weighted for
|
|
@@ -2186,6 +2639,34 @@ per probe would be manual strips, with their own skirts and their own downsample
|
|
|
2186
2639
|
per storage binding. Two SH bands hold the rough end of the lobe well, and a single tap holds the
|
|
2187
2640
|
smooth end.
|
|
2188
2641
|
|
|
2642
|
+
**As built (S6a, S6b, 2026-10-28)**:
|
|
2643
|
+
- **The GGX resolve**, `resolve_irradiance_sh_to_specular` in `shader/chunk/spherical_harmonics/l1i/`
|
|
2644
|
+
(S6a). Its spec holds both invariants above. 7 of 7 mutants were killed.
|
|
2645
|
+
- **The sample**, `probe_clipmap_sample_radiance` in `probe/chunk_probe_clipmap_sample_radiance.js` (S6b).
|
|
2646
|
+
It returns the radiance and whether anything was found, as the SH sample does.
|
|
2647
|
+
- *The pick* is the diffuse's, `probe_clipmap_pick`.
|
|
2648
|
+
- *The re-weight* takes each picked probe's whole weight again with the lobe in place of the normal,
|
|
2649
|
+
at the visibility it was picked at. That visibility is recomputed, one depth tap toward the biased
|
|
2650
|
+
point. The crush below 0.2 keeps a ratio of the wrap terms alone from standing in for it.
|
|
2651
|
+
- *The parallax* takes the far root of the reflected ray against the sphere, so a point inside the
|
|
2652
|
+
sphere always meets it. Where the ray misses the sphere, the tap is along the lobe itself.
|
|
2653
|
+
- *The radiance tap* reads the radiance atlas through the depth's sampler, bilinear and clamped. Its
|
|
2654
|
+
tiles are placed as the depth's are, and the sampling uniform now carries the radiance atlas's shape
|
|
2655
|
+
as well (`radiance_resolution`, its tiles a row, its texel and the row's reciprocal).
|
|
2656
|
+
- *The blend* is `roughness_to_mip_ratio`, between the renormalised taps and the SH. The SH is mixed
|
|
2657
|
+
by the same shares and resolved for the lobe.
|
|
2658
|
+
- *Reads a picked probe*: its record, its state word, two depth taps (visibility and parallax) and one
|
|
2659
|
+
radiance tap.
|
|
2660
|
+
- *The probes' sampling group* takes the radiance atlas, so every reader binds it in every mode. The
|
|
2661
|
+
GI's placeholders gained a texel of radiance.
|
|
2662
|
+
- *The deferred diffuse pass* listed seven of those declarations by hand. A frame graph pass must declare every
|
|
2663
|
+
input it is given, so it threw under SSR on once the mode had probes. It declares the group whole since the
|
|
2664
|
+
fix that followed S6c1, pinned by its spec running it in a frame graph on the GI's bindings.
|
|
2665
|
+
- *Tier 2*: `probe/probe_clipmap_sample_radiance.spec.js` uses tiles whose interior texels hold their
|
|
2666
|
+
own uv, so a tap shows what it read. It covers the parallax tap at a depth that varies with
|
|
2667
|
+
direction, the GGX-resolved SH at the rough end, the mip ratio's blend between, the re-weighted
|
|
2668
|
+
shares with a relocated probe, and nothing found. 16 of 16 mutants were killed.
|
|
2669
|
+
|
|
2189
2670
|
### 9.4 Noise and its resolve
|
|
2190
2671
|
|
|
2191
2672
|
- Image passes take ξ from STBN at (pixel, frame), `chunk_stbn_sample_vec2`, as Brick4 does.
|
|
@@ -2203,7 +2684,7 @@ smooth end.
|
|
|
2203
2684
|
| `forward_sample_indirect(p, v, n, roughness, &radiance, &irradiance)` | one pick (hash noise); irradiance from §9.2, radiance from §9.3 (environment until S6) |
|
|
2204
2685
|
| `water_gi_irradiance(p, n)` / `water_gi_specular(p, n, v, roughness)` | §9.2 with v = n, since the irradiance call has no view direction / §9.3 (environment until S6); hash noise |
|
|
2205
2686
|
| `ssr_sample_radiance(p, n, v, roughness, coord)` | §9.3 with STBN at coord (environment until S6) |
|
|
2206
|
-
| `ssr_add_hit_irradiance(&result, confidence, p, hit_vs, hit_n, coord)` |
|
|
2687
|
+
| `ssr_add_hit_irradiance(&result, confidence, p, hit_vs, hit_n, coord)` | albedo·E/π at the hit when confidence > 0.05, E the deferred pass's own at the hit's texel (as built; LPV's body with §9.2 measured 41% of the resolve) |
|
|
2207
2688
|
| `irradiance_clipmap_gi_sample(p) -> array<vec3f,4>` | `probe_clipmap_sample_sh(p)` × `INDIRECT_LIGHT_INTENSITY`: the pick without the back-face term (there is no normal), returned as RGB SH2 |
|
|
2208
2689
|
|
|
2209
2690
|
Each alternative declares `PROBE_CLIPMAP_RESOURCES` with `requireResource`:
|
|
@@ -2216,6 +2697,152 @@ Each alternative declares `PROBE_CLIPMAP_RESOURCES` with `requireResource`:
|
|
|
2216
2697
|
|
|
2217
2698
|
Union bindings were measured free in stage 2 of the switch proposal (p50 1.000).
|
|
2218
2699
|
|
|
2700
|
+
**As built (S5c, 2026-10-28)**:
|
|
2701
|
+
- **The probes are bound in every mode.** A reader binds every mode's resources, and the GI is released
|
|
2702
|
+
out of its mode. So `GPUProbeClipmapGI.write_sampling_bindings` hands out its probes' bindings once it
|
|
2703
|
+
has probes. Before that, or out of the mode, it hands out a clipmap of no cascade: the sampling
|
|
2704
|
+
uniform counts none, so the pick selects no cascade and reads nothing past the levels. These are six
|
|
2705
|
+
objects of a few bytes, made on the first ask and destroyed with the GI. The seven declarations are
|
|
2706
|
+
`PROBE_CLIPMAP_SAMPLING_GROUP`, in `probe/chunk_probe_clipmap_pick.js`; the sampler comes from its
|
|
2707
|
+
declaration's descriptor.
|
|
2708
|
+
- **SSR's hit**, `chunk_ssr_add_hit_irradiance_probe_clipmap`: albedo × the deferred pass's indirect
|
|
2709
|
+
diffuse at the hit's texel / π, where the hit's confidence passes 0.05. That output is E with AO in,
|
|
2710
|
+
so a reflection shows the indirect diffuse of the surface it hits, with its AO, its pick and its IBL
|
|
2711
|
+
fallback.
|
|
2712
|
+
- The renderer records the diffuse pass before SSR, which it reads nothing of, and hands it to SSR as
|
|
2713
|
+
`input_indirect_diffuse` under this mode. The resolve's `tIndirectDiffuse` is black for the others.
|
|
2714
|
+
- Tier 2: `chunk_ssr_add_hit_irradiance.spec.js` reads the hit's texel of both inputs, and nothing at
|
|
2715
|
+
confidence 0.05. `SSR.spec.js` binds the diffuse under this mode alone. 7 of 7 mutants were killed.
|
|
2716
|
+
- **Why not the pick at the hit** (§9.5's first form, LPV's body; built in `9208dd7cf`, replaced by Alex's
|
|
2717
|
+
call the same day). Measured on Sponza at 1280×720, sun only, SSR on, the probes settled. The arms
|
|
2718
|
+
alternated frame by frame over one set of probes, and each frame's resolve was also drawn by every
|
|
2719
|
+
arm on the same inputs.
|
|
2720
|
+
- 0.81% of the pixels have a confident hit.
|
|
2721
|
+
- The pick's cost is its work at scattered hits, where a wave pays the whole pick for a few lanes. Its
|
|
2722
|
+
code present but never run is about a sixth of it.
|
|
2723
|
+
|
|
2724
|
+
| SSR resolve, p50 | RTX 4090 | AMD iGPU |
|
|
2725
|
+
|---|---:|---:|
|
|
2726
|
+
| no bounce (IBL's variant) | 39.9 µs | 2.06 ms |
|
|
2727
|
+
| the pick's code present, never run | 42.0 µs | 2.19 ms |
|
|
2728
|
+
| the pick at the hit | 54.3 µs | 2.91 ms |
|
|
2729
|
+
| **as built**: the deferred pass's texel, against no bounce in the same run | × 1.019 | × 1.034 (2.03 → 2.10 ms) |
|
|
2730
|
+
|
|
2731
|
+
The SSR trace, which no arm touches, reads 1.00 between arms on both GPUs.
|
|
2732
|
+
- The pick lit 0.64% of the pixels and added 2.4% of the reflections' light.
|
|
2733
|
+
- The texel lights 0.70%: where no probe weighs anything, the diffuse pass's IBL fallback lights the
|
|
2734
|
+
hit too. It adds 1.9% of the reflections' light, with the hit's AO now applied.
|
|
2735
|
+
- Neither darkens a pixel, and both leave the metadata the same to the bit.
|
|
2736
|
+
- **Forward**, `rasterize/forward/chunk_forward_sample_indirect_probe_clipmap.js`: one pick by the
|
|
2737
|
+
shading normal and the view, jittered by `jitter_from_vectors(p, n)`. That hash is extracted from
|
|
2738
|
+
Brick4's forward sampling into `shader/chunk/random/`.
|
|
2739
|
+
- E = π × the SH resolved at the normal, or the environment's where nothing is found.
|
|
2740
|
+
- The specular is the environment's until S6.
|
|
2741
|
+
- The AVBOIT draw and MBOIT's resolve add the probe declarations to their group 2, which both stages
|
|
2742
|
+
see, and set those entries to the fragment stage alone. The probes' two storage buffers would
|
|
2743
|
+
otherwise take the vertex stage to 10, the floor.
|
|
2744
|
+
- **Water**, `chunk_water_gi_probe_clipmap` in `water/chunk_water_gi.js`: the forward pick with v = n,
|
|
2745
|
+
since the irradiance call has no view, and the environment's specular until S6. The optics'
|
|
2746
|
+
brick4/view group takes the probes, and `water_pipeline` keeps them fragment-only.
|
|
2747
|
+
- **Bindings**: the owners write `write_sampling_bindings` into their hand-built bindings. The renderer
|
|
2748
|
+
hands the GI to the transparency orchestrators, and AVBOIT's hands it on to the water's side channel
|
|
2749
|
+
(`avboit_prepare`'s `probes`).
|
|
2750
|
+
- Tier 2 is `chunk_forward_sample_indirect_probe_clipmap.spec.js` and `water/chunk_water_gi.spec.js`
|
|
2751
|
+
against the pick and the environment map, the fragment-only test in
|
|
2752
|
+
`indirect_lighting_consumers.spec.js`, and the AVBOIT orchestrator's water optics in every mode.
|
|
2753
|
+
- 18 of 19 mutants were killed. MBOIT's resolve without the probes survives because no spec draws
|
|
2754
|
+
MBOIT; the device check below draws it.
|
|
2755
|
+
- **Measured** on Sponza at 1280×720, sun only, with five half-transparent boxes down the nave. Each
|
|
2756
|
+
pass alternated frame by frame with IBL's variant over one set of probes.
|
|
2757
|
+
|
|
2758
|
+
| p50, IBL → the probes | RTX 4090 | AMD iGPU |
|
|
2759
|
+
|---|---:|---:|
|
|
2760
|
+
| AVBOIT draw | 13.3 → 28.7 µs | 132 → 312 µs |
|
|
2761
|
+
| MBOIT's resolve | 11.3 → 26.6 µs | 129 → 317 µs |
|
|
2762
|
+
|
|
2763
|
+
- Per transparent fragment, that is about what the deferred pick costs a pixel.
|
|
2764
|
+
- Averaged over 24 frames, 2.8% of the pixels, the boxes, move by 14 of 255 between the arms. The noise
|
|
2765
|
+
floor, an arm against itself, is 0.1–0.25%. The boxes are lit brighter by the probes than by the
|
|
2766
|
+
sky (luma 97 against 88), the same on both GPUs and in both techniques.
|
|
2767
|
+
- The water optics compile under every mode on both GPUs.
|
|
2768
|
+
- **The irradiance clipmap**, `chunk_irradiance_clipmap_gi_sample_probe_clipmap`: the pick at the voxel,
|
|
2769
|
+
with no normal and no view, jittered by `jitter_from_vectors(p, 0)` and the view's frame. Where nothing
|
|
2770
|
+
is found, the environment SH.
|
|
2771
|
+
- The probes' Metro L1 is handed over as the radiance SH2 that `volumetrics_irradiance_sh_add_radiance`
|
|
2772
|
+
turns back into it: [DC, y, z, x] = [YCoCg→RGB(2π·(Y.w, 0.28209·CoCg)), 2π·Y.y, 2π·Y.z, 2π·Y.x], the
|
|
2773
|
+
band-1 terms grey.
|
|
2774
|
+
- The build binds the probes as a group of its own, which brings it to 9 of the floor's 10 storage
|
|
2775
|
+
buffers, and binds the view.
|
|
2776
|
+
- The cascades are a cache, so a voxel's pick holds until its cascade is rebuilt: cascade 0 and the
|
|
2777
|
+
ruler's each frame, the others as their windows move.
|
|
2778
|
+
- Tier 2: `chunk_irradiance_clipmap_gi_sample.spec.js` accumulates the handed-over SH2 as the build
|
|
2779
|
+
does and gets the pick back, with the frame's noise and the environment where nothing is found.
|
|
2780
|
+
`graph_build_froxel_gi.spec.js` finds the GI's probes and the view bound to the build. 11 of 11
|
|
2781
|
+
mutants were killed.
|
|
2782
|
+
- **Measured** on Sponza at 1280×720, sun only. A sink kept the volume, which only lit particles read.
|
|
2783
|
+
The build's p50 went from 7.2 to 15.4 µs on the 4090 and from 127 to 430 µs on the iGPU, for two
|
|
2784
|
+
cascades of 34³ voxels a frame.
|
|
2785
|
+
- The volume's band 0 moves by 0.0016 between the arms, 6× the probe arm's own change from one run to
|
|
2786
|
+
the next (0.00026). The IBL arm is steady to the bit. The sun is most of the volume here, so its mean
|
|
2787
|
+
moves by 0.5%.
|
|
2788
|
+
|
|
2789
|
+
**As built (S6c2, 2026-10-28)**: SSR's fallback, `chunk_ssr_sample_radiance_probe_clipmap`, is §9.3 at the pixel:
|
|
2790
|
+
the probes' specular picked by its normal and view, with STBN at the pixel and the resolve's frame index, as
|
|
2791
|
+
Brick4's is. Where no probe weighs anything, it is the environment's.
|
|
2792
|
+
- The resolve binds the probes' sampling group again, through SSR's `probes`, in every mode.
|
|
2793
|
+
- The radiance chunk now declares what it reads itself. `switched_shaders_declare_bindings.spec.js` audits it
|
|
2794
|
+
since SSR's resolve links it.
|
|
2795
|
+
- Tier 2: `chunk_ssr_sample_radiance.spec.js` runs the fallback against `probe_clipmap_sample_radiance` with the
|
|
2796
|
+
STBN texel at the pixel and frame, and against the environment where nothing is found. `SSR.spec.js` binds the
|
|
2797
|
+
probes in every mode; `indirect_lighting_consumers.spec.js` finds the probes' specular in this mode's variant
|
|
2798
|
+
alone. 7 of 7 mutants were killed.
|
|
2799
|
+
- **Measured** on Sponza at 1280×720, sun only, SSR on, the probes settled. One of three arms a frame, in turn,
|
|
2800
|
+
over one set of probes; 131 frames an arm, p50:
|
|
2801
|
+
|
|
2802
|
+
| arm | deferred pass | SSR resolve | together, RTX 4090 | together, AMD iGPU |
|
|
2803
|
+
|---|---|---|---:|---:|
|
|
2804
|
+
| **as built**: the fallback picks again | 87 µs / 6.64 ms | 140 µs / 10.39 ms | 227 µs | 17.03 ms |
|
|
2805
|
+
| S5c: IBL's fallback | 87 µs / 6.64 ms | 61 µs / 2.09 ms | 148 µs | 8.74 ms |
|
|
2806
|
+
| the deferred pass's one pick writes the specular too, and the fallback reads its texel | 114 µs / 9.02 ms | 61 µs / 1.93 ms | 175 µs | 10.95 ms |
|
|
2807
|
+
|
|
2808
|
+
- The SSR trace, which no arm touches, reads 0.98–1.01 between arms.
|
|
2809
|
+
- The fallback runs at every foreground pixel, so its pick is the whole pick again: 4.96× the resolve on the
|
|
2810
|
+
iGPU. The texel arm's light is the as-built arm's in the mean; 1% of the pixels differ, by the pick's noise.
|
|
2811
|
+
- The probes' fallback lifts the reflections' mean luma 28× over the harness's dim sky. No pixel goes darker.
|
|
2812
|
+
- **Alex kept the pick** (2026-10-28), as this section designs it, with these measurements before him.
|
|
2813
|
+
|
|
2814
|
+
**As built (S6d, 2026-10-28)**: the forward passes' and the water's specular from the probes, §9.3.
|
|
2815
|
+
- **Forward**, `chunk_forward_sample_indirect_probe_clipmap`: one pick for both lobes, as the fused deferred pass
|
|
2816
|
+
takes it (S6c1). The irradiance is the pick's SH resolved at the normal, as before. The specular is
|
|
2817
|
+
`probe_clipmap_radiance_of` the same pick, at the fragment's roughness. Where no probe weighs anything, both are
|
|
2818
|
+
the environment's.
|
|
2819
|
+
- **Water**, `water_gi_specular`: `probe_clipmap_sample_radiance` by the surface's normal and view, its noise the
|
|
2820
|
+
irradiance's hash, or the environment's where nothing is found. The optics ask for the irradiance at other points
|
|
2821
|
+
and by other normals (the in-scatter's column, a receiver's downwelling light), so each call picks for itself.
|
|
2822
|
+
- **Bindings** are unchanged. Each of the three passes has declared the probes' group whole since S6b, the radiance
|
|
2823
|
+
atlas with it, fragment-only.
|
|
2824
|
+
- **Tier 2**: `chunk_forward_sample_indirect_probe_clipmap.spec.js` and `water/chunk_water_gi.spec.js` give each
|
|
2825
|
+
probe a colour of its own over its radiance tile. They check the specular against `probe_clipmap_sample_radiance`
|
|
2826
|
+
with the call's own noise, view and roughness, and against the environment where nothing is found.
|
|
2827
|
+
`indirect_lighting_consumers.spec.js` finds the probes' radiance in this mode's forward and water variants alone.
|
|
2828
|
+
16 of 16 mutants were killed.
|
|
2829
|
+
- **Measured** on Sponza at 1280×720, sun only, with S5c's five half-transparent boxes down the nave and S5c's
|
|
2830
|
+
harness. Each pass alternated frame by frame with IBL's variant over one set of probes. IBL's arm reads within
|
|
2831
|
+
1 µs between S5c's run and this one.
|
|
2832
|
+
|
|
2833
|
+
| p50, IBL → the probes | S5c: the irradiance | S6d: both lobes |
|
|
2834
|
+
|---|---:|---:|
|
|
2835
|
+
| AVBOIT draw, RTX 4090 | 13.3 → 28.7 µs | 12.3 → 31.7 µs |
|
|
2836
|
+
| MBOIT's resolve, RTX 4090 | 11.3 → 26.6 µs | 11.3 → 30.7 µs |
|
|
2837
|
+
| AVBOIT draw, AMD iGPU | 132 → 312 µs | 131 → 379 µs |
|
|
2838
|
+
| MBOIT's resolve, AMD iGPU | 129 → 317 µs | 130 → 383 µs |
|
|
2839
|
+
|
|
2840
|
+
- The specular adds 26–27% (4090) and 35–38% (iGPU) to what the probes' light costs each pass over IBL's.
|
|
2841
|
+
- Averaged over 24 frames, 3.6–3.8% of the pixels move by more than 4 of 255 between the arms, against 2.8–3.0% in
|
|
2842
|
+
S5c: the boxes' reflections now take the probes' light too. An arm against itself moves 0.2–0.7% of them
|
|
2843
|
+
(S5c 0.1–0.25%), since the specular carries the pick's noise; TAA is off in this harness.
|
|
2844
|
+
- The water optics compile under every mode on both GPUs.
|
|
2845
|
+
|
|
2219
2846
|
### 9.6 Deferred
|
|
2220
2847
|
|
|
2221
2848
|
A new case in `#graph_indirect_lighting`, shaped like Brick4's:
|
|
@@ -2227,6 +2854,72 @@ A new case in `#graph_indirect_lighting`, shaped like Brick4's:
|
|
|
2227
2854
|
|
|
2228
2855
|
Until S6, specular is `shader_specular_IBL`, as LPV mode does today.
|
|
2229
2856
|
|
|
2857
|
+
**As built (S4f, 2026-10-28)**: `probe/shader_probe_clipmap_diffuse.js` serves SSR on and off alike.
|
|
2858
|
+
The fused `shader_probe_clipmap_indirect_lighting` waits for S6, since until then there is no probe
|
|
2859
|
+
specular to fuse with.
|
|
2860
|
+
- `Renderer` swaps the pass in for `shader_diffuse_IBL` once `GPUProbeClipmapGI.has_probes`, that
|
|
2861
|
+
is, once the mode has run a frame.
|
|
2862
|
+
- A pixel with no probe found takes `get_ibl_irradiance(bent normal)` inside the pass.
|
|
2863
|
+
- AO multiplies the result, as in the IBL pass.
|
|
2864
|
+
- **Tier 2**: `shader_probe_clipmap_diffuse.spec.js` runs the pass whole on the emulator:
|
|
2865
|
+
- π × the SH resolved at the bent normal × occlusion;
|
|
2866
|
+
- IBL's output where nothing is found;
|
|
2867
|
+
- the pick by the surface normal and the view, against `probe_clipmap_sample_sh` itself.
|
|
2868
|
+
- **Mutants**: 32 of 32 killed (the pick 16, the visibility weight 3, the pick of two 2, the cage 1,
|
|
2869
|
+
the host's uniform 2, the pass 8). Four were killed by tests added for them:
|
|
2870
|
+
- a relocated probe weighed from where it moved;
|
|
2871
|
+
- the pair's second slot and the blend's direction, both by two corners blended in every draw;
|
|
2872
|
+
- the depth read at its direction's own texel.
|
|
2873
|
+
- **Measured** on Sponza at 1280×720, a still camera, the caches baked. The frame's mean luma goes
|
|
2874
|
+
from 2.6 under IBL to 66 under the probes (8-bit), the same on both GPUs.
|
|
2875
|
+
|
|
2876
|
+
| p50 | AMD iGPU | RTX 4090 |
|
|
2877
|
+
|---|---:|---:|
|
|
2878
|
+
| the diffuse | 7.06 ms | 58 µs |
|
|
2879
|
+
|
|
2880
|
+
The iGPU's time, split by twins drawn in the same frame:
|
|
2881
|
+
|
|
2882
|
+
| part | iGPU |
|
|
2883
|
+
|---|---:|
|
|
2884
|
+
| depth taps and Chebyshev | 2.37 ms |
|
|
2885
|
+
| the draw of two | 1.30 ms |
|
|
2886
|
+
| SH reads and the resolve | 0.18 ms |
|
|
2887
|
+
| the skirt | 0.04 ms |
|
|
2888
|
+
| the pass without a pick | 0.42 ms |
|
|
2889
|
+
| the rest of the corner weights: indirection loads, state words, the trilinear and back-face terms | ~3.0 ms |
|
|
2890
|
+
|
|
2891
|
+
**As built (S6c1, 2026-10-28)**: `probe/shader_probe_clipmap_indirect_lighting.js`, the fused pass, with SSR off.
|
|
2892
|
+
- `Renderer` draws it in place of the diffuse, specular and resolve passes once `has_probes`, as Brick4's fused
|
|
2893
|
+
pass replaces its three. With SSR on, the diffuse pass and SSR stay.
|
|
2894
|
+
- **One pick a pixel**, by its own normal and the view, with the frame's STBN, for both lobes.
|
|
2895
|
+
`probe_clipmap_sh_of(pick)` and `probe_clipmap_radiance_of(pick, …)` take a pick already made;
|
|
2896
|
+
`probe_clipmap_sample_sh` and `probe_clipmap_sample_radiance` make one and call them.
|
|
2897
|
+
- **The diffuse** is the diffuse pass's: the pick's SH resolved at the bent normal, times π, times AO. Its
|
|
2898
|
+
shares are not re-weighted for the bent normal, as Brick4's are. The diffuse pass takes them as picked (S4f),
|
|
2899
|
+
so the fused pass lights the diffuse as SSR on does.
|
|
2900
|
+
- **The specular** is `probe_clipmap_radiance_of` at the surface's roughness, as the IBL pass samples its own.
|
|
2901
|
+
- A pixel no probe weighs anything for takes the environment's pair, as the IBL passes give it. The pair is
|
|
2902
|
+
resolved as `shader_resolve_indirect_lighting` resolves it and added to the scene's colour.
|
|
2903
|
+
- **Tier 2**: `shader_probe_clipmap_indirect_lighting.spec.js` runs the pass on the emulator against the
|
|
2904
|
+
diffuse pass, `probe_clipmap_sample_radiance` and the resolve pass on the same pixel. Where nothing is found,
|
|
2905
|
+
it runs against the IBL pair. The pixel's bent normal is placed so that its visibility cone ends across the
|
|
2906
|
+
specular lobe, and the specular occlusion shows. 14 of 14 mutants were killed.
|
|
2907
|
+
- **Measured** on Sponza at 1280×720, sun only, SSR off, the probes settled. The fused pass's node drew one of
|
|
2908
|
+
three arms a frame, in turn, over one set of probes. 138 frames an arm; p50 of the indirect light's passes:
|
|
2909
|
+
|
|
2910
|
+
| arm | RTX 4090 | AMD iGPU |
|
|
2911
|
+
|---|---:|---:|
|
|
2912
|
+
| **as built**: the fused pass | 265 µs | 9.66 ms |
|
|
2913
|
+
| before: the probes' diffuse, IBL's specular, the resolve | 270 µs (208 + 31 + 33) | 8.17 ms (6.65 + 0.62 + 0.91) |
|
|
2914
|
+
| the same light unfused: the probes' diffuse, a pass of their specular, the resolve | 480 µs (208 + 237 + 36) | 16.18 ms (6.65 + 8.62 + 0.92) |
|
|
2915
|
+
|
|
2916
|
+
- The probe trace, which no arm touches, reads 0.97–1.00 between arms.
|
|
2917
|
+
- One pick for both lobes costs 0.55× (4090) and 0.60× (iGPU) of the two picks unfused. Against the passes it
|
|
2918
|
+
replaces, the probes' specular costs nothing measurable on the 4090 and 1.5 ms on the iGPU.
|
|
2919
|
+
- The fused pass's light matches the unfused passes' to 0.6% of a pixel's luma, their f16 intermediates. The
|
|
2920
|
+
probes' specular lifts the indirect light's mean luma 36% over the sky's, which is dim in this harness. Both
|
|
2921
|
+
GPUs agree.
|
|
2922
|
+
|
|
2230
2923
|
### 9.7 Every file the change touches
|
|
2231
2924
|
|
|
2232
2925
|
The new mode value:
|
|
@@ -2430,6 +3123,138 @@ specs. A real compile on a device catches what the validator misses.
|
|
|
2430
3123
|
allocation failures, period T.
|
|
2431
3124
|
- **The storage-write rounding question of §8.8.**
|
|
2432
3125
|
|
|
3126
|
+
**As built, S4's done-when (S4g, 2026-10-28)**: one bounce, on both GPUs. The harness is the session's
|
|
3127
|
+
scratchpad `device/s4g.js`.
|
|
3128
|
+
- **How the runs were held.** The GI's clock is the caller's `performance.now()`, so α followed each run's
|
|
3129
|
+
frame pacing, and a furnace read differently from run to run. Every run below holds the GI's clock at
|
|
3130
|
+
1/60 s a frame by wrapping `GPUProbeClipmapGI.graph_update`. The furnaces then let it settle 900
|
|
3131
|
+
frames, 30 τ.
|
|
3132
|
+
- **White furnace, one bounce.**
|
|
3133
|
+
- *A closed room* of 8 × 4 × 8 m, every face glowing L, no light. Before AO, E/πL is 0.9991 at p50 and
|
|
3134
|
+
0.994 at p01, on both GPUs and at 60 or 240 Hz. Every ray hits, every texel holds L, and the
|
|
3135
|
+
records' E/π is 0.4998 against 0.5. The 1.5% and 5.9% short before were the store's rounding (§8.8).
|
|
3136
|
+
- GTAO's occlusion multiplies the probes' diffuse, as it does IBL's. The room's corners then read p10
|
|
3137
|
+
0.886 and p01 0.557: the probes see those walls glowing, and AO calls them dark. A furnace therefore
|
|
3138
|
+
reads E before AO.
|
|
3139
|
+
- *An open sky* of radiance L over a dark floor of 200 m, no light. L1 holds a lit hemisphere's
|
|
3140
|
+
irradiance exactly at its pole, so πL is the floor's exact answer. E/πL is 0.997 on the iGPU and
|
|
3141
|
+
0.980 on the RTX 4090. **Open, not root-caused**: the shortfall is in the 4090's probes, not the
|
|
3142
|
+
resolve, since the surface normal gives 0.983 against the bent normal's 0.981.
|
|
3143
|
+
- Texel by texel, every probe holds L from 10° above the horizon up on both GPUs. At 2–10° the
|
|
3144
|
+
4090's texels hold 0.98 and 0.96 of L, against the iGPU's 0.996 and 0.993, on rays that resolve
|
|
3145
|
+
alike.
|
|
3146
|
+
- **D4's reversal condition is met here.** 64 steps leave 63% of the rays within 2° above the
|
|
3147
|
+
horizon unresolved, 41% at 2–5°, 28% at 5–10° and 10% at 10–20°. Over Sponza it is 0.24–0.34% of
|
|
3148
|
+
all rays.
|
|
3149
|
+
- **Stability**, on Sponza with the sun: each pixel against the same surface point a frame before,
|
|
3150
|
+
reprojected through the previous frame's view-projection. The two GPUs agree to the per-mille.
|
|
3151
|
+
- Raw, the indirect diffuse before TAA:
|
|
3152
|
+
|
|
3153
|
+
| p50 | still | orbit | dolly |
|
|
3154
|
+
|---|---:|---:|---:|
|
|
3155
|
+
| the eight corners blended (a twin) | 5.0% | 10.1% | 9.2% |
|
|
3156
|
+
| the pick of two (shipped) | 16.9% | 26.4% | 17.6% |
|
|
3157
|
+
| pixels matched | 99.6% | 95.8% | 98.3% |
|
|
3158
|
+
|
|
3159
|
+
- With TAA, the final image:
|
|
3160
|
+
- Still: the pick 0.95% against the eight's 0.65%.
|
|
3161
|
+
- Orbiting, the medians are alike, 14.3% against 13.1%, which is texture detail resampled.
|
|
3162
|
+
**The tails are not: p90 50% against 21%, mean 22.9% against 13.5%.** TAA does not resolve the
|
|
3163
|
+
pick's noise under motion in a tenth of the frames, which §9.4 expected it to.
|
|
3164
|
+
- The eight corners' cost, paired in one frame with the order alternating: iGPU 5.93 ms against the
|
|
3165
|
+
pick's 6.64 ms, **0.72 ms less**; RTX 4090 65.5 µs against 58.4 µs.
|
|
3166
|
+
- **Alex kept the pick of two** for the diffuse (2026-10-28), with these measurements before him.
|
|
3167
|
+
- **Counters**, on Sponza:
|
|
3168
|
+
- live probes: 769 still, 1,341 orbiting; dead: 13 and 54;
|
|
3169
|
+
- the period 2, and nothing starved;
|
|
3170
|
+
- rays: 3–5% miss, 0.24–0.34% unresolved, 2% back faces;
|
|
3171
|
+
- all 103 geometries' fields coarsened; the bake starved 16 frames and skipped 90.
|
|
3172
|
+
- **Timings**, Sponza at 1280 × 720, p50:
|
|
3173
|
+
|
|
3174
|
+
| | AMD iGPU (orbit) | RTX 4090 (still) |
|
|
3175
|
+
|---|---:|---:|
|
|
3176
|
+
| want | 116 µs | 7 µs |
|
|
3177
|
+
| relocate | 20 µs | 25 µs |
|
|
3178
|
+
| trace, with the sun's shadow | 5.16 ms | 310 µs |
|
|
3179
|
+
| shade | 1.04 ms | 26 µs |
|
|
3180
|
+
| accumulate, the projection's rows and probes, publish | 0.75 ms | 33 µs |
|
|
3181
|
+
| the diffuse | 6.65 ms (7.06 before S4g's tile address) | 58 µs |
|
|
3182
|
+
| the rest, each | under 8 µs | under 8 µs |
|
|
3183
|
+
|
|
3184
|
+
**As built, S5's done-when (S5d, 2026-10-28)**. The harness is the session's scratchpad `device/s5d2.js`.
|
|
3185
|
+
- **The multi-bounce furnace** reads 0.994 once a hit wants its probes (S5a, §8.7).
|
|
3186
|
+
- **Cornell box against the path tracer.**
|
|
3187
|
+
- *The scene*: a box 4 m inside, open toward the camera, under a black sky, lit by a point light 0.4 m
|
|
3188
|
+
below the ceiling. The walls have the classic reflectances (white 0.73, red (0.63, 0.065, 0.05),
|
|
3189
|
+
green (0.14, 0.45, 0.091)), with a short and a tall white box inside.
|
|
3190
|
+
- *The images*: 480×360, tone mapping NONE at a fixed exposure, the sRGB transfer undone on the CPU.
|
|
3191
|
+
Each image's mean is taken per surface, the surface found by casting each pixel's ray on the CPU.
|
|
3192
|
+
The probes' image is the mean of 64 frames after 900 frames of settling.
|
|
3193
|
+
- *The reference is the accumulating path tracer through twins of its shader.* The engine's tracer
|
|
3194
|
+
stops at three bounces (`BOUNCE_LIMIT = 3u`). In this box that leaves its total at 84–94% of the
|
|
3195
|
+
converged one, and its indirect at 72–77%. So the reference traces 24 bounces at 512 samples a
|
|
3196
|
+
pixel, and a twin at one bounce gives the tracer's own direct light.
|
|
3197
|
+
|
|
3198
|
+
| surface | total, probes / traced | indirect, probes / traced | direct, raster / traced |
|
|
3199
|
+
|---|---:|---:|---:|
|
|
3200
|
+
| floor | 0.88 | 0.89 | 0.88 |
|
|
3201
|
+
| ceiling | 0.90 | 0.94 | 0.89 |
|
|
3202
|
+
| back | 1.03 | 1.07 | 0.98 |
|
|
3203
|
+
| red wall | 1.03 | 1.15 (r 1.41, g 0.78, b 0.73) | 0.87 |
|
|
3204
|
+
| green wall | 0.99 | 1.04 (r 0.82, g 1.09, b 0.76) | 0.92 |
|
|
3205
|
+
| the boxes | 1.14 | 1.55 | 0.64 |
|
|
3206
|
+
|
|
3207
|
+
The iGPU's probe image agrees with the 4090's within 0.5% on every surface.
|
|
3208
|
+
- **The direct light differs before the GI does.** The rasterizer's point light is 2–13% under the
|
|
3209
|
+
tracer's on the walls and 36% under on the boxes. That is the direct path's, not the probes'; it was
|
|
3210
|
+
measured here and not looked into.
|
|
3211
|
+
- **The indirect alone**, each image less its own direct, is within −11% to +7% on the floor, ceiling,
|
|
3212
|
+
back and green wall, +15% on the red wall, and +55% on the boxes.
|
|
3213
|
+
- **The colour bleed is too saturated** on the coloured walls: the red wall's indirect is redder than
|
|
3214
|
+
traced, the green wall's greener. The probes keep chroma in the constant band alone (Metro L1), so a
|
|
3215
|
+
probe beside a coloured wall lends its colour to every direction.
|
|
3216
|
+
- **None of these gaps is a follow-up** (Alex, 2026-10-28). The tracer at three bounces is the engine's
|
|
3217
|
+
reference, and a probe GI cannot match a path tracer: angular resolution, depth bias and the probes'
|
|
3218
|
+
sampling offsets bound it. S5's done-when is the comparison, not parity with it.
|
|
3219
|
+
- **Timings**, the probe clipmap's passes summed at their p50, Cornell at 480×360: **0.22 ms** on the 4090
|
|
3220
|
+
and **5.6 ms** on the iGPU. The iGPU's trace is 2.8 ms of it, the light 0.62 ms, the diffuse 0.80 ms,
|
|
3221
|
+
shade 0.34 ms and the hit wants 0.32 ms.
|
|
3222
|
+
|
|
3223
|
+
**As built, S6's done-when (S6e, 2026-10-28)**. The harness is the session's scratchpad `device/s6e.js`.
|
|
3224
|
+
- **The room** is S4g's, 8 × 4 × 8 m inside six slabs, with no light under a black sky. SSR is off, so the fused
|
|
3225
|
+
pass draws, and the probes settle for 900 frames. Captures drawn beside the fused pass, on its bindings, write
|
|
3226
|
+
the probes' specular at each pixel as the pass reads it: `probe_clipmap_sample_radiance` with the pixel's STBN
|
|
3227
|
+
noise. A twin of the sample without the parallax, which taps along the lobe, is drawn beside it every frame.
|
|
3228
|
+
- **Reflections in a closed room, as a furnace.** Every face glows L with albedo 0, so every probe texel holds L.
|
|
3229
|
+
The specular over L at roughness 0.05, 0.3, 0.6 and 1 is 0.9995 at p50 and at least 0.996 at p01, on both GPUs,
|
|
3230
|
+
with the parallax and without.
|
|
3231
|
+
- **The parallax A/B.** The walls and the ceiling each glow a colour of their own with albedo 0; the floor is black
|
|
3232
|
+
metal at roughness 0.05. Each floor pixel's specular, averaged over one cycle of the STBN (64 frames), is held
|
|
3233
|
+
against the exact mirror: the face the pixel's mirror ray meets, cast on the CPU.
|
|
3234
|
+
- *Right face* is the share of floor pixels whose nearest face colour is that face's.
|
|
3235
|
+
- *The error* is |L − L_face|₁ / |L_face|₁.
|
|
3236
|
+
- The table gives the range over two runs on each GPU; the two GPUs agree within half a percent.
|
|
3237
|
+
|
|
3238
|
+
| the mirror ray's face is | with the parallax (as built) | without |
|
|
3239
|
+
|---|---:|---:|
|
|
3240
|
+
| under 2 m away (17% of the floor) | 87.5–87.8% right, error 0.200–0.202 | 97.0%, 0.105–0.107 |
|
|
3241
|
+
| 2–4 m away (32%) | 97.2–97.3%, 0.146–0.148 | 95.5–95.9%, 0.141–0.144 |
|
|
3242
|
+
| 4–8 m away (51%) | 87.8–88.4%, 0.255–0.258 | 79.6–79.9%, 0.245–0.246 |
|
|
3243
|
+
| the whole floor | 90.7–91.0%; error p50 0.159–0.162, p90 0.384–0.390 | 87.6–87.9%; 0.140–0.143, 0.384–0.388 |
|
|
3244
|
+
|
|
3245
|
+
- The parallax puts more of the reflections from 2 m out on their face, and fewer of those under 2 m. The
|
|
3246
|
+
ceiling, 4 m and more above the floor, is the one face it does worse on as a whole: 85.7–87.0% right against
|
|
3247
|
+
100%.
|
|
3248
|
+
- *Two other forms were measured and not taken.*
|
|
3249
|
+
- Leaving the parallax out where the depth reads its √3 limit gives 86.1–86.4% right and a p90 error of
|
|
3250
|
+
0.44–0.45. Each probe decides alone, so a picked pair can tap along two inconsistent directions.
|
|
3251
|
+
- Meeting the sphere where the ray enters, for a point outside it, is the same from 2 m out and worse under
|
|
3252
|
+
it: 85.3% right, error 0.222.
|
|
3253
|
+
- The parallax costs the capture 133 against 126–128 µs on the 4090 and 4.86 against 4.59 ms on the iGPU, at
|
|
3254
|
+
960 × 540 with every pixel the room's.
|
|
3255
|
+
- **The parallax stays as built.** It places more of the floor's reflections, and the far ones most; the near ones
|
|
3256
|
+
it misplaces about four times as often as a flat tap does.
|
|
3257
|
+
|
|
2433
3258
|
## 13. Stages
|
|
2434
3259
|
|
|
2435
3260
|
Each stage lands suite-green and is measured before the next.
|
|
@@ -2555,6 +3380,10 @@ Each says what would reverse it.
|
|
|
2555
3380
|
- The cost of sparse: no probes where nothing visible needs them, so off-screen bounces see only the
|
|
2556
3381
|
probes that exist.
|
|
2557
3382
|
- *Reverse:* if the off-screen bounce measures short, Enshrouded's world-cache probes (§17).
|
|
3383
|
+
- **Reversed in part at S5a (Alex, 2026-10-28)**: the multi-bounce furnace measured the off-screen
|
|
3384
|
+
bounce short (§8.7), so hits want the probes they read as well, one step from the screen, as the one
|
|
3385
|
+
probe kind (§8.3). "If we get no indirect bounce, it is better to lose energy than to invent energy":
|
|
3386
|
+
where no probe has data yet the bounce stays zero, and no other probe stands in.
|
|
2558
3387
|
- **D32. The mover pass moves from VSM to scene level, with a skinned bit** (Alex, as recommended).
|
|
2559
3388
|
- The kernels and the list stay the same. The scene context owns them and produces the list once a
|
|
2560
3389
|
frame for any reader.
|
|
@@ -2621,7 +3450,6 @@ Each item is added only if a measurement asks for it, or, where marked, when Ale
|
|
|
2621
3450
|
emissive. The measured fix is the BRDF's hemispherical albedo captured in place of the diffuse
|
|
2622
3451
|
one.
|
|
2623
3452
|
- **Caches persisted across sessions**, an offline bake format.
|
|
2624
|
-
- World-cache probes at ray hits (Enshrouded's second kind), if the off-screen bounce measures short.
|
|
2625
3453
|
- Importance-sampled ray directions, if noise at the defaults measures visible.
|
|
2626
3454
|
- Ray and history reprojection between probes, if long-range propagation into enclosed spaces
|
|
2627
3455
|
measures short.
|