@woosh/meep-engine 3.53.0 → 3.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/package.json +1 -1
  2. package/src/core/math/noise/worley_noise_2d.d.ts +14 -0
  3. package/src/core/math/noise/worley_noise_2d.d.ts.map +1 -0
  4. package/src/core/math/noise/worley_noise_2d.js +74 -0
  5. package/src/engine/graphics/texture/atlas/CachingTextureAtlas.d.ts +6 -1
  6. package/src/engine/graphics/texture/atlas/CachingTextureAtlas.d.ts.map +1 -1
  7. package/src/engine/graphics/texture/atlas/CachingTextureAtlas.js +363 -325
  8. package/src/engine/graphics/texture/atlas/ManagedTextureAtlas.d.ts +93 -14
  9. package/src/engine/graphics/texture/atlas/ManagedTextureAtlas.d.ts.map +1 -1
  10. package/src/engine/graphics/texture/atlas/ManagedTextureAtlas.js +322 -183
  11. package/src/engine/graphics/texture/atlas/ReferencedTextureAtlas.d.ts +9 -0
  12. package/src/engine/graphics/texture/atlas/ReferencedTextureAtlas.d.ts.map +1 -1
  13. package/src/engine/graphics/texture/atlas/ReferencedTextureAtlas.js +110 -83
  14. package/src/engine/graphics/texture/atlas/SpriteAtlas.d.ts +42 -0
  15. package/src/engine/graphics/texture/atlas/SpriteAtlas.d.ts.map +1 -0
  16. package/src/engine/{graphics3/decal/DecalAtlas.js → graphics/texture/atlas/SpriteAtlas.js} +202 -194
  17. package/src/engine/{graphics3/decal/DecalAtlasFill.d.ts → graphics/texture/atlas/SpriteAtlasFill.d.ts} +4 -4
  18. package/src/engine/graphics/texture/atlas/SpriteAtlasFill.d.ts.map +1 -0
  19. package/src/engine/graphics/texture/atlas/SpriteAtlasFill.js +30 -0
  20. package/src/engine/graphics/texture/atlas/TextureAtlas.d.ts +7 -0
  21. package/src/engine/graphics/texture/atlas/TextureAtlas.d.ts.map +1 -1
  22. package/src/engine/graphics/texture/atlas/TextureAtlas.js +11 -1
  23. package/src/engine/{graphics3/decal/decal_atlas_border.d.ts → graphics/texture/atlas/sprite_atlas_border.d.ts} +12 -12
  24. package/src/engine/graphics/texture/atlas/sprite_atlas_border.d.ts.map +1 -0
  25. package/src/engine/{graphics3/decal/decal_atlas_border.js → graphics/texture/atlas/sprite_atlas_border.js} +69 -69
  26. package/src/engine/graphics3/DecalSystem.d.ts.map +1 -1
  27. package/src/engine/graphics3/DecalSystem.js +447 -450
  28. package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts +9 -22
  29. package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts.map +1 -1
  30. package/src/engine/graphics3/GPUParticleEmitterSystem.js +892 -951
  31. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  32. package/src/generation/foliage/FOLIAGE_DESIGN.md +2 -1
  33. package/src/generation/foliage/config/BarkMaterialConfig.d.ts +4 -3
  34. package/src/generation/foliage/config/BarkMaterialConfig.d.ts.map +1 -1
  35. package/src/generation/foliage/config/BarkMaterialConfig.js +6 -6
  36. package/src/generation/foliage/config/FoliageBarkType.d.ts +11 -0
  37. package/src/generation/foliage/config/FoliageBarkType.d.ts.map +1 -0
  38. package/src/generation/foliage/config/FoliageBarkType.js +16 -0
  39. package/src/generation/foliage/config/FoliageMaterialConfig.d.ts +58 -5
  40. package/src/generation/foliage/config/FoliageMaterialConfig.d.ts.map +1 -1
  41. package/src/generation/foliage/config/FoliageMaterialConfig.js +279 -181
  42. package/src/generation/foliage/index.d.ts +2 -1
  43. package/src/generation/foliage/index.js +44 -43
  44. package/src/generation/foliage/material/make_bark_material.d.ts.map +1 -1
  45. package/src/generation/foliage/material/make_bark_material.js +23 -95
  46. package/src/generation/foliage/material/make_foliage_material.d.ts +29 -3
  47. package/src/generation/foliage/material/make_foliage_material.d.ts.map +1 -1
  48. package/src/generation/foliage/material/make_foliage_material.js +605 -359
  49. package/src/generation/foliage/mesh/branch_collar_project.d.ts.map +1 -1
  50. package/src/generation/foliage/mesh/branch_collar_project.js +205 -203
  51. package/src/generation/foliage/mesh/foliage_collect_attachments.d.ts +15 -0
  52. package/src/generation/foliage/mesh/foliage_collect_attachments.d.ts.map +1 -1
  53. package/src/generation/foliage/mesh/foliage_collect_attachments.js +569 -502
  54. package/src/generation/foliage/mesh/tree_mesh_build_bark.d.ts.map +1 -1
  55. package/src/generation/foliage/mesh/tree_mesh_build_bark.js +1049 -1047
  56. package/src/generation/foliage/mesh/tree_mesh_build_foliage.d.ts +2 -1
  57. package/src/generation/foliage/mesh/tree_mesh_build_foliage.d.ts.map +1 -1
  58. package/src/generation/foliage/mesh/tree_mesh_build_foliage.js +454 -415
  59. package/src/generation/foliage/presets/make_tree_species_preset.d.ts.map +1 -1
  60. package/src/generation/foliage/presets/make_tree_species_preset.js +870 -837
  61. package/src/generation/foliage/skeleton/crown_envelope_evaluate.js +121 -121
  62. package/src/generation/foliage/skeleton/tree_skeleton_compute_pipe_radii.d.ts +10 -7
  63. package/src/generation/foliage/skeleton/tree_skeleton_compute_pipe_radii.d.ts.map +1 -1
  64. package/src/generation/foliage/skeleton/tree_skeleton_compute_pipe_radii.js +206 -179
  65. package/src/generation/foliage/skeleton/tree_skeleton_grow.d.ts.map +1 -1
  66. package/src/generation/foliage/skeleton/tree_skeleton_grow.js +1871 -1104
  67. package/src/shade/RENDERER_CONTRACT.md +5 -2
  68. package/src/shade/device/SHADE_DEVICE_FLOOR.d.ts.map +1 -1
  69. package/src/shade/device/SHADE_DEVICE_FLOOR.js +2 -0
  70. package/src/shade/playground/foliage_wind/foliage_world.d.ts +6 -0
  71. package/src/shade/playground/foliage_wind/foliage_world.d.ts.map +1 -1
  72. package/src/shade/playground/foliage_wind/foliage_world.js +602 -593
  73. package/src/shade/playground/foliage_wind/index.html +260 -254
  74. package/src/shade/playground/foliage_wind/main.js +275 -263
  75. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  76. package/src/shade/renderer/Renderer.js +1 -1
  77. package/src/shade/renderer/fluid/GPUFluidCascade.d.ts +3 -3
  78. package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts +1 -1
  79. package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts +6 -6
  80. package/src/shade/renderer/fluid/pressure/GPUFluidPressureSolve.d.ts +1 -1
  81. package/src/shade/renderer/fluid/query/GPUFluidQueries.d.ts +1 -1
  82. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +1 -1
  83. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts +3 -3
  84. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts.map +1 -1
  85. package/src/shade/renderer/hiz/HierarchicalZBuffer.js +350 -294
  86. package/src/shade/renderer/hiz/generate_downsampler_code.d.ts +0 -2
  87. package/src/shade/renderer/hiz/generate_downsampler_code.d.ts.map +1 -1
  88. package/src/shade/renderer/hiz/generate_downsampler_code.js +99 -100
  89. package/src/shade/renderer/hiz/hzb_pick_alignment.d.ts +15 -0
  90. package/src/shade/renderer/hiz/hzb_pick_alignment.d.ts.map +1 -0
  91. package/src/shade/renderer/hiz/hzb_pick_alignment.js +148 -0
  92. package/src/shade/renderer/hiz/hzb_plan_reduction_steps.d.ts +28 -0
  93. package/src/shade/renderer/hiz/hzb_plan_reduction_steps.d.ts.map +1 -0
  94. package/src/shade/renderer/hiz/hzb_plan_reduction_steps.js +58 -0
  95. package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts +5 -5
  96. package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts.map +1 -1
  97. package/src/shade/renderer/hiz/shader_hzb_reduce_any.js +57 -67
  98. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts +3 -3
  99. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts.map +1 -1
  100. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.js +111 -112
  101. package/src/shade/renderer/hiz/shader_hzb_spd.d.ts +5 -0
  102. package/src/shade/renderer/hiz/shader_hzb_spd.d.ts.map +1 -0
  103. package/src/shade/renderer/hiz/shader_hzb_spd.js +160 -0
  104. package/src/shade/renderer/light/GPULightCollection.d.ts +8 -0
  105. package/src/shade/renderer/light/GPULightCollection.d.ts.map +1 -1
  106. package/src/shade/renderer/light/GPULightCollection.js +67 -0
  107. package/src/shade/renderer/light/environment/FAR_FIELD_IRRADIANCE_SH_RESOURCE.d.ts +7 -0
  108. package/src/shade/renderer/light/environment/FAR_FIELD_IRRADIANCE_SH_RESOURCE.d.ts.map +1 -0
  109. package/src/shade/renderer/light/environment/FAR_FIELD_IRRADIANCE_SH_RESOURCE.js +13 -0
  110. package/src/shade/renderer/light/environment/shader_project_far_field_irradiance_sh.d.ts +10 -0
  111. package/src/shade/renderer/light/environment/shader_project_far_field_irradiance_sh.d.ts.map +1 -0
  112. package/src/shade/renderer/light/environment/shader_project_far_field_irradiance_sh.js +72 -0
  113. package/src/shade/renderer/light/world_cluster/WORLD_LIGHT_CLUSTER.d.ts +39 -0
  114. package/src/shade/renderer/light/world_cluster/WORLD_LIGHT_CLUSTER.d.ts.map +1 -0
  115. package/src/shade/renderer/light/world_cluster/WORLD_LIGHT_CLUSTER.js +90 -0
  116. package/src/shade/renderer/light/world_cluster/chunk_world_light_cluster_lookup.d.ts +12 -0
  117. package/src/shade/renderer/light/world_cluster/chunk_world_light_cluster_lookup.d.ts.map +1 -0
  118. package/src/shade/renderer/light/world_cluster/chunk_world_light_cluster_lookup.js +56 -0
  119. package/src/shade/renderer/light/world_cluster/graph_build_world_light_clusters.d.ts +31 -0
  120. package/src/shade/renderer/light/world_cluster/graph_build_world_light_clusters.d.ts.map +1 -0
  121. package/src/shade/renderer/light/world_cluster/graph_build_world_light_clusters.js +196 -0
  122. package/src/shade/renderer/light/world_cluster/shader_world_cluster_assign_lights.d.ts +10 -0
  123. package/src/shade/renderer/light/world_cluster/shader_world_cluster_assign_lights.d.ts.map +1 -0
  124. package/src/shade/renderer/light/world_cluster/shader_world_cluster_assign_lights.js +163 -0
  125. package/src/shade/renderer/light/world_cluster/shader_world_cluster_cull_lights.d.ts +4 -0
  126. package/src/shade/renderer/light/world_cluster/shader_world_cluster_cull_lights.d.ts.map +1 -0
  127. package/src/shade/renderer/light/world_cluster/shader_world_cluster_cull_lights.js +137 -0
  128. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  129. package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
  130. package/src/shade/renderer/particles/GPUParticleSystem.js +4 -3
  131. package/src/shade/renderer/particles/graph_particles_avboit.d.ts +4 -3
  132. package/src/shade/renderer/particles/graph_particles_avboit.d.ts.map +1 -1
  133. package/src/shade/renderer/particles/graph_particles_avboit.js +28 -11
  134. package/src/shade/renderer/particles/shaders/chunk_particle_lighting.d.ts +9 -13
  135. package/src/shade/renderer/particles/shaders/chunk_particle_lighting.d.ts.map +1 -1
  136. package/src/shade/renderer/particles/shaders/chunk_particle_lighting.js +25 -20
  137. package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.d.ts.map +1 -1
  138. package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.js +21 -38
  139. package/src/shade/renderer/postprocess/denoise/fragment_shader_denoise_ao.d.ts.map +1 -1
  140. package/src/shade/renderer/postprocess/denoise/fragment_shader_denoise_ao.js +154 -147
  141. package/src/shade/renderer/postprocess/gtao/GTAO.d.ts +2 -1
  142. package/src/shade/renderer/postprocess/gtao/GTAO.d.ts.map +1 -1
  143. package/src/shade/renderer/postprocess/gtao/GTAO.js +5 -1
  144. package/src/shade/renderer/postprocess/gtao/GTAO_CONSTANTS.d.ts +8 -0
  145. package/src/shade/renderer/postprocess/gtao/GTAO_CONSTANTS.d.ts.map +1 -1
  146. package/src/shade/renderer/postprocess/gtao/GTAO_CONSTANTS.js +10 -1
  147. package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.d.ts.map +1 -1
  148. package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.js +5 -0
  149. package/src/shade/renderer/postprocess/gtao/chunk_gtao_compute_mip_level.d.ts +22 -0
  150. package/src/shade/renderer/postprocess/gtao/chunk_gtao_compute_mip_level.d.ts.map +1 -0
  151. package/src/shade/renderer/postprocess/gtao/chunk_gtao_compute_mip_level.js +33 -0
  152. package/src/shade/renderer/postprocess/gtao/chunk_gtao_resolve_hzb_depth.d.ts +22 -0
  153. package/src/shade/renderer/postprocess/gtao/chunk_gtao_resolve_hzb_depth.d.ts.map +1 -0
  154. package/src/shade/renderer/postprocess/gtao/chunk_gtao_resolve_hzb_depth.js +56 -0
  155. package/src/shade/renderer/postprocess/gtao/chunk_gtao_sample_depth.d.ts +13 -0
  156. package/src/shade/renderer/postprocess/gtao/chunk_gtao_sample_depth.d.ts.map +1 -0
  157. package/src/shade/renderer/postprocess/gtao/chunk_gtao_sample_depth.js +42 -0
  158. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.d.ts +2 -1
  159. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.d.ts.map +1 -1
  160. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.js +382 -357
  161. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.d.ts.map +1 -1
  162. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.js +414 -410
  163. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  164. package/src/shade/renderer/postprocess/ssr/prefilter/NOTES.md +0 -7
  165. package/src/shade/renderer/shader/chunk/depth/chunk_query_depth_from_screen_space_bb.d.ts.map +1 -1
  166. package/src/shade/renderer/shader/chunk/depth/chunk_query_depth_from_screen_space_bb.js +62 -62
  167. package/src/shade/renderer/shader/chunk/random/lowbias/chunk_hash_lowbias32_vec2u.d.ts +6 -0
  168. package/src/shade/renderer/shader/chunk/random/lowbias/chunk_hash_lowbias32_vec2u.d.ts.map +1 -0
  169. package/src/shade/renderer/shader/chunk/random/lowbias/chunk_hash_lowbias32_vec2u.js +19 -0
  170. package/src/shade/renderer/shader/chunk/screen_space/chunk_ffx_sssr_get_mip_resolution.d.ts.map +1 -1
  171. package/src/shade/renderer/shader/chunk/screen_space/chunk_ffx_sssr_get_mip_resolution.js +16 -10
  172. package/src/shade/renderer/shader/chunk/screen_space/chunk_ffx_sssr_hierarchical_raymarch.d.ts.map +1 -1
  173. package/src/shade/renderer/shader/chunk/screen_space/chunk_ffx_sssr_hierarchical_raymarch.js +118 -117
  174. package/src/shade/renderer/texture/atlas/GPUTileAtlas3D.d.ts +41 -7
  175. package/src/shade/renderer/texture/atlas/GPUTileAtlas3D.d.ts.map +1 -1
  176. package/src/shade/renderer/texture/atlas/GPUTileAtlas3D.js +162 -58
  177. package/src/shade/renderer/volumetrics/GPUSceneVolumetrics.js +1 -1
  178. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.d.ts +10 -12
  179. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.d.ts.map +1 -1
  180. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.js +583 -606
  181. package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts +1 -1
  182. package/src/shade/renderer/volumetrics/graph_build_volumetrics.js +640 -640
  183. package/src/shade/renderer/volumetrics/irradiance_clipmap/GPUIrradianceClipmap.d.ts +113 -0
  184. package/src/shade/renderer/volumetrics/irradiance_clipmap/GPUIrradianceClipmap.d.ts.map +1 -0
  185. package/src/shade/renderer/volumetrics/irradiance_clipmap/GPUIrradianceClipmap.js +484 -0
  186. package/src/shade/renderer/volumetrics/irradiance_clipmap/IRRADIANCE_CLIPMAP.d.ts +50 -0
  187. package/src/shade/renderer/volumetrics/irradiance_clipmap/IRRADIANCE_CLIPMAP.d.ts.map +1 -0
  188. package/src/shade/renderer/volumetrics/irradiance_clipmap/IRRADIANCE_CLIPMAP.js +60 -0
  189. package/src/shade/renderer/volumetrics/irradiance_clipmap/IRRADIANCE_CLIPMAP_STRUCT.d.ts +25 -0
  190. package/src/shade/renderer/volumetrics/irradiance_clipmap/IRRADIANCE_CLIPMAP_STRUCT.d.ts.map +1 -0
  191. package/src/shade/renderer/volumetrics/irradiance_clipmap/IRRADIANCE_CLIPMAP_STRUCT.js +47 -0
  192. package/src/shade/renderer/volumetrics/irradiance_clipmap/IrradianceCascadePlacement.d.ts +74 -0
  193. package/src/shade/renderer/volumetrics/irradiance_clipmap/IrradianceCascadePlacement.d.ts.map +1 -0
  194. package/src/shade/renderer/volumetrics/irradiance_clipmap/IrradianceCascadePlacement.js +173 -0
  195. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_gi_sample.d.ts +26 -0
  196. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_gi_sample.d.ts.map +1 -0
  197. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_gi_sample.js +83 -0
  198. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_local_light.d.ts +36 -0
  199. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_local_light.d.ts.map +1 -0
  200. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_local_light.js +129 -0
  201. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_sample.d.ts +121 -0
  202. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_sample.d.ts.map +1 -0
  203. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_sample.js +370 -0
  204. package/src/shade/renderer/volumetrics/irradiance_clipmap/shader_irradiance_clipmap_build.d.ts +16 -0
  205. package/src/shade/renderer/volumetrics/irradiance_clipmap/shader_irradiance_clipmap_build.d.ts.map +1 -0
  206. package/src/shade/renderer/volumetrics/irradiance_clipmap/shader_irradiance_clipmap_build.js +191 -0
  207. package/src/shade/renderer/wind/GPUSceneWind.d.ts +1 -1
  208. package/src/shade/renderer/wind/GPUSceneWind.js +1 -1
  209. package/src/engine/graphics3/decal/DecalAtlas.d.ts +0 -43
  210. package/src/engine/graphics3/decal/DecalAtlas.d.ts.map +0 -1
  211. package/src/engine/graphics3/decal/DecalAtlasFill.d.ts.map +0 -1
  212. package/src/engine/graphics3/decal/DecalAtlasFill.js +0 -32
  213. package/src/engine/graphics3/decal/decal_atlas_border.d.ts.map +0 -1
  214. package/src/shade/renderer/postprocess/gtao/graph_build_depth_mipmap.d.ts +0 -26
  215. package/src/shade/renderer/postprocess/gtao/graph_build_depth_mipmap.d.ts.map +0 -1
  216. package/src/shade/renderer/postprocess/gtao/graph_build_depth_mipmap.js +0 -268
  217. package/src/shade/renderer/postprocess/ssr/copy_shader_f32.d.ts +0 -3
  218. package/src/shade/renderer/postprocess/ssr/copy_shader_f32.d.ts.map +0 -1
  219. package/src/shade/renderer/postprocess/ssr/copy_shader_f32.js +0 -32
  220. package/src/shade/renderer/postprocess/ssr/depth_max_shader.d.ts +0 -17
  221. package/src/shade/renderer/postprocess/ssr/depth_max_shader.d.ts.map +0 -1
  222. package/src/shade/renderer/postprocess/ssr/depth_max_shader.js +0 -98
  223. package/src/shade/renderer/postprocess/ssr/graph_build_depth_max_pyramid.d.ts +0 -23
  224. package/src/shade/renderer/postprocess/ssr/graph_build_depth_max_pyramid.d.ts.map +0 -1
  225. package/src/shade/renderer/postprocess/ssr/graph_build_depth_max_pyramid.js +0 -118
  226. package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_froxel_centre_ws.d.ts +0 -15
  227. package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_froxel_centre_ws.d.ts.map +0 -1
  228. package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_froxel_centre_ws.js +0 -49
  229. package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_gi.d.ts +0 -17
  230. package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_gi.d.ts.map +0 -1
  231. package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_gi.js +0 -50
  232. package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_gi_sample.d.ts +0 -35
  233. package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_gi_sample.d.ts.map +0 -1
  234. package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_gi_sample.js +0 -105
  235. package/src/shade/renderer/volumetrics/gi/graph_build_froxel_gi.d.ts +0 -35
  236. package/src/shade/renderer/volumetrics/gi/graph_build_froxel_gi.d.ts.map +0 -1
  237. package/src/shade/renderer/volumetrics/gi/graph_build_froxel_gi.js +0 -80
  238. package/src/shade/renderer/volumetrics/gi/shader_volumetrics_gi.d.ts +0 -10
  239. package/src/shade/renderer/volumetrics/gi/shader_volumetrics_gi.d.ts.map +0 -1
  240. package/src/shade/renderer/volumetrics/gi/shader_volumetrics_gi.js +0 -51
  241. package/src/shade/renderer/volumetrics/gi/volumetrics_gi_resources.d.ts +0 -28
  242. package/src/shade/renderer/volumetrics/gi/volumetrics_gi_resources.d.ts.map +0 -1
  243. package/src/shade/renderer/volumetrics/gi/volumetrics_gi_resources.js +0 -39
  244. package/src/shade/renderer/volumetrics/graph_build_froxel_irradiance.d.ts +0 -48
  245. package/src/shade/renderer/volumetrics/graph_build_froxel_irradiance.d.ts.map +0 -1
  246. package/src/shade/renderer/volumetrics/graph_build_froxel_irradiance.js +0 -121
  247. package/src/shade/renderer/volumetrics/shader_volumetrics_build_irradiance.d.ts +0 -28
  248. package/src/shade/renderer/volumetrics/shader_volumetrics_build_irradiance.d.ts.map +0 -1
  249. package/src/shade/renderer/volumetrics/shader_volumetrics_build_irradiance.js +0 -237
