@woosh/meep-engine 3.11.1 → 3.11.3

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 (225) hide show
  1. package/build/bundle-worker-terrain.js +1 -1
  2. package/package.json +1 -1
  3. package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
  4. package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +294 -290
  5. package/src/engine/graphics3/ParticipatingMedia.d.ts +111 -0
  6. package/src/engine/graphics3/ParticipatingMedia.d.ts.map +1 -0
  7. package/src/engine/graphics3/ParticipatingMedia.js +203 -0
  8. package/src/engine/graphics3/ParticipatingMediaSerializationAdapter.d.ts +34 -0
  9. package/src/engine/graphics3/ParticipatingMediaSerializationAdapter.d.ts.map +1 -0
  10. package/src/engine/graphics3/ParticipatingMediaSerializationAdapter.js +73 -0
  11. package/src/engine/graphics3/ParticipatingMediaSystem3.d.ts +87 -0
  12. package/src/engine/graphics3/ParticipatingMediaSystem3.d.ts.map +1 -0
  13. package/src/engine/graphics3/ParticipatingMediaSystem3.js +184 -0
  14. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  15. package/src/shade/descriptor/pipeline/PipelineLayoutDescriptor.d.ts +16 -2
  16. package/src/shade/descriptor/pipeline/PipelineLayoutDescriptor.d.ts.map +1 -1
  17. package/src/shade/descriptor/pipeline/PipelineLayoutDescriptor.js +28 -4
  18. package/src/shade/descriptor/pipeline/compute/ComputePipelineDescriptor.d.ts +1 -1
  19. package/src/shade/descriptor/pipeline/compute/ComputePipelineDescriptor.d.ts.map +1 -1
  20. package/src/shade/descriptor/pipeline/compute/ComputePipelineDescriptor.js +5 -2
  21. package/src/shade/device/IMMEDIATE_DATA_PLAN_2026_08_28.md +564 -451
  22. package/src/shade/device/ShadeGPUCommandContext.d.ts +18 -2
  23. package/src/shade/device/ShadeGPUCommandContext.d.ts.map +1 -1
  24. package/src/shade/device/ShadeGPUCommandContext.js +994 -940
  25. package/src/shade/device/immediate_data_supported.d.ts +33 -0
  26. package/src/shade/device/immediate_data_supported.d.ts.map +1 -0
  27. package/src/shade/device/immediate_data_supported.js +53 -0
  28. package/src/shade/device/mock/SoftwareGPUCommandEncoder.d.ts +18 -0
  29. package/src/shade/device/mock/SoftwareGPUCommandEncoder.d.ts.map +1 -1
  30. package/src/shade/device/mock/SoftwareGPUCommandEncoder.js +24 -0
  31. package/src/shade/device/mock/SoftwareGPUComputePassEncoder.d.ts +7 -0
  32. package/src/shade/device/mock/SoftwareGPUComputePassEncoder.d.ts.map +1 -1
  33. package/src/shade/device/mock/SoftwareGPUComputePassEncoder.js +49 -1
  34. package/src/shade/device/mock/SoftwareGPUDevice.d.ts.map +1 -1
  35. package/src/shade/device/mock/SoftwareGPUDevice.js +20 -2
  36. package/src/shade/device/mock/SoftwareGPUImmediateData.d.ts +65 -0
  37. package/src/shade/device/mock/SoftwareGPUImmediateData.d.ts.map +1 -0
  38. package/src/shade/device/mock/SoftwareGPUImmediateData.js +160 -0
  39. package/src/shade/device/mock/SoftwareGPURenderPassEncoder.d.ts +7 -0
  40. package/src/shade/device/mock/SoftwareGPURenderPassEncoder.d.ts.map +1 -1
  41. package/src/shade/device/mock/SoftwareGPURenderPassEncoder.js +50 -0
  42. package/src/shade/device/mock/pipeline_immediate_size.d.ts +11 -0
  43. package/src/shade/device/mock/pipeline_immediate_size.d.ts.map +1 -0
  44. package/src/shade/device/mock/pipeline_immediate_size.js +18 -0
  45. package/src/shade/device/pipeline/PipelineLayoutManager.d.ts.map +1 -1
  46. package/src/shade/device/pipeline/PipelineLayoutManager.js +1 -0
  47. package/src/shade/playground/volumetrics_froxel/README.md +104 -1
  48. package/src/shade/playground/volumetrics_froxel/index.html +20 -1
  49. package/src/shade/playground/volumetrics_froxel/main.d.ts.map +1 -1
  50. package/src/shade/playground/volumetrics_froxel/main.js +298 -0
  51. package/src/shade/playground/volumetrics_froxel/measure.d.ts +119 -0
  52. package/src/shade/playground/volumetrics_froxel/measure.d.ts.map +1 -0
  53. package/src/shade/playground/volumetrics_froxel/measure.js +559 -0
  54. package/src/shade/renderer/GraphicsContext.d.ts +9 -2
  55. package/src/shade/renderer/GraphicsContext.d.ts.map +1 -1
  56. package/src/shade/renderer/GraphicsContext.js +13 -1
  57. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  58. package/src/shade/renderer/Renderer.js +22 -18
  59. package/src/shade/renderer/atmosphere/GPUSky.d.ts.map +1 -1
  60. package/src/shade/renderer/atmosphere/GPUSky.js +145 -142
  61. package/src/shade/renderer/atmosphere/MULTISCATTER_LUT_CONFIG.d.ts +34 -0
  62. package/src/shade/renderer/atmosphere/MULTISCATTER_LUT_CONFIG.d.ts.map +1 -0
  63. package/src/shade/renderer/atmosphere/MULTISCATTER_LUT_CONFIG.js +36 -0
  64. package/src/shade/renderer/atmosphere/SAMPLER_SKY_LUT.d.ts +20 -0
  65. package/src/shade/renderer/atmosphere/SAMPLER_SKY_LUT.d.ts.map +1 -0
  66. package/src/shade/renderer/atmosphere/SAMPLER_SKY_LUT.js +25 -0
  67. package/src/shade/renderer/atmosphere/chunk_get_multiscatter_values.d.ts +14 -1
  68. package/src/shade/renderer/atmosphere/chunk_get_multiscatter_values.d.ts.map +1 -1
  69. package/src/shade/renderer/atmosphere/chunk_get_multiscatter_values.js +163 -177
  70. package/src/shade/renderer/atmosphere/chunk_multiscatter_sample_direction.d.ts +20 -0
  71. package/src/shade/renderer/atmosphere/chunk_multiscatter_sample_direction.d.ts.map +1 -0
  72. package/src/shade/renderer/atmosphere/chunk_multiscatter_sample_direction.js +46 -0
  73. package/src/shade/renderer/atmosphere/chunk_sample_multiscatter_lut.d.ts +11 -1
  74. package/src/shade/renderer/atmosphere/chunk_sample_multiscatter_lut.d.ts.map +1 -1
  75. package/src/shade/renderer/atmosphere/chunk_sample_multiscatter_lut.js +57 -27
  76. package/src/shade/renderer/atmosphere/chunk_sample_transmittance_lut.d.ts +19 -1
  77. package/src/shade/renderer/atmosphere/chunk_sample_transmittance_lut.d.ts.map +1 -1
  78. package/src/shade/renderer/atmosphere/chunk_sample_transmittance_lut.js +69 -33
  79. package/src/shade/renderer/atmosphere/chunk_sky_lut_compute_uv.d.ts +13 -0
  80. package/src/shade/renderer/atmosphere/chunk_sky_lut_compute_uv.d.ts.map +1 -1
  81. package/src/shade/renderer/atmosphere/chunk_sky_lut_compute_uv.js +35 -35
  82. package/src/shade/renderer/atmosphere/chunk_sky_lut_params.d.ts +13 -0
  83. package/src/shade/renderer/atmosphere/chunk_sky_lut_params.d.ts.map +1 -0
  84. package/src/shade/renderer/atmosphere/chunk_sky_lut_params.js +38 -0
  85. package/src/shade/renderer/atmosphere/chunk_sky_lut_texel_to_unit.d.ts +17 -0
  86. package/src/shade/renderer/atmosphere/chunk_sky_lut_texel_to_unit.d.ts.map +1 -0
  87. package/src/shade/renderer/atmosphere/chunk_sky_lut_texel_to_unit.js +29 -0
  88. package/src/shade/renderer/atmosphere/chunk_sky_lut_uv.d.ts +21 -0
  89. package/src/shade/renderer/atmosphere/chunk_sky_lut_uv.d.ts.map +1 -0
  90. package/src/shade/renderer/atmosphere/chunk_sky_lut_uv.js +42 -0
  91. package/src/shade/renderer/atmosphere/shader_multiscatter_lut.d.ts +2 -2
  92. package/src/shade/renderer/atmosphere/shader_multiscatter_lut.d.ts.map +1 -1
  93. package/src/shade/renderer/atmosphere/shader_multiscatter_lut.js +165 -84
  94. package/src/shade/renderer/atmosphere/shader_sky_irradiance_lut.d.ts.map +1 -1
  95. package/src/shade/renderer/atmosphere/shader_sky_irradiance_lut.js +215 -204
  96. package/src/shade/renderer/atmosphere/shader_transmittance_lut.d.ts.map +1 -1
  97. package/src/shade/renderer/atmosphere/shader_transmittance_lut.js +79 -67
  98. package/src/shade/renderer/light/cluster/graph_assign_clsuters.d.ts.map +1 -1
  99. package/src/shade/renderer/light/cluster/graph_assign_clsuters.js +6 -2
  100. package/src/shade/renderer/postprocess/gtao/GTAO.d.ts.map +1 -1
  101. package/src/shade/renderer/postprocess/gtao/GTAO.js +4 -0
  102. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  103. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.d.ts.map +1 -1
  104. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.js +341 -345
  105. package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.d.ts +0 -2
  106. package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.d.ts.map +1 -1
  107. package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.js +58 -57
  108. package/src/shade/renderer/rasterize/expand/mesh/graph_expand_meshes_to_meshlets.d.ts.map +1 -1
  109. package/src/shade/renderer/rasterize/expand/mesh/graph_expand_meshes_to_meshlets.js +7 -3
  110. package/src/shade/renderer/rasterize/fast/graph_extract_meshes_from_bucket.d.ts.map +1 -1
  111. package/src/shade/renderer/rasterize/fast/graph_extract_meshes_from_bucket.js +77 -81
  112. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.d.ts.map +1 -1
  113. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.js +5 -1
  114. package/src/shade/renderer/shader/ComputeShader.d.ts.map +1 -1
  115. package/src/shade/renderer/shader/ComputeShader.js +6 -2
  116. package/src/shade/renderer/shader/ImageShader.d.ts.map +1 -1
  117. package/src/shade/renderer/shader/ImageShader.js +4 -1
  118. package/src/shade/renderer/shader/ShaderDescriptor.d.ts.map +1 -1
  119. package/src/shade/renderer/shader/ShaderDescriptor.js +26 -0
  120. package/src/shade/renderer/shader/chunk/atmosphere/ATMOSPHERE_RADII_DECLARATION.d.ts +18 -0
  121. package/src/shade/renderer/shader/chunk/atmosphere/ATMOSPHERE_RADII_DECLARATION.d.ts.map +1 -0
  122. package/src/shade/renderer/shader/chunk/atmosphere/ATMOSPHERE_RADII_DECLARATION.js +32 -0
  123. package/src/shade/renderer/shader/chunk/atmosphere/PLANET_CENTER_DECLARATION.d.ts +7 -0
  124. package/src/shade/renderer/shader/chunk/atmosphere/PLANET_CENTER_DECLARATION.d.ts.map +1 -1
  125. package/src/shade/renderer/shader/chunk/atmosphere/PLANET_CENTER_DECLARATION.js +17 -6
  126. package/src/shade/renderer/shader/chunk/atmosphere/chunk_atmosphere_ray_bounds.d.ts +21 -0
  127. package/src/shade/renderer/shader/chunk/atmosphere/chunk_atmosphere_ray_bounds.d.ts.map +1 -0
  128. package/src/shade/renderer/shader/chunk/atmosphere/chunk_atmosphere_ray_bounds.js +69 -0
  129. package/src/shade/renderer/shader/chunk/geometry/aabb3/chunk_aabb3_contains_aabb3.d.ts +9 -0
  130. package/src/shade/renderer/shader/chunk/geometry/aabb3/chunk_aabb3_contains_aabb3.d.ts.map +1 -0
  131. package/src/shade/renderer/shader/chunk/geometry/aabb3/chunk_aabb3_contains_aabb3.js +22 -0
  132. package/src/shade/renderer/shader/chunk/geometry/aabb3/chunk_aabb3_from_transformed_corners.d.ts +16 -0
  133. package/src/shade/renderer/shader/chunk/geometry/aabb3/chunk_aabb3_from_transformed_corners.d.ts.map +1 -0
  134. package/src/shade/renderer/shader/chunk/geometry/aabb3/chunk_aabb3_from_transformed_corners.js +41 -0
  135. package/src/shade/renderer/shader/chunk/geometry/frustum/chunk_frustum_transform_m4.d.ts +11 -0
  136. package/src/shade/renderer/shader/chunk/geometry/frustum/chunk_frustum_transform_m4.d.ts.map +1 -1
  137. package/src/shade/renderer/shader/chunk/geometry/frustum/chunk_frustum_transform_m4.js +15 -19
  138. package/src/shade/renderer/shader/chunk/geometry/frustum/chunk_frustum_transform_plane_matrix.d.ts +23 -0
  139. package/src/shade/renderer/shader/chunk/geometry/frustum/chunk_frustum_transform_plane_matrix.d.ts.map +1 -0
  140. package/src/shade/renderer/shader/chunk/geometry/frustum/chunk_frustum_transform_plane_matrix.js +44 -0
  141. package/src/shade/renderer/shader/chunk/geometry/octahedron/chunk_uv_octahedral_unit_decode.d.ts.map +1 -1
  142. package/src/shade/renderer/shader/chunk/geometry/octahedron/chunk_uv_octahedral_unit_decode.js +1 -4
  143. package/src/shade/renderer/shader/compiler/CodeChunk.d.ts +7 -1
  144. package/src/shade/renderer/shader/compiler/CodeChunk.d.ts.map +1 -1
  145. package/src/shade/renderer/shader/compiler/CodeChunk.js +37 -2
  146. package/src/shade/renderer/shader/graph/compute/compute_binding_read_names.d.ts.map +1 -1
  147. package/src/shade/renderer/shader/graph/compute/compute_binding_read_names.js +6 -0
  148. package/src/shade/renderer/shader/graph/compute/graph_compute_indirect_pass.js +1 -1
  149. package/src/shade/renderer/shader/graph/compute/graph_compute_pass.js +1 -1
  150. package/src/shade/renderer/shader/graph/graph_image_pass.js +1 -1
  151. package/src/shade/renderer/shader/graph/graph_prepare_bindings.d.ts.map +1 -1
  152. package/src/shade/renderer/shader/graph/graph_prepare_bindings.js +7 -0
  153. package/src/shade/renderer/shader/graph/graph_prepare_pass_input_data.d.ts +3 -1
  154. package/src/shade/renderer/shader/graph/graph_prepare_pass_input_data.d.ts.map +1 -1
  155. package/src/shade/renderer/shader/graph/graph_prepare_pass_input_data.js +12 -1
  156. package/src/shade/renderer/shader/pass/RenderPassDescriptor.d.ts.map +1 -1
  157. package/src/shade/renderer/shader/pass/RenderPassDescriptor.js +1 -0
  158. package/src/shade/renderer/shader/resource/IMMEDIATE_DATA.d.ts +21 -0
  159. package/src/shade/renderer/shader/resource/IMMEDIATE_DATA.d.ts.map +1 -0
  160. package/src/shade/renderer/shader/resource/IMMEDIATE_DATA.js +21 -0
  161. package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.d.ts +59 -1
  162. package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.d.ts.map +1 -1
  163. package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.js +195 -2
  164. package/src/shade/renderer/shader/type/type_has_arrays.d.ts +14 -0
  165. package/src/shade/renderer/shader/type/type_has_arrays.d.ts.map +1 -0
  166. package/src/shade/renderer/shader/type/type_has_arrays.js +33 -0
  167. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_point2.d.ts +14 -13
  168. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_point2.d.ts.map +1 -1
  169. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_point2.js +133 -129
  170. package/src/shade/renderer/view/GPUViewSkyContext.d.ts.map +1 -1
  171. package/src/shade/renderer/view/GPUViewSkyContext.js +169 -173
  172. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.d.ts.map +1 -1
  173. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.js +480 -315
  174. package/src/shade/renderer/volumetrics/NOTES.md +887 -8
  175. package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.d.ts +5 -0
  176. package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.d.ts.map +1 -1
  177. package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.js +21 -2
  178. package/src/shade/renderer/volumetrics/SceneVolumetrics.d.ts +26 -0
  179. package/src/shade/renderer/volumetrics/SceneVolumetrics.d.ts.map +1 -1
  180. package/src/shade/renderer/volumetrics/SceneVolumetrics.js +64 -21
  181. package/src/shade/renderer/volumetrics/VOLUMETRICS_METADATA_STRUCT.d.ts.map +1 -1
  182. package/src/shade/renderer/volumetrics/VOLUMETRICS_METADATA_STRUCT.js +84 -28
  183. package/src/shade/renderer/volumetrics/VOLUMETRICS_SUN_SHADOW_RESOLUTION.d.ts +98 -0
  184. package/src/shade/renderer/volumetrics/VOLUMETRICS_SUN_SHADOW_RESOLUTION.d.ts.map +1 -0
  185. package/src/shade/renderer/volumetrics/VOLUMETRICS_SUN_SHADOW_RESOLUTION.js +111 -0
  186. package/src/shade/renderer/volumetrics/build_volumetrics_sun_shadow_transform.d.ts +84 -0
  187. package/src/shade/renderer/volumetrics/build_volumetrics_sun_shadow_transform.d.ts.map +1 -0
  188. package/src/shade/renderer/volumetrics/build_volumetrics_sun_shadow_transform.js +252 -0
  189. package/src/shade/renderer/volumetrics/chunk_integrate_optical_depth.js +75 -75
  190. package/src/shade/renderer/volumetrics/chunk_volumetrics_local_light_shadow.d.ts +43 -0
  191. package/src/shade/renderer/volumetrics/chunk_volumetrics_local_light_shadow.d.ts.map +1 -0
  192. package/src/shade/renderer/volumetrics/chunk_volumetrics_local_light_shadow.js +145 -0
  193. package/src/shade/renderer/volumetrics/chunk_volumetrics_sample_froxel_extinction.d.ts +20 -0
  194. package/src/shade/renderer/volumetrics/chunk_volumetrics_sample_froxel_extinction.d.ts.map +1 -0
  195. package/src/shade/renderer/volumetrics/chunk_volumetrics_sample_froxel_extinction.js +48 -0
  196. package/src/shade/renderer/volumetrics/chunk_volumetrics_sun_optical_depth.d.ts +38 -0
  197. package/src/shade/renderer/volumetrics/chunk_volumetrics_sun_optical_depth.d.ts.map +1 -0
  198. package/src/shade/renderer/volumetrics/chunk_volumetrics_sun_optical_depth.js +130 -0
  199. package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts +18 -3
  200. package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts.map +1 -1
  201. package/src/shade/renderer/volumetrics/graph_build_volumetrics.js +193 -70
  202. package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.d.ts +16 -0
  203. package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.d.ts.map +1 -1
  204. package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.js +83 -15
  205. package/src/shade/renderer/volumetrics/shader_volumetrics_build_participating_media.d.ts.map +1 -1
  206. package/src/shade/renderer/volumetrics/shader_volumetrics_build_participating_media.js +29 -18
  207. package/src/shade/renderer/volumetrics/shader_volumetrics_build_scattering_lut.js +260 -260
  208. package/src/shade/renderer/volumetrics/shader_volumetrics_build_sun_shadow_volume.d.ts +58 -0
  209. package/src/shade/renderer/volumetrics/shader_volumetrics_build_sun_shadow_volume.d.ts.map +1 -0
  210. package/src/shade/renderer/volumetrics/shader_volumetrics_build_sun_shadow_volume.js +290 -0
  211. package/src/shade/wgsl/emulator/CPUBitmapData.d.ts +25 -0
  212. package/src/shade/wgsl/emulator/CPUBitmapData.d.ts.map +1 -1
  213. package/src/shade/wgsl/emulator/CPUBitmapData.js +33 -0
  214. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +2228 -2215
  215. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
  216. package/src/shade/wgsl/emulator/WGSLLanguageCore.js +210 -2
  217. package/src/shade/wgsl/emulator/cpu_texture_gather.d.ts +69 -0
  218. package/src/shade/wgsl/emulator/cpu_texture_gather.d.ts.map +1 -0
  219. package/src/shade/wgsl/emulator/cpu_texture_gather.js +148 -0
  220. package/src/shade/wgsl/emulator/cpu_texture_sample.d.ts +16 -0
  221. package/src/shade/wgsl/emulator/cpu_texture_sample.d.ts.map +1 -1
  222. package/src/shade/wgsl/emulator/cpu_texture_sample.js +176 -172
  223. package/src/shade/renderer/volumetrics/VOLUMETRICS_WORKGROUP_RESOLUTION.d.ts +0 -2
  224. package/src/shade/renderer/volumetrics/VOLUMETRICS_WORKGROUP_RESOLUTION.d.ts.map +0 -1
  225. package/src/shade/renderer/volumetrics/VOLUMETRICS_WORKGROUP_RESOLUTION.js +0 -1
