@woosh/meep-engine 3.11.1 → 3.11.2

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 (73) hide show
  1. package/package.json +1 -1
  2. package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
  3. package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +294 -290
  4. package/src/engine/graphics3/ParticipatingMedia.d.ts +111 -0
  5. package/src/engine/graphics3/ParticipatingMedia.d.ts.map +1 -0
  6. package/src/engine/graphics3/ParticipatingMedia.js +203 -0
  7. package/src/engine/graphics3/ParticipatingMediaSerializationAdapter.d.ts +34 -0
  8. package/src/engine/graphics3/ParticipatingMediaSerializationAdapter.d.ts.map +1 -0
  9. package/src/engine/graphics3/ParticipatingMediaSerializationAdapter.js +73 -0
  10. package/src/engine/graphics3/ParticipatingMediaSystem3.d.ts +87 -0
  11. package/src/engine/graphics3/ParticipatingMediaSystem3.d.ts.map +1 -0
  12. package/src/engine/graphics3/ParticipatingMediaSystem3.js +184 -0
  13. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  14. package/src/shade/playground/volumetrics_froxel/README.md +104 -1
  15. package/src/shade/playground/volumetrics_froxel/index.html +20 -1
  16. package/src/shade/playground/volumetrics_froxel/main.d.ts.map +1 -1
  17. package/src/shade/playground/volumetrics_froxel/main.js +298 -0
  18. package/src/shade/playground/volumetrics_froxel/measure.d.ts +119 -0
  19. package/src/shade/playground/volumetrics_froxel/measure.d.ts.map +1 -0
  20. package/src/shade/playground/volumetrics_froxel/measure.js +559 -0
  21. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  22. package/src/shade/renderer/Renderer.js +22 -18
  23. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  24. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_point2.d.ts +14 -13
  25. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_point2.d.ts.map +1 -1
  26. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_point2.js +133 -129
  27. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.d.ts.map +1 -1
  28. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.js +480 -315
  29. package/src/shade/renderer/volumetrics/NOTES.md +649 -8
  30. package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.d.ts +5 -0
  31. package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.d.ts.map +1 -1
  32. package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.js +21 -2
  33. package/src/shade/renderer/volumetrics/SceneVolumetrics.d.ts +26 -0
  34. package/src/shade/renderer/volumetrics/SceneVolumetrics.d.ts.map +1 -1
  35. package/src/shade/renderer/volumetrics/SceneVolumetrics.js +64 -21
  36. package/src/shade/renderer/volumetrics/VOLUMETRICS_METADATA_STRUCT.d.ts.map +1 -1
  37. package/src/shade/renderer/volumetrics/VOLUMETRICS_METADATA_STRUCT.js +84 -28
  38. package/src/shade/renderer/volumetrics/VOLUMETRICS_SUN_SHADOW_RESOLUTION.d.ts +98 -0
  39. package/src/shade/renderer/volumetrics/VOLUMETRICS_SUN_SHADOW_RESOLUTION.d.ts.map +1 -0
  40. package/src/shade/renderer/volumetrics/VOLUMETRICS_SUN_SHADOW_RESOLUTION.js +111 -0
  41. package/src/shade/renderer/volumetrics/build_volumetrics_sun_shadow_transform.d.ts +84 -0
  42. package/src/shade/renderer/volumetrics/build_volumetrics_sun_shadow_transform.d.ts.map +1 -0
  43. package/src/shade/renderer/volumetrics/build_volumetrics_sun_shadow_transform.js +252 -0
  44. package/src/shade/renderer/volumetrics/chunk_integrate_optical_depth.js +75 -75
  45. package/src/shade/renderer/volumetrics/chunk_volumetrics_local_light_shadow.d.ts +43 -0
  46. package/src/shade/renderer/volumetrics/chunk_volumetrics_local_light_shadow.d.ts.map +1 -0
  47. package/src/shade/renderer/volumetrics/chunk_volumetrics_local_light_shadow.js +145 -0
  48. package/src/shade/renderer/volumetrics/chunk_volumetrics_sample_froxel_extinction.d.ts +20 -0
  49. package/src/shade/renderer/volumetrics/chunk_volumetrics_sample_froxel_extinction.d.ts.map +1 -0
  50. package/src/shade/renderer/volumetrics/chunk_volumetrics_sample_froxel_extinction.js +48 -0
  51. package/src/shade/renderer/volumetrics/chunk_volumetrics_sun_optical_depth.d.ts +38 -0
  52. package/src/shade/renderer/volumetrics/chunk_volumetrics_sun_optical_depth.d.ts.map +1 -0
  53. package/src/shade/renderer/volumetrics/chunk_volumetrics_sun_optical_depth.js +107 -0
  54. package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts +4 -1
  55. package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts.map +1 -1
  56. package/src/shade/renderer/volumetrics/graph_build_volumetrics.js +577 -471
  57. package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.d.ts.map +1 -1
  58. package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.js +64 -13
  59. package/src/shade/renderer/volumetrics/shader_volumetrics_build_sun_shadow_volume.d.ts +8 -0
  60. package/src/shade/renderer/volumetrics/shader_volumetrics_build_sun_shadow_volume.d.ts.map +1 -0
  61. package/src/shade/renderer/volumetrics/shader_volumetrics_build_sun_shadow_volume.js +174 -0
  62. package/src/shade/wgsl/emulator/CPUBitmapData.d.ts +25 -0
  63. package/src/shade/wgsl/emulator/CPUBitmapData.d.ts.map +1 -1
  64. package/src/shade/wgsl/emulator/CPUBitmapData.js +33 -0
  65. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +2221 -2215
  66. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
  67. package/src/shade/wgsl/emulator/WGSLLanguageCore.js +1297 -1117
  68. package/src/shade/wgsl/emulator/cpu_texture_gather.d.ts +69 -0
  69. package/src/shade/wgsl/emulator/cpu_texture_gather.d.ts.map +1 -0
  70. package/src/shade/wgsl/emulator/cpu_texture_gather.js +148 -0
  71. package/src/shade/wgsl/emulator/cpu_texture_sample.d.ts +16 -0
  72. package/src/shade/wgsl/emulator/cpu_texture_sample.d.ts.map +1 -1
  73. package/src/shade/wgsl/emulator/cpu_texture_sample.js +176 -172
