@woosh/meep-engine 3.60.6 → 3.60.8

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.
Files changed (328) hide show
  1. package/build/bundle-worker-terrain.js +1 -1
  2. package/package.json +1 -1
  3. package/src/core/binary/BitSet.d.ts.map +1 -1
  4. package/src/core/binary/BitSet.js +2 -1
  5. package/src/{format/image/avif/av1/util → core/binary/operations}/ceil_log2.d.ts +2 -2
  6. package/src/core/binary/operations/ceil_log2.d.ts.map +1 -0
  7. package/src/{format/image/avif/av1/util → core/binary/operations}/ceil_log2.js +18 -18
  8. package/src/core/binary/zstd/ZstdReverseBitReader.d.ts.map +1 -1
  9. package/src/core/binary/zstd/ZstdReverseBitReader.js +225 -223
  10. package/src/core/binary/zstd/zstd_fse.d.ts.map +1 -1
  11. package/src/core/binary/zstd/zstd_fse.js +267 -265
  12. package/src/core/binary/zstd/zstd_huffman.d.ts.map +1 -1
  13. package/src/core/binary/zstd/zstd_huffman.js +246 -244
  14. package/src/core/bvh2/binary/2/BinaryUint32BVH.d.ts.map +1 -1
  15. package/src/core/bvh2/binary/2/BinaryUint32BVH.js +8 -2
  16. package/src/core/bvh2/bvh3/ebvh_optimize_treelet.d.ts.map +1 -1
  17. package/src/core/bvh2/bvh3/ebvh_optimize_treelet.js +8 -7
  18. package/src/core/collection/PairUint32Map.d.ts.map +1 -1
  19. package/src/core/collection/PairUint32Map.js +2 -2
  20. package/src/core/collection/Uint32Map.d.ts.map +1 -1
  21. package/src/core/collection/Uint32Map.js +2 -4
  22. package/src/core/collection/heap/FibonacciHeap.d.ts.map +1 -1
  23. package/src/core/collection/heap/FibonacciHeap.js +2 -1
  24. package/src/core/geom/2d/r-tree/StaticR2Tree.d.ts.map +1 -1
  25. package/src/core/geom/2d/r-tree/StaticR2Tree.js +8 -2
  26. package/src/core/geom/3d/shape/util/mesh_compute_triangle_edge_flags.d.ts.map +1 -1
  27. package/src/core/geom/3d/shape/util/mesh_compute_triangle_edge_flags.js +2 -3
  28. package/src/engine/graphics/texture/atlas/TextureAtlas.d.ts.map +1 -1
  29. package/src/engine/graphics/texture/atlas/TextureAtlas.js +13 -14
  30. package/src/engine/graphics/texture/atlas/sprite_atlas_border.d.ts.map +1 -1
  31. package/src/engine/graphics/texture/atlas/sprite_atlas_border.js +70 -69
  32. package/src/engine/graphics/texture/reflection/convolve_equirectangular_reflection.d.ts.map +1 -1
  33. package/src/engine/graphics/texture/reflection/convolve_equirectangular_reflection.js +2 -1
  34. package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts.map +1 -1
  35. package/src/engine/graphics3/GPUParticleEmitterSystem.js +2 -1
  36. package/src/engine/graphics3/decal/GPUDecalRenderer.d.ts.map +1 -1
  37. package/src/engine/graphics3/decal/GPUDecalRenderer.js +2 -1
  38. package/src/engine/graphics3/decal/chunk_decal_record.d.ts +14 -0
  39. package/src/engine/graphics3/decal/chunk_decal_record.d.ts.map +1 -0
  40. package/src/engine/graphics3/decal/chunk_decal_record.js +44 -0
  41. package/src/engine/graphics3/decal/shader_cluster_assign_decals.d.ts.map +1 -1
  42. package/src/engine/graphics3/decal/shader_cluster_assign_decals.js +2 -15
  43. package/src/engine/graphics3/decal/shader_cull_decals_frustum.d.ts.map +1 -1
  44. package/src/engine/graphics3/decal/shader_cull_decals_frustum.js +3 -16
  45. package/src/engine/graphics3/decal/shader_decal_composite.d.ts.map +1 -1
  46. package/src/engine/graphics3/decal/shader_decal_composite.js +3 -23
  47. package/src/engine/graphics3/terrain/TERRAIN_MATERIAL_TEMPLATE.d.ts.map +1 -1
  48. package/src/engine/graphics3/terrain/TERRAIN_MATERIAL_TEMPLATE.js +2 -1
  49. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  50. package/src/engine/physics/queries/shape_cast.js +2 -2
  51. package/src/format/image/avif/av1/decode/decode_palette.js +442 -442
  52. package/src/format/image/avif/av1/entropy/SymbolWriter.d.ts.map +1 -1
  53. package/src/format/image/avif/av1/entropy/SymbolWriter.js +2 -1
  54. package/src/generation/foliage/material/foliage_tile.d.ts.map +1 -1
  55. package/src/generation/foliage/material/foliage_tile.js +2 -1
  56. package/src/generation/markers/GridActionRuleSet.d.ts.map +1 -1
  57. package/src/generation/markers/GridActionRuleSet.js +12 -3
  58. package/src/shade/device/mock/SoftwareGPUDevice.d.ts.map +1 -1
  59. package/src/shade/device/mock/SoftwareGPUDevice.js +6 -13
  60. package/src/shade/playground/caldera/main.js +3 -2
  61. package/src/shade/playground/probe_clipmap/README.md +2 -1
  62. package/src/shade/playground/probe_clipmap/index.html +7 -2
  63. package/src/shade/playground/probe_clipmap/main.d.ts.map +1 -1
  64. package/src/shade/playground/probe_clipmap/main.js +17 -7
  65. package/src/shade/playground/sh_exponential/sh_exp_playground_shaders.d.ts.map +1 -1
  66. package/src/shade/playground/sh_exponential/sh_exp_playground_shaders.js +7 -13
  67. package/src/shade/playground/vgeo_scene/main.js +2 -1
  68. package/src/shade/playground/vsm/VSMDebugOverlay.d.ts.map +1 -1
  69. package/src/shade/playground/vsm/VSMDebugOverlay.js +3 -1
  70. package/src/shade/renderer/fluid/GPUFluidCascade.d.ts +3 -3
  71. package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts +1 -1
  72. package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts +6 -6
  73. package/src/shade/renderer/fluid/pressure/GPUFluidPressureSolve.d.ts +1 -1
  74. package/src/shade/renderer/fluid/pressure/shader_fluid_mg_coarsest.d.ts.map +1 -1
  75. package/src/shade/renderer/fluid/pressure/shader_fluid_mg_coarsest.js +5 -4
  76. package/src/shade/renderer/fluid/pressure/shader_fluid_mg_residual_restrict.d.ts.map +1 -1
  77. package/src/shade/renderer/fluid/pressure/shader_fluid_mg_residual_restrict.js +168 -166
  78. package/src/shade/renderer/fluid/pressure/shader_fluid_mg_smooth.d.ts.map +1 -1
  79. package/src/shade/renderer/fluid/pressure/shader_fluid_mg_smooth.js +221 -219
  80. package/src/shade/renderer/fluid/query/GPUFluidQueries.d.ts +1 -1
  81. package/src/shade/renderer/fow/shader_fog_of_war.d.ts.map +1 -1
  82. package/src/shade/renderer/fow/shader_fog_of_war.js +8 -19
  83. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts.map +1 -1
  84. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +6 -6
  85. package/src/shade/renderer/geometry/sdf/MeshSDFLayout.d.ts.map +1 -1
  86. package/src/shade/renderer/geometry/sdf/MeshSDFLayout.js +2 -1
  87. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts.map +1 -1
  88. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.js +2 -1
  89. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.d.ts.map +1 -1
  90. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.js +2 -1
  91. package/src/shade/renderer/global_illumination/brick4/gpu/encoding/v1/chunk_brick4_sh3_color_decode_from_storage.d.ts.map +1 -1
  92. package/src/shade/renderer/global_illumination/brick4/gpu/encoding/v1/chunk_brick4_sh3_color_decode_from_storage.js +10 -31
  93. package/src/shade/renderer/global_illumination/brick4/gpu/encoding/v1/chunk_brick4_sh3_color_encode_to_storage.d.ts.map +1 -1
  94. package/src/shade/renderer/global_illumination/brick4/gpu/encoding/v1/chunk_brick4_sh3_color_encode_to_storage.js +9 -15
  95. package/src/shade/renderer/global_illumination/clipmap/shader_debug_clipmap.d.ts +2 -1
  96. package/src/shade/renderer/global_illumination/clipmap/shader_debug_clipmap.d.ts.map +1 -1
  97. package/src/shade/renderer/global_illumination/clipmap/shader_debug_clipmap.js +6 -5
  98. package/src/shade/renderer/global_illumination/probe/octahedral/convolve/graph_reduce_atlas_to_sh.d.ts.map +1 -1
  99. package/src/shade/renderer/global_illumination/probe/octahedral/convolve/graph_reduce_atlas_to_sh.js +2 -1
  100. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts +15 -3
  101. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts.map +1 -1
  102. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.js +168 -18
  103. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.d.ts +98 -57
  104. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.d.ts.map +1 -1
  105. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.js +168 -77
  106. package/src/shade/renderer/global_illumination/probe_clipmap/brick/GPUBrickClipmap.d.ts +4 -2
  107. package/src/shade/renderer/global_illumination/probe_clipmap/brick/GPUBrickClipmap.d.ts.map +1 -1
  108. package/src/shade/renderer/global_illumination/probe_clipmap/brick/GPUBrickClipmap.js +27 -12
  109. package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_clipmap.d.ts.map +1 -1
  110. package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_clipmap.js +12 -7
  111. package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_clipmap_trace.d.ts.map +1 -1
  112. package/src/shade/renderer/global_illumination/probe_clipmap/brick/chunk_brick_clipmap_trace.js +8 -2
  113. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_walk.d.ts.map +1 -1
  114. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_walk.js +5 -2
  115. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/shader_mesh_brick_batch.js +1 -1
  116. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/shader_mesh_brick_gather.d.ts.map +1 -1
  117. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/shader_mesh_brick_gather.js +5 -3
  118. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/shader_mesh_brick_resolve.js +1 -1
  119. package/src/shade/renderer/global_illumination/probe_clipmap/brick/shader_brick_clipmap_free.d.ts.map +1 -1
  120. package/src/shade/renderer/global_illumination/probe_clipmap/brick/shader_brick_clipmap_free.js +1 -1
  121. package/src/shade/renderer/global_illumination/probe_clipmap/brick/shader_brick_clipmap_movers_command.d.ts.map +1 -1
  122. package/src/shade/renderer/global_illumination/probe_clipmap/brick/shader_brick_clipmap_movers_command.js +4 -2
  123. package/src/shade/renderer/global_illumination/probe_clipmap/brick/shader_brick_clipmap_scroll.d.ts.map +1 -1
  124. package/src/shade/renderer/global_illumination/probe_clipmap/brick/shader_brick_clipmap_scroll.js +1 -1
  125. package/src/shade/renderer/global_illumination/probe_clipmap/brick/shader_brick_clipmap_store.d.ts.map +1 -1
  126. package/src/shade/renderer/global_illumination/probe_clipmap/brick/shader_brick_clipmap_store.js +1 -1
  127. package/src/shade/renderer/global_illumination/probe_clipmap/brick/shader_brick_clipmap_take.d.ts.map +1 -1
  128. package/src/shade/renderer/global_illumination/probe_clipmap/brick/shader_brick_clipmap_take.js +3 -1
  129. package/src/shade/renderer/global_illumination/probe_clipmap/debug/graph_probe_clipmap_debug_mesh_cards.js +2 -2
  130. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts +114 -41
  131. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts.map +1 -1
  132. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.js +424 -207
  133. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts +34 -25
  134. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts.map +1 -1
  135. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.js +41 -31
  136. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP_RESAMPLING.d.ts +7 -5
  137. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP_RESAMPLING.d.ts.map +1 -1
  138. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP_RESAMPLING.js +7 -5
  139. package/src/shade/renderer/global_illumination/probe_clipmap/probe/ProbeClipmapHitWants.d.ts +47 -0
  140. package/src/shade/renderer/global_illumination/probe_clipmap/probe/ProbeClipmapHitWants.d.ts.map +1 -0
  141. package/src/shade/renderer/global_illumination/probe_clipmap/probe/ProbeClipmapHitWants.js +84 -0
  142. package/src/shade/renderer/global_illumination/probe_clipmap/probe/ProbeClipmapScreenWants.d.ts +48 -0
  143. package/src/shade/renderer/global_illumination/probe_clipmap/probe/ProbeClipmapScreenWants.d.ts.map +1 -0
  144. package/src/shade/renderer/global_illumination/probe_clipmap/probe/ProbeClipmapScreenWants.js +101 -0
  145. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts +58 -4
  146. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts.map +1 -1
  147. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.js +127 -30
  148. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_cage.d.ts +18 -7
  149. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_cage.d.ts.map +1 -1
  150. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_cage.js +39 -13
  151. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_pick.d.ts +29 -8
  152. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_pick.d.ts.map +1 -1
  153. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_pick.js +112 -56
  154. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_resample.d.ts +19 -0
  155. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_resample.d.ts.map +1 -0
  156. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_resample.js +35 -0
  157. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.d.ts.map +1 -1
  158. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.js +9 -3
  159. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_want.d.ts +3 -3
  160. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_want.js +5 -5
  161. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts +8 -12
  162. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts.map +1 -1
  163. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.js +23 -56
  164. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_admit.d.ts +0 -3
  165. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_admit.d.ts.map +1 -1
  166. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_admit.js +11 -15
  167. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_bounce.d.ts +26 -0
  168. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_bounce.d.ts.map +1 -0
  169. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_bounce.js +173 -0
  170. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_candidate.d.ts +32 -0
  171. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_candidate.d.ts.map +1 -0
  172. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_candidate.js +166 -0
  173. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_due.d.ts +3 -4
  174. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_due.d.ts.map +1 -1
  175. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_due.js +3 -4
  176. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_guide.d.ts.map +1 -1
  177. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_guide.js +3 -1
  178. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_light.d.ts +6 -12
  179. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_light.d.ts.map +1 -1
  180. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_light.js +15 -65
  181. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_order.d.ts.map +1 -1
  182. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_order.js +7 -2
  183. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_project.d.ts +4 -4
  184. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_project.d.ts.map +1 -1
  185. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_project.js +24 -17
  186. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_publish.d.ts +7 -5
  187. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_publish.d.ts.map +1 -1
  188. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_publish.js +18 -10
  189. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_ray_layout.d.ts +2 -2
  190. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_ray_layout.d.ts.map +1 -1
  191. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_ray_layout.js +9 -5
  192. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_relocate.d.ts +3 -0
  193. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_relocate.d.ts.map +1 -1
  194. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_relocate.js +9 -6
  195. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_resample.d.ts +1 -1
  196. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_resample.d.ts.map +1 -1
  197. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_resample.js +16 -16
  198. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_resample_rays.d.ts +36 -0
  199. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_resample_rays.d.ts.map +1 -0
  200. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_resample_rays.js +233 -0
  201. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_schedule.d.ts.map +1 -1
  202. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_schedule.js +7 -5
  203. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.d.ts.map +1 -1
  204. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.js +5 -3
  205. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_trace.d.ts.map +1 -1
  206. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_trace.js +10 -4
  207. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_trace_command.d.ts +2 -1
  208. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_trace_command.d.ts.map +1 -1
  209. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_trace_command.js +12 -6
  210. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want.d.ts +1 -2
  211. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want.d.ts.map +1 -1
  212. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want.js +5 -6
  213. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_hits.d.ts +14 -18
  214. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_hits.d.ts.map +1 -1
  215. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_want_hits.js +25 -35
  216. package/src/shade/renderer/gpu_primitive/WEBGPU_GUARANTEED_MAX_UNIFORM_BUFFER_BINDING_SIZE.d.ts +13 -0
  217. package/src/shade/renderer/gpu_primitive/WEBGPU_GUARANTEED_MAX_UNIFORM_BUFFER_BINDING_SIZE.d.ts.map +1 -0
  218. package/src/shade/renderer/gpu_primitive/WEBGPU_GUARANTEED_MAX_UNIFORM_BUFFER_BINDING_SIZE.js +12 -0
  219. package/src/shade/renderer/gpu_primitive/hash_table/cpu_build_hash_table.d.ts +27 -2
  220. package/src/shade/renderer/gpu_primitive/hash_table/cpu_build_hash_table.d.ts.map +1 -1
  221. package/src/shade/renderer/gpu_primitive/hash_table/cpu_build_hash_table.js +62 -6
  222. package/src/shade/renderer/gpu_primitive/indirect/IndirectComputeCommandBuildPass.d.ts.map +1 -1
  223. package/src/shade/renderer/gpu_primitive/indirect/IndirectComputeCommandBuildPass.js +10 -19
  224. package/src/shade/renderer/gpu_primitive/indirect/chunk_folded_dispatch.d.ts.map +1 -1
  225. package/src/shade/renderer/gpu_primitive/indirect/chunk_folded_dispatch.js +6 -3
  226. package/src/shade/renderer/hiz/hzb_pick_alignment.d.ts.map +1 -1
  227. package/src/shade/renderer/hiz/hzb_pick_alignment.js +149 -148
  228. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_environment_cells.d.ts.map +1 -1
  229. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_environment_cells.js +4 -2
  230. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_log_floor.d.ts.map +1 -1
  231. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_log_floor.js +6 -5
  232. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_project.d.ts.map +1 -1
  233. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_project.js +3 -1
  234. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_reference_reflectance.d.ts.map +1 -1
  235. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_reference_reflectance.js +3 -1
  236. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_solve.d.ts.map +1 -1
  237. package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_solve.js +3 -1
  238. package/src/shade/renderer/light/world_cluster/WORLD_LIGHT_CLUSTER.d.ts +0 -5
  239. package/src/shade/renderer/light/world_cluster/WORLD_LIGHT_CLUSTER.d.ts.map +1 -1
  240. package/src/shade/renderer/light/world_cluster/WORLD_LIGHT_CLUSTER.js +0 -7
  241. package/src/shade/renderer/light/world_cluster/graph_build_world_light_clusters.d.ts +15 -2
  242. package/src/shade/renderer/light/world_cluster/graph_build_world_light_clusters.d.ts.map +1 -1
  243. package/src/shade/renderer/light/world_cluster/graph_build_world_light_clusters.js +51 -16
  244. package/src/shade/renderer/light/world_cluster/shader_world_cluster_assign_lights.d.ts.map +1 -1
  245. package/src/shade/renderer/light/world_cluster/shader_world_cluster_assign_lights.js +3 -2
  246. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  247. package/src/shade/renderer/postprocess/dof/raymarch/chunk_dof_lds_coc_cache.d.ts +17 -0
  248. package/src/shade/renderer/postprocess/dof/raymarch/chunk_dof_lds_coc_cache.d.ts.map +1 -0
  249. package/src/shade/renderer/postprocess/dof/raymarch/chunk_dof_lds_coc_cache.js +81 -0
  250. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_raymarch_compute.d.ts.map +1 -1
  251. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_raymarch_compute.js +5 -48
  252. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_slice_compute.d.ts.map +1 -1
  253. package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_slice_compute.js +4 -44
  254. package/src/shade/renderer/postprocess/gtao/chunk_integrate_lighting_sample.d.ts.map +1 -1
  255. package/src/shade/renderer/postprocess/gtao/chunk_integrate_lighting_sample.js +4 -1
  256. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.d.ts.map +1 -1
  257. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.js +8 -7
  258. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  259. package/src/shade/renderer/postprocess/nss/chunk_nss_luma_derivative.d.ts.map +1 -1
  260. package/src/shade/renderer/postprocess/nss/chunk_nss_luma_derivative.js +5 -7
  261. package/src/shade/renderer/postprocess/nss/chunk_nss_tonemap.d.ts +17 -0
  262. package/src/shade/renderer/postprocess/nss/chunk_nss_tonemap.d.ts.map +1 -0
  263. package/src/shade/renderer/postprocess/nss/chunk_nss_tonemap.js +56 -0
  264. package/src/shade/renderer/postprocess/nss/shader_nss_postprocess.d.ts.map +1 -1
  265. package/src/shade/renderer/postprocess/nss/shader_nss_postprocess.js +2 -22
  266. package/src/shade/renderer/postprocess/nss/shader_nss_preprocess.d.ts.map +1 -1
  267. package/src/shade/renderer/postprocess/nss/shader_nss_preprocess.js +2 -13
  268. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.d.ts.map +1 -1
  269. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.js +10 -13
  270. package/src/shade/renderer/rasterize/expand/bucket/RASTERIZATION_BUCKET_LIMIT.d.ts.map +1 -1
  271. package/src/shade/renderer/rasterize/expand/bucket/RASTERIZATION_BUCKET_LIMIT.js +39 -38
  272. package/src/shade/renderer/rasterize/expand/mesh/shader_prefix_sum_to_command.d.ts.map +1 -1
  273. package/src/shade/renderer/rasterize/expand/mesh/shader_prefix_sum_to_command.js +7 -12
  274. package/src/shade/renderer/shader/chunk/clipmap/CLIPMAP_LEVEL_STRUCT.d.ts +8 -6
  275. package/src/shade/renderer/shader/chunk/clipmap/CLIPMAP_LEVEL_STRUCT.d.ts.map +1 -1
  276. package/src/shade/renderer/shader/chunk/clipmap/CLIPMAP_LEVEL_STRUCT.js +10 -7
  277. package/src/shade/renderer/shader/chunk/clipmap/chunk_clipmap_key.d.ts +16 -0
  278. package/src/shade/renderer/shader/chunk/clipmap/chunk_clipmap_key.d.ts.map +1 -0
  279. package/src/shade/renderer/shader/chunk/clipmap/chunk_clipmap_key.js +35 -0
  280. package/src/shade/renderer/shader/chunk/clipmap/chunk_clipmap_select_finest.d.ts +5 -2
  281. package/src/shade/renderer/shader/chunk/clipmap/chunk_clipmap_select_finest.d.ts.map +1 -1
  282. package/src/shade/renderer/shader/chunk/clipmap/chunk_clipmap_select_finest.js +27 -18
  283. package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh2_color_get_irradiance_at.d.ts.map +1 -1
  284. package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh2_color_get_irradiance_at.js +5 -15
  285. package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh3_convolve_with_cosine_kernel.d.ts.map +1 -1
  286. package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh3_convolve_with_cosine_kernel.js +5 -19
  287. package/src/shade/renderer/shader/chunk/spherical_harmonics/dering/chunk_sh3_windowing.d.ts.map +1 -1
  288. package/src/shade/renderer/shader/chunk/spherical_harmonics/dering/chunk_sh3_windowing.js +3 -22
  289. package/src/shade/renderer/shadow/map/ShadowmapDrawSelector.d.ts.map +1 -1
  290. package/src/shade/renderer/shadow/map/ShadowmapDrawSelector.js +2 -1
  291. package/src/shade/renderer/volumetrics/chunk_volumetrics_sun_shadow_group_list.d.ts.map +1 -1
  292. package/src/shade/renderer/volumetrics/chunk_volumetrics_sun_shadow_group_list.js +154 -152
  293. package/src/shade/renderer/volumetrics/irradiance_clipmap/GPUIrradianceClipmap.d.ts.map +1 -1
  294. package/src/shade/renderer/volumetrics/irradiance_clipmap/GPUIrradianceClipmap.js +2 -1
  295. package/src/shade/renderer/volumetrics/irradiance_clipmap/IRRADIANCE_CLIPMAP.d.ts +3 -1
  296. package/src/shade/renderer/volumetrics/irradiance_clipmap/IRRADIANCE_CLIPMAP.d.ts.map +1 -1
  297. package/src/shade/renderer/volumetrics/irradiance_clipmap/IRRADIANCE_CLIPMAP.js +4 -3
  298. package/src/shade/renderer/volumetrics/irradiance_clipmap/IRRADIANCE_CLIPMAP_STRUCT.d.ts +7 -1
  299. package/src/shade/renderer/volumetrics/irradiance_clipmap/IRRADIANCE_CLIPMAP_STRUCT.d.ts.map +1 -1
  300. package/src/shade/renderer/volumetrics/irradiance_clipmap/IRRADIANCE_CLIPMAP_STRUCT.js +54 -47
  301. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_sample.d.ts.map +1 -1
  302. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_sample.js +371 -370
  303. package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
  304. package/src/shade/renderer/water/GPUWaterRenderer.js +2 -1
  305. package/src/shade/renderer/water/query/GPUWaterQueries.d.ts +1 -1
  306. package/src/shade/renderer/water/surface/lod/shader_water_lod.d.ts.map +1 -1
  307. package/src/shade/renderer/water/surface/lod/shader_water_lod.js +12 -14
  308. package/src/shade/renderer/water/surface/particles/GPUWaterParticles.d.ts.map +1 -1
  309. package/src/shade/renderer/water/surface/particles/GPUWaterParticles.js +2 -9
  310. package/src/shade/renderer/water/surface/particles/chunk_water_particle.d.ts.map +1 -1
  311. package/src/shade/renderer/water/surface/particles/chunk_water_particle.js +2 -1
  312. package/src/shade/renderer/wind/WIND_FIELD.d.ts +3 -1
  313. package/src/shade/renderer/wind/WIND_FIELD.d.ts.map +1 -1
  314. package/src/shade/renderer/wind/WIND_FIELD.js +4 -3
  315. package/src/shade/renderer/wind/WIND_FIELD_STRUCT.d.ts +8 -2
  316. package/src/shade/renderer/wind/WIND_FIELD_STRUCT.d.ts.map +1 -1
  317. package/src/shade/renderer/wind/WIND_FIELD_STRUCT.js +11 -4
  318. package/src/shade/renderer/wind/chunk_wind_field_sample_cascades.d.ts +2 -2
  319. package/src/shade/renderer/wind/chunk_wind_field_sample_cascades.d.ts.map +1 -1
  320. package/src/shade/renderer/wind/chunk_wind_field_sample_cascades.js +13 -4
  321. package/src/shade/util/texture_mip_extent.d.ts.map +1 -1
  322. package/src/shade/util/texture_mip_extent.js +88 -87
  323. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
  324. package/src/shade/wgsl/emulator/WGSLLanguageCore.js +9 -7
  325. package/src/format/image/avif/av1/util/ceil_log2.d.ts.map +0 -1
  326. package/src/shade/renderer/shader/chunk/color/chunk_hash_squirrel3.d.ts +0 -1
  327. package/src/shade/renderer/shader/chunk/color/chunk_hash_squirrel3.d.ts.map +0 -1
  328. package/src/shade/renderer/shader/chunk/color/chunk_hash_squirrel3.js +0 -0