@@ -1,261 +1,261 @@
1
- import { CAMERA_UNIFORM_STRUCT } from "../camera/CAMERA_UNIFORM_STRUCT.js";
2
- import { chunk_stbn_sample_vec3 } from "../shader/chunk/noise/chunk_stbn_sample_vec3.js";
3
- import {
4
- chunk_compute_near_far_from_projection
5
- } from "../shader/chunk/camera/chunk_compute_near_far_from_projection.js";
6
- import { chunk_rgb_to_luminance } from "../shader/chunk/color/chunk_rgb_to_luminance.js";
7
- import { chunk_texel_coordinate_to_uv } from "../shader/chunk/texture/chunk_texel_coordinate_to_uv.js";
8
- import { chunk_uv_to_texel_coordinate } from "../shader/chunk/texture/chunk_uv_to_texel_coordinate.js";
9
- import { CodeChunk } from "../shader/compiler/CodeChunk.js";
10
- import { ComputeShader } from "../shader/ComputeShader.js";
11
- import { ShaderResourceSetDescriptor } from "../shader/resource/ShaderResourceSetDescriptor.js";
12
- import { VIEW_UNIFORM_STRUCT } from "../view/VIEW_UNIFORM_STRUCT.js";
13
- import { chunk_cluster_depth_from_z_slice } from "../light/cluster/chunk_cluster_depth_from_z_slice.js";
14
- import { chunk_cluster_depth_to_z_slice } from "../light/cluster/chunk_cluster_depth_to_z_slice.js";
15
- import { VOLUMETRICS_METADATA_STRUCT } from "./VOLUMETRICS_METADATA_STRUCT.js";
16
- import { WebGPUArray } from "../shader/type/WebGPUArray.js";
17
- import { chunk_camera_ray_from_uv } from "../shader/chunk/geometry/ray/chunk_camera_ray_from_uv.js";
18
- import { chunk_directional_lights_iteration_mask } from "../shader/chunk/light/io/chunk_directional_lights_iteration_mask.js";
19
- import { chunk_get_directional_light_info_by_index } from "../shader/chunk/light/io/chunk_get_directional_light_info_by_index.js";
20
- import { chunk_sky_lut_compute_uv } from "../atmosphere/chunk_sky_lut_compute_uv.js";
21
- import { chunk_calculate_multiscattering } from "./chunk_calculate_multiscattering.js";
22
- import { chunk_camera_ray_planar_depth_scale } from "./chunk_camera_ray_planar_depth_scale.js";
23
- import { PARTICIPATING_MEDIA_VOLUME_STRUCT } from "./PARTICIPATING_MEDIA_VOLUME_STRUCT.js";
24
- import { SCENE_VOLUMETRICS_METADATA_STRUCT } from "./SCENE_VOLUMETRICS_METADATA_STRUCT.js";
25
-
26
- const resources = new ShaderResourceSetDescriptor();
27
-
28
- resources.createGroup()
29
- .addUniform("camera", CAMERA_UNIFORM_STRUCT)
30
- .addUniform("view", VIEW_UNIFORM_STRUCT)
31
- .addTexture("tSTBN_vec3", "unfilterable-float", "3d")
32
- .addUniform("volumetrics_metadata", VOLUMETRICS_METADATA_STRUCT)
33
- .addTexture("tParticipatingMedia0", "float", "3d")
34
- .addTexture("tParticipatingMedia1", "float", "3d")
35
- .addTexture("tVolumeLighting", "float", "3d") // pre-integrated volumetric light scattering
36
- // RDR2-style far field: beyond froxel_far the pass ray-marches the scene volumes directly,
37
- // lit by the directional sun + atmosphere (single scattering, no self-shadow).
38
- .addUniform("scene_volumetrics_metadata", SCENE_VOLUMETRICS_METADATA_STRUCT)
39
- .addStorageBuffer("scene_volumes", WebGPUArray.from(PARTICIPATING_MEDIA_VOLUME_STRUCT))
40
- .addStorageBuffer("light_data", WebGPUArray.u32)
41
- .addTexture("tSunTransmittance", "float", "2d")
42
- .addSampler("sLinear")
43
- .addStorageTexture("tOutput", "rgba16float", "3d", "write-only");
44
-
45
-
46
- /**
47
- * see https://github.com/GPUOpen-Effects/Hybrid-Stochastic-Reflections/blob/b3eef4be70db3522c54d289766e6db797777163c/sample/src/Shaders/Atmosphere.h#L149
48
- * see https://github.com/AirGuanZ/AtmosphereRenderer/blob/94055f272814a850aafa072555a839311b3d2924/asset/aerial_lut.hlsl#L33
49
- * @type {CodeChunk}
50
- */
51
- const body = CodeChunk.from(
52
- //language=WGSL
53
- `
54
- struct FarVolumeSample {
55
- scattering: vec3<f32>,
56
- extinction: vec3<f32>,
57
- phase_g: f32,
58
- }
59
-
60
- // Point-sample the scene participating-media volumes (coarse: full density inside the box, no edge
61
- // fade — that is a near-field nicety). Used only beyond the froxel grid.
62
- fn far_sample_volumes(world_pos: vec3<f32>) -> FarVolumeSample {
63
- var scattering = vec3<f32>(0.0);
64
- var extinction = vec3<f32>(0.0);
65
- var g_weighted = 0.0;
66
- var scattering_luma = 0.0;
67
-
68
- let count = scene_volumetrics_metadata.instance_count;
69
- for (var i = 0u; i < count; i++) {
70
- let volume = scene_volumes[i];
71
- let local = (volume.transform_inverse * vec4<f32>(world_pos, 1.0)).xyz;
72
- if (any(abs(local) > vec3<f32>(0.5))) {
73
- continue;
74
- }
75
- let weight = volume.density;
76
- let vs = volume.particle_spec.scattering * weight;
77
- let ve = volume.particle_spec.extinction * weight;
78
- scattering += vs;
79
- extinction += ve;
80
- let luma = rgb_to_luminance(vs);
81
- g_weighted += volume.particle_spec.g * luma;
82
- scattering_luma += luma;
83
- }
84
- var g = 0.0;
85
- if (scattering_luma > 0.0) {
86
- g = g_weighted / scattering_luma;
87
- }
88
- return FarVolumeSample(scattering, extinction, g);
89
- }
90
-
91
- // In-scatter (.rgb) + mono extinction (.a) at a march point. Inside the froxel grid (depth <=
92
- // froxel_far) this is the pre-lit grid sample; beyond it we ray-march the volumes directly and light
93
- // them with the directional sun + atmospheric transmittance (single scattering, no self-shadow).
94
- fn far_sample_lighting(depth: f32, uvw_grid: vec3<f32>, world_pos: vec3<f32>, ray_dir: vec3<f32>) -> vec4<f32> {
95
- if (depth <= volumetrics_metadata.froxel_far) {
96
- // The froxel grid uses its own (tighter) depth curve, so map this LUT depth to the grid
97
- // slice before sampling (identity when froxel_far == camera.far).
98
- let res_z = f32(textureDimensions(tVolumeLighting).z);
99
- let grid_slice = cluster_depth_to_z_slice(depth, volumetrics_metadata.froxel_cluster_parameters, res_z);
100
- let vl = textureSampleLevel(tVolumeLighting, sLinear, vec3<f32>(uvw_grid.xy, grid_slice / res_z), 0.0);
101
- return vec4<f32>(vl.rgb, vl.a);
102
- }
103
-
104
- let s = far_sample_volumes(world_pos);
105
-
106
- var inscatter = vec3<f32>(0.0);
107
- var directional_bits = directional_lights_iteration_mask(&light_data);
108
- while (directional_bits != 0u) {
109
- let directional_index = countTrailingZeros(directional_bits);
110
- directional_bits &= ~(1u << directional_index);
111
-
112
- let sun = get_directional_light_info_by_index(&light_data, directional_index);
113
- let u = dot(sun.direction, ray_dir);
114
- let sun_lut_uv = sky_lut_compute_uv(world_pos, sun.direction);
115
- let transmittance_atmospheric = textureSampleLevel(tSunTransmittance, sLinear, sun_lut_uv, 0.0).rgb;
116
-
117
- let ms = calculate_multiscattering(vec3<f32>(0.0), s.scattering, s.extinction, u, s.phase_g, volumetrics_metadata.ms_octave_count, volumetrics_metadata.ms_extinction_decay);
118
- inscatter += ms * transmittance_atmospheric * sun.color;
119
- }
120
-
121
- return vec4<f32>(inscatter, rgb_to_luminance(s.extinction));
122
- }
123
-
124
- @compute @workgroup_size(16, 16, 1)
125
- fn main(
126
- @builtin(global_invocation_id) voxel_coord: vec3<u32>
127
- ){
128
- let resolution = textureDimensions(tOutput);
129
-
130
- if (any(voxel_coord.xy >= resolution.xy)) {
131
- return;
132
- }
133
-
134
- let noise = stbn_sample_vec3(vec3(voxel_coord.xy, view.frame_index));
135
-
136
- let tile_uv = saturate(
137
- texel_coordinate_to_uv(vec2f(voxel_coord.xy) + vec2(noise.xy - 0.5), resolution.xy)
138
- );
139
-
140
- let camera_near_far = compute_near_far_from_projection(camera.projection_matrix);
141
- let camera_far = camera_near_far.y;
142
-
143
- // view ray for this froxel column, used to place far-field march samples in world space
144
- let view_ray = camera_ray_from_uv(tile_uv, camera);
145
-
146
- var inScatter = vec3<f32>(0.0);
147
- var throughput = vec3<f32>(1.0);
148
-
149
- // Every t below is a PLANAR view-space depth off the froxel curve, but the loop uses their
150
- // difference as a segment length in Beer's law and walks them along a unit ray to place world
151
- // positions. Both want arc length, which is longer by 1/cos(theta). Uncorrected, the optical
152
- // path is under-integrated by cos(theta) and fog thins toward the frame edges.
153
- //
154
- // Constant for the whole column, so this is one reciprocal per invocation rather than per slice.
155
- let depth_to_distance = camera_ray_planar_depth_scale(view_ray.direction, camera);
156
-
157
- // start at the near plane
158
- var t_prev = camera_near_far.x;
159
-
160
- // --- TRAPEZOIDAL SETUP ---
161
- // Prime the loop with the value at the start (Slice 0)
162
- let uvw_start = vec3<f32>(tile_uv, 0.0);
163
-
164
- // in-scatter (.rgb) and the TAA-accumulated mono extinction coefficient (.a) now share the
165
- // volume-lighting texture, so a single sample provides both.
166
- let vl_start = far_sample_lighting(camera_near_far.x, uvw_start, view_ray.origin + view_ray.direction * (camera_near_far.x * depth_to_distance), view_ray.direction);
167
- var light_prev = vl_start.rgb;
168
- var extinction_prev = vl_start.a;
169
-
170
- for (var z_index: u32 = 0u; z_index < resolution.z; z_index++ ) {
171
-
172
- // --- STORE BEFORE INTEGRATE ---
173
- // Texel z_index holds the cumulative in-scatter / transmittance integrated from the near
174
- // plane up to the NEAR face of this slab, i.e. up to depth from_z_slice(z_index). Storing
175
- // the running accumulator BEFORE integrating this slab's segment is what aligns texel
176
- // z_index with from_z_slice(z_index) -- the same convention the deferred read samples back
177
- // with (slice + 0.5) / resolution.z (see chunk_sample_volumetrics and
178
- // volumetrics_position_world_to_froxel_uvw). Storing after-integrate instead would shift
179
- // every texel one slab deeper, over-attenuating the read by a full froxel.
180
- let transmittance = rgb_to_luminance( throughput );
181
-
182
- textureStore(
183
- tOutput,
184
- vec3<u32>(voxel_coord.x, voxel_coord.y, z_index),
185
- vec4<f32>(inScatter, transmittance)
186
- );
187
-
188
- // We clamp the index to not sample outside the texture
189
- let next_index = min(z_index + 1u, resolution.z - 1u);
190
- let next_index_dithered = f32(next_index) + noise.z;
191
-
192
- // 1. Calculate Geometry
193
- let t_next = min(camera_far, cluster_depth_from_z_slice(
194
- next_index_dithered,
195
- volumetrics_metadata.cluster_parameters,
196
- f32(resolution.z)
197
- ));
198
-
199
- // planar depth difference -> arc length along the view ray (C-01b)
200
- let dt = max(0.0, t_next - t_prev) * depth_to_distance;
201
-
202
- // 2. Fetch "Next" Value (The end of this segment)
203
- let uvw_next = vec3<f32>(tile_uv, next_index_dithered / f32(resolution.z));
204
-
205
- let world_pos_next = view_ray.origin + view_ray.direction * (t_next * depth_to_distance);
206
- let vl_next = far_sample_lighting(t_next, uvw_next, world_pos_next, view_ray.direction);
207
- let media_extinction = vl_next.a;
208
- let light_next = vl_next.rgb;
209
-
210
- // TODO we should probably offset this to be linear in view-space distance
211
- const SAMPLE_CENTER_POINT = 0.5;
212
-
213
- // 3. Trapezoidal Average
214
- // Instead of taking just the center, we average start and end to form a slope
215
- let extinction_step = mix(extinction_prev, media_extinction, SAMPLE_CENTER_POINT);
216
- let light_step = mix(light_prev, light_next, SAMPLE_CENTER_POINT);
217
-
218
- // 4. Integration (Hillaire) using the averaged values. Extinction is a mono sigma_t scalar,
219
- // so transmittance is mono and broadcasts to weight the RGB throughput / in-scatter.
220
- let sample_extinction = dt * extinction_step;
221
- let sample_transmittance = exp(-sample_extinction);
222
-
223
- let media_extinction_safe = max(extinction_step, 1e-9);
224
-
225
- // "S - S * T" is the integral of constant L over the interval.
226
- // Since we trapezoid-averaged L (light_step), this is a much better approximation
227
- // for the interval than just using L_center.
228
- let light_scattering_integrated = (light_step - light_step * sample_transmittance) / media_extinction_safe;
229
-
230
- inScatter += throughput * light_scattering_integrated;
231
- throughput *= sample_transmittance;
232
-
233
- // Advance
234
- t_prev = t_next;
235
- extinction_prev = media_extinction;
236
- light_prev = light_next;
237
-
238
- }
239
- }
240
- `, [
241
- chunk_rgb_to_luminance,
242
- chunk_compute_near_far_from_projection,
243
- chunk_texel_coordinate_to_uv,
244
- chunk_uv_to_texel_coordinate,
245
- chunk_cluster_depth_from_z_slice,
246
- chunk_cluster_depth_to_z_slice,
247
- chunk_stbn_sample_vec3,
248
- chunk_camera_ray_from_uv,
249
- chunk_camera_ray_planar_depth_scale,
250
- chunk_directional_lights_iteration_mask,
251
- chunk_get_directional_light_info_by_index,
252
- chunk_sky_lut_compute_uv,
253
- chunk_calculate_multiscattering,
254
- ]
255
- )
256
-
257
- export const shader_volumetrics_build_scattering_lut = ComputeShader.from({
258
- label: "Volumetrics / build scattering lut",
259
- resources,
260
- body
1
+ import { CAMERA_UNIFORM_STRUCT } from "../camera/CAMERA_UNIFORM_STRUCT.js";
2
+ import { chunk_stbn_sample_vec3 } from "../shader/chunk/noise/chunk_stbn_sample_vec3.js";
3
+ import {
4
+ chunk_compute_near_far_from_projection
5
+ } from "../shader/chunk/camera/chunk_compute_near_far_from_projection.js";
6
+ import { chunk_rgb_to_luminance } from "../shader/chunk/color/chunk_rgb_to_luminance.js";
7
+ import { chunk_texel_coordinate_to_uv } from "../shader/chunk/texture/chunk_texel_coordinate_to_uv.js";
8
+ import { chunk_uv_to_texel_coordinate } from "../shader/chunk/texture/chunk_uv_to_texel_coordinate.js";
9
+ import { CodeChunk } from "../shader/compiler/CodeChunk.js";
10
+ import { ComputeShader } from "../shader/ComputeShader.js";
11
+ import { ShaderResourceSetDescriptor } from "../shader/resource/ShaderResourceSetDescriptor.js";
12
+ import { VIEW_UNIFORM_STRUCT } from "../view/VIEW_UNIFORM_STRUCT.js";
13
+ import { chunk_cluster_depth_from_z_slice } from "../light/cluster/chunk_cluster_depth_from_z_slice.js";
14
+ import { chunk_cluster_depth_to_z_slice } from "../light/cluster/chunk_cluster_depth_to_z_slice.js";
15
+ import { VOLUMETRICS_METADATA_STRUCT } from "./VOLUMETRICS_METADATA_STRUCT.js";
16
+ import { WebGPUArray } from "../shader/type/WebGPUArray.js";
17
+ import { chunk_camera_ray_from_uv } from "../shader/chunk/geometry/ray/chunk_camera_ray_from_uv.js";
18
+ import { chunk_directional_lights_iteration_mask } from "../shader/chunk/light/io/chunk_directional_lights_iteration_mask.js";
19
+ import { chunk_get_directional_light_info_by_index } from "../shader/chunk/light/io/chunk_get_directional_light_info_by_index.js";
20
+ import { chunk_sky_lut_compute_uv } from "../atmosphere/chunk_sky_lut_compute_uv.js";
21
+ import { chunk_calculate_multiscattering } from "./chunk_calculate_multiscattering.js";
22
+ import { chunk_camera_ray_planar_depth_scale } from "./chunk_camera_ray_planar_depth_scale.js";
23
+ import { PARTICIPATING_MEDIA_VOLUME_STRUCT } from "./PARTICIPATING_MEDIA_VOLUME_STRUCT.js";
24
+ import { SCENE_VOLUMETRICS_METADATA_STRUCT } from "./SCENE_VOLUMETRICS_METADATA_STRUCT.js";
25
+
26
+ const resources = new ShaderResourceSetDescriptor();
27
+
28
+ resources.createGroup()
29
+ .addUniform("camera", CAMERA_UNIFORM_STRUCT)
30
+ .addUniform("view", VIEW_UNIFORM_STRUCT)
31
+ .addTexture("tSTBN_vec3", "unfilterable-float", "3d")
32
+ .addUniform("volumetrics_metadata", VOLUMETRICS_METADATA_STRUCT)
33
+ .addTexture("tParticipatingMedia0", "float", "3d")
34
+ .addTexture("tParticipatingMedia1", "float", "3d")
35
+ .addTexture("tVolumeLighting", "float", "3d") // pre-integrated volumetric light scattering
36
+ // RDR2-style far field: beyond froxel_far the pass ray-marches the scene volumes directly,
37
+ // lit by the directional sun + atmosphere (single scattering, no self-shadow).
38
+ .addUniform("scene_volumetrics_metadata", SCENE_VOLUMETRICS_METADATA_STRUCT)
39
+ .addStorageBuffer("scene_volumes", WebGPUArray.from(PARTICIPATING_MEDIA_VOLUME_STRUCT))
40
+ .addStorageBuffer("light_data", WebGPUArray.u32)
41
+ .addTexture("tSunTransmittance", "float", "2d")
42
+ .addSampler("sLinear")
43
+ .addStorageTexture("tOutput", "rgba16float", "3d", "write-only");
44
+
45
+
46
+ /**
47
+ * see https://github.com/GPUOpen-Effects/Hybrid-Stochastic-Reflections/blob/b3eef4be70db3522c54d289766e6db797777163c/sample/src/Shaders/Atmosphere.h#L149
48
+ * see https://github.com/AirGuanZ/AtmosphereRenderer/blob/94055f272814a850aafa072555a839311b3d2924/asset/aerial_lut.hlsl#L33
49
+ * @type {CodeChunk}
50
+ */
51
+ const body = CodeChunk.from(
52
+ //language=WGSL
53
+ `
54
+ struct FarVolumeSample {
55
+ scattering: vec3<f32>,
56
+ extinction: vec3<f32>,
57
+ phase_g: f32,
58
+ }
59
+
60
+ // Point-sample the scene participating-media volumes (coarse: full density inside the box, no edge
61
+ // fade — that is a near-field nicety). Used only beyond the froxel grid.
62
+ fn far_sample_volumes(world_pos: vec3<f32>) -> FarVolumeSample {
63
+ var scattering = vec3<f32>(0.0);
64
+ var extinction = vec3<f32>(0.0);
65
+ var g_weighted = 0.0;
66
+ var scattering_luma = 0.0;
67
+
68
+ let count = scene_volumetrics_metadata.instance_count;
69
+ for (var i = 0u; i < count; i++) {
70
+ let volume = scene_volumes[i];
71
+ let local = (volume.transform_inverse * vec4<f32>(world_pos, 1.0)).xyz;
72
+ if (any(abs(local) > vec3<f32>(0.5))) {
73
+ continue;
74
+ }
75
+ let weight = volume.density;
76
+ let vs = volume.particle_spec.scattering * weight;
77
+ let ve = volume.particle_spec.extinction * weight;
78
+ scattering += vs;
79
+ extinction += ve;
80
+ let luma = rgb_to_luminance(vs);
81
+ g_weighted += volume.particle_spec.g * luma;
82
+ scattering_luma += luma;
83
+ }
84
+ var g = 0.0;
85
+ if (scattering_luma > 0.0) {
86
+ g = g_weighted / scattering_luma;
87
+ }
88
+ return FarVolumeSample(scattering, extinction, g);
89
+ }
90
+
91
+ // In-scatter (.rgb) + mono extinction (.a) at a march point. Inside the froxel grid (depth <=
92
+ // froxel_far) this is the pre-lit grid sample; beyond it we ray-march the volumes directly and light
93
+ // them with the directional sun + atmospheric transmittance (single scattering, no self-shadow).
94
+ fn far_sample_lighting(depth: f32, uvw_grid: vec3<f32>, world_pos: vec3<f32>, ray_dir: vec3<f32>) -> vec4<f32> {
95
+ if (depth <= volumetrics_metadata.froxel_far) {
96
+ // The froxel grid uses its own (tighter) depth curve, so map this LUT depth to the grid
97
+ // slice before sampling (identity when froxel_far == camera.far).
98
+ let res_z = f32(textureDimensions(tVolumeLighting).z);
99
+ let grid_slice = cluster_depth_to_z_slice(depth, volumetrics_metadata.froxel_cluster_parameters, res_z);
100
+ let vl = textureSampleLevel(tVolumeLighting, sLinear, vec3<f32>(uvw_grid.xy, grid_slice / res_z), 0.0);
101
+ return vec4<f32>(vl.rgb, vl.a);
102
+ }
103
+
104
+ let s = far_sample_volumes(world_pos);
105
+
106
+ var inscatter = vec3<f32>(0.0);
107
+ var directional_bits = directional_lights_iteration_mask(&light_data);
108
+ while (directional_bits != 0u) {
109
+ let directional_index = countTrailingZeros(directional_bits);
110
+ directional_bits &= ~(1u << directional_index);
111
+
112
+ let sun = get_directional_light_info_by_index(&light_data, directional_index);
113
+ let u = dot(sun.direction, ray_dir);
114
+ let sun_lut_uv = sky_lut_compute_uv(world_pos, sun.direction, vec2<f32>(textureDimensions(tSunTransmittance)));
115
+ let transmittance_atmospheric = textureSampleLevel(tSunTransmittance, sLinear, sun_lut_uv, 0.0).rgb;
116
+
117
+ let ms = calculate_multiscattering(vec3<f32>(0.0), s.scattering, s.extinction, u, s.phase_g, volumetrics_metadata.ms_octave_count, volumetrics_metadata.ms_extinction_decay);
118
+ inscatter += ms * transmittance_atmospheric * sun.color;
119
+ }
120
+
121
+ return vec4<f32>(inscatter, rgb_to_luminance(s.extinction));
122
+ }
123
+
124
+ @compute @workgroup_size(16, 16, 1)
125
+ fn main(
126
+ @builtin(global_invocation_id) voxel_coord: vec3<u32>
127
+ ){
128
+ let resolution = textureDimensions(tOutput);
129
+
130
+ if (any(voxel_coord.xy >= resolution.xy)) {
131
+ return;
132
+ }
133
+
134
+ let noise = stbn_sample_vec3(vec3(voxel_coord.xy, view.frame_index));
135
+
136
+ let tile_uv = saturate(
137
+ texel_coordinate_to_uv(vec2f(voxel_coord.xy) + vec2(noise.xy - 0.5), resolution.xy)
138
+ );
139
+
140
+ let camera_near_far = compute_near_far_from_projection(camera.projection_matrix);
141
+ let camera_far = camera_near_far.y;
142
+
143
+ // view ray for this froxel column, used to place far-field march samples in world space
144
+ let view_ray = camera_ray_from_uv(tile_uv, camera);
145
+
146
+ var inScatter = vec3<f32>(0.0);
147
+ var throughput = vec3<f32>(1.0);
148
+
149
+ // Every t below is a PLANAR view-space depth off the froxel curve, but the loop uses their
150
+ // difference as a segment length in Beer's law and walks them along a unit ray to place world
151
+ // positions. Both want arc length, which is longer by 1/cos(theta). Uncorrected, the optical
152
+ // path is under-integrated by cos(theta) and fog thins toward the frame edges.
153
+ //
154
+ // Constant for the whole column, so this is one reciprocal per invocation rather than per slice.
155
+ let depth_to_distance = camera_ray_planar_depth_scale(view_ray.direction, camera);
156
+
157
+ // start at the near plane
158
+ var t_prev = camera_near_far.x;
159
+
160
+ // --- TRAPEZOIDAL SETUP ---
161
+ // Prime the loop with the value at the start (Slice 0)
162
+ let uvw_start = vec3<f32>(tile_uv, 0.0);
163
+
164
+ // in-scatter (.rgb) and the TAA-accumulated mono extinction coefficient (.a) now share the
165
+ // volume-lighting texture, so a single sample provides both.
166
+ let vl_start = far_sample_lighting(camera_near_far.x, uvw_start, view_ray.origin + view_ray.direction * (camera_near_far.x * depth_to_distance), view_ray.direction);
167
+ var light_prev = vl_start.rgb;
168
+ var extinction_prev = vl_start.a;
169
+
170
+ for (var z_index: u32 = 0u; z_index < resolution.z; z_index++ ) {
171
+
172
+ // --- STORE BEFORE INTEGRATE ---
173
+ // Texel z_index holds the cumulative in-scatter / transmittance integrated from the near
174
+ // plane up to the NEAR face of this slab, i.e. up to depth from_z_slice(z_index). Storing
175
+ // the running accumulator BEFORE integrating this slab's segment is what aligns texel
176
+ // z_index with from_z_slice(z_index) -- the same convention the deferred read samples back
177
+ // with (slice + 0.5) / resolution.z (see chunk_sample_volumetrics and
178
+ // volumetrics_position_world_to_froxel_uvw). Storing after-integrate instead would shift
179
+ // every texel one slab deeper, over-attenuating the read by a full froxel.
180
+ let transmittance = rgb_to_luminance( throughput );
181
+
182
+ textureStore(
183
+ tOutput,
184
+ vec3<u32>(voxel_coord.x, voxel_coord.y, z_index),
185
+ vec4<f32>(inScatter, transmittance)
186
+ );
187
+
188
+ // We clamp the index to not sample outside the texture
189
+ let next_index = min(z_index + 1u, resolution.z - 1u);
190
+ let next_index_dithered = f32(next_index) + noise.z;
191
+
192
+ // 1. Calculate Geometry
193
+ let t_next = min(camera_far, cluster_depth_from_z_slice(
194
+ next_index_dithered,
195
+ volumetrics_metadata.cluster_parameters,
196
+ f32(resolution.z)
197
+ ));
198
+
199
+ // planar depth difference -> arc length along the view ray (C-01b)
200
+ let dt = max(0.0, t_next - t_prev) * depth_to_distance;
201
+
202
+ // 2. Fetch "Next" Value (The end of this segment)
203
+ let uvw_next = vec3<f32>(tile_uv, next_index_dithered / f32(resolution.z));
204
+
205
+ let world_pos_next = view_ray.origin + view_ray.direction * (t_next * depth_to_distance);
206
+ let vl_next = far_sample_lighting(t_next, uvw_next, world_pos_next, view_ray.direction);
207
+ let media_extinction = vl_next.a;
208
+ let light_next = vl_next.rgb;
209
+
210
+ // TODO we should probably offset this to be linear in view-space distance
211
+ const SAMPLE_CENTER_POINT = 0.5;
212
+
213
+ // 3. Trapezoidal Average
214
+ // Instead of taking just the center, we average start and end to form a slope
215
+ let extinction_step = mix(extinction_prev, media_extinction, SAMPLE_CENTER_POINT);
216
+ let light_step = mix(light_prev, light_next, SAMPLE_CENTER_POINT);
217
+
218
+ // 4. Integration (Hillaire) using the averaged values. Extinction is a mono sigma_t scalar,
219
+ // so transmittance is mono and broadcasts to weight the RGB throughput / in-scatter.
220
+ let sample_extinction = dt * extinction_step;
221
+ let sample_transmittance = exp(-sample_extinction);
222
+
223
+ let media_extinction_safe = max(extinction_step, 1e-9);
224
+
225
+ // "S - S * T" is the integral of constant L over the interval.
226
+ // Since we trapezoid-averaged L (light_step), this is a much better approximation
227
+ // for the interval than just using L_center.
228
+ let light_scattering_integrated = (light_step - light_step * sample_transmittance) / media_extinction_safe;
229
+
230
+ inScatter += throughput * light_scattering_integrated;
231
+ throughput *= sample_transmittance;
232
+
233
+ // Advance
234
+ t_prev = t_next;
235
+ extinction_prev = media_extinction;
236
+ light_prev = light_next;
237
+
238
+ }
239
+ }
240
+ `, [
241
+ chunk_rgb_to_luminance,
242
+ chunk_compute_near_far_from_projection,
243
+ chunk_texel_coordinate_to_uv,
244
+ chunk_uv_to_texel_coordinate,
245
+ chunk_cluster_depth_from_z_slice,
246
+ chunk_cluster_depth_to_z_slice,
247
+ chunk_stbn_sample_vec3,
248
+ chunk_camera_ray_from_uv,
249
+ chunk_camera_ray_planar_depth_scale,
250
+ chunk_directional_lights_iteration_mask,
251
+ chunk_get_directional_light_info_by_index,
252
+ chunk_sky_lut_compute_uv,
253
+ chunk_calculate_multiscattering,
254
+ ]
255
+ )
256
+
257
+ export const shader_volumetrics_build_scattering_lut = ComputeShader.from({
258
+ label: "Volumetrics / build scattering lut",
259
+ resources,
260
+ body
261
261
  });
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Q-04. Builds one cascade of the sun's optical-depth volume so the lighting pass can read a
3
+ * froxel's media self-shadowing in one fetch instead of marching 16 steps toward the sun for it.
4
+ *
5
+ * The structure is a light-aligned voxel grid holding a prefix sum of extinction along the sun
6
+ * axis — in the shadow-map taxonomy an opacity shadow map (Kim & Neumann 2001) with enough slices
7
+ * and hardware trilinear reconstruction that the slice artefacts it is remembered for do not
8
+ * appear, and exact rather than approximate for the piecewise-constant field a voxel grid is.
9
+ * Enshrouded (GPC 2024) ships the same thing, cascaded: 3D volumes with their z axis on the primary
10
+ * light, initialised from the participating media and then integrated by a 2D dispatch per column,
11
+ * each cascade seeded from the one outside it. Frostbite (SIGGRAPH 2015) cascades its extinction
12
+ * volume the same way, three clip-map levels of it, for the same reason.
13
+ *
14
+ * Fourier Opacity Mapping is the better-known answer and is the wrong one here. It exists because
15
+ * the medium usually arrives as unordered rasterized particles, where the only thing you can do is
16
+ * accumulate a fixed-size, order-independent, additively-blendable summary — and it pays for that
17
+ * with ringing at sharp density transitions. This engine's medium is already a 3D texture with a
18
+ * defined order along any axis, so the scan is available, exact, and has nothing to ring.
19
+ *
20
+ * Two shaders, written out: the outermost cascade, whose scan starts at zero, and every cascade
21
+ * inside it, whose scan starts from the cascade outside. That is a difference in *bindings* — the
22
+ * second reads a texture the first does not have — so it cannot be data. Which cascade is being
23
+ * built can be, and is: it arrives as immediate data, so the inner cascades share one pipeline.
24
+ *
25
+ * @see chunk_volumetrics_sun_optical_depth for the read side
26
+ * @see build_volumetrics_sun_shadow_transform for where each box is placed, and why it is stable
27
+ */
28
+ /**
29
+ * Which cascade a dispatch is building.
30
+ *
31
+ * Per-dispatch, uniform, read-only and eight bytes, which is the shape immediate data exists for:
32
+ * no pooled buffer, no staging copy, no bind group.
33
+ *
34
+ * `sub_steps` travels with it rather than being derived in the shader because it is a property of
35
+ * the cascade ladder rather than of the box: {@link volumetrics_sun_shadow_sub_steps} is where the
36
+ * rule that it is even and at least two lives, and nothing in the metadata lets the shader
37
+ * re-derive it.
38
+ *
39
+ * @type {WebGPUStruct}
40
+ */
41
+ export const VOLUMETRICS_SUN_SHADOW_BUILD_SETTINGS: WebGPUStruct;
42
+ /**
43
+ * The outermost cascade. Nothing is outside it, so its scan starts from zero and it binds no outer
44
+ * volume.
45
+ *
46
+ * @type {ComputeShader}
47
+ */
48
+ export const shader_volumetrics_build_sun_shadow_volume_outermost: ComputeShader;
49
+ /**
50
+ * Every cascade but the outermost, seeded from the one outside it. Which one that is arrives as
51
+ * immediate data, so the whole inner ladder shares this pipeline.
52
+ *
53
+ * @type {ComputeShader}
54
+ */
55
+ export const shader_volumetrics_build_sun_shadow_volume_seeded: ComputeShader;
56
+ import { WebGPUStruct } from "../shader/type/WebGPUStruct.js";
57
+ import { ComputeShader } from "../shader/ComputeShader.js";
58
+ //# sourceMappingURL=shader_volumetrics_build_sun_shadow_volume.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shader_volumetrics_build_sun_shadow_volume.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/volumetrics/shader_volumetrics_build_sun_shadow_volume.js"],"names":[],"mappings":"AAYA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH;;;;;;;;;;;;GAYG;AACH,oDAFU,YAAY,CAKkB;AAaxC;;;;;GAKG;AACH,mEAFU,aAAa,CA8FpB;AAcH;;;;;GAKG;AACH,gEAFU,aAAa,CAwGpB;6BA3R0B,gCAAgC;8BAH/B,4BAA4B"}