@woosh/meep-engine 3.42.0 → 3.43.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 (62) hide show
  1. package/package.json +91 -96
  2. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  3. package/src/shade/playground/vgeo_runtime/README.md +5 -4
  4. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  5. package/src/shade/renderer/Renderer.js +6 -5
  6. package/src/shade/renderer/atmosphere/GPUSky.d.ts +12 -0
  7. package/src/shade/renderer/atmosphere/GPUSky.d.ts.map +1 -1
  8. package/src/shade/renderer/atmosphere/GPUSky.js +162 -147
  9. package/src/shade/renderer/lightmap/bake/shader_lightmap_denoise_sh.d.ts.map +1 -1
  10. package/src/shade/renderer/lightmap/bake/shader_lightmap_denoise_sh.js +10 -3
  11. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  12. package/src/shade/renderer/particles/DESIGN.md +13 -9
  13. package/src/shade/renderer/particles/shaders/chunk_particle_lighting.d.ts +1 -1
  14. package/src/shade/renderer/particles/shaders/chunk_particle_lighting.js +1 -1
  15. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  16. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.d.ts +3 -2
  17. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.d.ts.map +1 -1
  18. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.js +3 -2
  19. package/src/shade/renderer/scene/GPUSceneContext.d.ts +9 -1
  20. package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
  21. package/src/shade/renderer/scene/GPUSceneContext.js +23 -1
  22. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.d.ts +4 -1
  23. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.d.ts.map +1 -1
  24. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.js +74 -3
  25. package/src/shade/renderer/volumetrics/NOTES.md +1023 -957
  26. package/src/shade/renderer/volumetrics/chunk_volumetrics_irradiance_sh.d.ts +5 -4
  27. package/src/shade/renderer/volumetrics/chunk_volumetrics_irradiance_sh.d.ts.map +1 -1
  28. package/src/shade/renderer/volumetrics/chunk_volumetrics_irradiance_sh.js +5 -4
  29. package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_gi.d.ts +1 -1
  30. package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_gi.js +1 -1
  31. package/src/shade/renderer/volumetrics/gi/graph_build_froxel_gi.d.ts +1 -1
  32. package/src/shade/renderer/volumetrics/gi/graph_build_froxel_gi.js +1 -1
  33. package/src/shade/renderer/volumetrics/graph_build_froxel_irradiance.d.ts +41 -0
  34. package/src/shade/renderer/volumetrics/graph_build_froxel_irradiance.d.ts.map +1 -0
  35. package/src/shade/renderer/volumetrics/graph_build_froxel_irradiance.js +112 -0
  36. package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts +9 -23
  37. package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts.map +1 -1
  38. package/src/shade/renderer/volumetrics/graph_build_volumetrics.js +18 -126
  39. package/src/shade/renderer/volumetrics/shader_volumetrics_build_irradiance.d.ts +22 -0
  40. package/src/shade/renderer/volumetrics/shader_volumetrics_build_irradiance.d.ts.map +1 -0
  41. package/src/shade/renderer/volumetrics/shader_volumetrics_build_irradiance.js +228 -0
  42. package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.d.ts.map +1 -1
  43. package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.js +0 -53
  44. package/src/shade/renderer/water/surface/chunk_water_surface_sample.d.ts +4 -3
  45. package/src/shade/renderer/water/surface/chunk_water_surface_sample.d.ts.map +1 -1
  46. package/src/shade/renderer/water/surface/chunk_water_surface_sample.js +17 -7
  47. package/src/shade/renderer/water/surface/particles/GPUWaterParticles.d.ts.map +1 -1
  48. package/src/shade/renderer/water/surface/particles/GPUWaterParticles.js +69 -23
  49. package/src/shade/renderer/water/surface/particles/WATER_PARTICLE_STRUCT.d.ts.map +1 -1
  50. package/src/shade/renderer/water/surface/particles/WATER_PARTICLE_STRUCT.js +13 -5
  51. package/src/shade/renderer/water/surface/particles/chunk_water_particle.d.ts +29 -8
  52. package/src/shade/renderer/water/surface/particles/chunk_water_particle.d.ts.map +1 -1
  53. package/src/shade/renderer/water/surface/particles/chunk_water_particle.js +155 -30
  54. package/src/shade/renderer/water/surface/particles/chunk_water_particle_step.d.ts +6 -1
  55. package/src/shade/renderer/water/surface/particles/chunk_water_particle_step.d.ts.map +1 -1
  56. package/src/shade/renderer/water/surface/particles/chunk_water_particle_step.js +157 -39
  57. package/src/shade/renderer/water/surface/particles/shader_water_particles.d.ts +91 -21
  58. package/src/shade/renderer/water/surface/particles/shader_water_particles.d.ts.map +1 -1
  59. package/src/shade/renderer/water/surface/particles/shader_water_particles.js +442 -70
  60. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +11 -3
  61. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
  62. package/src/shade/wgsl/emulator/WGSLLanguageCore.js +3 -0