@@ -1,370 +1,371 @@
1
- import { FAR_FIELD_IRRADIANCE_SH_RESOURCE } from "../../light/environment/FAR_FIELD_IRRADIANCE_SH_RESOURCE.js";
2
- import { chunk_clipmap_select_finest } from "../../shader/chunk/clipmap/chunk_clipmap_select_finest.js";
3
- import { chunk_hash_lowbias32 } from "../../shader/chunk/random/chunk_hash_lowbias32.js";
4
- import { chunk_hash_to_float01 } from "../../shader/chunk/random/chunk_hash_to_float01.js";
5
- import {
6
- METRO_L1_IRRADIANCE_STRUCT
7
- } from "../../shader/chunk/spherical_harmonics/l1i/chunk_convert_to_irradiance_sh.js";
8
- import { CodeChunk } from "../../shader/compiler/CodeChunk.js";
9
- import { ShaderResourceDescriptor } from "../../shader/resource/ShaderResourceDescriptor.js";
10
- import {
11
- IRRADIANCE_CLIPMAP_DEFAULT_BLEND_SKIRT,
12
- IRRADIANCE_CLIPMAP_SAMPLER_DESCRIPTOR,
13
- } from "./IRRADIANCE_CLIPMAP.js";
14
- import { IRRADIANCE_CLIPMAP_STRUCT } from "./IRRADIANCE_CLIPMAP_STRUCT.js";
15
-
16
- /**
17
- * Uniform buffer binding descriptor for `irradiance_clipmap` ({@link IRRADIANCE_CLIPMAP_STRUCT}).
18
- * @type {ShaderResourceDescriptor}
19
- */
20
- export const IRRADIANCE_CLIPMAP_UNIFORM_RESOURCE = ShaderResourceDescriptor.uniform(
21
- "irradiance_clipmap",
22
- IRRADIANCE_CLIPMAP_STRUCT
23
- );
24
-
25
- /**
26
- * 3D atlas texture binding descriptor for the luma SH2 (`Y`) texture of the irradiance clipmap.
27
- * @type {ShaderResourceDescriptor}
28
- */
29
- export const IRRADIANCE_CLIPMAP_ATLAS_Y_RESOURCE = ShaderResourceDescriptor.texture(
30
- "irradiance_clipmap_atlas_y",
31
- "float",
32
- "3d"
33
- );
34
-
35
- /**
36
- * 3D atlas texture binding descriptor for the chroma (`CoCg`) texture of the irradiance clipmap.
37
- * @type {ShaderResourceDescriptor}
38
- */
39
- export const IRRADIANCE_CLIPMAP_ATLAS_COCG_RESOURCE = ShaderResourceDescriptor.texture(
40
- "irradiance_clipmap_atlas_cocg",
41
- "float",
42
- "3d"
43
- );
44
-
45
- /**
46
- * Linear clamped sampler binding descriptor for the irradiance clipmap atlas textures.
47
- * @type {ShaderResourceDescriptor}
48
- */
49
- export const IRRADIANCE_CLIPMAP_SAMPLER_RESOURCE = ShaderResourceDescriptor.sampler(
50
- "irradiance_clipmap_sampler",
51
- "filtering",
52
- IRRADIANCE_CLIPMAP_SAMPLER_DESCRIPTOR
53
- );
54
-
55
- /**
56
- * Declares the world-space irradiance clipmap's bindings on a shader resource group.
57
- *
58
- * @param {import("../../shader/resource/ShaderResourceGroupDescriptor.js").ShaderResourceGroupDescriptor} group
59
- * @param {Object} [options]
60
- * @param {boolean} [options.include_far_field=false] whether to also declare `far_field_irradiance_sh`
61
- * @returns {import("../../shader/resource/ShaderResourceGroupDescriptor.js").ShaderResourceGroupDescriptor}
62
- */
63
- export function irradiance_clipmap_add_resources(group, { include_far_field = false } = {}) {
64
- group
65
- .add(IRRADIANCE_CLIPMAP_UNIFORM_RESOURCE)
66
- .add(IRRADIANCE_CLIPMAP_ATLAS_Y_RESOURCE)
67
- .add(IRRADIANCE_CLIPMAP_ATLAS_COCG_RESOURCE)
68
- .add(IRRADIANCE_CLIPMAP_SAMPLER_RESOURCE);
69
-
70
- if (include_far_field) {
71
- group.add(FAR_FIELD_IRRADIANCE_SH_RESOURCE);
72
- }
73
-
74
- return group;
75
- }
76
-
77
- /**
78
- * World-space irradiance clipmap sampling:
79
- *
80
- * - `irradiance_clipmap_cascade_read_stochastic(c, position_ws, &u) -> MetroL1Irradiance`
81
- * Evaluates a 2-tap antithetic Quadratic B-Spline (`B_2 = B_1 * B_0`) read of cascade `c`'s tile
82
- * in `irradiance_clipmap_atlas_y` and `irradiance_clipmap_atlas_cocg`.
83
- * - `irradiance_clipmap_cascade_read(c, position_ws) -> MetroL1Irradiance`
84
- * Deterministic convenience wrapper around `irradiance_clipmap_cascade_read_stochastic`.
85
- * - `irradiance_clipmap_sample_stochastic(position_ws, random_u) -> MetroL1Irradiance`
86
- * Selects the finest cascade covering `position_ws` via continuous radial distance from `anchor`,
87
- * computes a `smoothstep(0.0, 1.0, selection.edge_weight)` transition weight across the outer band
88
- * (`blend_skirt` cells), and stochastically reads `selection.level` or `selection.level + 1`.
89
- *
90
- * ## Why Hardware Trilinear (`B_1`) Shows Visible Grid Lines (`┐`)
91
- *
92
- * In coarse clipmap cascades (`h = 0.5 m .. 1.0 m`), a single voxel spans 50-100 screen pixels across
93
- * translucent smoke billboards, and near a point light the `1 / r^2` falloff is strongly convex
94
- * (`d^2 I / dy^2 >> 0`, e.g. slope dropping by >4x from one voxel to the next):
95
- * 1. **Dirac-Delta Curvature / Mach Bands:** Hardware trilinear interpolation (`B_1`) is piecewise
96
- * linear (`C^0`), so inside each 60-pixel voxel cell its second derivative is identically zero
97
- * (`d^2 I / dy^2 = 0`), and *all* of the curvature is concentrated into a 1-pixel Dirac delta
98
- * spike along the voxel-centre plane `y = k + 0.5`:
99
- * `d^2 I_trilinear / dy^2 = sum_k (I_{k+1} - 2*I_k + I_{k-1}) * delta(y - (k + 0.5))`.
100
- * Because human vision (lateral inhibition / Mach band illusion) and contrast stretching respond
101
- * to the Laplacian `nabla^2 I`, concentrating the entire slope transition onto a single plane
102
- * produces a sharp visible bright/dark ridge line at every voxel centre.
103
- * 2. **Oblique Billboard Saddle Cross-Terms:** Camera-facing particle billboards slice obliquely
104
- * through the world-aligned `(x, y, z)` grid. Within each cell, trilinear interpolation is a
105
- * hyperbolic paraboloid (`a + b*x + c*y + d*x*y + ...`) that curves along diagonals but collapses
106
- * to straight lines on the axis-aligned planes `x = x_k, y = y_k, z = z_k`, creating visible
107
- * grid-aligned creases where adjacent saddle patches meet.
108
- *
109
- * ## Why `floor(coord)`-Partitioned Sigg & Hadwiger Stochastic WRS Fails
110
- *
111
- * Standard 8-tap Sigg & Hadwiger tricubic B-spline (`B_3`) decomposes the 4-texel 1D cubic weights
112
- * `(w_0, w_1, w_2, w_3)` around `tc = floor(coord)` (`f = coord - tc in [0, 1)`) into two hardware
113
- * linear taps: tap 0 pairing `(w_0, w_1)` with weight `g_0 = w_0 + w_1`, and tap 1 pairing `(w_2, w_3)`
114
- * with weight `g_1 = w_2 + w_3`. In deterministic 8-tap evaluation both taps are summed, so the
115
- * `floor(coord)` partition is invisible. When sub-sampled stochastically via WRS, however:
116
- * 1. **Tap-Pairing Flip Across `f = 1^- -> 0^+`:** At a voxel-centre plane, the 3 non-zero B-spline
117
- * weights are `(1/6, 4/6, 1/6)`. Just below the plane (`f = 0.999`), Sigg & Hadwiger pairs the
118
- * central `4/6` texel with the upper texel (`g_1 = 5/6` at `+0.2` voxels, `g_0 = 1/6` at `-1.0`
119
- * voxel). Just above the plane (`f = 0.001`), `tc = floor(coord)` increments and switches the
120
- * central `4/6` texel into the lower pair (`g_0 = 5/6` at `-0.2` voxels, `g_1 = 1/6` at `+1.0`
121
- * voxel). The 83.3% majority tap therefore jumps backwards by `-0.4` voxels and the variance
122
- * jumps by 2.8x across every integer coordinate plane.
123
- * 2. **Double-Weighting Bias (`w_i^2`) in `weight1 / (weight0 + weight1)`:** Because WRS already
124
- * selects tap `i` with probability `w_i`, blending two WRS taps by `weight1 / (weight0 + weight1)`
125
- * weights each tap by `w_i` a second time (`w_i^2`), creating a >60% step discontinuity in the
126
- * expected value across `tc = floor(coord)`.
127
- *
128
- * ## Why 2-Tap Antithetic Quadratic B-Spline (`B_2 = B_1 * B_0`) Is the Sweet Spot
129
- *
130
- * Hardware `textureSampleLevel` already evaluates the 3D linear tent filter `B_1`. Convolving `B_1`
131
- * with a uniform unit box filter `B_0` on `[-0.5, +0.5]^3` via two antithetic taps at
132
- * `center + delta` and `center - delta` (`delta in [-0.5, +0.5]^3`, mixed `0.5 / 0.5`) yields the
133
- * exact `C^1` Quadratic B-Spline `B_2 = B_1 * B_0`:
134
- * - **Constant Second Derivative Across Each Cell:** Because the `[-0.5, +0.5]` smoothing window is
135
- * exactly 1 voxel wide, adjacent voxel-centre smoothing windows meet edge-to-edge and spread the
136
- * slope change `I_{k+1} - 2*I_k + I_{k-1}` uniformly across all 60 pixels of the cell
137
- * (`d^2 B_2 / dx^2 = const`), completely eliminating the Mach-band Dirac spike.
138
- * - **Zero `floor(coord)` Discontinuity:** Neither the tap offsets nor the weights depend on
139
- * `floor(coord)`, so both the mean and the variance are 100% continuous across all voxel planes.
140
- * - **First-Order Gradient Cancellation & Compact Support:** Antithetic pairing (`+delta`, `-delta`)
141
- * cancels all linear gradient noise `+/- dot(nabla I, delta)` on every pixel, while keeping taps
142
- * within `+/- 0.5` voxels of `center` (half the radius of `B_3 = B_1 * B_1`), avoiding `+/- 1`
143
- * voxel fire-bleed grain and staying strictly inside the 1-texel cascade border.
144
- *
145
- * @type {CodeChunk}
146
- */
147
- export const chunk_irradiance_clipmap_sample = CodeChunk.from(
148
- //language=WGSL
149
- `
150
- const IRRADIANCE_CLIPMAP_DEFAULT_BLEND_SKIRT: f32 = ${IRRADIANCE_CLIPMAP_DEFAULT_BLEND_SKIRT}.0;
151
-
152
- fn irradiance_clipmap_select_finest(
153
- position_ws: vec3<f32>,
154
- band_width: f32,
155
- ) -> ClipmapSelection {
156
- let count = irradiance_clipmap.cascade_count;
157
- let anchor_ws = irradiance_clipmap.anchor;
158
-
159
- for (var i = 0u; i < 16u; i++) {
160
- if (i >= count) {
161
- break;
162
- }
163
-
164
- let level = irradiance_clipmap.levels[i];
165
- let position_cells = position_ws / level.cell_size;
166
- let half_size = 0.5 * vec3<f32>(level.size);
167
- let level_center = vec3<f32>(level.corner) + half_size;
168
- let anchor_cells = anchor_ws / level.cell_size;
169
-
170
- // Use the continuous camera anchor when within the 1-voxel rounding bound of level.corner,
171
- // so cascade selection and edge_weight vary C^1-smoothly with camera motion without
172
- // stair-stepping at integer corner transitions.
173
- let center_cells = select(
174
- level_center,
175
- anchor_cells,
176
- all(abs(anchor_cells - level_center) <= vec3<f32>(0.501))
177
- );
178
-
179
- // Subtract 0.5 cells from half_size so that even when anchor_cells is offset by up to
180
- // +/-0.5 cells from level_center, the selection sphere stays strictly inside [0, size]^3
181
- // and the +/-0.5 voxel B_2 jitter taps stay strictly inside the 1-texel border
182
- // [-0.5, size + 0.5]^3 without hitting the border clamp.
183
- let radius = max(0.5, min(half_size.x, min(half_size.y, half_size.z)) - 0.5);
184
- let edge = radius - length(position_cells - center_cells);
185
-
186
- if (edge <= 0.0) {
187
- continue;
188
- }
189
-
190
- return ClipmapSelection(i, saturate(1.0 - edge / band_width));
191
- }
192
-
193
- return ClipmapSelection(CLIPMAP_LEVEL_NONE, 0.0);
194
- }
195
-
196
- fn irradiance_clipmap_box_jitter(u: ptr<function, f32>, salt: u32) -> vec3<f32> {
197
- let h0 = hash_lowbias32(bitcast<u32>(*u) ^ salt);
198
- let h1 = hash_lowbias32(h0 ^ 0x9e3779b9u);
199
- let h2 = hash_lowbias32(h1 ^ 0x85ebca6bu);
200
- *u = hash_to_float01(hash_lowbias32(h2 ^ 0xc2b2ae35u));
201
-
202
- // Uniform box jitter in [-0.5, +0.5]^3 paired antithetically (center + delta, center - delta):
203
- // convolving hardware trilinear B_1 with B_0 on [-0.5, +0.5]^3 yields the exact C^1 Quadratic
204
- // B-Spline B_2 = B_1 * B_0 with constant second derivative across each cell, zero floor/integer
205
- // discontinuity, and support staying strictly within the 1-texel cascade border.
206
- return vec3<f32>(
207
- hash_to_float01(h0) - 0.5,
208
- hash_to_float01(h1) - 0.5,
209
- hash_to_float01(h2) - 0.5,
210
- );
211
- }
212
-
213
- fn irradiance_clipmap_cascade_read_stochastic(
214
- c: u32,
215
- position_ws: vec3<f32>,
216
- u: ptr<function, f32>,
217
- ) -> ${METRO_L1_IRRADIANCE_STRUCT.wgsl_ref} {
218
- let level = irradiance_clipmap.levels[c];
219
- let cascade = irradiance_clipmap.cascades[c];
220
- let inv_atlas = irradiance_clipmap.inverse_atlas_size;
221
-
222
- let local = position_ws / level.cell_size - vec3<f32>(level.corner);
223
- let center = cascade.content_origin + local;
224
- let delta = irradiance_clipmap_box_jitter(u, (c + 1u) * 0x27d4eb2du);
225
-
226
- // Clamp hardware linear tap coordinates to the cascade tile's valid texel centres (including its 1-texel border)
227
- let t_min = cascade.content_origin - vec3<f32>(0.5);
228
- let t_max = cascade.content_origin + vec3<f32>(level.size) + vec3<f32>(0.5);
229
- let uvw0 = clamp(center + delta, t_min, t_max) * inv_atlas;
230
- let uvw1 = clamp(center - delta, t_min, t_max) * inv_atlas;
231
-
232
- let sh0 = ${METRO_L1_IRRADIANCE_STRUCT.make({
233
- CoCg: "textureSampleLevel(irradiance_clipmap_atlas_cocg, irradiance_clipmap_sampler, uvw0, 0.0).xy",
234
- Y: "textureSampleLevel(irradiance_clipmap_atlas_y, irradiance_clipmap_sampler, uvw0, 0.0)",
235
- })};
236
- let sh1 = ${METRO_L1_IRRADIANCE_STRUCT.make({
237
- CoCg: "textureSampleLevel(irradiance_clipmap_atlas_cocg, irradiance_clipmap_sampler, uvw1, 0.0).xy",
238
- Y: "textureSampleLevel(irradiance_clipmap_atlas_y, irradiance_clipmap_sampler, uvw1, 0.0)",
239
- })};
240
-
241
- return irradiance_clipmap_mix_sh(sh0, sh1, 0.5);
242
- }
243
-
244
- fn irradiance_clipmap_cascade_read(c: u32, position_ws: vec3<f32>) -> ${METRO_L1_IRRADIANCE_STRUCT.wgsl_ref} {
245
- let level = irradiance_clipmap.levels[c];
246
- let cascade = irradiance_clipmap.cascades[c];
247
- let local = position_ws / level.cell_size - vec3<f32>(level.corner);
248
- let center = cascade.content_origin + local;
249
- let t_min = cascade.content_origin - vec3<f32>(0.5);
250
- let t_max = cascade.content_origin + vec3<f32>(level.size) + vec3<f32>(0.5);
251
- let uvw = clamp(center, t_min, t_max) * irradiance_clipmap.inverse_atlas_size;
252
-
253
- return ${METRO_L1_IRRADIANCE_STRUCT.make({
254
- CoCg: "textureSampleLevel(irradiance_clipmap_atlas_cocg, irradiance_clipmap_sampler, uvw, 0.0).xy",
255
- Y: "textureSampleLevel(irradiance_clipmap_atlas_y, irradiance_clipmap_sampler, uvw, 0.0)",
256
- })};
257
- }
258
-
259
- fn irradiance_clipmap_mix_sh(
260
- a: ${METRO_L1_IRRADIANCE_STRUCT.wgsl_ref},
261
- b: ${METRO_L1_IRRADIANCE_STRUCT.wgsl_ref},
262
- t: f32,
263
- ) -> ${METRO_L1_IRRADIANCE_STRUCT.wgsl_ref} {
264
- return ${METRO_L1_IRRADIANCE_STRUCT.make({
265
- CoCg: "a.CoCg + (b.CoCg - a.CoCg) * t",
266
- Y: "a.Y + (b.Y - a.Y) * t",
267
- })};
268
- }
269
-
270
- fn irradiance_clipmap_sample_stochastic(
271
- position_ws: vec3<f32>,
272
- random_u: f32,
273
- ) -> ${METRO_L1_IRRADIANCE_STRUCT.wgsl_ref} {
274
- let count = irradiance_clipmap.cascade_count;
275
- let blend_skirt = select(IRRADIANCE_CLIPMAP_DEFAULT_BLEND_SKIRT, irradiance_clipmap.blend_skirt, irradiance_clipmap.blend_skirt > 0.0);
276
- let selection = irradiance_clipmap_select_finest(position_ws, blend_skirt);
277
-
278
- if (selection.level == CLIPMAP_LEVEL_NONE) {
279
- return ${METRO_L1_IRRADIANCE_STRUCT.make({
280
- CoCg: "vec2<f32>(0.0)",
281
- Y: "vec4<f32>(0.0)",
282
- })};
283
- }
284
-
285
- var u = clamp(random_u, 0.0, 0.99999994);
286
- let parent = selection.level + 1u;
287
- if (selection.edge_weight <= 0.0 || parent >= count) {
288
- return irradiance_clipmap_cascade_read_stochastic(selection.level, position_ws, &u);
289
- }
290
-
291
- // Non-linear smoothstep transition keeps sampling in the higher-resolution cascade longer,
292
- // rescaling u via sample reuse so the downstream WRS tricubic tap gets an independent uniform sample.
293
- let blend = smoothstep(0.0, 1.0, selection.edge_weight);
294
- var chosen_cascade = selection.level;
295
- if (u < blend) {
296
- chosen_cascade = parent;
297
- u = u / max(blend, 1e-6);
298
- } else {
299
- u = (u - blend) / max(1.0 - blend, 1e-6);
300
- }
301
-
302
- return irradiance_clipmap_cascade_read_stochastic(chosen_cascade, position_ws, &u);
303
- }
304
- `, [
305
- IRRADIANCE_CLIPMAP_STRUCT.declaration_chunk,
306
- METRO_L1_IRRADIANCE_STRUCT.declaration_chunk,
307
- chunk_clipmap_select_finest,
308
- chunk_hash_lowbias32,
309
- chunk_hash_to_float01,
310
- ]
311
- );
312
-
313
- chunk_irradiance_clipmap_sample.requireResource(IRRADIANCE_CLIPMAP_UNIFORM_RESOURCE);
314
- chunk_irradiance_clipmap_sample.requireResource(IRRADIANCE_CLIPMAP_ATLAS_Y_RESOURCE);
315
- chunk_irradiance_clipmap_sample.requireResource(IRRADIANCE_CLIPMAP_ATLAS_COCG_RESOURCE);
316
- chunk_irradiance_clipmap_sample.requireResource(IRRADIANCE_CLIPMAP_SAMPLER_RESOURCE);
317
-
318
- /**
319
- * Samples the world-space irradiance clipmap with deterministic `smoothstep` cascade blending and
320
- * automatic fallback into the scene's cached `far_field_irradiance_sh` (used by particle lighting):
321
- *
322
- * - Outside all cascades (`CLIPMAP_LEVEL_NONE`), returns `far_field_irradiance_sh`.
323
- * - In the interior of cascade `c` (`edge_weight <= 0.0`), performs a 2-tap antithetic Quadratic
324
- * B-Spline (`B_2 = B_1 * B_0`) read of cascade `c`.
325
- * - In the outer transition band of cascade `c < count - 1` (`edge_weight > 0.0`), smoothly blends
326
- * between cascade `c` and parent cascade `c + 1` using `smoothstep(0.0, 1.0, edge_weight)` with
327
- * hash-rescaled sample reuse across both cascades.
328
- * - In the outer transition band of the coarsest cascade (`c == count - 1`), smoothly blends between
329
- * the coarsest cascade and `far_field_irradiance_sh` using `smoothstep(0.0, 1.0, edge_weight)`.
330
- *
331
- * @type {CodeChunk}
332
- */
333
- export const chunk_irradiance_clipmap_sample_with_far_field = CodeChunk.from(
334
- //language=WGSL
335
- `
336
- fn irradiance_clipmap_sample_with_far_field(
337
- position_ws: vec3<f32>,
338
- random_u: f32,
339
- ) -> ${METRO_L1_IRRADIANCE_STRUCT.wgsl_ref} {
340
- let count = irradiance_clipmap.cascade_count;
341
- let blend_skirt = select(IRRADIANCE_CLIPMAP_DEFAULT_BLEND_SKIRT, irradiance_clipmap.blend_skirt, irradiance_clipmap.blend_skirt > 0.0);
342
- let selection = irradiance_clipmap_select_finest(position_ws, blend_skirt);
343
-
344
- if (selection.level == CLIPMAP_LEVEL_NONE) {
345
- return far_field_irradiance_sh;
346
- }
347
-
348
- var u = clamp(random_u, 0.0, 0.99999994);
349
- let inner = irradiance_clipmap_cascade_read_stochastic(selection.level, position_ws, &u);
350
- if (selection.edge_weight <= 0.0) {
351
- return inner;
352
- }
353
-
354
- let blend = smoothstep(0.0, 1.0, selection.edge_weight);
355
- let parent = selection.level + 1u;
356
-
357
- if (parent < count) {
358
- let outer = irradiance_clipmap_cascade_read_stochastic(parent, position_ws, &u);
359
- return irradiance_clipmap_mix_sh(inner, outer, blend);
360
- }
361
-
362
- return irradiance_clipmap_mix_sh(inner, far_field_irradiance_sh, blend);
363
- }
364
- `, [
365
- chunk_irradiance_clipmap_sample,
366
- ]
367
- );
368
-
369
- chunk_irradiance_clipmap_sample_with_far_field.requireResource(IRRADIANCE_CLIPMAP_UNIFORM_RESOURCE);
370
- chunk_irradiance_clipmap_sample_with_far_field.requireResource(FAR_FIELD_IRRADIANCE_SH_RESOURCE);
1
+ import { FAR_FIELD_IRRADIANCE_SH_RESOURCE } from "../../light/environment/FAR_FIELD_IRRADIANCE_SH_RESOURCE.js";
2
+ import { chunk_clipmap_select_finest } from "../../shader/chunk/clipmap/chunk_clipmap_select_finest.js";
3
+ import { chunk_hash_lowbias32 } from "../../shader/chunk/random/chunk_hash_lowbias32.js";
4
+ import { chunk_hash_to_float01 } from "../../shader/chunk/random/chunk_hash_to_float01.js";
5
+ import {
6
+ METRO_L1_IRRADIANCE_STRUCT
7
+ } from "../../shader/chunk/spherical_harmonics/l1i/chunk_convert_to_irradiance_sh.js";
8
+ import { CodeChunk } from "../../shader/compiler/CodeChunk.js";
9
+ import { ShaderResourceDescriptor } from "../../shader/resource/ShaderResourceDescriptor.js";
10
+ import {
11
+ IRRADIANCE_CLIPMAP_CASCADE_LIMIT,
12
+ IRRADIANCE_CLIPMAP_DEFAULT_BLEND_SKIRT,
13
+ IRRADIANCE_CLIPMAP_SAMPLER_DESCRIPTOR,
14
+ } from "./IRRADIANCE_CLIPMAP.js";
15
+ import { IRRADIANCE_CLIPMAP_STRUCT } from "./IRRADIANCE_CLIPMAP_STRUCT.js";
16
+
17
+ /**
18
+ * Uniform buffer binding descriptor for `irradiance_clipmap` ({@link IRRADIANCE_CLIPMAP_STRUCT}).
19
+ * @type {ShaderResourceDescriptor}
20
+ */
21
+ export const IRRADIANCE_CLIPMAP_UNIFORM_RESOURCE = ShaderResourceDescriptor.uniform(
22
+ "irradiance_clipmap",
23
+ IRRADIANCE_CLIPMAP_STRUCT
24
+ );
25
+
26
+ /**
27
+ * 3D atlas texture binding descriptor for the luma SH2 (`Y`) texture of the irradiance clipmap.
28
+ * @type {ShaderResourceDescriptor}
29
+ */
30
+ export const IRRADIANCE_CLIPMAP_ATLAS_Y_RESOURCE = ShaderResourceDescriptor.texture(
31
+ "irradiance_clipmap_atlas_y",
32
+ "float",
33
+ "3d"
34
+ );
35
+
36
+ /**
37
+ * 3D atlas texture binding descriptor for the chroma (`CoCg`) texture of the irradiance clipmap.
38
+ * @type {ShaderResourceDescriptor}
39
+ */
40
+ export const IRRADIANCE_CLIPMAP_ATLAS_COCG_RESOURCE = ShaderResourceDescriptor.texture(
41
+ "irradiance_clipmap_atlas_cocg",
42
+ "float",
43
+ "3d"
44
+ );
45
+
46
+ /**
47
+ * Linear clamped sampler binding descriptor for the irradiance clipmap atlas textures.
48
+ * @type {ShaderResourceDescriptor}
49
+ */
50
+ export const IRRADIANCE_CLIPMAP_SAMPLER_RESOURCE = ShaderResourceDescriptor.sampler(
51
+ "irradiance_clipmap_sampler",
52
+ "filtering",
53
+ IRRADIANCE_CLIPMAP_SAMPLER_DESCRIPTOR
54
+ );
55
+
56
+ /**
57
+ * Declares the world-space irradiance clipmap's bindings on a shader resource group.
58
+ *
59
+ * @param {import("../../shader/resource/ShaderResourceGroupDescriptor.js").ShaderResourceGroupDescriptor} group
60
+ * @param {Object} [options]
61
+ * @param {boolean} [options.include_far_field=false] whether to also declare `far_field_irradiance_sh`
62
+ * @returns {import("../../shader/resource/ShaderResourceGroupDescriptor.js").ShaderResourceGroupDescriptor}
63
+ */
64
+ export function irradiance_clipmap_add_resources(group, { include_far_field = false } = {}) {
65
+ group
66
+ .add(IRRADIANCE_CLIPMAP_UNIFORM_RESOURCE)
67
+ .add(IRRADIANCE_CLIPMAP_ATLAS_Y_RESOURCE)
68
+ .add(IRRADIANCE_CLIPMAP_ATLAS_COCG_RESOURCE)
69
+ .add(IRRADIANCE_CLIPMAP_SAMPLER_RESOURCE);
70
+
71
+ if (include_far_field) {
72
+ group.add(FAR_FIELD_IRRADIANCE_SH_RESOURCE);
73
+ }
74
+
75
+ return group;
76
+ }
77
+
78
+ /**
79
+ * World-space irradiance clipmap sampling:
80
+ *
81
+ * - `irradiance_clipmap_cascade_read_stochastic(c, position_ws, &u) -> MetroL1Irradiance`
82
+ * Evaluates a 2-tap antithetic Quadratic B-Spline (`B_2 = B_1 * B_0`) read of cascade `c`'s tile
83
+ * in `irradiance_clipmap_atlas_y` and `irradiance_clipmap_atlas_cocg`.
84
+ * - `irradiance_clipmap_cascade_read(c, position_ws) -> MetroL1Irradiance`
85
+ * Deterministic convenience wrapper around `irradiance_clipmap_cascade_read_stochastic`.
86
+ * - `irradiance_clipmap_sample_stochastic(position_ws, random_u) -> MetroL1Irradiance`
87
+ * Selects the finest cascade covering `position_ws` via continuous radial distance from `anchor`,
88
+ * computes a `smoothstep(0.0, 1.0, selection.edge_weight)` transition weight across the outer band
89
+ * (`blend_skirt` cells), and stochastically reads `selection.level` or `selection.level + 1`.
90
+ *
91
+ * ## Why Hardware Trilinear (`B_1`) Shows Visible Grid Lines (`┐`)
92
+ *
93
+ * In coarse clipmap cascades (`h = 0.5 m .. 1.0 m`), a single voxel spans 50-100 screen pixels across
94
+ * translucent smoke billboards, and near a point light the `1 / r^2` falloff is strongly convex
95
+ * (`d^2 I / dy^2 >> 0`, e.g. slope dropping by >4x from one voxel to the next):
96
+ * 1. **Dirac-Delta Curvature / Mach Bands:** Hardware trilinear interpolation (`B_1`) is piecewise
97
+ * linear (`C^0`), so inside each 60-pixel voxel cell its second derivative is identically zero
98
+ * (`d^2 I / dy^2 = 0`), and *all* of the curvature is concentrated into a 1-pixel Dirac delta
99
+ * spike along the voxel-centre plane `y = k + 0.5`:
100
+ * `d^2 I_trilinear / dy^2 = sum_k (I_{k+1} - 2*I_k + I_{k-1}) * delta(y - (k + 0.5))`.
101
+ * Because human vision (lateral inhibition / Mach band illusion) and contrast stretching respond
102
+ * to the Laplacian `nabla^2 I`, concentrating the entire slope transition onto a single plane
103
+ * produces a sharp visible bright/dark ridge line at every voxel centre.
104
+ * 2. **Oblique Billboard Saddle Cross-Terms:** Camera-facing particle billboards slice obliquely
105
+ * through the world-aligned `(x, y, z)` grid. Within each cell, trilinear interpolation is a
106
+ * hyperbolic paraboloid (`a + b*x + c*y + d*x*y + ...`) that curves along diagonals but collapses
107
+ * to straight lines on the axis-aligned planes `x = x_k, y = y_k, z = z_k`, creating visible
108
+ * grid-aligned creases where adjacent saddle patches meet.
109
+ *
110
+ * ## Why `floor(coord)`-Partitioned Sigg & Hadwiger Stochastic WRS Fails
111
+ *
112
+ * Standard 8-tap Sigg & Hadwiger tricubic B-spline (`B_3`) decomposes the 4-texel 1D cubic weights
113
+ * `(w_0, w_1, w_2, w_3)` around `tc = floor(coord)` (`f = coord - tc in [0, 1)`) into two hardware
114
+ * linear taps: tap 0 pairing `(w_0, w_1)` with weight `g_0 = w_0 + w_1`, and tap 1 pairing `(w_2, w_3)`
115
+ * with weight `g_1 = w_2 + w_3`. In deterministic 8-tap evaluation both taps are summed, so the
116
+ * `floor(coord)` partition is invisible. When sub-sampled stochastically via WRS, however:
117
+ * 1. **Tap-Pairing Flip Across `f = 1^- -> 0^+`:** At a voxel-centre plane, the 3 non-zero B-spline
118
+ * weights are `(1/6, 4/6, 1/6)`. Just below the plane (`f = 0.999`), Sigg & Hadwiger pairs the
119
+ * central `4/6` texel with the upper texel (`g_1 = 5/6` at `+0.2` voxels, `g_0 = 1/6` at `-1.0`
120
+ * voxel). Just above the plane (`f = 0.001`), `tc = floor(coord)` increments and switches the
121
+ * central `4/6` texel into the lower pair (`g_0 = 5/6` at `-0.2` voxels, `g_1 = 1/6` at `+1.0`
122
+ * voxel). The 83.3% majority tap therefore jumps backwards by `-0.4` voxels and the variance
123
+ * jumps by 2.8x across every integer coordinate plane.
124
+ * 2. **Double-Weighting Bias (`w_i^2`) in `weight1 / (weight0 + weight1)`:** Because WRS already
125
+ * selects tap `i` with probability `w_i`, blending two WRS taps by `weight1 / (weight0 + weight1)`
126
+ * weights each tap by `w_i` a second time (`w_i^2`), creating a >60% step discontinuity in the
127
+ * expected value across `tc = floor(coord)`.
128
+ *
129
+ * ## Why 2-Tap Antithetic Quadratic B-Spline (`B_2 = B_1 * B_0`) Is the Sweet Spot
130
+ *
131
+ * Hardware `textureSampleLevel` already evaluates the 3D linear tent filter `B_1`. Convolving `B_1`
132
+ * with a uniform unit box filter `B_0` on `[-0.5, +0.5]^3` via two antithetic taps at
133
+ * `center + delta` and `center - delta` (`delta in [-0.5, +0.5]^3`, mixed `0.5 / 0.5`) yields the
134
+ * exact `C^1` Quadratic B-Spline `B_2 = B_1 * B_0`:
135
+ * - **Constant Second Derivative Across Each Cell:** Because the `[-0.5, +0.5]` smoothing window is
136
+ * exactly 1 voxel wide, adjacent voxel-centre smoothing windows meet edge-to-edge and spread the
137
+ * slope change `I_{k+1} - 2*I_k + I_{k-1}` uniformly across all 60 pixels of the cell
138
+ * (`d^2 B_2 / dx^2 = const`), completely eliminating the Mach-band Dirac spike.
139
+ * - **Zero `floor(coord)` Discontinuity:** Neither the tap offsets nor the weights depend on
140
+ * `floor(coord)`, so both the mean and the variance are 100% continuous across all voxel planes.
141
+ * - **First-Order Gradient Cancellation & Compact Support:** Antithetic pairing (`+delta`, `-delta`)
142
+ * cancels all linear gradient noise `+/- dot(nabla I, delta)` on every pixel, while keeping taps
143
+ * within `+/- 0.5` voxels of `center` (half the radius of `B_3 = B_1 * B_1`), avoiding `+/- 1`
144
+ * voxel fire-bleed grain and staying strictly inside the 1-texel cascade border.
145
+ *
146
+ * @type {CodeChunk}
147
+ */
148
+ export const chunk_irradiance_clipmap_sample = CodeChunk.from(
149
+ //language=WGSL
150
+ `
151
+ const IRRADIANCE_CLIPMAP_DEFAULT_BLEND_SKIRT: f32 = ${IRRADIANCE_CLIPMAP_DEFAULT_BLEND_SKIRT}.0;
152
+
153
+ fn irradiance_clipmap_select_finest(
154
+ position_ws: vec3<f32>,
155
+ band_width: f32,
156
+ ) -> ClipmapSelection {
157
+ let count = irradiance_clipmap.cascade_count;
158
+ let anchor_ws = irradiance_clipmap.anchor;
159
+
160
+ for (var i = 0u; i < ${IRRADIANCE_CLIPMAP_CASCADE_LIMIT}u; i++) {
161
+ if (i >= count) {
162
+ break;
163
+ }
164
+
165
+ let level = irradiance_clipmap.levels[i];
166
+ let position_cells = position_ws / level.cell_size;
167
+ let half_size = 0.5 * vec3<f32>(level.size);
168
+ let level_center = vec3<f32>(level.corner) + half_size;
169
+ let anchor_cells = anchor_ws / level.cell_size;
170
+
171
+ // Use the continuous camera anchor when within the 1-voxel rounding bound of level.corner,
172
+ // so cascade selection and edge_weight vary C^1-smoothly with camera motion without
173
+ // stair-stepping at integer corner transitions.
174
+ let center_cells = select(
175
+ level_center,
176
+ anchor_cells,
177
+ all(abs(anchor_cells - level_center) <= vec3<f32>(0.501))
178
+ );
179
+
180
+ // Subtract 0.5 cells from half_size so that even when anchor_cells is offset by up to
181
+ // +/-0.5 cells from level_center, the selection sphere stays strictly inside [0, size]^3
182
+ // and the +/-0.5 voxel B_2 jitter taps stay strictly inside the 1-texel border
183
+ // [-0.5, size + 0.5]^3 without hitting the border clamp.
184
+ let radius = max(0.5, min(half_size.x, min(half_size.y, half_size.z)) - 0.5);
185
+ let edge = radius - length(position_cells - center_cells);
186
+
187
+ if (edge <= 0.0) {
188
+ continue;
189
+ }
190
+
191
+ return ClipmapSelection(i, saturate(1.0 - edge / band_width));
192
+ }
193
+
194
+ return ClipmapSelection(CLIPMAP_LEVEL_NONE, 0.0);
195
+ }
196
+
197
+ fn irradiance_clipmap_box_jitter(u: ptr<function, f32>, salt: u32) -> vec3<f32> {
198
+ let h0 = hash_lowbias32(bitcast<u32>(*u) ^ salt);
199
+ let h1 = hash_lowbias32(h0 ^ 0x9e3779b9u);
200
+ let h2 = hash_lowbias32(h1 ^ 0x85ebca6bu);
201
+ *u = hash_to_float01(hash_lowbias32(h2 ^ 0xc2b2ae35u));
202
+
203
+ // Uniform box jitter in [-0.5, +0.5]^3 paired antithetically (center + delta, center - delta):
204
+ // convolving hardware trilinear B_1 with B_0 on [-0.5, +0.5]^3 yields the exact C^1 Quadratic
205
+ // B-Spline B_2 = B_1 * B_0 with constant second derivative across each cell, zero floor/integer
206
+ // discontinuity, and support staying strictly within the 1-texel cascade border.
207
+ return vec3<f32>(
208
+ hash_to_float01(h0) - 0.5,
209
+ hash_to_float01(h1) - 0.5,
210
+ hash_to_float01(h2) - 0.5,
211
+ );
212
+ }
213
+
214
+ fn irradiance_clipmap_cascade_read_stochastic(
215
+ c: u32,
216
+ position_ws: vec3<f32>,
217
+ u: ptr<function, f32>,
218
+ ) -> ${METRO_L1_IRRADIANCE_STRUCT.wgsl_ref} {
219
+ let level = irradiance_clipmap.levels[c];
220
+ let cascade = irradiance_clipmap.cascades[c];
221
+ let inv_atlas = irradiance_clipmap.inverse_atlas_size;
222
+
223
+ let local = position_ws / level.cell_size - vec3<f32>(level.corner);
224
+ let center = cascade.content_origin + local;
225
+ let delta = irradiance_clipmap_box_jitter(u, (c + 1u) * 0x27d4eb2du);
226
+
227
+ // Clamp hardware linear tap coordinates to the cascade tile's valid texel centres (including its 1-texel border)
228
+ let t_min = cascade.content_origin - vec3<f32>(0.5);
229
+ let t_max = cascade.content_origin + vec3<f32>(level.size) + vec3<f32>(0.5);
230
+ let uvw0 = clamp(center + delta, t_min, t_max) * inv_atlas;
231
+ let uvw1 = clamp(center - delta, t_min, t_max) * inv_atlas;
232
+
233
+ let sh0 = ${METRO_L1_IRRADIANCE_STRUCT.make({
234
+ CoCg: "textureSampleLevel(irradiance_clipmap_atlas_cocg, irradiance_clipmap_sampler, uvw0, 0.0).xy",
235
+ Y: "textureSampleLevel(irradiance_clipmap_atlas_y, irradiance_clipmap_sampler, uvw0, 0.0)",
236
+ })};
237
+ let sh1 = ${METRO_L1_IRRADIANCE_STRUCT.make({
238
+ CoCg: "textureSampleLevel(irradiance_clipmap_atlas_cocg, irradiance_clipmap_sampler, uvw1, 0.0).xy",
239
+ Y: "textureSampleLevel(irradiance_clipmap_atlas_y, irradiance_clipmap_sampler, uvw1, 0.0)",
240
+ })};
241
+
242
+ return irradiance_clipmap_mix_sh(sh0, sh1, 0.5);
243
+ }
244
+
245
+ fn irradiance_clipmap_cascade_read(c: u32, position_ws: vec3<f32>) -> ${METRO_L1_IRRADIANCE_STRUCT.wgsl_ref} {
246
+ let level = irradiance_clipmap.levels[c];
247
+ let cascade = irradiance_clipmap.cascades[c];
248
+ let local = position_ws / level.cell_size - vec3<f32>(level.corner);
249
+ let center = cascade.content_origin + local;
250
+ let t_min = cascade.content_origin - vec3<f32>(0.5);
251
+ let t_max = cascade.content_origin + vec3<f32>(level.size) + vec3<f32>(0.5);
252
+ let uvw = clamp(center, t_min, t_max) * irradiance_clipmap.inverse_atlas_size;
253
+
254
+ return ${METRO_L1_IRRADIANCE_STRUCT.make({
255
+ CoCg: "textureSampleLevel(irradiance_clipmap_atlas_cocg, irradiance_clipmap_sampler, uvw, 0.0).xy",
256
+ Y: "textureSampleLevel(irradiance_clipmap_atlas_y, irradiance_clipmap_sampler, uvw, 0.0)",
257
+ })};
258
+ }
259
+
260
+ fn irradiance_clipmap_mix_sh(
261
+ a: ${METRO_L1_IRRADIANCE_STRUCT.wgsl_ref},
262
+ b: ${METRO_L1_IRRADIANCE_STRUCT.wgsl_ref},
263
+ t: f32,
264
+ ) -> ${METRO_L1_IRRADIANCE_STRUCT.wgsl_ref} {
265
+ return ${METRO_L1_IRRADIANCE_STRUCT.make({
266
+ CoCg: "a.CoCg + (b.CoCg - a.CoCg) * t",
267
+ Y: "a.Y + (b.Y - a.Y) * t",
268
+ })};
269
+ }
270
+
271
+ fn irradiance_clipmap_sample_stochastic(
272
+ position_ws: vec3<f32>,
273
+ random_u: f32,
274
+ ) -> ${METRO_L1_IRRADIANCE_STRUCT.wgsl_ref} {
275
+ let count = irradiance_clipmap.cascade_count;
276
+ let blend_skirt = select(IRRADIANCE_CLIPMAP_DEFAULT_BLEND_SKIRT, irradiance_clipmap.blend_skirt, irradiance_clipmap.blend_skirt > 0.0);
277
+ let selection = irradiance_clipmap_select_finest(position_ws, blend_skirt);
278
+
279
+ if (selection.level == CLIPMAP_LEVEL_NONE) {
280
+ return ${METRO_L1_IRRADIANCE_STRUCT.make({
281
+ CoCg: "vec2<f32>(0.0)",
282
+ Y: "vec4<f32>(0.0)",
283
+ })};
284
+ }
285
+
286
+ var u = clamp(random_u, 0.0, 0.99999994);
287
+ let parent = selection.level + 1u;
288
+ if (selection.edge_weight <= 0.0 || parent >= count) {
289
+ return irradiance_clipmap_cascade_read_stochastic(selection.level, position_ws, &u);
290
+ }
291
+
292
+ // Non-linear smoothstep transition keeps sampling in the higher-resolution cascade longer,
293
+ // rescaling u via sample reuse so the downstream WRS tricubic tap gets an independent uniform sample.
294
+ let blend = smoothstep(0.0, 1.0, selection.edge_weight);
295
+ var chosen_cascade = selection.level;
296
+ if (u < blend) {
297
+ chosen_cascade = parent;
298
+ u = u / max(blend, 1e-6);
299
+ } else {
300
+ u = (u - blend) / max(1.0 - blend, 1e-6);
301
+ }
302
+
303
+ return irradiance_clipmap_cascade_read_stochastic(chosen_cascade, position_ws, &u);
304
+ }
305
+ `, [
306
+ IRRADIANCE_CLIPMAP_STRUCT.declaration_chunk,
307
+ METRO_L1_IRRADIANCE_STRUCT.declaration_chunk,
308
+ chunk_clipmap_select_finest,
309
+ chunk_hash_lowbias32,
310
+ chunk_hash_to_float01,
311
+ ]
312
+ );
313
+
314
+ chunk_irradiance_clipmap_sample.requireResource(IRRADIANCE_CLIPMAP_UNIFORM_RESOURCE);
315
+ chunk_irradiance_clipmap_sample.requireResource(IRRADIANCE_CLIPMAP_ATLAS_Y_RESOURCE);
316
+ chunk_irradiance_clipmap_sample.requireResource(IRRADIANCE_CLIPMAP_ATLAS_COCG_RESOURCE);
317
+ chunk_irradiance_clipmap_sample.requireResource(IRRADIANCE_CLIPMAP_SAMPLER_RESOURCE);
318
+
319
+ /**
320
+ * Samples the world-space irradiance clipmap with deterministic `smoothstep` cascade blending and
321
+ * automatic fallback into the scene's cached `far_field_irradiance_sh` (used by particle lighting):
322
+ *
323
+ * - Outside all cascades (`CLIPMAP_LEVEL_NONE`), returns `far_field_irradiance_sh`.
324
+ * - In the interior of cascade `c` (`edge_weight <= 0.0`), performs a 2-tap antithetic Quadratic
325
+ * B-Spline (`B_2 = B_1 * B_0`) read of cascade `c`.
326
+ * - In the outer transition band of cascade `c < count - 1` (`edge_weight > 0.0`), smoothly blends
327
+ * between cascade `c` and parent cascade `c + 1` using `smoothstep(0.0, 1.0, edge_weight)` with
328
+ * hash-rescaled sample reuse across both cascades.
329
+ * - In the outer transition band of the coarsest cascade (`c == count - 1`), smoothly blends between
330
+ * the coarsest cascade and `far_field_irradiance_sh` using `smoothstep(0.0, 1.0, edge_weight)`.
331
+ *
332
+ * @type {CodeChunk}
333
+ */
334
+ export const chunk_irradiance_clipmap_sample_with_far_field = CodeChunk.from(
335
+ //language=WGSL
336
+ `
337
+ fn irradiance_clipmap_sample_with_far_field(
338
+ position_ws: vec3<f32>,
339
+ random_u: f32,
340
+ ) -> ${METRO_L1_IRRADIANCE_STRUCT.wgsl_ref} {
341
+ let count = irradiance_clipmap.cascade_count;
342
+ let blend_skirt = select(IRRADIANCE_CLIPMAP_DEFAULT_BLEND_SKIRT, irradiance_clipmap.blend_skirt, irradiance_clipmap.blend_skirt > 0.0);
343
+ let selection = irradiance_clipmap_select_finest(position_ws, blend_skirt);
344
+
345
+ if (selection.level == CLIPMAP_LEVEL_NONE) {
346
+ return far_field_irradiance_sh;
347
+ }
348
+
349
+ var u = clamp(random_u, 0.0, 0.99999994);
350
+ let inner = irradiance_clipmap_cascade_read_stochastic(selection.level, position_ws, &u);
351
+ if (selection.edge_weight <= 0.0) {
352
+ return inner;
353
+ }
354
+
355
+ let blend = smoothstep(0.0, 1.0, selection.edge_weight);
356
+ let parent = selection.level + 1u;
357
+
358
+ if (parent < count) {
359
+ let outer = irradiance_clipmap_cascade_read_stochastic(parent, position_ws, &u);
360
+ return irradiance_clipmap_mix_sh(inner, outer, blend);
361
+ }
362
+
363
+ return irradiance_clipmap_mix_sh(inner, far_field_irradiance_sh, blend);
364
+ }
365
+ `, [
366
+ chunk_irradiance_clipmap_sample,
367
+ ]
368
+ );
369
+
370
+ chunk_irradiance_clipmap_sample_with_far_field.requireResource(IRRADIANCE_CLIPMAP_UNIFORM_RESOURCE);
371
+ chunk_irradiance_clipmap_sample_with_far_field.requireResource(FAR_FIELD_IRRADIANCE_SH_RESOURCE);