@@ -1,472 +1,578 @@
1
-
2
-
3
- import { assert } from "../../../core/assert.js";
4
- import { SamplerDescriptor } from "../../descriptor/texture/SamplerDescriptor.js";
5
- import { TextureResourceDescriptor } from "../../device/graph/TextureResourceDescriptor.js";
6
- import { SHADOWMAP_COMPARE_SAMPLER_DESCRIPTOR } from "../shadow/map/SHADOWMAP_COMPARE_SAMPLER_DESCRIPTOR.js";
7
- import { STATIC_GRAPHICS_ENGINE_ASSETS } from "../STATIC_GRAPHICS_ENGINE_ASSETS.js";
8
- import { DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR } from "../texture/DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR.js";
9
- import { graph_compute_pass } from "../shader/graph/compute/graph_compute_pass.js";
10
- import { graph_import_buffer } from "../shader/graph/graph_import_buffer.js";
11
- import { shader_multiscatter_lut } from "../atmosphere/shader_multiscatter_lut.js";
12
- import { shader_transmittance_lut } from "../atmosphere/shader_transmittance_lut.js";
13
- import { shader_volumetrics_build_lighting } from "./shader_volumetrics_build_lighting.js";
14
- import { shader_volumetrics_build_participating_media } from "./shader_volumetrics_build_participating_media.js";
15
- import { shader_volumetrics_build_scattering_lut } from "./shader_volumetrics_build_scattering_lut.js";
16
- import { shader_volumetrics_taa } from "./taa/shader_volumetrics_taa.js";
17
-
18
- /**
19
- *
20
- * @param {FrameGraph} graph
21
- */
22
- function graph_integrate_sky({ graph }) {
23
- const t_data = {};
24
-
25
- const t_pass = graph.add("transmittance", t_data, (data, resources, context) => {
26
-
27
- const output = resources.get(data.output);
28
-
29
- const encoder = context.encoder;
30
-
31
- const settings_type = shader_transmittance_lut.descriptor.resources.getResourceByName("settings").buffer_type;
32
-
33
- const settings = encoder.allocateTransientValueBuffer(settings_type, {
34
- steps: 40
35
- });
36
-
37
- shader_transmittance_lut.draw({
38
- encoder: encoder,
39
- bindings: {
40
- settings
41
- },
42
- colorAttachments: [{
43
- view: output.obtainView(DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR),
44
- loadOp: "clear",
45
- storeOp: "store",
46
- }]
47
- })
48
- });
49
-
50
- t_data.output = t_pass.create("transmittance lut", TextureResourceDescriptor.from({
51
- resolution: [256, 64],
52
- format: "rgba16float",
53
- usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT
54
- }));
55
-
56
- const m_data = {};
57
-
58
- const m_pass = graph.add("multiscatter lut", m_data, (data, resources, context) => {
59
- const output = resources.get(data.output);
60
-
61
- const settings_type = shader_multiscatter_lut.descriptor.resources.getResourceByName("settings").buffer_type;
62
-
63
- const encoder = context.encoder;
64
-
65
- const settings = encoder.allocateTransientValueBuffer(settings_type, {
66
- steps: 20,
67
- sqrt_samples: 8,
68
- });
69
-
70
- const tSkyLutTransmittance = resources.get(data.tSkyLutTransmittance);
71
-
72
- shader_multiscatter_lut.draw({
73
- encoder: encoder,
74
- bindings: {
75
- settings,
76
- tSkyLutTransmittance
77
- },
78
- colorAttachments: [{
79
- view: output.obtainView(DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR),
80
- loadOp: "clear",
81
- storeOp: "store",
82
- }]
83
- })
84
- });
85
-
86
- m_data.output = m_pass.create("multiscatter lut", TextureResourceDescriptor.from({
87
- resolution: [32, 32],
88
- format: "rgba16float",
89
- usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT
90
- }))
91
-
92
- m_data.tSkyLutTransmittance = m_pass.read(t_data.output);
93
-
94
-
95
- return { transmittance_lut: t_data.output, multiscatted_lut: m_data.output };
96
- }
97
-
98
-
99
- /**
100
- *
101
- * @param {FrameGraph} graph
102
- * @param {GPUCameraContext} camera
103
- * @param {GPUSceneContext} scene
104
- * @param {GPUViewContext} view
105
- * @param {number} volumetrics_metadata
106
- * @param {number[]} resolution output resolution
107
- */
108
- function graph_integrate_participating_media({
109
- graph,
110
- camera,
111
- scene,
112
- view,
113
- volumetrics_metadata,
114
- resolution,
115
- }) {
116
-
117
- const data = {};
118
-
119
-
120
- const pass = graph.add(
121
- "aerial lut",
122
- data,
123
- /**
124
- *
125
- * @param {} data
126
- * @param {FramePassResources} resources
127
- * @param {GraphicsExecutionContext} context
128
- */
129
- (data, resources, context) => {
130
-
131
- /**
132
- * @type {GPUTextureContext}
133
- */
134
- const output0 = resources.get(data.output0);
135
- /**
136
- * @type {GPUTextureContext}
137
- */
138
- const output1 = resources.get(data.output1);
139
-
140
- const encoder = context.encoder;
141
-
142
- const volumetrics_metadata = resources.get(data.volumetrics_metadata);
143
-
144
- shader_volumetrics_build_participating_media.dispatch({
145
- encoder,
146
- group_count_x: Math.ceil(output0.width / 16),
147
- group_count_y: Math.ceil(output0.height / 16),
148
- group_count_z: output0.depth,
149
- bindings: {
150
- camera: camera.buffer.buffer,
151
- view: view.uniform_buffer.buffer,
152
- volumetrics_metadata,
153
- scene_volumetrics_metadata: scene.volumetrics.metadata_buffer.buffer,
154
- scene_volumes: scene.volumetrics.table.buffer,
155
- tOutput0: output0.obtainView(DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR),
156
- tOutput1: output1.obtainView(DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR),
157
- }
158
- });
159
-
160
-
161
- });
162
-
163
- data.volumetrics_metadata = pass.read(volumetrics_metadata);
164
-
165
- data.output0 = pass.create("Participating Media 0", TextureResourceDescriptor.from({
166
- resolution,
167
- dimension: "3d",
168
- format: "rgba16float",
169
- usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT
170
- }));
171
- data.output1 = pass.create("Participating Media 1", TextureResourceDescriptor.from({
172
- resolution,
173
- dimension: "3d",
174
- format: "rgba16float",
175
- usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT
176
- }));
177
-
178
- return {
179
- participating_media0: data.output0,
180
- participating_media1: data.output1,
181
- volumetrics_metadata: data.volumetrics_metadata,
182
- };
183
- }
184
-
185
- /**
186
- *
187
- * @param {FrameGraph} graph
188
- * @param participating_media0
189
- * @param participating_media1
190
- * @param volumetrics_metadata
191
- * @param light_cluster_data
192
- * @param light_cluster_lookup
193
- * @param light_cluster_parameters
194
- * @param shadowmap_atlas
195
- * @param sun_transmittance_lut
196
- * @param sun_multiple_scattering_lut
197
- * @param lights
198
- * @param view
199
- * @param camera
200
- * @param resolution
201
- */
202
- function graph_integrate_light_scattering({
203
- graph,
204
- participating_media0,
205
- participating_media1,
206
- volumetrics_metadata,
207
- light_cluster_data,
208
- light_cluster_lookup,
209
- light_cluster_parameters,
210
- shadowmap_atlas,
211
- sun_transmittance_lut,
212
- sun_multiple_scattering_lut,
213
- lights,
214
- view,
215
- camera,
216
- resolution,
217
- }) {
218
-
219
- const data = {};
220
-
221
- const pass = graph.add("scattering lut", data, (data, resources, context) => {
222
- const output = resources.get(data.output);
223
-
224
- const volumetrics_metadata = resources.get(data.volumetrics_metadata);
225
-
226
- const participating_media0 = resources.get(data.participating_media0);
227
- const participating_media1 = resources.get(data.participating_media1);
228
-
229
- const light_cluster_data = resources.get(data.light_cluster_data);
230
- const light_cluster_lookup = resources.get(data.light_cluster_lookup);
231
- const light_cluster_parameters = resources.get(data.light_cluster_parameters);
232
- const shadowmap_atlas = resources.get(data.shadowmap_atlas);
233
-
234
- const sun_transmittance_lut = resources.get(data.sun_transmittance_lut);
235
- const sun_multiple_scattering_lut = resources.get(data.sun_multiple_scattering_lut);
236
-
237
- const sampler_shadowmap = context.graphics.samplers.obtain(SHADOWMAP_COMPARE_SAMPLER_DESCRIPTOR);
238
- const sLinear = context.graphics.samplers.obtain(SamplerDescriptor.from({
239
- magFilter: "linear",
240
- }));
241
-
242
- // Spatiotemporal blue noise for the per-froxel sample offset. The pass takes one sample and
243
- // leans on the volumetric TAA to integrate, which only works if each froxel's sequence over
244
- // frames is well distributed — the property a per-froxel hash does not have.
245
- const tSTBN_vec3 = context.graphics.textures.obtain(STATIC_GRAPHICS_ENGINE_ASSETS.stbn_vec3);
246
-
247
- const encoder = context.encoder;
248
-
249
- shader_volumetrics_build_lighting.dispatch({
250
- encoder,
251
- group_count_x: Math.ceil(output.width / 16),
252
- group_count_y: Math.ceil(output.height / 16),
253
- group_count_z: output.depth,
254
- bindings: {
255
- camera: camera.buffer.buffer,
256
- view: view.uniform_buffer.buffer,
257
- volumetrics_metadata,
258
- sLinear,
259
- tSunTransmittance: sun_transmittance_lut.obtainView(),
260
- tMultiScattering: sun_multiple_scattering_lut.obtainView(),
261
- tSTBN_vec3: tSTBN_vec3.obtainView(),
262
- tParticipatingMedia0: participating_media0.obtainView(),
263
- tParticipatingMedia1: participating_media1.obtainView(),
264
- tLighting: output.obtainView(DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR),
265
- //
266
- light_data: lights.buffer_data,
267
-
268
- cluster_data: light_cluster_data,
269
- cluster_lookup: light_cluster_lookup,
270
- cluster_parameters: light_cluster_parameters,
271
-
272
- shadowmapAtlas: shadowmap_atlas.obtainView(),
273
- sCompareLinear: sampler_shadowmap,
274
- }
275
- })
276
- });
277
-
278
- data.participating_media0 = pass.read(participating_media0);
279
- data.participating_media1 = pass.read(participating_media1);
280
- data.volumetrics_metadata = pass.read(volumetrics_metadata);
281
-
282
- data.shadowmap_atlas = pass.read(shadowmap_atlas);
283
- data.light_cluster_data = pass.read(light_cluster_data);
284
- data.light_cluster_lookup = pass.read(light_cluster_lookup);
285
- data.light_cluster_parameters = pass.read(light_cluster_parameters);
286
-
287
- data.sun_transmittance_lut = pass.read(sun_transmittance_lut);
288
- data.sun_multiple_scattering_lut = pass.read(sun_multiple_scattering_lut);
289
-
290
- data.output = pass.create("Light Scattering LUT", TextureResourceDescriptor.from({
291
- resolution,
292
- dimension: "3d",
293
- format: "rgba16float",
294
- usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT
295
- }));
296
-
297
-
298
- return data.output;
299
-
300
- }
301
-
302
- /**
303
- *
304
- * @param {FrameGraph} graph
305
- * @param {GPUCameraContext} camera
306
- * @param {GPULightCollection} lights
307
- * @param {GPUViewContext} view
308
- * @param {number} light_cluster_data
309
- * @param {number} light_cluster_lookup
310
- * @param {number} light_cluster_parameters
311
- * @param {number} shadowmap_atlas
312
- * @param {number[]} volumetric_resolution
313
- * @param {number} volumetrics_metadata
314
- * @param {number} [taa_history] imported previous-frame accumulated scattering volume
315
- * @param {number} [taa_output] imported destination for this frame's accumulated scattering volume
316
- * @param {GPUCameraContext} [camera_previous] previous-frame camera, for temporal reprojection
317
- */
318
- export function graph_build_volumetrics({
319
- graph,
320
- camera,
321
- lights,
322
- view,
323
- light_cluster_data,
324
- light_cluster_lookup,
325
- light_cluster_parameters,
326
- shadowmap_atlas,
327
- volumetric_resolution,
328
- volumetrics_metadata,
329
- taa_history,
330
- taa_output,
331
- camera_previous,
332
- }) {
333
-
334
- assert.defined(view, 'view');
335
- assert.defined(volumetric_resolution, 'volumetric_resolution');
336
-
337
- const { transmittance_lut, multiscatted_lut } = graph_integrate_sky({ graph: graph });
338
-
339
- const {
340
- participating_media0,
341
- participating_media1,
342
- } = graph_integrate_participating_media({
343
- graph,
344
- scene: view.scene,
345
- view,
346
- resolution: volumetric_resolution,
347
- volumetrics_metadata,
348
- camera,
349
- });
350
-
351
- const light_scattering_lut = graph_integrate_light_scattering({
352
- graph,
353
- resolution: volumetric_resolution,
354
- camera,
355
- view,
356
- sun_transmittance_lut: transmittance_lut,
357
- sun_multiple_scattering_lut: multiscatted_lut,
358
- participating_media0,
359
- participating_media1,
360
- volumetrics_metadata,
361
- light_cluster_data,
362
- light_cluster_lookup,
363
- light_cluster_parameters,
364
- shadowmap_atlas,
365
- lights,
366
- })
367
-
368
- // Temporal reprojection of the per-froxel lit scattering volume, applied BEFORE aerial
369
- // integration — reprojecting the already-integrated aerial LUT would double-integrate along the
370
- // view ray. Optional: skipped when no history ping-pong is supplied (TAA disabled), in which
371
- // case the aerial pass consumes the raw (noisier) scattering LUT directly.
372
- let volume_lighting_resolved = light_scattering_lut;
373
-
374
- if (taa_history !== undefined && taa_output !== undefined) {
375
- const taa_outputs = graph_compute_pass({
376
- graph,
377
- shader: shader_volumetrics_taa,
378
- group_count: [
379
- Math.ceil(volumetric_resolution[0] / 8),
380
- Math.ceil(volumetric_resolution[1] / 8),
381
- Math.ceil(volumetric_resolution[2] / 4),
382
- ],
383
- bindings: {
384
- t_current: light_scattering_lut,
385
- t_history: taa_history,
386
- t_output: taa_output,
387
- volumetrics_metadata,
388
- camera_current: graph_import_buffer(graph, camera.buffer.buffer, "volumetrics taa camera"),
389
- camera_previous: graph_import_buffer(graph, camera_previous.buffer.buffer, "volumetrics taa camera previous"),
390
- },
391
- });
392
-
393
- volume_lighting_resolved = taa_outputs.t_output;
394
- }
395
-
396
- const a_data = {};
397
-
398
- const a_pass = graph.add("aerial lut", a_data, (data, resources, context) => {
399
- const output = resources.get(data.output);
400
-
401
- const participating_media0 = resources.get(data.participating_media0);
402
- const participating_media1 = resources.get(data.participating_media1);
403
- const volumetrics_metadata = resources.get(data.volumetrics_metadata);
404
-
405
- const light_scattering_lut = resources.get(data.light_scattering_lut);
406
- const sun_transmittance_lut = resources.get(data.sun_transmittance_lut);
407
-
408
- const encoder = context.encoder;
409
-
410
-
411
- const graphics = context.graphics;
412
- const device = graphics.device;
413
-
414
- const sLinear = graphics.samplers.obtain(SamplerDescriptor.from({
415
- magFilter: "linear",
416
- }));
417
-
418
- const tSTBN_vec3 = graphics.textures.obtain(STATIC_GRAPHICS_ENGINE_ASSETS.stbn_vec3);
419
-
420
- shader_volumetrics_build_scattering_lut.dispatch({
421
- encoder,
422
- group_count_x: Math.ceil(output.width / 16),
423
- group_count_y: Math.ceil(output.height / 16),
424
- bindings: {
425
- camera: camera.buffer.buffer,
426
- view: view.uniform_buffer.buffer,
427
- volumetrics_metadata,
428
- tSTBN_vec3: tSTBN_vec3.obtainView(),
429
- sLinear,
430
-
431
- tVolumeLighting: light_scattering_lut.obtainView(),
432
- tParticipatingMedia0: participating_media0.obtainView(),
433
- tParticipatingMedia1: participating_media1.obtainView(),
434
-
435
- // RDR2-style far-field march inputs (used beyond froxel_far)
436
- scene_volumetrics_metadata: view.scene.volumetrics.metadata_buffer.buffer,
437
- scene_volumes: view.scene.volumetrics.table.buffer,
438
- light_data: lights.buffer_data,
439
- tSunTransmittance: sun_transmittance_lut.obtainView(),
440
-
441
- tOutput: output.obtainView(DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR),
442
- }
443
- });
444
-
445
-
446
- });
447
-
448
- a_data.participating_media0 = a_pass.read(participating_media0);
449
- a_data.participating_media1 = a_pass.read(participating_media1);
450
- a_data.volumetrics_metadata = a_pass.read(volumetrics_metadata);
451
- a_data.sun_transmittance_lut = a_pass.read(transmittance_lut);
452
-
453
- a_data.output = a_pass.create("Integrated Volumetrics", TextureResourceDescriptor.from({
454
- resolution: volumetric_resolution,
455
- dimension: "3d",
456
- format: "rgba16float",
457
- usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING
458
- }))
459
-
460
-
461
- a_data.light_scattering_lut = a_pass.read(volume_lighting_resolved);
462
-
463
-
464
- // TODO add temporal reprojection
465
-
466
- return {
467
- lut_transmittance: transmittance_lut,
468
- lut_multiple_scattering: multiscatted_lut,
469
- lut_aerial: a_data.output,
470
- volumetrics_metadata,
471
- };
1
+
2
+
3
+ import { assert } from "../../../core/assert.js";
4
+ import { SamplerDescriptor } from "../../descriptor/texture/SamplerDescriptor.js";
5
+ import { TextureResourceDescriptor } from "../../device/graph/TextureResourceDescriptor.js";
6
+ import { SHADOWMAP_COMPARE_SAMPLER_DESCRIPTOR } from "../shadow/map/SHADOWMAP_COMPARE_SAMPLER_DESCRIPTOR.js";
7
+ import { STATIC_GRAPHICS_ENGINE_ASSETS } from "../STATIC_GRAPHICS_ENGINE_ASSETS.js";
8
+ import { DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR } from "../texture/DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR.js";
9
+ import { graph_compute_pass } from "../shader/graph/compute/graph_compute_pass.js";
10
+ import { graph_import_buffer } from "../shader/graph/graph_import_buffer.js";
11
+ import { shader_multiscatter_lut } from "../atmosphere/shader_multiscatter_lut.js";
12
+ import { shader_transmittance_lut } from "../atmosphere/shader_transmittance_lut.js";
13
+ import { shader_volumetrics_build_lighting } from "./shader_volumetrics_build_lighting.js";
14
+ import { shader_volumetrics_build_participating_media } from "./shader_volumetrics_build_participating_media.js";
15
+ import { shader_volumetrics_build_scattering_lut } from "./shader_volumetrics_build_scattering_lut.js";
16
+ import { shader_volumetrics_build_sun_shadow_volume } from "./shader_volumetrics_build_sun_shadow_volume.js";
17
+ import {
18
+ VOLUMETRICS_SUN_SHADOW_CASCADE_COUNT,
19
+ VOLUMETRICS_SUN_SHADOW_WORKGROUP_RESOLUTION
20
+ } from "./VOLUMETRICS_SUN_SHADOW_RESOLUTION.js";
21
+ import { volumetrics_sun_shadow_texture_name } from "./chunk_volumetrics_sun_optical_depth.js";
22
+ import { shader_volumetrics_taa } from "./taa/shader_volumetrics_taa.js";
23
+
24
+ /**
25
+ *
26
+ * @param {FrameGraph} graph
27
+ */
28
+ function graph_integrate_sky({ graph }) {
29
+ const t_data = {};
30
+
31
+ const t_pass = graph.add("transmittance", t_data, (data, resources, context) => {
32
+
33
+ const output = resources.get(data.output);
34
+
35
+ const encoder = context.encoder;
36
+
37
+ const settings_type = shader_transmittance_lut.descriptor.resources.getResourceByName("settings").buffer_type;
38
+
39
+ const settings = encoder.allocateTransientValueBuffer(settings_type, {
40
+ steps: 40
41
+ });
42
+
43
+ shader_transmittance_lut.draw({
44
+ encoder: encoder,
45
+ bindings: {
46
+ settings
47
+ },
48
+ colorAttachments: [{
49
+ view: output.obtainView(DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR),
50
+ loadOp: "clear",
51
+ storeOp: "store",
52
+ }]
53
+ })
54
+ });
55
+
56
+ t_data.output = t_pass.create("transmittance lut", TextureResourceDescriptor.from({
57
+ resolution: [256, 64],
58
+ format: "rgba16float",
59
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT
60
+ }));
61
+
62
+ const m_data = {};
63
+
64
+ const m_pass = graph.add("multiscatter lut", m_data, (data, resources, context) => {
65
+ const output = resources.get(data.output);
66
+
67
+ const settings_type = shader_multiscatter_lut.descriptor.resources.getResourceByName("settings").buffer_type;
68
+
69
+ const encoder = context.encoder;
70
+
71
+ const settings = encoder.allocateTransientValueBuffer(settings_type, {
72
+ steps: 20,
73
+ sqrt_samples: 8,
74
+ });
75
+
76
+ const tSkyLutTransmittance = resources.get(data.tSkyLutTransmittance);
77
+
78
+ shader_multiscatter_lut.draw({
79
+ encoder: encoder,
80
+ bindings: {
81
+ settings,
82
+ tSkyLutTransmittance
83
+ },
84
+ colorAttachments: [{
85
+ view: output.obtainView(DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR),
86
+ loadOp: "clear",
87
+ storeOp: "store",
88
+ }]
89
+ })
90
+ });
91
+
92
+ m_data.output = m_pass.create("multiscatter lut", TextureResourceDescriptor.from({
93
+ resolution: [32, 32],
94
+ format: "rgba16float",
95
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT
96
+ }))
97
+
98
+ m_data.tSkyLutTransmittance = m_pass.read(t_data.output);
99
+
100
+
101
+ return { transmittance_lut: t_data.output, multiscatted_lut: m_data.output };
102
+ }
103
+
104
+
105
+ /**
106
+ *
107
+ * @param {FrameGraph} graph
108
+ * @param {GPUCameraContext} camera
109
+ * @param {GPUSceneContext} scene
110
+ * @param {GPUViewContext} view
111
+ * @param {number} volumetrics_metadata
112
+ * @param {number[]} resolution output resolution
113
+ */
114
+ function graph_integrate_participating_media({
115
+ graph,
116
+ camera,
117
+ scene,
118
+ view,
119
+ volumetrics_metadata,
120
+ resolution,
121
+ }) {
122
+
123
+ const data = {};
124
+
125
+
126
+ const pass = graph.add(
127
+ "aerial lut",
128
+ data,
129
+ /**
130
+ *
131
+ * @param {} data
132
+ * @param {FramePassResources} resources
133
+ * @param {GraphicsExecutionContext} context
134
+ */
135
+ (data, resources, context) => {
136
+
137
+ /**
138
+ * @type {GPUTextureContext}
139
+ */
140
+ const output0 = resources.get(data.output0);
141
+ /**
142
+ * @type {GPUTextureContext}
143
+ */
144
+ const output1 = resources.get(data.output1);
145
+
146
+ const encoder = context.encoder;
147
+
148
+ const volumetrics_metadata = resources.get(data.volumetrics_metadata);
149
+
150
+ shader_volumetrics_build_participating_media.dispatch({
151
+ encoder,
152
+ group_count_x: Math.ceil(output0.width / 16),
153
+ group_count_y: Math.ceil(output0.height / 16),
154
+ group_count_z: output0.depth,
155
+ bindings: {
156
+ camera: camera.buffer.buffer,
157
+ view: view.uniform_buffer.buffer,
158
+ volumetrics_metadata,
159
+ scene_volumetrics_metadata: scene.volumetrics.metadata_buffer.buffer,
160
+ scene_volumes: scene.volumetrics.table.buffer,
161
+ tOutput0: output0.obtainView(DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR),
162
+ tOutput1: output1.obtainView(DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR),
163
+ }
164
+ });
165
+
166
+
167
+ });
168
+
169
+ data.volumetrics_metadata = pass.read(volumetrics_metadata);
170
+
171
+ data.output0 = pass.create("Participating Media 0", TextureResourceDescriptor.from({
172
+ resolution,
173
+ dimension: "3d",
174
+ format: "rgba16float",
175
+ usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT
176
+ }));
177
+ data.output1 = pass.create("Participating Media 1", TextureResourceDescriptor.from({
178
+ resolution,
179
+ dimension: "3d",
180
+ format: "rgba16float",
181
+ usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT
182
+ }));
183
+
184
+ return {
185
+ participating_media0: data.output0,
186
+ participating_media1: data.output1,
187
+ volumetrics_metadata: data.volumetrics_metadata,
188
+ };
189
+ }
190
+
191
+ /**
192
+ * Q-04. The sun's optical depth, integrated once per frame into cascaded light-aligned volumes so
193
+ * the lighting pass can read it in a single fetch instead of marching toward the sun from every
194
+ * froxel.
195
+ *
196
+ * Sits between the participating-media build and the lighting pass because it reads the former and
197
+ * is read by the latter; the frame graph derives that ordering from the handles alone. The cascades
198
+ * are ordered among themselves for the same reason: each one is seeded from the value the cascade
199
+ * outside it already computed at its entry face, so the outermost is built first and the handle it
200
+ * returns is what makes the next pass wait for it.
201
+ *
202
+ * A texture per cascade rather than one texture with the cascades stacked along w. Stacking would
203
+ * put a pass's own output and its input in the same texture, which WebGPU rejects — a subresource
204
+ * cannot be bound as writable storage and as a sampled texture at once — and there is no
205
+ * subresource finer than the whole 3D texture to split it on.
206
+ *
207
+ * Always present, even with the mode off — the volumes are then dispatched at 1x1x1 (see
208
+ * `GPUViewVolumetrics`), which keeps the graph's topology identical on both sides of the A/B. A
209
+ * pass appearing and disappearing would change resource aliasing and pass scheduling along with the
210
+ * thing being measured, and this subsystem has already produced one confident wrong answer from a
211
+ * measurement where the two sides did not pay the same fixed costs.
212
+ *
213
+ * @param {FrameGraph} graph
214
+ * @param {GPUCameraContext} camera
215
+ * @param {number} participating_media1
216
+ * @param {number} volumetrics_metadata
217
+ * @param {number[]} resolution voxel counts of one cascade
218
+ * @returns {number[]} handles of the cascades, innermost first
219
+ */
220
+ function graph_build_sun_shadow_volume({
221
+ graph,
222
+ camera,
223
+ participating_media1,
224
+ volumetrics_metadata,
225
+ resolution,
226
+ }) {
227
+
228
+ const camera_buffer = graph_import_buffer(graph, camera.buffer.buffer, "volumetrics sun shadow camera");
229
+
230
+ const group_count = [
231
+ // one invocation per COLUMN along the sun axis, so only x and y are dispatched
232
+ Math.ceil(resolution[0] / VOLUMETRICS_SUN_SHADOW_WORKGROUP_RESOLUTION[0]),
233
+ Math.ceil(resolution[1] / VOLUMETRICS_SUN_SHADOW_WORKGROUP_RESOLUTION[1]),
234
+ 1,
235
+ ];
236
+
237
+ const cascades = [];
238
+
239
+ for (let cascade = VOLUMETRICS_SUN_SHADOW_CASCADE_COUNT - 1; cascade >= 0; cascade--) {
240
+
241
+ const bindings = {
242
+ camera: camera_buffer,
243
+ volumetrics_metadata,
244
+ tParticipatingMedia1: participating_media1,
245
+ tSunShadowVolume: TextureResourceDescriptor.from({
246
+ resolution,
247
+ dimension: "3d",
248
+ format: "rgba16float",
249
+ usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING
250
+ }),
251
+ };
252
+
253
+ if (cascade < VOLUMETRICS_SUN_SHADOW_CASCADE_COUNT - 1) {
254
+ bindings.tSunShadowOuter = cascades[cascade + 1];
255
+ }
256
+
257
+ const outputs = graph_compute_pass({
258
+ graph,
259
+ shader: shader_volumetrics_build_sun_shadow_volume[cascade],
260
+ group_count,
261
+ bindings,
262
+ });
263
+
264
+ cascades[cascade] = outputs.tSunShadowVolume;
265
+ }
266
+
267
+ return cascades;
268
+ }
269
+
270
+ /**
271
+ *
272
+ * @param {FrameGraph} graph
273
+ * @param participating_media0
274
+ * @param participating_media1
275
+ * @param volumetrics_metadata
276
+ * @param light_cluster_data
277
+ * @param light_cluster_lookup
278
+ * @param light_cluster_parameters
279
+ * @param shadowmap_atlas
280
+ * @param sun_transmittance_lut
281
+ * @param sun_multiple_scattering_lut
282
+ * @param {number[]} sun_shadow_cascades handles of the Q-04 optical-depth cascades, innermost first
283
+ * @param lights
284
+ * @param view
285
+ * @param camera
286
+ * @param resolution
287
+ */
288
+ function graph_integrate_light_scattering({
289
+ graph,
290
+ participating_media0,
291
+ participating_media1,
292
+ volumetrics_metadata,
293
+ light_cluster_data,
294
+ light_cluster_lookup,
295
+ light_cluster_parameters,
296
+ shadowmap_atlas,
297
+ sun_transmittance_lut,
298
+ sun_multiple_scattering_lut,
299
+ sun_shadow_cascades,
300
+ lights,
301
+ view,
302
+ camera,
303
+ resolution,
304
+ }) {
305
+
306
+ const data = {};
307
+
308
+ const pass = graph.add("scattering lut", data, (data, resources, context) => {
309
+ const output = resources.get(data.output);
310
+
311
+ const volumetrics_metadata = resources.get(data.volumetrics_metadata);
312
+
313
+ const participating_media0 = resources.get(data.participating_media0);
314
+ const participating_media1 = resources.get(data.participating_media1);
315
+
316
+ const light_cluster_data = resources.get(data.light_cluster_data);
317
+ const light_cluster_lookup = resources.get(data.light_cluster_lookup);
318
+ const light_cluster_parameters = resources.get(data.light_cluster_parameters);
319
+ const shadowmap_atlas = resources.get(data.shadowmap_atlas);
320
+
321
+ const sun_transmittance_lut = resources.get(data.sun_transmittance_lut);
322
+ const sun_multiple_scattering_lut = resources.get(data.sun_multiple_scattering_lut);
323
+ const sun_shadow_cascades = data.sun_shadow_cascades.map(handle => resources.get(handle));
324
+
325
+ const sampler_shadowmap = context.graphics.samplers.obtain(SHADOWMAP_COMPARE_SAMPLER_DESCRIPTOR);
326
+ const sLinear = context.graphics.samplers.obtain(SamplerDescriptor.from({
327
+ magFilter: "linear",
328
+ }));
329
+
330
+ // Spatiotemporal blue noise for the per-froxel sample offset. The pass takes one sample and
331
+ // leans on the volumetric TAA to integrate, which only works if each froxel's sequence over
332
+ // frames is well distributed — the property a per-froxel hash does not have.
333
+ const tSTBN_vec3 = context.graphics.textures.obtain(STATIC_GRAPHICS_ENGINE_ASSETS.stbn_vec3);
334
+
335
+ const encoder = context.encoder;
336
+
337
+ shader_volumetrics_build_lighting.dispatch({
338
+ encoder,
339
+ group_count_x: Math.ceil(output.width / 16),
340
+ group_count_y: Math.ceil(output.height / 16),
341
+ group_count_z: output.depth,
342
+ bindings: {
343
+ camera: camera.buffer.buffer,
344
+ view: view.uniform_buffer.buffer,
345
+ volumetrics_metadata,
346
+ sLinear,
347
+ tSunTransmittance: sun_transmittance_lut.obtainView(),
348
+ tMultiScattering: sun_multiple_scattering_lut.obtainView(),
349
+ tSTBN_vec3: tSTBN_vec3.obtainView(),
350
+ tParticipatingMedia0: participating_media0.obtainView(),
351
+ tParticipatingMedia1: participating_media1.obtainView(),
352
+ ...Object.fromEntries(sun_shadow_cascades.map(
353
+ (texture, cascade) => [volumetrics_sun_shadow_texture_name(cascade), texture.obtainView()]
354
+ )),
355
+ tLighting: output.obtainView(DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR),
356
+ //
357
+ light_data: lights.buffer_data,
358
+
359
+ cluster_data: light_cluster_data,
360
+ cluster_lookup: light_cluster_lookup,
361
+ cluster_parameters: light_cluster_parameters,
362
+
363
+ shadowmapAtlas: shadowmap_atlas.obtainView(),
364
+ sCompareLinear: sampler_shadowmap,
365
+ }
366
+ })
367
+ });
368
+
369
+ data.participating_media0 = pass.read(participating_media0);
370
+ data.participating_media1 = pass.read(participating_media1);
371
+ data.volumetrics_metadata = pass.read(volumetrics_metadata);
372
+
373
+ data.shadowmap_atlas = pass.read(shadowmap_atlas);
374
+ data.light_cluster_data = pass.read(light_cluster_data);
375
+ data.light_cluster_lookup = pass.read(light_cluster_lookup);
376
+ data.light_cluster_parameters = pass.read(light_cluster_parameters);
377
+
378
+ data.sun_transmittance_lut = pass.read(sun_transmittance_lut);
379
+ data.sun_multiple_scattering_lut = pass.read(sun_multiple_scattering_lut);
380
+ data.sun_shadow_cascades = sun_shadow_cascades.map(handle => pass.read(handle));
381
+
382
+ data.output = pass.create("Light Scattering LUT", TextureResourceDescriptor.from({
383
+ resolution,
384
+ dimension: "3d",
385
+ format: "rgba16float",
386
+ usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT
387
+ }));
388
+
389
+
390
+ return data.output;
391
+
392
+ }
393
+
394
+ /**
395
+ *
396
+ * @param {FrameGraph} graph
397
+ * @param {GPUCameraContext} camera
398
+ * @param {GPULightCollection} lights
399
+ * @param {GPUViewContext} view
400
+ * @param {number} light_cluster_data
401
+ * @param {number} light_cluster_lookup
402
+ * @param {number} light_cluster_parameters
403
+ * @param {number} shadowmap_atlas
404
+ * @param {number[]} volumetric_resolution
405
+ * @param {number[]} sun_shadow_resolution voxel counts of one Q-04 sun optical-depth cascade;
406
+ * `[1, 1, 1]` when the froxels are marching instead, which keeps the passes in the graph without
407
+ * doing any work
408
+ * @param {number} volumetrics_metadata
409
+ * @param {number} [taa_history] imported previous-frame accumulated scattering volume
410
+ * @param {number} [taa_output] imported destination for this frame's accumulated scattering volume
411
+ * @param {GPUCameraContext} [camera_previous] previous-frame camera, for temporal reprojection
412
+ */
413
+ export function graph_build_volumetrics({
414
+ graph,
415
+ camera,
416
+ lights,
417
+ view,
418
+ light_cluster_data,
419
+ light_cluster_lookup,
420
+ light_cluster_parameters,
421
+ shadowmap_atlas,
422
+ volumetric_resolution,
423
+ sun_shadow_resolution,
424
+ volumetrics_metadata,
425
+ taa_history,
426
+ taa_output,
427
+ camera_previous,
428
+ }) {
429
+
430
+ assert.defined(view, 'view');
431
+ assert.defined(volumetric_resolution, 'volumetric_resolution');
432
+ assert.defined(sun_shadow_resolution, 'sun_shadow_resolution');
433
+
434
+ const { transmittance_lut, multiscatted_lut } = graph_integrate_sky({ graph: graph });
435
+
436
+ const {
437
+ participating_media0,
438
+ participating_media1,
439
+ } = graph_integrate_participating_media({
440
+ graph,
441
+ scene: view.scene,
442
+ view,
443
+ resolution: volumetric_resolution,
444
+ volumetrics_metadata,
445
+ camera,
446
+ });
447
+
448
+ const sun_shadow_cascades = graph_build_sun_shadow_volume({
449
+ graph,
450
+ camera,
451
+ participating_media1,
452
+ volumetrics_metadata,
453
+ resolution: sun_shadow_resolution,
454
+ });
455
+
456
+ const light_scattering_lut = graph_integrate_light_scattering({
457
+ graph,
458
+ resolution: volumetric_resolution,
459
+ camera,
460
+ view,
461
+ sun_transmittance_lut: transmittance_lut,
462
+ sun_multiple_scattering_lut: multiscatted_lut,
463
+ sun_shadow_cascades,
464
+ participating_media0,
465
+ participating_media1,
466
+ volumetrics_metadata,
467
+ light_cluster_data,
468
+ light_cluster_lookup,
469
+ light_cluster_parameters,
470
+ shadowmap_atlas,
471
+ lights,
472
+ })
473
+
474
+ // Temporal reprojection of the per-froxel lit scattering volume, applied BEFORE aerial
475
+ // integration — reprojecting the already-integrated aerial LUT would double-integrate along the
476
+ // view ray. Optional: skipped when no history ping-pong is supplied (TAA disabled), in which
477
+ // case the aerial pass consumes the raw (noisier) scattering LUT directly.
478
+ let volume_lighting_resolved = light_scattering_lut;
479
+
480
+ if (taa_history !== undefined && taa_output !== undefined) {
481
+ const taa_outputs = graph_compute_pass({
482
+ graph,
483
+ shader: shader_volumetrics_taa,
484
+ group_count: [
485
+ Math.ceil(volumetric_resolution[0] / 8),
486
+ Math.ceil(volumetric_resolution[1] / 8),
487
+ Math.ceil(volumetric_resolution[2] / 4),
488
+ ],
489
+ bindings: {
490
+ t_current: light_scattering_lut,
491
+ t_history: taa_history,
492
+ t_output: taa_output,
493
+ volumetrics_metadata,
494
+ camera_current: graph_import_buffer(graph, camera.buffer.buffer, "volumetrics taa camera"),
495
+ camera_previous: graph_import_buffer(graph, camera_previous.buffer.buffer, "volumetrics taa camera previous"),
496
+ },
497
+ });
498
+
499
+ volume_lighting_resolved = taa_outputs.t_output;
500
+ }
501
+
502
+ const a_data = {};
503
+
504
+ const a_pass = graph.add("aerial lut", a_data, (data, resources, context) => {
505
+ const output = resources.get(data.output);
506
+
507
+ const participating_media0 = resources.get(data.participating_media0);
508
+ const participating_media1 = resources.get(data.participating_media1);
509
+ const volumetrics_metadata = resources.get(data.volumetrics_metadata);
510
+
511
+ const light_scattering_lut = resources.get(data.light_scattering_lut);
512
+ const sun_transmittance_lut = resources.get(data.sun_transmittance_lut);
513
+
514
+ const encoder = context.encoder;
515
+
516
+
517
+ const graphics = context.graphics;
518
+ const device = graphics.device;
519
+
520
+ const sLinear = graphics.samplers.obtain(SamplerDescriptor.from({
521
+ magFilter: "linear",
522
+ }));
523
+
524
+ const tSTBN_vec3 = graphics.textures.obtain(STATIC_GRAPHICS_ENGINE_ASSETS.stbn_vec3);
525
+
526
+ shader_volumetrics_build_scattering_lut.dispatch({
527
+ encoder,
528
+ group_count_x: Math.ceil(output.width / 16),
529
+ group_count_y: Math.ceil(output.height / 16),
530
+ bindings: {
531
+ camera: camera.buffer.buffer,
532
+ view: view.uniform_buffer.buffer,
533
+ volumetrics_metadata,
534
+ tSTBN_vec3: tSTBN_vec3.obtainView(),
535
+ sLinear,
536
+
537
+ tVolumeLighting: light_scattering_lut.obtainView(),
538
+ tParticipatingMedia0: participating_media0.obtainView(),
539
+ tParticipatingMedia1: participating_media1.obtainView(),
540
+
541
+ // RDR2-style far-field march inputs (used beyond froxel_far)
542
+ scene_volumetrics_metadata: view.scene.volumetrics.metadata_buffer.buffer,
543
+ scene_volumes: view.scene.volumetrics.table.buffer,
544
+ light_data: lights.buffer_data,
545
+ tSunTransmittance: sun_transmittance_lut.obtainView(),
546
+
547
+ tOutput: output.obtainView(DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR),
548
+ }
549
+ });
550
+
551
+
552
+ });
553
+
554
+ a_data.participating_media0 = a_pass.read(participating_media0);
555
+ a_data.participating_media1 = a_pass.read(participating_media1);
556
+ a_data.volumetrics_metadata = a_pass.read(volumetrics_metadata);
557
+ a_data.sun_transmittance_lut = a_pass.read(transmittance_lut);
558
+
559
+ a_data.output = a_pass.create("Integrated Volumetrics", TextureResourceDescriptor.from({
560
+ resolution: volumetric_resolution,
561
+ dimension: "3d",
562
+ format: "rgba16float",
563
+ usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING
564
+ }))
565
+
566
+
567
+ a_data.light_scattering_lut = a_pass.read(volume_lighting_resolved);
568
+
569
+
570
+ // TODO add temporal reprojection
571
+
572
+ return {
573
+ lut_transmittance: transmittance_lut,
574
+ lut_multiple_scattering: multiscatted_lut,
575
+ lut_aerial: a_data.output,
576
+ volumetrics_metadata,
577
+ };
472
578
  }