@@ -39,8 +39,15 @@ fn estimate_sh_similarity(
39
39
  // extract dominant luma direction
40
40
  // Metro encodes SH coeffcients in axis-aligned way (L11, L1_1, L10) with L00 being in forth position (w)
41
41
 
42
- let a_luma_direction = a.Y.xyz / a.Y.w;
43
- let b_luma_direction = b.Y.xyz / b.Y.w;
42
+ // Zero DC is black and has no dominant direction.
43
+ var a_luma_direction = vec3<f32>(0.0);
44
+ var b_luma_direction = vec3<f32>(0.0);
45
+ if (a.Y.w != 0.0) {
46
+ a_luma_direction = a.Y.xyz / a.Y.w;
47
+ }
48
+ if (b.Y.w != 0.0) {
49
+ b_luma_direction = b.Y.xyz / b.Y.w;
50
+ }
44
51
 
45
52
  const DIRECTION_PHI = 10.0;
46
53
 
@@ -189,4 +196,4 @@ export const shader_lightmap_denoise_sh = ImageShader.from({
189
196
  resources,
190
197
  }),
191
198
  targets: LIGHTMAP_SH_COLOR_TARGETS_DESCRIPTOR
192
- })
199
+ })
@@ -13,7 +13,7 @@ export class TinyGltf {
13
13
  * @readonly
14
14
  * @type {TextureFormat}
15
15
  */
16
- static readonly "__#264@#UNCOMPRESSED_TARGET": TextureFormat;
16
+ static readonly "__#265@#UNCOMPRESSED_TARGET": TextureFormat;
17
17
  /**
18
18
  * @param {AssetManager} assetManager
19
19
  * @param {AssetRequestScope} [scope]
@@ -791,14 +791,18 @@ it is evaluated per pixel (`chunk_particle_lighting`, the model in `chunk_partic
791
791
  a vertex need not be on screen, and one quad can span the frustum.
792
792
 
793
793
  - **Direct light comes from the froxel irradiance volume** (`volumetrics/chunk_volumetrics_irradiance_sh`),
794
- which `shader_volumetrics_build_lighting` writes in the loop that lights the fog: every
795
- directional, point and spot light at the froxel, shadowed, attenuated by the fog toward it, stored
796
- as luma SH2 + constant chroma (Metro's L1 YCoCg, 6 coefficients in two `rgba16float` volumes, since
797
- `rg16float` is not a storage format without `texture-formats-tier1`). The volume rides the
798
- orchestrator to side channels as `volumetrics_irradiance`. Resolution is the fog's — 16 px tiles
799
- and 64 slices — so light and shadow edges inside a particle are soft. idTech 8 builds the same
800
- volume in the same pass (Sousa, SIGGRAPH 2025, p.29); Ghost of Tsushima lights particles from its
801
- haze grid the same way. Chosen over per-pixel forward shading (UE's most expensive mode, and a
794
+ which `shader_volumetrics_build_irradiance` writes: every directional, point and spot light at a
795
+ point jittered through the froxel every frame, shadowed, stored as luma SH2 + constant chroma (Metro's L1 YCoCg, 6 coefficients
796
+ in two `rgba16float` volumes, since `rg16float` is not a storage format without
797
+ `texture-formats-tier1`). The volume rides the orchestrator to side channels as
798
+ `volumetrics_irradiance`. Resolution is the fog's froxel grid — 16 px tiles and 64 slices — so light
799
+ and shadow edges inside a particle are soft. idTech 8 builds the same volume (Sousa, SIGGRAPH 2025,
800
+ p.29); Ghost of Tsushima lights particles from its haze grid the same way. idTech 8 builds it in the
801
+ pass that lights its fog, and this one did too until 2026-10-09, dimming each light by the medium
802
+ on its way in. That tied particle lighting to the fog's whole chain (media, sun cascades, sky LUTs),
803
+ so the fog could not be skipped in a scene without volumes. It now has its own pass with no medium
804
+ in it: a lit particle is lit as the surfaces beside it are, which the deferred and forward paths
805
+ never dimmed either, and the fog is composited over both. Chosen over per-pixel forward shading (UE's most expensive mode, and a
802
806
  flat card lit that way goes black from behind), per-vertex/per-particle lighting (the light
803
807
  clusters are screen-space: a point off screen has no light list, the artefact that moved idTech 8
804
808
  off it) and a per-particle lighting atlas (DOOM 2016, CoD IW: best-looking, but it needs a
@@ -814,7 +818,7 @@ a vertex need not be on screen, and one quad can span the frustum.
814
818
  - **Indirect light is in the same volume**, whatever the renderer's `indirect_lighting_mode`. The
815
819
  froxel GI stage (`volumetrics/gi/`) starts the volume with one concrete module per mode — IBL,
816
820
  Brick4, LPV — each asking its source what radiance arrives at the froxel's centre, as RGB SH2,
817
- and `shader_volumetrics_build_lighting` adds the lights to that. The particle knows no mode: a new
821
+ and `shader_volumetrics_build_irradiance` adds the lights to that. The particle knows no mode: a new
818
822
  GI system is one more module. IBL's source is `GPULightCollection.environment_sh`, the
819
823
  environment projected to radiance SH2 when it changes (`shader_project_environment_sh`); Brick4
820
824
  interpolates its probes (SH3, truncated) with a zero normal, which weighs every probe of the
@@ -4,7 +4,7 @@
4
4
  * returns what it reflects and transmits (`chunk_particle_lighting_model`), from the froxel
5
5
  * irradiance volume (`chunk_volumetrics_irradiance_sh`) at the fragment: the renderer's indirect
6
6
  * lighting, whichever mode it runs (the froxel GI stage, `volumetrics/gi/`), plus every directional,
7
- * point and spot light, shadowed, and attenuated by the fog on its way in. One trilinear read of each
7
+ * point and spot light, shadowed, as the surfaces around the particle receive them. One trilinear read of each
8
8
  * of its two textures, so the cost grows with neither the lights nor the GI; the price is resolution —
9
9
  * a froxel is 16 pixels across and a slice deep, and light and shadow edges inside a particle are that
10
10
  * soft.
@@ -10,7 +10,7 @@ import { chunk_particle_lighting_model } from "./chunk_particle_lighting_model.j
10
10
  * returns what it reflects and transmits (`chunk_particle_lighting_model`), from the froxel
11
11
  * irradiance volume (`chunk_volumetrics_irradiance_sh`) at the fragment: the renderer's indirect
12
12
  * lighting, whichever mode it runs (the froxel GI stage, `volumetrics/gi/`), plus every directional,
13
- * point and spot light, shadowed, and attenuated by the fog on its way in. One trilinear read of each
13
+ * point and spot light, shadowed, as the surfaces around the particle receive them. One trilinear read of each
14
14
  * of its two textures, so the cost grows with neither the lights nor the GI; the price is resolution —
15
15
  * a froxel is 16 pixels across and a slice deep, and light and shadow edges inside a particle are that
16
16
  * soft.
@@ -53,7 +53,7 @@ export class NSS {
53
53
  * @returns {number}
54
54
  */
55
55
  static recommended_jitter_sequence_size(upscale_ratio: number): number;
56
- static "__#192@#preprocess_tensor_descriptor"(render_w: any, render_h: any): TextureResourceDescriptor;
56
+ static "__#193@#preprocess_tensor_descriptor"(render_w: any, render_h: any): TextureResourceDescriptor;
57
57
  /**
58
58
  * @param {GraphicsContext} graphics
59
59
  */
@@ -99,8 +99,9 @@ export type AVBOITSideChannel = {
99
99
  * nothing into the volume passes `0`.
100
100
  * `volumetrics_irradiance` is what a fragment standing in a froxel is lit by
101
101
  * (`chunk_volumetrics_irradiance_sh`), for a channel whose fragments are lit: the renderer's
102
- * indirect lighting, whichever mode it runs, and the scene's lights, shadowed and attenuated by
103
- * the fog, written by the pass that lights the fog.
102
+ * indirect lighting, whichever mode it runs, and the scene's lights, shadowed — lit as the
103
+ * surfaces around it are, with no fog on the way in. Its own pass builds it, whether or not the
104
+ * scene has fog.
104
105
  */
105
106
  avboit_draw: (args: {
106
107
  graph: import("../../../../../engine/graphics/render/frame_graph/FrameGraph.js").FrameGraph;
@@ -1 +1 @@
1
- {"version":3,"file":"AVBOITSideChannel.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAoCc,CAAC,IAAI,EAAE;QAAC,KAAK,EAAE,OAAO,iEAAiE,EAAE,UAAU,CAAC;QACjH,QAAc,EAAE,OAAO,iCAAiC,EAAE,cAAc,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QACzF,aAAmB,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAC3E,WAAiB,EAAE,MAAM,CAAC;QAAC,mBAAmB,EAAE,MAAM,CAAC;QACvD,wBAA8B,EAAE,MAAM,CAAC;QAAC,oBAAoB,EAAE,MAAM,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAC;QACjG,eAAqB,EAAE,MAAM,CAAC;QAC9B,sBAA4B,EAAE,OAAO,uCAAuC,EAAE,yBAAyB,CAAC;KACrG,KAAK;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAC;;;;;sBAKtB,CAAC,IAAI,EAAE;QACpB,KAAW,EAAE,OAAO,iEAAiE,EAAE,UAAU,CAAC;QAClG,MAAY,EAAE,MAAM,CAAC;QACrB,kBAAwB,EAAE,UAAU,MAAM,CAAC,CAAC;QAC5C,SAAe,EAAE,MAAM,CAAC;KACrB,KAAK,MAAM;;;;;;;;kBAED,CAAC,IAAI,EAAE;QACpB,KAAW,EAAE,OAAO,iEAAiE,EAAE,UAAU,CAAC;QAClG,MAAY,EAAE,MAAM,CAAC;QACrB,IAAU,EAAE,MAAM,CAAC;QACnB,aAAmB,EAAE,MAAM,CAAC;QAC5B,gBAAsB,EAAE,MAAM,EAAE,CAAC;QACjC,MAAY,EAAE;YAAC,UAAU,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAC,CAAC;KACjE,KAAK;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC;;;;;;;;;;;;;iBAKhD,CAAC,IAAI,EAAE;QACpB,KAAW,EAAE,OAAO,iEAAiE,EAAE,UAAU,CAAC;QAClG,QAAc,EAAE,OAAO,iCAAiC,EAAE,cAAc,CAAC;QACzE,MAAY,EAAE,MAAM,CAAC;QACrB,aAAmB,EAAE,MAAM,CAAC;QAC5B,IAAU,EAAE,MAAM,CAAC;QACnB,QAAc,EAAE,MAAM,CAAC;QACvB,eAAqB,EAAE,MAAM,CAAC;QAC9B,oBAA0B,EAAE,MAAM,CAAC;QACnC,sBAA4B,EAAE;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAC,CAAC;QACxD,YAAkB,EAAE;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAC,CAAC;KACrF,KAAK;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAC;;;;;;sBAU9D,CAAC,IAAI,EAAE,OAAO,iCAAiC,EAAE,cAAc,KAAK,OAAO"}
1
+ {"version":3,"file":"AVBOITSideChannel.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAoCc,CAAC,IAAI,EAAE;QAAC,KAAK,EAAE,OAAO,iEAAiE,EAAE,UAAU,CAAC;QACjH,QAAc,EAAE,OAAO,iCAAiC,EAAE,cAAc,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QACzF,aAAmB,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAC3E,WAAiB,EAAE,MAAM,CAAC;QAAC,mBAAmB,EAAE,MAAM,CAAC;QACvD,wBAA8B,EAAE,MAAM,CAAC;QAAC,oBAAoB,EAAE,MAAM,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAC;QACjG,eAAqB,EAAE,MAAM,CAAC;QAC9B,sBAA4B,EAAE,OAAO,uCAAuC,EAAE,yBAAyB,CAAC;KACrG,KAAK;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAC;;;;;sBAKtB,CAAC,IAAI,EAAE;QACpB,KAAW,EAAE,OAAO,iEAAiE,EAAE,UAAU,CAAC;QAClG,MAAY,EAAE,MAAM,CAAC;QACrB,kBAAwB,EAAE,UAAU,MAAM,CAAC,CAAC;QAC5C,SAAe,EAAE,MAAM,CAAC;KACrB,KAAK,MAAM;;;;;;;;kBAED,CAAC,IAAI,EAAE;QACpB,KAAW,EAAE,OAAO,iEAAiE,EAAE,UAAU,CAAC;QAClG,MAAY,EAAE,MAAM,CAAC;QACrB,IAAU,EAAE,MAAM,CAAC;QACnB,aAAmB,EAAE,MAAM,CAAC;QAC5B,gBAAsB,EAAE,MAAM,EAAE,CAAC;QACjC,MAAY,EAAE;YAAC,UAAU,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAC,CAAC;KACjE,KAAK;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC;;;;;;;;;;;;;;iBAKhD,CAAC,IAAI,EAAE;QACpB,KAAW,EAAE,OAAO,iEAAiE,EAAE,UAAU,CAAC;QAClG,QAAc,EAAE,OAAO,iCAAiC,EAAE,cAAc,CAAC;QACzE,MAAY,EAAE,MAAM,CAAC;QACrB,aAAmB,EAAE,MAAM,CAAC;QAC5B,IAAU,EAAE,MAAM,CAAC;QACnB,QAAc,EAAE,MAAM,CAAC;QACvB,eAAqB,EAAE,MAAM,CAAC;QAC9B,oBAA0B,EAAE,MAAM,CAAC;QACnC,sBAA4B,EAAE;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAC,CAAC;QACxD,YAAkB,EAAE;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAC,CAAC;KACrF,KAAK;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAC;;;;;;sBAW9D,CAAC,IAAI,EAAE,OAAO,iCAAiC,EAAE,cAAc,KAAK,OAAO"}
@@ -84,8 +84,9 @@
84
84
  * nothing into the volume passes `0`.
85
85
  * `volumetrics_irradiance` is what a fragment standing in a froxel is lit by
86
86
  * (`chunk_volumetrics_irradiance_sh`), for a channel whose fragments are lit: the renderer's
87
- * indirect lighting, whichever mode it runs, and the scene's lights, shadowed and attenuated by
88
- * the fog, written by the pass that lights the fog.
87
+ * indirect lighting, whichever mode it runs, and the scene's lights, shadowed — lit as the
88
+ * surfaces around it are, with no fog on the way in. Its own pass builds it, whether or not the
89
+ * scene has fog.
89
90
  * @property {(view: import("../../../view/GPUViewContext.js").GPUViewContext) => boolean} [avboit_is_empty] whether the channel would add nothing for this view;
90
91
  * asked before the orchestrator records, and a channel that answers `true` is not asked for
91
92
  * anything else in that frame.
@@ -38,6 +38,13 @@ export class GPUSceneContext {
38
38
  */
39
39
  get scene(): Scene;
40
40
  get volumetrics(): GPUSceneVolumetrics;
41
+ /**
42
+ * Not necessarily current: a reader calls {@link GPUSky#update} first, which does the work the
43
+ * first time and nothing after.
44
+ *
45
+ * @returns {GPUSky}
46
+ */
47
+ get sky(): GPUSky;
41
48
  /**
42
49
  *
43
50
  * @returns {GPULightCollection}
@@ -186,7 +193,7 @@ export class GPUSceneContext {
186
193
  * Release this context's virtual geometry — everything {@link enable_virtual_geometry} built,
187
194
  * so that a host tears a scene down by destroying the renderer and nothing of the streaming loop
188
195
  * is left for it to find — then its rows, its database, its TLAS, its lights, its probe volume,
189
- * its volumetrics, its volumetric light map, its animation and its skinning: every buffer the
196
+ * its volumetrics, its sky, its volumetric light map, its animation and its skinning: every buffer the
190
197
  * context made. Safe to call twice, and on a context that never enabled virtual geometry.
191
198
  */
192
199
  destroy(): void;
@@ -205,6 +212,7 @@ export type GPURecordWriter = import("../buffer/table/serde/generate_record_writ
205
212
  import { Brick4LightMap } from "../global_illumination/brick4/Brick4LightMap.js";
206
213
  import { Scene } from "./Scene.js";
207
214
  import { GPUSceneVolumetrics } from "../volumetrics/GPUSceneVolumetrics.js";
215
+ import { GPUSky } from "../atmosphere/GPUSky.js";
208
216
  import { GPULightCollection } from "../light/GPULightCollection.js";
209
217
  import { GPULightProbeVolume } from "../global_illumination/lpv/GPULightProbeVolume.js";
210
218
  import { GPUInstancesAccelerationStructure } from "./GPUInstancesAccelerationStructure.js";
@@ -1 +1 @@
1
- {"version":3,"file":"GPUSceneContext.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/scene/GPUSceneContext.js"],"names":[],"mappings":"AAqEA;;;;;;;;;GASG;AACH;IA0II;;;;OAIG;IACH,sBAHW,eAAe,SACf,KAAK,EAoCf;IA/KD;;;OAGG;IACH,aAFU,MAAM,CAEE;IAElB;;OAEG;IACH,YAFU,kBAAkB,CAEjB;IAEX;;OAEG;IACH,WAFU,kBAAkB,CAElB;IAGV;;OAEG;IACH,sBAFU,cAAc,CAEH;IASrB;;;OAGG;IACH,mBAEC;IAOD,uCAEC;IAQD;;;OAGG;IACH,iCAEC;IAOD;;;OAGG;IACH,8CAEC;IAOD;;;OAGG;IACH,8CAEC;IA8HD;;OAEG;IACH,kDAEC;IAED;;OAEG;IACH,gDAEC;IAED;;OAEG;IACH,kDAEC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH;QAH0C,SAAS,GAAxC,oBAAoB;QAClB,yBAAyB,CAuDrC;IAED;;OAEG;IACH,6CAEC;IAYD;;;OAGG;IACH,kCAEC;IAED;;;;;OAKG;IACH,uCAEC;IAED;;;;;;;;;;;OAWG;IACH,uCAUC;IAED,+BAUC;IAED;;;;;;;;;;OAUG;IACH,yBAEC;IAED;;;;;OAKG;IACH,sCAMC;IAOD;;;;;;;OAOG;IACH,8EAEC;IA0BD;;;;;OAKG;IACH,cAsEC;IAED;;;OAGG;IACH,YAFW,eAAe,QA0BzB;IAED;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,cAHW,sBAAsB,cACtB,MAAM,QAuBhB;IAoCD;;;;;;OAMG;IACH,gBAiBC;IAoCL;;;OAGG;IACH,4BAFU,OAAO,CAE0B;;CAN1C;8BAzuBY,OAAO,uBAAuB,EAAE,eAAe;iCAC/C,OAAO,mCAAmC,EAAE,kBAAkB;iCAC9D,OAAO,mCAAmC,EAAE,kBAAkB;4BAC9D,OAAO,8BAA8B,EAAE,aAAa;8BACpD,OAAO,iDAAiD,EAAE,eAAe;+BAtBvD,iDAAiD;sBAI1D,YAAY;oCADE,uCAAuC;mCADxC,gCAAgC;oCAD/B,mDAAmD;kDAIrC,wCAAwC;0CAfhD,0DAA0D;8BAItE,8CAA8C;iCAF3C,iDAAiD;qCAG7C,qDAAqD;oCAGtD,qCAAqC;4BAC7C,gCAAgC;uCAOrB,iDAAiD;6BAM3D,wBAAwB;wCADb,mCAAmC;uCAdpC,wCAAwC"}
1
+ {"version":3,"file":"GPUSceneContext.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/scene/GPUSceneContext.js"],"names":[],"mappings":"AAsEA;;;;;;;;;GASG;AACH;IA4JI;;;;OAIG;IACH,sBAHW,eAAe,SACf,KAAK,EAsCf;IAnMD;;;OAGG;IACH,aAFU,MAAM,CAEE;IAElB;;OAEG;IACH,YAFU,kBAAkB,CAEjB;IAEX;;OAEG;IACH,WAFU,kBAAkB,CAElB;IAGV;;OAEG;IACH,sBAFU,cAAc,CAEH;IASrB;;;OAGG;IACH,mBAEC;IAOD,uCAEC;IAUD;;;;;OAKG;IACH,kBAEC;IAQD;;;OAGG;IACH,iCAEC;IAOD;;;OAGG;IACH,8CAEC;IAOD;;;OAGG;IACH,8CAEC;IAgID;;OAEG;IACH,kDAEC;IAED;;OAEG;IACH,gDAEC;IAED;;OAEG;IACH,kDAEC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH;QAH0C,SAAS,GAAxC,oBAAoB;QAClB,yBAAyB,CAuDrC;IAED;;OAEG;IACH,6CAEC;IAYD;;;OAGG;IACH,kCAEC;IAED;;;;;OAKG;IACH,uCAEC;IAED;;;;;;;;;;;OAWG;IACH,uCAUC;IAED,+BAUC;IAED;;;;;;;;;;OAUG;IACH,yBAEC;IAED;;;;;OAKG;IACH,sCAMC;IAOD;;;;;;;OAOG;IACH,8EAEC;IA0BD;;;;;OAKG;IACH,cAsEC;IAED;;;OAGG;IACH,YAFW,eAAe,QA0BzB;IAED;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,cAHW,sBAAsB,cACtB,MAAM,QAuBhB;IAoCD;;;;;;OAMG;IACH,gBAkBC;IAoCL;;;OAGG;IACH,4BAFU,OAAO,CAE0B;;CAN1C;8BA9vBY,OAAO,uBAAuB,EAAE,eAAe;iCAC/C,OAAO,mCAAmC,EAAE,kBAAkB;iCAC9D,OAAO,mCAAmC,EAAE,kBAAkB;4BAC9D,OAAO,8BAA8B,EAAE,aAAa;8BACpD,OAAO,iDAAiD,EAAE,eAAe;+BAvBvD,iDAAiD;sBAK1D,YAAY;oCADE,uCAAuC;uBAFpD,yBAAyB;mCACb,gCAAgC;oCAF/B,mDAAmD;kDAKrC,wCAAwC;0CAhBhD,0DAA0D;8BAItE,8CAA8C;iCAF3C,iDAAiD;qCAG7C,qDAAqD;oCAGtD,qCAAqC;4BAC7C,gCAAgC;uCAQrB,iDAAiD;6BAM3D,wBAAwB;wCADb,mCAAmC;uCAfpC,wCAAwC"}
@@ -11,6 +11,7 @@ import { GPUAnimationManager } from "../animation/GPUAnimationManager.js";
11
11
  import { GPUDatabase } from "../buffer/table/GPUDatabase.js";
12
12
  import { Brick4LightMap } from "../global_illumination/brick4/Brick4LightMap.js";
13
13
  import { GPULightProbeVolume } from "../global_illumination/lpv/GPULightProbeVolume.js";
14
+ import { GPUSky } from "../atmosphere/GPUSky.js";
14
15
  import { GPULightCollection } from "../light/GPULightCollection.js";
15
16
  import { GPUSceneVolumetrics } from "../volumetrics/GPUSceneVolumetrics.js";
16
17
  import { Scene } from "./Scene.js";
@@ -125,6 +126,24 @@ export class GPUSceneContext {
125
126
  return this.#volumetrics;
126
127
  }
127
128
 
129
+ /**
130
+ * The atmosphere's LUTs, built once for the scene and read by every view that needs them. They
131
+ * depend on the atmosphere's constants alone, so no view owns a copy.
132
+ *
133
+ * @type {GPUSky}
134
+ */
135
+ #sky;
136
+
137
+ /**
138
+ * Not necessarily current: a reader calls {@link GPUSky#update} first, which does the work the
139
+ * first time and nothing after.
140
+ *
141
+ * @returns {GPUSky}
142
+ */
143
+ get sky() {
144
+ return this.#sky;
145
+ }
146
+
128
147
  /**
129
148
  * @type {GPULightCollection}
130
149
  */
@@ -253,6 +272,8 @@ export class GPUSceneContext {
253
272
  scene.volumetrics
254
273
  );
255
274
 
275
+ this.#sky = new GPUSky(graphics);
276
+
256
277
  this.#animation_manager = new GPUAnimationManager(device, `GPUSceneContext[${this.id}]/Animation`, this);
257
278
  }
258
279
 
@@ -718,7 +739,7 @@ export class GPUSceneContext {
718
739
  * Release this context's virtual geometry — everything {@link enable_virtual_geometry} built,
719
740
  * so that a host tears a scene down by destroying the renderer and nothing of the streaming loop
720
741
  * is left for it to find — then its rows, its database, its TLAS, its lights, its probe volume,
721
- * its volumetrics, its volumetric light map, its animation and its skinning: every buffer the
742
+ * its volumetrics, its sky, its volumetric light map, its animation and its skinning: every buffer the
722
743
  * context made. Safe to call twice, and on a context that never enabled virtual geometry.
723
744
  */
724
745
  destroy() {
@@ -733,6 +754,7 @@ export class GPUSceneContext {
733
754
  this.#lights.destroy();
734
755
  this.#light_probe_volume.destroy();
735
756
  this.#volumetrics.destroy();
757
+ this.#sky.destroy();
736
758
  this.volumetric_light_map.destroy();
737
759
  this.#animation_manager.destroy();
738
760
  if (this.#skinning !== null) {
@@ -34,7 +34,10 @@ export class GPUViewVolumetrics {
34
34
  * module that starts the froxel irradiance volume
35
35
  * @returns {{resolved: number, metadata: number, irradiance: {Y: number, CoCg: number}}} the
36
36
  * integrated aerial LUT, the metadata buffer, and the froxel irradiance volume
37
- * (chunk_volumetrics_irradiance_sh) for the transparent surfaces the lighting pass cannot see
37
+ * (chunk_volumetrics_irradiance_sh) that lit particles are lit by. The two do not depend on
38
+ * each other, and a pass building only one of them is culled when nothing reads it. A scene
39
+ * with no participating-media volume records no fog at all, and `resolved` is then a constant:
40
+ * no in-scatter, full transmittance.
38
41
  */
39
42
  graph_update({ graph, light_cluster_data, light_cluster_lookup, light_cluster_parameters, shadowmap_atlas, indirect_lighting_mode, }: {
40
43
  graph: FrameGraph;
@@ -1 +1 @@
1
- {"version":3,"file":"GPUViewVolumetrics.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/volumetrics/GPUViewVolumetrics.js"],"names":[],"mappings":"AAiCA;IAkII;;;;OAIG;IACH,kBAHW,cAAc,qBAwBxB;IAxHD;;OAEG;IACH,4BAOC;IAjBD;;OAEG;IACH,2BAEC;IAsCD;;OAEG;IACH,0BAEC;IAZD;;OAEG;IACH,yBAEC;IAwFD,gBAWC;IA6ND;;;;;;;;;;;;;OAaG;IACH;QAX+B,KAAK,EAAzB,UAAU;QACM,kBAAkB,EAAlC,MAAM;QACU,oBAAoB,EAApC,MAAM;QACU,wBAAwB,EAAxC,MAAM;QACU,eAAe,EAA/B,MAAM;QACuE,sBAAsB,EAAnG,OAAO,iCAAiC,EAAE,yBAAyB;QAEjE;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAC,CAAA;KAAC,CAyFvF;;CAEJ;yBApfY,OAAO,2DAA2D,EAAE,UAAU;6BAC9E,OAAO,2BAA2B,EAAE,cAAc"}
1
+ {"version":3,"file":"GPUViewVolumetrics.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/volumetrics/GPUViewVolumetrics.js"],"names":[],"mappings":"AAkCA;IA2II;;;;OAIG;IACH,kBAHW,cAAc,qBAyCxB;IAlJD;;OAEG;IACH,4BAOC;IAjBD;;OAEG;IACH,2BAEC;IAsCD;;OAEG;IACH,0BAEC;IAZD;;OAEG;IACH,yBAEC;IAkHD,gBAaC;IA6ND;;;;;;;;;;;;;;;;OAgBG;IACH;QAd+B,KAAK,EAAzB,UAAU;QACM,kBAAkB,EAAlC,MAAM;QACU,oBAAoB,EAApC,MAAM;QACU,wBAAwB,EAAxC,MAAM;QACU,eAAe,EAA/B,MAAM;QACuE,sBAAsB,EAAnG,OAAO,iCAAiC,EAAE,yBAAyB;QAEjE;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAC,CAAA;KAAC,CAmIvF;;CAEJ;yBA1jBY,OAAO,2DAA2D,EAAE,UAAU;6BAC9E,OAAO,2BAA2B,EAAE,cAAc"}
@@ -10,6 +10,7 @@ import {
10
10
  build_volumetrics_sun_shadow_transform,
11
11
  volumetrics_sun_shadow_cascade_far
12
12
  } from "./build_volumetrics_sun_shadow_transform.js";
13
+ import { graph_build_froxel_irradiance } from "./graph_build_froxel_irradiance.js";
13
14
  import { graph_build_volumetrics } from "./graph_build_volumetrics.js";
14
15
  import {
15
16
  VOLUMETRICS_SUN_SHADOW_CASCADE_COUNT,
@@ -151,6 +152,15 @@ export class GPUViewVolumetrics {
151
152
  */
152
153
  #sun_shadow_cascade_far = new Float32Array(4);
153
154
 
155
+ /**
156
+ * What the fog integrates to through no medium: nothing in-scattered (.rgb 0), everything
157
+ * transmitted (.a 1). Handed out in place of the aerial LUT on a frame whose scene has no
158
+ * participating-media volume, so every surface that samples the fog keeps doing so and reads
159
+ * it as none, for one texel and no pass. Written once, at construction.
160
+ * @type {GPUTextureContext}
161
+ */
162
+ #no_medium;
163
+
154
164
  /**
155
165
  * @type {GPUTypedBuffer}
156
166
  */
@@ -181,6 +191,23 @@ export class GPUViewVolumetrics {
181
191
  this.#volumetrics_textures[i] = texture;
182
192
  }
183
193
 
194
+ const no_medium = new GPUTextureContext(device);
195
+ no_medium.descriptor.label = "volumetrics/no medium";
196
+ no_medium.descriptor.format = "rgba16float";
197
+ no_medium.descriptor.dimension = "3d";
198
+ no_medium.descriptor.size = [1, 1, 1];
199
+ no_medium.descriptor.usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST;
200
+
201
+ device.queue.writeTexture(
202
+ { texture: no_medium.gpu_texture },
203
+ new Float16Array([0, 0, 0, 1]),
204
+ { bytesPerRow: 8, rowsPerImage: 1 },
205
+ [1, 1, 1]
206
+ );
207
+ no_medium.incrementVersion();
208
+
209
+ this.#no_medium = no_medium;
210
+
184
211
  this.#metadata = GPUTypedBuffer.create({
185
212
  type: VOLUMETRICS_METADATA_STRUCT,
186
213
  device,
@@ -200,6 +227,8 @@ export class GPUViewVolumetrics {
200
227
  textures[i] = null;
201
228
  }
202
229
 
230
+ this.#no_medium.destroy();
231
+
203
232
  }
204
233
 
205
234
  #update_resolution() {
@@ -433,7 +462,10 @@ export class GPUViewVolumetrics {
433
462
  * module that starts the froxel irradiance volume
434
463
  * @returns {{resolved: number, metadata: number, irradiance: {Y: number, CoCg: number}}} the
435
464
  * integrated aerial LUT, the metadata buffer, and the froxel irradiance volume
436
- * (chunk_volumetrics_irradiance_sh) for the transparent surfaces the lighting pass cannot see
465
+ * (chunk_volumetrics_irradiance_sh) that lit particles are lit by. The two do not depend on
466
+ * each other, and a pass building only one of them is culled when nothing reads it. A scene
467
+ * with no participating-media volume records no fog at all, and `resolved` is then a constant:
468
+ * no in-scatter, full transmittance.
437
469
  */
438
470
  graph_update({
439
471
  graph,
@@ -479,6 +511,35 @@ export class GPUViewVolumetrics {
479
511
 
480
512
  const resolution = this.#volumetrics_textures[0].size;
481
513
 
514
+ // Particle lighting first, and on its own: it reads no medium. The frame graph culls it when
515
+ // nothing reads it.
516
+ const irradiance = graph_build_froxel_irradiance({
517
+ graph,
518
+ camera,
519
+ view,
520
+ lights,
521
+ light_cluster_data,
522
+ light_cluster_lookup,
523
+ light_cluster_parameters,
524
+ shadowmap_atlas,
525
+ volumetrics_metadata: gr_metadata,
526
+ resolution,
527
+ indirect_lighting_mode,
528
+ });
529
+
530
+ if (view.scene.volumetrics.source.volumes.length === 0) {
531
+ // No medium: nothing scatters and nothing absorbs, so the fog is the identity everywhere
532
+ // and the passes that would integrate it to that are not recorded. Nothing accumulates
533
+ // either, which leaves the history stale for whenever a volume arrives.
534
+ this.#reset_history = true;
535
+
536
+ return {
537
+ resolved: graph_import_texture(graph, this.#no_medium, "volumetrics no medium"),
538
+ metadata: gr_metadata,
539
+ irradiance,
540
+ };
541
+ }
542
+
482
543
  // Ping-pong the two persistent froxel textures: this frame writes [parity] and reads the
483
544
  // previous frame's accumulation from [parity ^ 1]. On the first frame / after a resize the
484
545
  // history is invalid, which #update_metadata signals via history_reset (the TAA then emits
@@ -493,16 +554,26 @@ export class GPUViewVolumetrics {
493
554
  taa_output = graph_import_texture(graph, this.#volumetrics_textures[parity], "volumetrics taa output");
494
555
  }
495
556
 
496
- const { lut_aerial, irradiance } = graph_build_volumetrics({
557
+ /*
558
+ The sun's transmittance and the multiple scattering are the scene sky's, built once and
559
+ read from here on. Brought current now rather than in a pass: the sky submits commands of
560
+ its own, which land ahead of this frame's -- recorded into the frame graph they would be
561
+ paid every frame, which is what they used to be. A no-op after the first call.
562
+ */
563
+ const sky = view.scene.sky;
564
+
565
+ sky.update();
566
+
567
+ const { lut_aerial } = graph_build_volumetrics({
497
568
  graph,
498
569
  camera,
499
570
  lights,
500
571
  view,
572
+ sky,
501
573
  light_cluster_data,
502
574
  light_cluster_lookup,
503
575
  light_cluster_parameters,
504
576
  shadowmap_atlas,
505
- indirect_lighting_mode,
506
577
 
507
578
  volumetrics_metadata: gr_metadata,
508
579
  volumetric_resolution: resolution,