@@ -1,640 +1,640 @@
1
- import { assert } from "../../../core/assert.js";
2
- import { SamplerDescriptor } from "../../descriptor/texture/SamplerDescriptor.js";
3
- import { BufferResourceDescriptor } from "../../device/graph/BufferResourceDescriptor.js";
4
- import { TextureResourceDescriptor } from "../../device/graph/TextureResourceDescriptor.js";
5
- import { ShaderSwitchSet } from "../shader/compiler/ShaderSwitchSet.js";
6
- import { graph_compute_pass } from "../shader/graph/compute/graph_compute_pass.js";
7
- import { graph_import_buffer } from "../shader/graph/graph_import_buffer.js";
8
- import { graph_import_texture } from "../shader/graph/graph_import_texture.js";
9
- import { SHADOWMAP_COMPARE_SAMPLER_DESCRIPTOR } from "../shadow/map/SHADOWMAP_COMPARE_SAMPLER_DESCRIPTOR.js";
10
- import { STATIC_GRAPHICS_ENGINE_ASSETS } from "../STATIC_GRAPHICS_ENGINE_ASSETS.js";
11
- import { DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR } from "../texture/DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR.js";
12
- import { WIND_FIELD_UNIFORM } from "../wind/WIND_FIELD_UNIFORM.js";
13
- import { volumetrics_sun_shadow_texture_name } from "./chunk_volumetrics_sun_optical_depth.js";
14
- import { PARTICIPATING_MEDIA_NOISE_SAMPLER_DESCRIPTOR } from "./noise/PARTICIPATING_MEDIA_NOISE.js";
15
- import { VOLUMETRICS_MEDIA_WORKGROUP_RESOLUTION } from "./chunk_volumetrics_media_group_list.js";
16
- import { shader_volumetrics_bin_participating_media } from "./shader_volumetrics_bin_participating_media.js";
17
- import {
18
- shader_volumetrics_split_participating_media,
19
- VOLUMETRICS_MEDIA_SPLIT_LANES
20
- } from "./shader_volumetrics_split_participating_media.js";
21
- import { shader_volumetrics_build_lighting } from "./shader_volumetrics_build_lighting.js";
22
- import { shader_volumetrics_build_participating_media } from "./shader_volumetrics_build_participating_media.js";
23
- import { shader_volumetrics_build_scattering_lut } from "./shader_volumetrics_build_scattering_lut.js";
24
- import {
25
- shader_volumetrics_build_sun_shadow_volume_outermost,
26
- shader_volumetrics_build_sun_shadow_volume_seeded
27
- } from "./shader_volumetrics_build_sun_shadow_volume.js";
28
- import { shader_volumetrics_taa } from "./taa/shader_volumetrics_taa.js";
29
- import {
30
- VOLUMETRICS_SUN_SHADOW_CASCADE_COUNT,
31
- VOLUMETRICS_SUN_SHADOW_WORKGROUP_RESOLUTION,
32
- volumetrics_sun_shadow_sub_steps
33
- } from "./VOLUMETRICS_SUN_SHADOW_RESOLUTION.js";
34
-
35
- /**
36
- *
37
- * @param {Object} options
38
- * @param {FrameGraph} options.graph
39
- * @param {GPUCameraContext} options.camera
40
- * @param {GPUSceneContext} options.scene
41
- * @param {GPUViewContext} options.view
42
- * @param {number} options.volumetrics_metadata
43
- * @param {number[]} options.resolution output resolution
44
- * @param {ShaderSwitchSet} options.shader_switches the renderer's; the build adds the wind field's shape
45
- */
46
- function graph_integrate_participating_media({
47
- graph,
48
- camera,
49
- scene,
50
- view,
51
- volumetrics_metadata,
52
- resolution,
53
- shader_switches,
54
- }) {
55
-
56
- const data = {};
57
-
58
- // the media build's workgroups, each of which the bin gives a list of the volumes it may see
59
- const groups = [
60
- Math.ceil(resolution[0] / VOLUMETRICS_MEDIA_WORKGROUP_RESOLUTION[0]),
61
- Math.ceil(resolution[1] / VOLUMETRICS_MEDIA_WORKGROUP_RESOLUTION[1]),
62
- resolution[2],
63
- ];
64
-
65
- // A bit a volume. Sized from the scene's volumes, which the table's rows never outnumber; the
66
- // shaders take the volume count off the table and the words a list gets off this buffer.
67
- const volume_count = scene.volumetrics.source.volumes.length;
68
- const list_words = Math.ceil(volume_count / 32);
69
- const group_lists_bytes = 4 * Math.max(4, groups[0] * groups[1] * groups[2] * list_words);
70
-
71
- // the near and the far field: a count, then a row for every volume either may hold
72
- const field_bytes = 4 * Math.max(4, 1 + volume_count);
73
-
74
- const pass = graph.add(
75
- "aerial lut",
76
- data,
77
- /**
78
- *
79
- * @param {} data
80
- * @param {FramePassResources} resources
81
- * @param {GraphicsExecutionContext} context
82
- */
83
- (data, resources, context) => {
84
-
85
- /**
86
- * @type {GPUTextureContext}
87
- */
88
- const output0 = resources.get(data.output0);
89
- /**
90
- * @type {GPUTextureContext}
91
- */
92
- const output1 = resources.get(data.output1);
93
-
94
- const group_lists = resources.get(data.group_lists);
95
- const near_field = resources.get(data.near_field);
96
- const far_field = resources.get(data.far_field);
97
-
98
- const encoder = context.encoder;
99
-
100
- const volumetrics_metadata = resources.get(data.volumetrics_metadata);
101
-
102
- shader_volumetrics_split_participating_media.dispatch({
103
- encoder,
104
- bindings: {
105
- camera: camera.buffer.buffer,
106
- volumetrics_metadata,
107
- scene_database: scene.scene_database_buffer,
108
- near_field,
109
- far_field,
110
- },
111
- group_count_x: Math.max(1, Math.ceil(volume_count / VOLUMETRICS_MEDIA_SPLIT_LANES)),
112
- });
113
-
114
- shader_volumetrics_bin_participating_media.dispatch({
115
- encoder,
116
- bindings: {
117
- camera: camera.buffer.buffer,
118
- volumetrics_metadata,
119
- scene_database: scene.scene_database_buffer,
120
- near_field,
121
- group_lists,
122
- },
123
- group_count_x: groups[0],
124
- group_count_y: groups[1],
125
- group_count_z: groups[2],
126
- });
127
-
128
- /**
129
- * The scene's noise atlas, imported this frame so a grow between frames is seen.
130
- * @type {GPUTextureContext}
131
- */
132
- const noise_atlas = resources.get(data.noise_atlas);
133
-
134
- // the per-froxel point the noise is read at moves by this volume every frame
135
- const tSTBN_vec3 = context.graphics.textures.obtain(STATIC_GRAPHICS_ENGINE_ASSETS.stbn_vec3);
136
-
137
- const bindings = {
138
- camera: camera.buffer.buffer,
139
- view: view.uniform_buffer.buffer,
140
- volumetrics_metadata,
141
- scene_database: scene.scene_database_buffer,
142
- tSTBN_vec3: tSTBN_vec3.obtainView(),
143
- tParticipatingMediaNoise: noise_atlas.obtainView(),
144
- sParticipatingMediaNoise: context.graphics.samplers.obtain(PARTICIPATING_MEDIA_NOISE_SAMPLER_DESCRIPTOR),
145
- tOutput0: output0.obtainView(DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR),
146
- tOutput1: output1.obtainView(DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR),
147
- group_lists,
148
- };
149
-
150
- // taken this frame rather than held: a producer that grows the field replaces its atlas
151
- scene.wind.bind(bindings);
152
-
153
- // and read in the shape it has this frame: with no cascades the wind is the far field
154
- // everywhere, and that variant links neither the cascades' read nor the noise's flow map
155
- const switches = shader_switches.with(WIND_FIELD_UNIFORM, scene.wind.cascade_count === 0);
156
-
157
- shader_volumetrics_build_participating_media.variant(switches).dispatchThreads({
158
- encoder,
159
- thread_count_x: output0.width,
160
- thread_count_y: output0.height,
161
- thread_count_z: output0.depth,
162
- bindings,
163
- });
164
-
165
-
166
- });
167
-
168
- data.volumetrics_metadata = pass.read(volumetrics_metadata);
169
- // the split appends to lists counted from zero
170
- data.near_field = pass.create("participating media near field", BufferResourceDescriptor.from(
171
- field_bytes,
172
- GPUBufferUsage.STORAGE,
173
- 0, 4,
174
- ));
175
- data.far_field = pass.create("participating media far field", BufferResourceDescriptor.from(
176
- field_bytes,
177
- GPUBufferUsage.STORAGE,
178
- 0, 4,
179
- ));
180
- // the bin ORs bits into zeroed lists
181
- data.group_lists = pass.create("participating media group lists", BufferResourceDescriptor.from(
182
- group_lists_bytes,
183
- GPUBufferUsage.STORAGE,
184
- 0, group_lists_bytes,
185
- ));
186
- data.noise_atlas = pass.read(graph_import_texture(graph, scene.volumetrics.noise_atlas.texture, "volumetrics noise atlas"));
187
-
188
- data.output0 = pass.create("Participating Media 0", TextureResourceDescriptor.from({
189
- resolution,
190
- dimension: "3d",
191
- format: "rgba16float",
192
- usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT
193
- }));
194
- data.output1 = pass.create("Participating Media 1", TextureResourceDescriptor.from({
195
- resolution,
196
- dimension: "3d",
197
- format: "rgba16float",
198
- usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT
199
- }));
200
-
201
- return {
202
- participating_media0: data.output0,
203
- participating_media1: data.output1,
204
- volumetrics_metadata: data.volumetrics_metadata,
205
- far_field: data.far_field,
206
- };
207
- }
208
-
209
- /**
210
- * Q-04. The sun's optical depth, integrated once per frame into cascaded light-aligned volumes so
211
- * the lighting pass can read it in a single fetch instead of marching toward the sun from every
212
- * froxel.
213
- *
214
- * Sits between the participating-media build and the lighting pass because it reads the former and
215
- * is read by the latter; the frame graph derives that ordering from the handles alone. The cascades
216
- * are ordered among themselves for the same reason: each one is seeded from the value the cascade
217
- * outside it already computed at its entry face, so the outermost is built first and the handle it
218
- * returns is what makes the next pass wait for it.
219
- *
220
- * A texture per cascade rather than one texture with the cascades stacked along w. Stacking would
221
- * put a pass's own output and its input in the same texture, which WebGPU rejects — a subresource
222
- * cannot be bound as writable storage and as a sampled texture at once — and there is no
223
- * subresource finer than the whole 3D texture to split it on.
224
- *
225
- * Always present, even with the mode off — the volumes are then dispatched at 1x1x1 (see
226
- * `GPUViewVolumetrics`), which keeps the graph's topology identical on both sides of the A/B. A
227
- * pass appearing and disappearing would change resource aliasing and pass scheduling along with the
228
- * thing being measured, and this subsystem has already produced one confident wrong answer from a
229
- * measurement where the two sides did not pay the same fixed costs.
230
- *
231
- * @param {Object} options
232
- * @param {FrameGraph} options.graph
233
- * @param {GPUCameraContext} options.camera
234
- * @param {number} options.participating_media1
235
- * @param {number} options.volumetrics_metadata
236
- * @param {number[]} options.resolution voxel counts of one cascade
237
- * @returns {number[]} handles of the cascades, innermost first
238
- */
239
- function graph_build_sun_shadow_volume({
240
- graph,
241
- camera,
242
- participating_media1,
243
- volumetrics_metadata,
244
- resolution,
245
- }) {
246
-
247
- const camera_buffer = graph_import_buffer(graph, camera.buffer.buffer, "volumetrics sun shadow camera");
248
-
249
- const group_count = [
250
- // one invocation per COLUMN along the sun axis, so only x and y are dispatched
251
- Math.ceil(resolution[0] / VOLUMETRICS_SUN_SHADOW_WORKGROUP_RESOLUTION[0]),
252
- Math.ceil(resolution[1] / VOLUMETRICS_SUN_SHADOW_WORKGROUP_RESOLUTION[1]),
253
- 1,
254
- ];
255
-
256
- const cascades = [];
257
-
258
- for (let cascade = VOLUMETRICS_SUN_SHADOW_CASCADE_COUNT - 1; cascade >= 0; cascade--) {
259
-
260
- // Every cascade but the outermost starts its scan from the one outside it — which the
261
- // descending loop has already added, and which is the one difference the two build shaders
262
- // exist for.
263
- const seeded = cascade < VOLUMETRICS_SUN_SHADOW_CASCADE_COUNT - 1;
264
-
265
- const bindings = {
266
- // which cascade this dispatch is building; the shader is shared across all of them
267
- settings: {
268
- cascade,
269
- sub_steps: volumetrics_sun_shadow_sub_steps(cascade),
270
- },
271
- camera: camera_buffer,
272
- volumetrics_metadata,
273
- tParticipatingMedia1: participating_media1,
274
- tSunShadowVolume: TextureResourceDescriptor.from({
275
- resolution,
276
- dimension: "3d",
277
- format: "rgba16float",
278
- usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING
279
- }),
280
- };
281
-
282
- if (seeded) {
283
- bindings.tSunShadowOuter = cascades[cascade + 1];
284
- }
285
-
286
- const outputs = graph_compute_pass({
287
- graph,
288
- shader: seeded
289
- ? shader_volumetrics_build_sun_shadow_volume_seeded
290
- : shader_volumetrics_build_sun_shadow_volume_outermost,
291
- group_count,
292
- bindings,
293
- });
294
-
295
- cascades[cascade] = outputs.tSunShadowVolume;
296
- }
297
-
298
- return cascades;
299
- }
300
-
301
- /**
302
- *
303
- * @param {FrameGraph} graph
304
- * @param participating_media0
305
- * @param participating_media1
306
- * @param volumetrics_metadata
307
- * @param light_cluster_data
308
- * @param light_cluster_lookup
309
- * @param light_cluster_parameters
310
- * @param shadowmap_atlas
311
- * @param sun_transmittance_lut
312
- * @param sun_multiple_scattering_lut
313
- * @param {number[]} sun_shadow_cascades handles of the Q-04 optical-depth cascades, innermost first
314
- * @param lights
315
- * @param view
316
- * @param camera
317
- * @param resolution
318
- * @param {ShaderSwitchSet} shader_switches the renderer's; the lighting pass is built for them
319
- * @returns {number} the lit scattering volume
320
- */
321
- function graph_integrate_light_scattering({
322
- graph,
323
- participating_media0,
324
- participating_media1,
325
- volumetrics_metadata,
326
- light_cluster_data,
327
- light_cluster_lookup,
328
- light_cluster_parameters,
329
- shadowmap_atlas,
330
- sun_transmittance_lut,
331
- sun_multiple_scattering_lut,
332
- sun_shadow_cascades,
333
- lights,
334
- view,
335
- camera,
336
- resolution,
337
- shader_switches,
338
- }) {
339
-
340
- const data = {};
341
-
342
- const pass = graph.add("scattering lut", data, (data, resources, context) => {
343
- const output = resources.get(data.output);
344
-
345
- const volumetrics_metadata = resources.get(data.volumetrics_metadata);
346
-
347
- const participating_media0 = resources.get(data.participating_media0);
348
- const participating_media1 = resources.get(data.participating_media1);
349
-
350
- const light_cluster_data = resources.get(data.light_cluster_data);
351
- const light_cluster_lookup = resources.get(data.light_cluster_lookup);
352
- const light_cluster_parameters = resources.get(data.light_cluster_parameters);
353
- const shadowmap_atlas = resources.get(data.shadowmap_atlas);
354
-
355
- const sun_transmittance_lut = resources.get(data.sun_transmittance_lut);
356
- const sun_multiple_scattering_lut = resources.get(data.sun_multiple_scattering_lut);
357
- const sun_shadow_cascades = data.sun_shadow_cascades.map(handle => resources.get(handle));
358
-
359
- const sampler_shadowmap = context.graphics.samplers.obtain(SHADOWMAP_COMPARE_SAMPLER_DESCRIPTOR);
360
- const sLinear = context.graphics.samplers.obtain(SamplerDescriptor.from({
361
- magFilter: "linear",
362
- }));
363
-
364
- // Spatiotemporal blue noise for the per-froxel sample offset. The pass takes one sample and
365
- // leans on the volumetric TAA to integrate, which only works if each froxel's sequence over
366
- // frames is well distributed — the property a per-froxel hash does not have.
367
- const tSTBN_vec3 = context.graphics.textures.obtain(STATIC_GRAPHICS_ENGINE_ASSETS.stbn_vec3);
368
-
369
- const encoder = context.encoder;
370
-
371
- const bindings = {
372
- camera: camera.buffer.buffer,
373
- view: view.uniform_buffer.buffer,
374
- volumetrics_metadata,
375
- sLinear,
376
- tSunTransmittance: sun_transmittance_lut.obtainView(),
377
- tMultiScattering: sun_multiple_scattering_lut.obtainView(),
378
- tSTBN_vec3: tSTBN_vec3.obtainView(),
379
- tParticipatingMedia0: participating_media0.obtainView(),
380
- tParticipatingMedia1: participating_media1.obtainView(),
381
- tLighting: output.obtainView(DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR),
382
- //
383
- light_data: lights.buffer_data,
384
-
385
- cluster_data: light_cluster_data,
386
- cluster_lookup: light_cluster_lookup,
387
- cluster_parameters: light_cluster_parameters,
388
-
389
- shadowmapAtlas: shadowmap_atlas.obtainView(),
390
- sCompareLinear: sampler_shadowmap,
391
- };
392
-
393
- // One texture per cascade, under the names the read side declares. Bound here rather than
394
- // above because the count is a constant of the structure, not of this pass.
395
- for (let cascade = 0; cascade < sun_shadow_cascades.length; cascade++) {
396
- bindings[volumetrics_sun_shadow_texture_name(cascade)] = sun_shadow_cascades[cascade].obtainView();
397
- }
398
-
399
- shader_volumetrics_build_lighting.variant(shader_switches).dispatchThreads({
400
- encoder,
401
- thread_count_x: output.width,
402
- thread_count_y: output.height,
403
- thread_count_z: output.depth,
404
- bindings
405
- })
406
- });
407
-
408
- data.participating_media0 = pass.read(participating_media0);
409
- data.participating_media1 = pass.read(participating_media1);
410
- data.volumetrics_metadata = pass.read(volumetrics_metadata);
411
-
412
- data.shadowmap_atlas = pass.read(shadowmap_atlas);
413
- data.light_cluster_data = pass.read(light_cluster_data);
414
- data.light_cluster_lookup = pass.read(light_cluster_lookup);
415
- data.light_cluster_parameters = pass.read(light_cluster_parameters);
416
-
417
- data.sun_transmittance_lut = pass.read(sun_transmittance_lut);
418
- data.sun_multiple_scattering_lut = pass.read(sun_multiple_scattering_lut);
419
- data.sun_shadow_cascades = sun_shadow_cascades.map(handle => pass.read(handle));
420
-
421
- data.output = pass.create("Light Scattering LUT", TextureResourceDescriptor.from({
422
- resolution,
423
- dimension: "3d",
424
- format: "rgba16float",
425
- usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT
426
- }));
427
-
428
- return data.output;
429
-
430
- }
431
-
432
- /**
433
- * The fog: participating media -> per-froxel lighting -> ray-marched integration into the aerial LUT
434
- * every surface behind it is seen through. Particle lighting is not here — it needs no medium, and
435
- * `graph_build_froxel_irradiance` builds it on its own.
436
- *
437
- * @param {Object} options
438
- * @param {FrameGraph} options.graph
439
- * @param {GPUCameraContext} options.camera
440
- * @param {GPULightCollection} options.lights
441
- * @param {GPUViewContext} options.view
442
- * @param {GPUSky} options.sky the scene's sky, already {@link GPUSky#update updated}: its two
443
- * persistent LUTs are imported and read, not rebuilt. The sun's transmittance and the multiple
444
- * scattering depend on nothing but the atmosphere's constants, so building them again per frame
445
- * bought the same texels at a cost that was paid whether or not the scene had any fog.
446
- * @param {number} options.light_cluster_data
447
- * @param {number} options.light_cluster_lookup
448
- * @param {number} options.light_cluster_parameters
449
- * @param {number} options.shadowmap_atlas
450
- * @param {number[]} options.volumetric_resolution
451
- * @param {number[]} options.sun_shadow_resolution voxel counts of one Q-04 sun optical-depth cascade;
452
- * `[1, 1, 1]` when the froxels are marching instead, which keeps the passes in the graph without
453
- * doing any work
454
- * @param {number} options.volumetrics_metadata
455
- * @param {number} [options.taa_history] imported previous-frame accumulated scattering volume
456
- * @param {number} [options.taa_output] imported destination for this frame's accumulated scattering volume
457
- * @param {GPUCameraContext} [options.camera_previous] previous-frame camera, for temporal reprojection
458
- * @param {ShaderSwitchSet} [options.shader_switches] the renderer's; the lighting pass is built for them
459
- */
460
- export function graph_build_volumetrics({
461
- graph,
462
- camera,
463
- lights,
464
- view,
465
- sky,
466
- light_cluster_data,
467
- light_cluster_lookup,
468
- light_cluster_parameters,
469
- shadowmap_atlas,
470
- volumetric_resolution,
471
- sun_shadow_resolution,
472
- volumetrics_metadata,
473
- taa_history,
474
- taa_output,
475
- camera_previous,
476
- shader_switches = ShaderSwitchSet.EMPTY,
477
- }) {
478
-
479
- assert.defined(view, 'view');
480
- assert.defined(sky, 'sky');
481
- assert.equal(sky.needs_update, false, 'sky has not been updated, its LUTs hold nothing yet');
482
- assert.defined(volumetric_resolution, 'volumetric_resolution');
483
- assert.defined(sun_shadow_resolution, 'sun_shadow_resolution');
484
-
485
- graph.push_scope("build volumetrics");
486
-
487
- // Read-only here, so nothing in this graph orders against a write to either; the sky wrote
488
- // them in commands of its own, submitted before this frame's.
489
- const transmittance_lut = graph_import_texture(graph, sky.lut_transmittance);
490
- const multiscatted_lut = graph_import_texture(graph, sky.lut_multiple_scattering);
491
-
492
- const {
493
- participating_media0,
494
- participating_media1,
495
- far_field,
496
- } = graph_integrate_participating_media({
497
- graph,
498
- scene: view.scene,
499
- view,
500
- resolution: volumetric_resolution,
501
- volumetrics_metadata,
502
- camera,
503
- shader_switches,
504
- });
505
-
506
- const sun_shadow_cascades = graph_build_sun_shadow_volume({
507
- graph,
508
- camera,
509
- participating_media1,
510
- volumetrics_metadata,
511
- resolution: sun_shadow_resolution,
512
- });
513
-
514
- const light_scattering_lut = graph_integrate_light_scattering({
515
- graph,
516
- resolution: volumetric_resolution,
517
- camera,
518
- view,
519
- sun_transmittance_lut: transmittance_lut,
520
- sun_multiple_scattering_lut: multiscatted_lut,
521
- sun_shadow_cascades,
522
- participating_media0,
523
- participating_media1,
524
- volumetrics_metadata,
525
- light_cluster_data,
526
- light_cluster_lookup,
527
- light_cluster_parameters,
528
- shadowmap_atlas,
529
- lights,
530
- shader_switches,
531
- })
532
-
533
- // Temporal reprojection of the per-froxel lit scattering volume, applied BEFORE aerial
534
- // integration — reprojecting the already-integrated aerial LUT would double-integrate along the
535
- // view ray. Optional: skipped when no history ping-pong is supplied (TAA disabled), in which
536
- // case the aerial pass consumes the raw (noisier) scattering LUT directly.
537
- let volume_lighting_resolved = light_scattering_lut;
538
-
539
- if (taa_history !== undefined && taa_output !== undefined) {
540
- const taa_outputs = graph_compute_pass({
541
- graph,
542
- shader: shader_volumetrics_taa,
543
- group_count: [
544
- Math.ceil(volumetric_resolution[0] / 8),
545
- Math.ceil(volumetric_resolution[1] / 8),
546
- Math.ceil(volumetric_resolution[2] / 4),
547
- ],
548
- bindings: {
549
- t_current: light_scattering_lut,
550
- t_history: taa_history,
551
- t_output: taa_output,
552
- volumetrics_metadata,
553
- camera_current: graph_import_buffer(graph, camera.buffer.buffer, "volumetrics taa camera"),
554
- camera_previous: graph_import_buffer(graph, camera_previous.buffer.buffer, "volumetrics taa camera previous"),
555
- },
556
- });
557
-
558
- volume_lighting_resolved = taa_outputs.t_output;
559
- }
560
-
561
- const a_data = {};
562
-
563
- const a_pass = graph.add("aerial lut", a_data, (data, resources, context) => {
564
- const output = resources.get(data.output);
565
-
566
- const participating_media0 = resources.get(data.participating_media0);
567
- const participating_media1 = resources.get(data.participating_media1);
568
- const volumetrics_metadata = resources.get(data.volumetrics_metadata);
569
-
570
- const light_scattering_lut = resources.get(data.light_scattering_lut);
571
- const sun_transmittance_lut = resources.get(data.sun_transmittance_lut);
572
- const far_field = resources.get(data.far_field);
573
-
574
- const encoder = context.encoder;
575
-
576
-
577
- const graphics = context.graphics;
578
-
579
- const sLinear = graphics.samplers.obtain(SamplerDescriptor.from({
580
- magFilter: "linear",
581
- }));
582
-
583
- const tSTBN_vec3 = graphics.textures.obtain(STATIC_GRAPHICS_ENGINE_ASSETS.stbn_vec3);
584
-
585
- shader_volumetrics_build_scattering_lut.dispatchThreads({
586
- encoder,
587
- thread_count_x: output.width,
588
- thread_count_y: output.height,
589
- bindings: {
590
- camera: camera.buffer.buffer,
591
- view: view.uniform_buffer.buffer,
592
- volumetrics_metadata,
593
- tSTBN_vec3: tSTBN_vec3.obtainView(),
594
- sLinear,
595
-
596
- tVolumeLighting: light_scattering_lut.obtainView(),
597
- tParticipatingMedia0: participating_media0.obtainView(),
598
- tParticipatingMedia1: participating_media1.obtainView(),
599
-
600
- // RDR2-style far-field march inputs (used beyond froxel_far)
601
- scene_database: view.scene.scene_database_buffer,
602
- far_field,
603
- light_data: lights.buffer_data,
604
- tSunTransmittance: sun_transmittance_lut.obtainView(),
605
-
606
- tOutput: output.obtainView(DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR),
607
- }
608
- });
609
-
610
-
611
- });
612
-
613
- a_data.participating_media0 = a_pass.read(participating_media0);
614
- a_data.participating_media1 = a_pass.read(participating_media1);
615
- a_data.volumetrics_metadata = a_pass.read(volumetrics_metadata);
616
- a_data.sun_transmittance_lut = a_pass.read(transmittance_lut);
617
- a_data.far_field = a_pass.read(far_field);
618
-
619
- a_data.output = a_pass.create("Integrated Volumetrics", TextureResourceDescriptor.from({
620
- resolution: volumetric_resolution,
621
- dimension: "3d",
622
- format: "rgba16float",
623
- usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING
624
- }))
625
-
626
-
627
- a_data.light_scattering_lut = a_pass.read(volume_lighting_resolved);
628
-
629
-
630
- // TODO add temporal reprojection
631
-
632
- graph.pop_scope();
633
-
634
- return {
635
- lut_transmittance: transmittance_lut,
636
- lut_multiple_scattering: multiscatted_lut,
637
- lut_aerial: a_data.output,
638
- volumetrics_metadata,
639
- };
640
- }
1
+ import { assert } from "../../../core/assert.js";
2
+ import { SamplerDescriptor } from "../../descriptor/texture/SamplerDescriptor.js";
3
+ import { BufferResourceDescriptor } from "../../device/graph/BufferResourceDescriptor.js";
4
+ import { TextureResourceDescriptor } from "../../device/graph/TextureResourceDescriptor.js";
5
+ import { ShaderSwitchSet } from "../shader/compiler/ShaderSwitchSet.js";
6
+ import { graph_compute_pass } from "../shader/graph/compute/graph_compute_pass.js";
7
+ import { graph_import_buffer } from "../shader/graph/graph_import_buffer.js";
8
+ import { graph_import_texture } from "../shader/graph/graph_import_texture.js";
9
+ import { SHADOWMAP_COMPARE_SAMPLER_DESCRIPTOR } from "../shadow/map/SHADOWMAP_COMPARE_SAMPLER_DESCRIPTOR.js";
10
+ import { STATIC_GRAPHICS_ENGINE_ASSETS } from "../STATIC_GRAPHICS_ENGINE_ASSETS.js";
11
+ import { DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR } from "../texture/DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR.js";
12
+ import { WIND_FIELD_UNIFORM } from "../wind/WIND_FIELD_UNIFORM.js";
13
+ import { VOLUMETRICS_MEDIA_WORKGROUP_RESOLUTION } from "./chunk_volumetrics_media_group_list.js";
14
+ import { volumetrics_sun_shadow_texture_name } from "./chunk_volumetrics_sun_optical_depth.js";
15
+ import { PARTICIPATING_MEDIA_NOISE_SAMPLER_DESCRIPTOR } from "./noise/PARTICIPATING_MEDIA_NOISE.js";
16
+ import { shader_volumetrics_bin_participating_media } from "./shader_volumetrics_bin_participating_media.js";
17
+ import { shader_volumetrics_build_lighting } from "./shader_volumetrics_build_lighting.js";
18
+ import { shader_volumetrics_build_participating_media } from "./shader_volumetrics_build_participating_media.js";
19
+ import { shader_volumetrics_build_scattering_lut } from "./shader_volumetrics_build_scattering_lut.js";
20
+ import {
21
+ shader_volumetrics_build_sun_shadow_volume_outermost,
22
+ shader_volumetrics_build_sun_shadow_volume_seeded
23
+ } from "./shader_volumetrics_build_sun_shadow_volume.js";
24
+ import {
25
+ shader_volumetrics_split_participating_media,
26
+ VOLUMETRICS_MEDIA_SPLIT_LANES
27
+ } from "./shader_volumetrics_split_participating_media.js";
28
+ import { shader_volumetrics_taa } from "./taa/shader_volumetrics_taa.js";
29
+ import {
30
+ VOLUMETRICS_SUN_SHADOW_CASCADE_COUNT,
31
+ VOLUMETRICS_SUN_SHADOW_WORKGROUP_RESOLUTION,
32
+ volumetrics_sun_shadow_sub_steps
33
+ } from "./VOLUMETRICS_SUN_SHADOW_RESOLUTION.js";
34
+
35
+ /**
36
+ *
37
+ * @param {Object} options
38
+ * @param {FrameGraph} options.graph
39
+ * @param {GPUCameraContext} options.camera
40
+ * @param {GPUSceneContext} options.scene
41
+ * @param {GPUViewContext} options.view
42
+ * @param {number} options.volumetrics_metadata
43
+ * @param {number[]} options.resolution output resolution
44
+ * @param {ShaderSwitchSet} options.shader_switches the renderer's; the build adds the wind field's shape
45
+ */
46
+ function graph_integrate_participating_media({
47
+ graph,
48
+ camera,
49
+ scene,
50
+ view,
51
+ volumetrics_metadata,
52
+ resolution,
53
+ shader_switches,
54
+ }) {
55
+
56
+ const data = {};
57
+
58
+ // the media build's workgroups, each of which the bin gives a list of the volumes it may see
59
+ const groups = [
60
+ Math.ceil(resolution[0] / VOLUMETRICS_MEDIA_WORKGROUP_RESOLUTION[0]),
61
+ Math.ceil(resolution[1] / VOLUMETRICS_MEDIA_WORKGROUP_RESOLUTION[1]),
62
+ resolution[2],
63
+ ];
64
+
65
+ // A bit a volume. Sized from the scene's volumes, which the table's rows never outnumber; the
66
+ // shaders take the volume count off the table and the words a list gets off this buffer.
67
+ const volume_count = scene.volumetrics.source.volumes.length;
68
+ const list_words = Math.ceil(volume_count / 32);
69
+ const group_lists_bytes = 4 * Math.max(4, groups[0] * groups[1] * groups[2] * list_words);
70
+
71
+ // the near and the far field: a count, then a row for every volume either may hold
72
+ const field_bytes = 4 * Math.max(4, 1 + volume_count);
73
+
74
+ const pass = graph.add(
75
+ "aerial lut",
76
+ data,
77
+ /**
78
+ *
79
+ * @param {} data
80
+ * @param {FramePassResources} resources
81
+ * @param {GraphicsExecutionContext} context
82
+ */
83
+ (data, resources, context) => {
84
+
85
+ /**
86
+ * @type {GPUTextureContext}
87
+ */
88
+ const output0 = resources.get(data.output0);
89
+ /**
90
+ * @type {GPUTextureContext}
91
+ */
92
+ const output1 = resources.get(data.output1);
93
+
94
+ const group_lists = resources.get(data.group_lists);
95
+ const near_field = resources.get(data.near_field);
96
+ const far_field = resources.get(data.far_field);
97
+
98
+ const encoder = context.encoder;
99
+
100
+ const volumetrics_metadata = resources.get(data.volumetrics_metadata);
101
+
102
+ shader_volumetrics_split_participating_media.dispatch({
103
+ encoder,
104
+ bindings: {
105
+ camera: camera.buffer.buffer,
106
+ volumetrics_metadata,
107
+ scene_database: scene.scene_database_buffer,
108
+ near_field,
109
+ far_field,
110
+ },
111
+ group_count_x: Math.max(1, Math.ceil(volume_count / VOLUMETRICS_MEDIA_SPLIT_LANES)),
112
+ });
113
+
114
+ shader_volumetrics_bin_participating_media.dispatch({
115
+ encoder,
116
+ bindings: {
117
+ camera: camera.buffer.buffer,
118
+ volumetrics_metadata,
119
+ scene_database: scene.scene_database_buffer,
120
+ near_field,
121
+ group_lists,
122
+ },
123
+ group_count_x: groups[0],
124
+ group_count_y: groups[1],
125
+ group_count_z: groups[2],
126
+ });
127
+
128
+ /**
129
+ * The scene's noise atlas, imported this frame so a grow between frames is seen.
130
+ * @type {GPUTextureContext}
131
+ */
132
+ const noise_atlas = resources.get(data.noise_atlas);
133
+
134
+ // the per-froxel point the noise is read at moves by this volume every frame
135
+ const tSTBN_vec3 = context.graphics.textures.obtain(STATIC_GRAPHICS_ENGINE_ASSETS.stbn_vec3);
136
+
137
+ const bindings = {
138
+ camera: camera.buffer.buffer,
139
+ view: view.uniform_buffer.buffer,
140
+ volumetrics_metadata,
141
+ scene_database: scene.scene_database_buffer,
142
+ tSTBN_vec3: tSTBN_vec3.obtainView(),
143
+ tParticipatingMediaNoise: noise_atlas.obtainView(),
144
+ sParticipatingMediaNoise: context.graphics.samplers.obtain(PARTICIPATING_MEDIA_NOISE_SAMPLER_DESCRIPTOR),
145
+ tOutput0: output0.obtainView(DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR),
146
+ tOutput1: output1.obtainView(DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR),
147
+ group_lists,
148
+ };
149
+
150
+ // taken this frame rather than held: a producer that grows the field replaces its atlas
151
+ scene.wind.bind(bindings);
152
+
153
+ // and read in the shape it has this frame: with no cascades the wind is the far field
154
+ // everywhere, and that variant links neither the cascades' read nor the noise's flow map
155
+ const switches = shader_switches.with(WIND_FIELD_UNIFORM, scene.wind.cascade_count === 0);
156
+
157
+ shader_volumetrics_build_participating_media.variant(switches).dispatchThreads({
158
+ encoder,
159
+ thread_count_x: output0.width,
160
+ thread_count_y: output0.height,
161
+ thread_count_z: output0.depth,
162
+ bindings,
163
+ });
164
+
165
+
166
+ });
167
+
168
+ data.volumetrics_metadata = pass.read(volumetrics_metadata);
169
+ // the split appends to lists counted from zero
170
+ data.near_field = pass.create("participating media near field", BufferResourceDescriptor.from(
171
+ field_bytes,
172
+ GPUBufferUsage.STORAGE,
173
+ 0, 4,
174
+ ));
175
+ data.far_field = pass.create("participating media far field", BufferResourceDescriptor.from(
176
+ field_bytes,
177
+ GPUBufferUsage.STORAGE,
178
+ 0, 4,
179
+ ));
180
+ // the bin ORs bits into zeroed lists
181
+ data.group_lists = pass.create("participating media group lists", BufferResourceDescriptor.from(
182
+ group_lists_bytes,
183
+ GPUBufferUsage.STORAGE,
184
+ 0, group_lists_bytes,
185
+ ));
186
+ data.noise_atlas = pass.read(graph_import_texture(graph, scene.volumetrics.noise_atlas.texture, "volumetrics noise atlas"));
187
+
188
+ data.output0 = pass.create("Participating Media 0", TextureResourceDescriptor.from({
189
+ resolution,
190
+ dimension: "3d",
191
+ format: "rgba16float",
192
+ usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT
193
+ }));
194
+ data.output1 = pass.create("Participating Media 1", TextureResourceDescriptor.from({
195
+ resolution,
196
+ dimension: "3d",
197
+ format: "rgba16float",
198
+ usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT
199
+ }));
200
+
201
+ return {
202
+ participating_media0: data.output0,
203
+ participating_media1: data.output1,
204
+ volumetrics_metadata: data.volumetrics_metadata,
205
+ far_field: data.far_field,
206
+ };
207
+ }
208
+
209
+ /**
210
+ * Q-04. The sun's optical depth, integrated once per frame into cascaded light-aligned volumes so
211
+ * the lighting pass can read it in a single fetch instead of marching toward the sun from every
212
+ * froxel.
213
+ *
214
+ * Sits between the participating-media build and the lighting pass because it reads the former and
215
+ * is read by the latter; the frame graph derives that ordering from the handles alone. The cascades
216
+ * are ordered among themselves for the same reason: each one is seeded from the value the cascade
217
+ * outside it already computed at its entry face, so the outermost is built first and the handle it
218
+ * returns is what makes the next pass wait for it.
219
+ *
220
+ * A texture per cascade rather than one texture with the cascades stacked along w. Stacking would
221
+ * put a pass's own output and its input in the same texture, which WebGPU rejects — a subresource
222
+ * cannot be bound as writable storage and as a sampled texture at once — and there is no
223
+ * subresource finer than the whole 3D texture to split it on.
224
+ *
225
+ * Always present, even with the mode off — the volumes are then dispatched at 1x1x1 (see
226
+ * `GPUViewVolumetrics`), which keeps the graph's topology identical on both sides of the A/B. A
227
+ * pass appearing and disappearing would change resource aliasing and pass scheduling along with the
228
+ * thing being measured, and this subsystem has already produced one confident wrong answer from a
229
+ * measurement where the two sides did not pay the same fixed costs.
230
+ *
231
+ * @param {Object} options
232
+ * @param {FrameGraph} options.graph
233
+ * @param {GPUCameraContext} options.camera
234
+ * @param {number} options.participating_media1
235
+ * @param {number} options.volumetrics_metadata
236
+ * @param {number[]} options.resolution voxel counts of one cascade
237
+ * @returns {number[]} handles of the cascades, innermost first
238
+ */
239
+ function graph_build_sun_shadow_volume({
240
+ graph,
241
+ camera,
242
+ participating_media1,
243
+ volumetrics_metadata,
244
+ resolution,
245
+ }) {
246
+
247
+ const camera_buffer = graph_import_buffer(graph, camera.buffer.buffer, "volumetrics sun shadow camera");
248
+
249
+ const group_count = [
250
+ // one invocation per COLUMN along the sun axis, so only x and y are dispatched
251
+ Math.ceil(resolution[0] / VOLUMETRICS_SUN_SHADOW_WORKGROUP_RESOLUTION[0]),
252
+ Math.ceil(resolution[1] / VOLUMETRICS_SUN_SHADOW_WORKGROUP_RESOLUTION[1]),
253
+ 1,
254
+ ];
255
+
256
+ const cascades = [];
257
+
258
+ for (let cascade = VOLUMETRICS_SUN_SHADOW_CASCADE_COUNT - 1; cascade >= 0; cascade--) {
259
+
260
+ // Every cascade but the outermost starts its scan from the one outside it — which the
261
+ // descending loop has already added, and which is the one difference the two build shaders
262
+ // exist for.
263
+ const seeded = cascade < VOLUMETRICS_SUN_SHADOW_CASCADE_COUNT - 1;
264
+
265
+ const bindings = {
266
+ // which cascade this dispatch is building; the shader is shared across all of them
267
+ settings: {
268
+ cascade,
269
+ sub_steps: volumetrics_sun_shadow_sub_steps(cascade),
270
+ },
271
+ camera: camera_buffer,
272
+ volumetrics_metadata,
273
+ tParticipatingMedia1: participating_media1,
274
+ tSunShadowVolume: TextureResourceDescriptor.from({
275
+ resolution,
276
+ dimension: "3d",
277
+ format: "rgba16float",
278
+ usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING
279
+ }),
280
+ };
281
+
282
+ if (seeded) {
283
+ bindings.tSunShadowOuter = cascades[cascade + 1];
284
+ }
285
+
286
+ const outputs = graph_compute_pass({
287
+ graph,
288
+ shader: seeded
289
+ ? shader_volumetrics_build_sun_shadow_volume_seeded
290
+ : shader_volumetrics_build_sun_shadow_volume_outermost,
291
+ group_count,
292
+ bindings,
293
+ });
294
+
295
+ cascades[cascade] = outputs.tSunShadowVolume;
296
+ }
297
+
298
+ return cascades;
299
+ }
300
+
301
+ /**
302
+ *
303
+ * @param {FrameGraph} graph
304
+ * @param participating_media0
305
+ * @param participating_media1
306
+ * @param volumetrics_metadata
307
+ * @param light_cluster_data
308
+ * @param light_cluster_lookup
309
+ * @param light_cluster_parameters
310
+ * @param shadowmap_atlas
311
+ * @param sun_transmittance_lut
312
+ * @param sun_multiple_scattering_lut
313
+ * @param {number[]} sun_shadow_cascades handles of the Q-04 optical-depth cascades, innermost first
314
+ * @param lights
315
+ * @param view
316
+ * @param camera
317
+ * @param resolution
318
+ * @param {ShaderSwitchSet} shader_switches the renderer's; the lighting pass is built for them
319
+ * @returns {number} the lit scattering volume
320
+ */
321
+ function graph_integrate_light_scattering({
322
+ graph,
323
+ participating_media0,
324
+ participating_media1,
325
+ volumetrics_metadata,
326
+ light_cluster_data,
327
+ light_cluster_lookup,
328
+ light_cluster_parameters,
329
+ shadowmap_atlas,
330
+ sun_transmittance_lut,
331
+ sun_multiple_scattering_lut,
332
+ sun_shadow_cascades,
333
+ lights,
334
+ view,
335
+ camera,
336
+ resolution,
337
+ shader_switches,
338
+ }) {
339
+
340
+ const data = {};
341
+
342
+ const pass = graph.add("scattering lut", data, (data, resources, context) => {
343
+ const output = resources.get(data.output);
344
+
345
+ const volumetrics_metadata = resources.get(data.volumetrics_metadata);
346
+
347
+ const participating_media0 = resources.get(data.participating_media0);
348
+ const participating_media1 = resources.get(data.participating_media1);
349
+
350
+ const light_cluster_data = resources.get(data.light_cluster_data);
351
+ const light_cluster_lookup = resources.get(data.light_cluster_lookup);
352
+ const light_cluster_parameters = resources.get(data.light_cluster_parameters);
353
+ const shadowmap_atlas = resources.get(data.shadowmap_atlas);
354
+
355
+ const sun_transmittance_lut = resources.get(data.sun_transmittance_lut);
356
+ const sun_multiple_scattering_lut = resources.get(data.sun_multiple_scattering_lut);
357
+ const sun_shadow_cascades = data.sun_shadow_cascades.map(handle => resources.get(handle));
358
+
359
+ const sampler_shadowmap = context.graphics.samplers.obtain(SHADOWMAP_COMPARE_SAMPLER_DESCRIPTOR);
360
+ const sLinear = context.graphics.samplers.obtain(SamplerDescriptor.from({
361
+ magFilter: "linear",
362
+ }));
363
+
364
+ // Spatiotemporal blue noise for the per-froxel sample offset. The pass takes one sample and
365
+ // leans on the volumetric TAA to integrate, which only works if each froxel's sequence over
366
+ // frames is well distributed — the property a per-froxel hash does not have.
367
+ const tSTBN_vec3 = context.graphics.textures.obtain(STATIC_GRAPHICS_ENGINE_ASSETS.stbn_vec3);
368
+
369
+ const encoder = context.encoder;
370
+
371
+ const bindings = {
372
+ camera: camera.buffer.buffer,
373
+ view: view.uniform_buffer.buffer,
374
+ volumetrics_metadata,
375
+ sLinear,
376
+ tSunTransmittance: sun_transmittance_lut.obtainView(),
377
+ tMultiScattering: sun_multiple_scattering_lut.obtainView(),
378
+ tSTBN_vec3: tSTBN_vec3.obtainView(),
379
+ tParticipatingMedia0: participating_media0.obtainView(),
380
+ tParticipatingMedia1: participating_media1.obtainView(),
381
+ tLighting: output.obtainView(DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR),
382
+ //
383
+ light_data: lights.buffer_data,
384
+
385
+ cluster_data: light_cluster_data,
386
+ cluster_lookup: light_cluster_lookup,
387
+ cluster_parameters: light_cluster_parameters,
388
+
389
+ shadowmapAtlas: shadowmap_atlas.obtainView(),
390
+ sCompareLinear: sampler_shadowmap,
391
+ };
392
+
393
+ // One texture per cascade, under the names the read side declares. Bound here rather than
394
+ // above because the count is a constant of the structure, not of this pass.
395
+ for (let cascade = 0; cascade < sun_shadow_cascades.length; cascade++) {
396
+ bindings[volumetrics_sun_shadow_texture_name(cascade)] = sun_shadow_cascades[cascade].obtainView();
397
+ }
398
+
399
+ shader_volumetrics_build_lighting.variant(shader_switches).dispatchThreads({
400
+ encoder,
401
+ thread_count_x: output.width,
402
+ thread_count_y: output.height,
403
+ thread_count_z: output.depth,
404
+ bindings
405
+ })
406
+ });
407
+
408
+ data.participating_media0 = pass.read(participating_media0);
409
+ data.participating_media1 = pass.read(participating_media1);
410
+ data.volumetrics_metadata = pass.read(volumetrics_metadata);
411
+
412
+ data.shadowmap_atlas = pass.read(shadowmap_atlas);
413
+ data.light_cluster_data = pass.read(light_cluster_data);
414
+ data.light_cluster_lookup = pass.read(light_cluster_lookup);
415
+ data.light_cluster_parameters = pass.read(light_cluster_parameters);
416
+
417
+ data.sun_transmittance_lut = pass.read(sun_transmittance_lut);
418
+ data.sun_multiple_scattering_lut = pass.read(sun_multiple_scattering_lut);
419
+ data.sun_shadow_cascades = sun_shadow_cascades.map(handle => pass.read(handle));
420
+
421
+ data.output = pass.create("Light Scattering LUT", TextureResourceDescriptor.from({
422
+ resolution,
423
+ dimension: "3d",
424
+ format: "rgba16float",
425
+ usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT
426
+ }));
427
+
428
+ return data.output;
429
+
430
+ }
431
+
432
+ /**
433
+ * The fog: participating media -> per-froxel lighting -> ray-marched integration into the aerial LUT
434
+ * every surface behind it is seen through. Particle lighting is not here — it needs no medium, and
435
+ * `GPUIrradianceClipmap` builds it on its own.
436
+ *
437
+ * @param {Object} options
438
+ * @param {FrameGraph} options.graph
439
+ * @param {GPUCameraContext} options.camera
440
+ * @param {GPULightCollection} options.lights
441
+ * @param {GPUViewContext} options.view
442
+ * @param {GPUSky} options.sky the scene's sky, already {@link GPUSky#update updated}: its two
443
+ * persistent LUTs are imported and read, not rebuilt. The sun's transmittance and the multiple
444
+ * scattering depend on nothing but the atmosphere's constants, so building them again per frame
445
+ * bought the same texels at a cost that was paid whether or not the scene had any fog.
446
+ * @param {number} options.light_cluster_data
447
+ * @param {number} options.light_cluster_lookup
448
+ * @param {number} options.light_cluster_parameters
449
+ * @param {number} options.shadowmap_atlas
450
+ * @param {number[]} options.volumetric_resolution
451
+ * @param {number[]} options.sun_shadow_resolution voxel counts of one Q-04 sun optical-depth cascade;
452
+ * `[1, 1, 1]` when the froxels are marching instead, which keeps the passes in the graph without
453
+ * doing any work
454
+ * @param {number} options.volumetrics_metadata
455
+ * @param {number} [options.taa_history] imported previous-frame accumulated scattering volume
456
+ * @param {number} [options.taa_output] imported destination for this frame's accumulated scattering volume
457
+ * @param {GPUCameraContext} [options.camera_previous] previous-frame camera, for temporal reprojection
458
+ * @param {ShaderSwitchSet} [options.shader_switches] the renderer's; the lighting pass is built for them
459
+ */
460
+ export function graph_build_volumetrics({
461
+ graph,
462
+ camera,
463
+ lights,
464
+ view,
465
+ sky,
466
+ light_cluster_data,
467
+ light_cluster_lookup,
468
+ light_cluster_parameters,
469
+ shadowmap_atlas,
470
+ volumetric_resolution,
471
+ sun_shadow_resolution,
472
+ volumetrics_metadata,
473
+ taa_history,
474
+ taa_output,
475
+ camera_previous,
476
+ shader_switches = ShaderSwitchSet.EMPTY,
477
+ }) {
478
+
479
+ assert.defined(view, 'view');
480
+ assert.defined(sky, 'sky');
481
+ assert.equal(sky.needs_update, false, 'sky has not been updated, its LUTs hold nothing yet');
482
+ assert.defined(volumetric_resolution, 'volumetric_resolution');
483
+ assert.defined(sun_shadow_resolution, 'sun_shadow_resolution');
484
+
485
+ graph.push_scope("build volumetrics");
486
+
487
+ // Read-only here, so nothing in this graph orders against a write to either; the sky wrote
488
+ // them in commands of its own, submitted before this frame's.
489
+ const transmittance_lut = graph_import_texture(graph, sky.lut_transmittance);
490
+ const multiscatted_lut = graph_import_texture(graph, sky.lut_multiple_scattering);
491
+
492
+ const {
493
+ participating_media0,
494
+ participating_media1,
495
+ far_field,
496
+ } = graph_integrate_participating_media({
497
+ graph,
498
+ scene: view.scene,
499
+ view,
500
+ resolution: volumetric_resolution,
501
+ volumetrics_metadata,
502
+ camera,
503
+ shader_switches,
504
+ });
505
+
506
+ const sun_shadow_cascades = graph_build_sun_shadow_volume({
507
+ graph,
508
+ camera,
509
+ participating_media1,
510
+ volumetrics_metadata,
511
+ resolution: sun_shadow_resolution,
512
+ });
513
+
514
+ const light_scattering_lut = graph_integrate_light_scattering({
515
+ graph,
516
+ resolution: volumetric_resolution,
517
+ camera,
518
+ view,
519
+ sun_transmittance_lut: transmittance_lut,
520
+ sun_multiple_scattering_lut: multiscatted_lut,
521
+ sun_shadow_cascades,
522
+ participating_media0,
523
+ participating_media1,
524
+ volumetrics_metadata,
525
+ light_cluster_data,
526
+ light_cluster_lookup,
527
+ light_cluster_parameters,
528
+ shadowmap_atlas,
529
+ lights,
530
+ shader_switches,
531
+ });
532
+
533
+ // Temporal reprojection of the per-froxel lit scattering volume, applied BEFORE aerial
534
+ // integration — reprojecting the already-integrated aerial LUT would double-integrate along the
535
+ // view ray. Optional: skipped when no history ping-pong is supplied (TAA disabled), in which
536
+ // case the aerial pass consumes the raw (noisier) scattering LUT directly.
537
+ let volume_lighting_resolved = light_scattering_lut;
538
+
539
+ if (taa_history !== undefined && taa_output !== undefined) {
540
+ const taa_outputs = graph_compute_pass({
541
+ graph,
542
+ shader: shader_volumetrics_taa,
543
+ group_count: [
544
+ Math.ceil(volumetric_resolution[0] / 8),
545
+ Math.ceil(volumetric_resolution[1] / 8),
546
+ Math.ceil(volumetric_resolution[2] / 4),
547
+ ],
548
+ bindings: {
549
+ t_current: light_scattering_lut,
550
+ t_history: taa_history,
551
+ t_output: taa_output,
552
+ volumetrics_metadata,
553
+ camera_current: graph_import_buffer(graph, camera.buffer.buffer, "volumetrics taa camera"),
554
+ camera_previous: graph_import_buffer(graph, camera_previous.buffer.buffer, "volumetrics taa camera previous"),
555
+ },
556
+ });
557
+
558
+ volume_lighting_resolved = taa_outputs.t_output;
559
+ }
560
+
561
+ const a_data = {};
562
+
563
+ const a_pass = graph.add("aerial lut", a_data, (data, resources, context) => {
564
+ const output = resources.get(data.output);
565
+
566
+ const participating_media0 = resources.get(data.participating_media0);
567
+ const participating_media1 = resources.get(data.participating_media1);
568
+ const volumetrics_metadata = resources.get(data.volumetrics_metadata);
569
+
570
+ const light_scattering_lut = resources.get(data.light_scattering_lut);
571
+ const sun_transmittance_lut = resources.get(data.sun_transmittance_lut);
572
+ const far_field = resources.get(data.far_field);
573
+
574
+ const encoder = context.encoder;
575
+
576
+
577
+ const graphics = context.graphics;
578
+
579
+ const sLinear = graphics.samplers.obtain(SamplerDescriptor.from({
580
+ magFilter: "linear",
581
+ }));
582
+
583
+ const tSTBN_vec3 = graphics.textures.obtain(STATIC_GRAPHICS_ENGINE_ASSETS.stbn_vec3);
584
+
585
+ shader_volumetrics_build_scattering_lut.dispatchThreads({
586
+ encoder,
587
+ thread_count_x: output.width,
588
+ thread_count_y: output.height,
589
+ bindings: {
590
+ camera: camera.buffer.buffer,
591
+ view: view.uniform_buffer.buffer,
592
+ volumetrics_metadata,
593
+ tSTBN_vec3: tSTBN_vec3.obtainView(),
594
+ sLinear,
595
+
596
+ tVolumeLighting: light_scattering_lut.obtainView(),
597
+ tParticipatingMedia0: participating_media0.obtainView(),
598
+ tParticipatingMedia1: participating_media1.obtainView(),
599
+
600
+ // RDR2-style far-field march inputs (used beyond froxel_far)
601
+ scene_database: view.scene.scene_database_buffer,
602
+ far_field,
603
+ light_data: lights.buffer_data,
604
+ tSunTransmittance: sun_transmittance_lut.obtainView(),
605
+
606
+ tOutput: output.obtainView(DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR),
607
+ }
608
+ });
609
+
610
+
611
+ });
612
+
613
+ a_data.participating_media0 = a_pass.read(participating_media0);
614
+ a_data.participating_media1 = a_pass.read(participating_media1);
615
+ a_data.volumetrics_metadata = a_pass.read(volumetrics_metadata);
616
+ a_data.sun_transmittance_lut = a_pass.read(transmittance_lut);
617
+ a_data.far_field = a_pass.read(far_field);
618
+
619
+ a_data.output = a_pass.create("Integrated Volumetrics", TextureResourceDescriptor.from({
620
+ resolution: volumetric_resolution,
621
+ dimension: "3d",
622
+ format: "rgba16float",
623
+ usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING
624
+ }))
625
+
626
+
627
+ a_data.light_scattering_lut = a_pass.read(volume_lighting_resolved);
628
+
629
+
630
+ // TODO add temporal reprojection
631
+
632
+ graph.pop_scope();
633
+
634
+ return {
635
+ lut_transmittance: transmittance_lut,
636
+ lut_multiple_scattering: multiscatted_lut,
637
+ lut_aerial: a_data.output,
638
+ volumetrics_metadata,
639
+ };
640
+ }