@woosh/meep-engine 3.54.0 → 3.56.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.
- package/package.json +1 -1
- package/src/engine/ecs/transform/Transform64.d.ts +21 -0
- package/src/engine/ecs/transform/Transform64.d.ts.map +1 -1
- package/src/engine/ecs/transform/Transform64.js +26 -0
- package/src/shade/RENDERER_CONTRACT.md +5 -2
- package/src/shade/device/SHADE_DEVICE_FLOOR.d.ts.map +1 -1
- package/src/shade/device/SHADE_DEVICE_FLOOR.js +2 -0
- package/src/shade/renderer/Renderer.d.ts +5 -0
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +26 -3
- package/src/shade/renderer/hiz/generate_downsampler_code.js +1 -1
- package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.js +1 -1
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts +2 -1
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
- package/src/shade/renderer/particles/GPUParticleSystem.js +1 -1
- package/src/shade/renderer/particles/graph_particles_avboit.d.ts +2 -2
- package/src/shade/renderer/particles/graph_particles_avboit.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph_particles_avboit.js +4 -9
- package/src/shade/renderer/postprocess/denoise/fragment_shader_denoise_ao.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/denoise/fragment_shader_denoise_ao.js +154 -147
- package/src/shade/renderer/postprocess/gtao/GTAO.d.ts +2 -1
- package/src/shade/renderer/postprocess/gtao/GTAO.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/gtao/GTAO.js +5 -1
- package/src/shade/renderer/postprocess/gtao/GTAO_CONSTANTS.d.ts +8 -0
- package/src/shade/renderer/postprocess/gtao/GTAO_CONSTANTS.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/gtao/GTAO_CONSTANTS.js +10 -1
- package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.js +5 -0
- package/src/shade/renderer/postprocess/gtao/chunk_gtao_compute_mip_level.d.ts +22 -0
- package/src/shade/renderer/postprocess/gtao/chunk_gtao_compute_mip_level.d.ts.map +1 -0
- package/src/shade/renderer/postprocess/gtao/chunk_gtao_compute_mip_level.js +33 -0
- package/src/shade/renderer/postprocess/gtao/chunk_gtao_resolve_hzb_depth.d.ts +22 -0
- package/src/shade/renderer/postprocess/gtao/chunk_gtao_resolve_hzb_depth.d.ts.map +1 -0
- package/src/shade/renderer/postprocess/gtao/chunk_gtao_resolve_hzb_depth.js +56 -0
- package/src/shade/renderer/postprocess/gtao/chunk_gtao_sample_depth.d.ts +13 -0
- package/src/shade/renderer/postprocess/gtao/chunk_gtao_sample_depth.d.ts.map +1 -0
- package/src/shade/renderer/postprocess/gtao/chunk_gtao_sample_depth.js +42 -0
- package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.d.ts +2 -1
- package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.js +382 -357
- package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.js +414 -410
- package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
- package/src/shade/renderer/postprocess/ssr/prefilter/NOTES.md +0 -7
- package/src/shade/renderer/view/GPUViewContext.d.ts +0 -14
- package/src/shade/renderer/view/GPUViewContext.d.ts.map +1 -1
- package/src/shade/renderer/view/GPUViewContext.js +0 -29
- package/src/shade/renderer/view/ViewManager.d.ts +1 -1
- package/src/shade/renderer/view/ViewManager.js +1 -1
- package/src/shade/renderer/volumetrics/GPUViewVolumetrics.d.ts +40 -3
- package/src/shade/renderer/volumetrics/GPUViewVolumetrics.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/GPUViewVolumetrics.js +660 -583
- package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_local_light.d.ts +10 -5
- package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_local_light.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_local_light.js +30 -14
- package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_sample.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_sample.js +17 -3
- package/src/shade/renderer/wind/GPUSceneWind.d.ts +1 -1
- package/src/engine/graphics3/decal/DecalAtlas.d.ts +0 -43
- package/src/engine/graphics3/decal/DecalAtlas.d.ts.map +0 -1
- package/src/engine/graphics3/decal/DecalAtlasFill.d.ts +0 -12
- package/src/engine/graphics3/decal/DecalAtlasFill.d.ts.map +0 -1
- package/src/engine/graphics3/decal/decal_atlas_border.d.ts +0 -52
- package/src/engine/graphics3/decal/decal_atlas_border.d.ts.map +0 -1
- package/src/shade/renderer/postprocess/gtao/graph_build_depth_mipmap.d.ts +0 -26
- package/src/shade/renderer/postprocess/gtao/graph_build_depth_mipmap.d.ts.map +0 -1
- package/src/shade/renderer/postprocess/gtao/graph_build_depth_mipmap.js +0 -268
- package/src/shade/renderer/postprocess/ssr/copy_shader_f32.d.ts +0 -3
- package/src/shade/renderer/postprocess/ssr/copy_shader_f32.d.ts.map +0 -1
- package/src/shade/renderer/postprocess/ssr/copy_shader_f32.js +0 -32
- package/src/shade/renderer/postprocess/ssr/depth_max_shader.d.ts +0 -17
- package/src/shade/renderer/postprocess/ssr/depth_max_shader.d.ts.map +0 -1
- package/src/shade/renderer/postprocess/ssr/depth_max_shader.js +0 -98
- package/src/shade/renderer/postprocess/ssr/graph_build_depth_max_pyramid.d.ts +0 -23
- package/src/shade/renderer/postprocess/ssr/graph_build_depth_max_pyramid.d.ts.map +0 -1
- package/src/shade/renderer/postprocess/ssr/graph_build_depth_max_pyramid.js +0 -118
- package/src/shade/renderer/volumetrics/VOLUMETRICS_RESOLUTION_Z.d.ts +0 -2
- package/src/shade/renderer/volumetrics/VOLUMETRICS_RESOLUTION_Z.d.ts.map +0 -1
- package/src/shade/renderer/volumetrics/VOLUMETRICS_RESOLUTION_Z.js +0 -1
- package/src/shade/renderer/volumetrics/chunk_volumetrics_froxel_sample.d.ts +0 -24
- package/src/shade/renderer/volumetrics/chunk_volumetrics_froxel_sample.d.ts.map +0 -1
- package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_froxel_centre_ws.d.ts +0 -15
- package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_froxel_centre_ws.d.ts.map +0 -1
- package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_gi.d.ts +0 -17
- package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_gi.d.ts.map +0 -1
- package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_gi_sample.d.ts +0 -35
- package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_gi_sample.d.ts.map +0 -1
- package/src/shade/renderer/volumetrics/gi/graph_build_froxel_gi.d.ts +0 -35
- package/src/shade/renderer/volumetrics/gi/graph_build_froxel_gi.d.ts.map +0 -1
- package/src/shade/renderer/volumetrics/gi/shader_volumetrics_gi.d.ts +0 -10
- package/src/shade/renderer/volumetrics/gi/shader_volumetrics_gi.d.ts.map +0 -1
- package/src/shade/renderer/volumetrics/gi/volumetrics_gi_resources.d.ts +0 -28
- package/src/shade/renderer/volumetrics/gi/volumetrics_gi_resources.d.ts.map +0 -1
- package/src/shade/renderer/volumetrics/graph_build_froxel_irradiance.d.ts +0 -48
- package/src/shade/renderer/volumetrics/graph_build_froxel_irradiance.d.ts.map +0 -1
- package/src/shade/renderer/volumetrics/shader_volumetrics_build_irradiance.d.ts +0 -28
- package/src/shade/renderer/volumetrics/shader_volumetrics_build_irradiance.d.ts.map +0 -1
- package/src/shade/renderer/volumetrics/shader_volumetrics_classify_froxels.d.ts +0 -16
- package/src/shade/renderer/volumetrics/shader_volumetrics_classify_froxels.d.ts.map +0 -1
- package/src/shade/renderer/volumetrics/shader_volumetrics_lighting_regimes.d.ts +0 -23
- package/src/shade/renderer/volumetrics/shader_volumetrics_lighting_regimes.d.ts.map +0 -1
|
@@ -1,583 +1,660 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
VOLUMETRICS_SUN_SHADOW_CASCADE_COUNT,
|
|
19
|
-
VOLUMETRICS_SUN_SHADOW_CASCADE_RATIO,
|
|
20
|
-
VOLUMETRICS_SUN_SHADOW_LIGHT_NONE,
|
|
21
|
-
VOLUMETRICS_SUN_SHADOW_RESOLUTION
|
|
22
|
-
} from "./VOLUMETRICS_SUN_SHADOW_RESOLUTION.js";
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* @typedef {import("../../../engine/graphics/render/frame_graph/FrameGraph.js").FrameGraph} FrameGraph
|
|
26
|
-
* @typedef {import("../view/GPUViewContext.js").GPUViewContext} GPUViewContext
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
*
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
*
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
*
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
this.#
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
* @
|
|
117
|
-
*/
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
*
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
* @
|
|
160
|
-
*/
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
*
|
|
181
|
-
* @
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* @
|
|
228
|
-
*/
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
)
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
const
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
//
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
//
|
|
398
|
-
//
|
|
399
|
-
//
|
|
400
|
-
const
|
|
401
|
-
|
|
402
|
-
const
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
this.#
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
this.#
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
}
|
|
1
|
+
import { assert } from "../../../core/assert.js";
|
|
2
|
+
import { GPUTypedBuffer } from "../buffer/GPUTypedBuffer.js";
|
|
3
|
+
import { build_light_cluster_parameters } from "../light/cluster/build_light_cluster_parameters.js";
|
|
4
|
+
import { ShaderSwitchSet } from "../shader/compiler/ShaderSwitchSet.js";
|
|
5
|
+
import { graph_import_buffer } from "../shader/graph/graph_import_buffer.js";
|
|
6
|
+
import { graph_import_texture } from "../shader/graph/graph_import_texture.js";
|
|
7
|
+
import { update_frustum_far_plane } from "../shadow/map/update_frustum_far_plane.js";
|
|
8
|
+
import { GPUTextureContext } from "../texture/GPUTextureContext.js";
|
|
9
|
+
import { slice_flat_array } from "../util/slice_flat_array.js";
|
|
10
|
+
import {
|
|
11
|
+
build_volumetrics_sun_shadow_transform,
|
|
12
|
+
volumetrics_sun_shadow_cascade_far
|
|
13
|
+
} from "./build_volumetrics_sun_shadow_transform.js";
|
|
14
|
+
import { graph_build_volumetrics } from "./graph_build_volumetrics.js";
|
|
15
|
+
import { GPUIrradianceClipmap } from "./irradiance_clipmap/GPUIrradianceClipmap.js";
|
|
16
|
+
import { VOLUMETRICS_METADATA_STRUCT } from "./VOLUMETRICS_METADATA_STRUCT.js";
|
|
17
|
+
import {
|
|
18
|
+
VOLUMETRICS_SUN_SHADOW_CASCADE_COUNT,
|
|
19
|
+
VOLUMETRICS_SUN_SHADOW_CASCADE_RATIO,
|
|
20
|
+
VOLUMETRICS_SUN_SHADOW_LIGHT_NONE,
|
|
21
|
+
VOLUMETRICS_SUN_SHADOW_RESOLUTION
|
|
22
|
+
} from "./VOLUMETRICS_SUN_SHADOW_RESOLUTION.js";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @typedef {import("../../../engine/graphics/render/frame_graph/FrameGraph.js").FrameGraph} FrameGraph
|
|
26
|
+
* @typedef {import("../view/GPUViewContext.js").GPUViewContext} GPUViewContext
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
export class GPUViewVolumetrics {
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* NOTE: Initialized in the constructor
|
|
33
|
+
* @type {GPUTextureContext[]}
|
|
34
|
+
*/
|
|
35
|
+
#volumetrics_textures = [];
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Should we perform temporal integration, or not?
|
|
39
|
+
* If set to true - the next update will discard history.
|
|
40
|
+
* Useful for significant scene transitions and screen resizing.
|
|
41
|
+
* @type {boolean}
|
|
42
|
+
*/
|
|
43
|
+
#reset_history = true;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* {@link GPUViewContext#id} of the view the history was accumulated from. The history is that
|
|
47
|
+
* view's fog; a frame from any other view must not blend against it.
|
|
48
|
+
* @type {number}
|
|
49
|
+
*/
|
|
50
|
+
#history_view_id = -1;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Whether to run the 3D temporal reprojection (volumetric TAA) over the per-froxel scattering
|
|
54
|
+
* volume. When disabled, the aerial pass integrates the raw (noisier) scattering LUT directly —
|
|
55
|
+
* a safe fallback if temporal artifacts appear.
|
|
56
|
+
* @type {boolean}
|
|
57
|
+
*/
|
|
58
|
+
#taa_enabled = true;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @returns {boolean}
|
|
62
|
+
*/
|
|
63
|
+
get taa_enabled() {
|
|
64
|
+
return this.#taa_enabled;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @param {boolean} v
|
|
69
|
+
*/
|
|
70
|
+
set taa_enabled(v) {
|
|
71
|
+
if (v === this.#taa_enabled) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
this.#taa_enabled = v;
|
|
75
|
+
// re-enabling must not blend against a stale / never-written history texture
|
|
76
|
+
this.#reset_history = true;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Size of one froxel in XY, in screen pixels.
|
|
81
|
+
* @type {number}
|
|
82
|
+
*/
|
|
83
|
+
#tile_size = 16;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Number of froxel slices along the view.
|
|
87
|
+
* @type {number}
|
|
88
|
+
*/
|
|
89
|
+
#resolution_z = 64;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Size of one froxel in XY, in screen pixels. Smaller is finer fog detail at a cost that grows
|
|
93
|
+
* with the inverse square. The grid is resized on the next update and the TAA history dropped,
|
|
94
|
+
* so the frame after a change starts its accumulation over.
|
|
95
|
+
*
|
|
96
|
+
* The grid's XY extent, `ceil(view resolution / tile_size)`, must stay within the device's
|
|
97
|
+
* `maxTextureDimension3D`.
|
|
98
|
+
* @returns {number}
|
|
99
|
+
*/
|
|
100
|
+
get tile_size() {
|
|
101
|
+
return this.#tile_size;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @param {number} v positive integer
|
|
106
|
+
*/
|
|
107
|
+
set tile_size(v) {
|
|
108
|
+
assert.isPositiveInteger(v, 'tile_size');
|
|
109
|
+
|
|
110
|
+
this.#tile_size = v;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Number of froxel slices between the camera near and {@link froxel_far}. Cost grows linearly.
|
|
115
|
+
* The grid is resized on the next update and the TAA history dropped, as for {@link tile_size}.
|
|
116
|
+
* @returns {number}
|
|
117
|
+
*/
|
|
118
|
+
get resolution_z() {
|
|
119
|
+
return this.#resolution_z;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* @param {number} v integer, at least 2 (the depth curve needs a first and a last slice) and at
|
|
124
|
+
* most the device's `maxTextureDimension3D`
|
|
125
|
+
*/
|
|
126
|
+
set resolution_z(v) {
|
|
127
|
+
assert.isPositiveInteger(v, 'resolution_z');
|
|
128
|
+
assert.greaterThanOrEqual(v, 2, 'resolution_z');
|
|
129
|
+
assert.lessThanOrEqual(v, this.#device.limits.maxTextureDimension3D, 'resolution_z must fit maxTextureDimension3D');
|
|
130
|
+
|
|
131
|
+
this.#resolution_z = v;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* View-space distance at which the froxel grid ends and the aerial pass switches to a direct
|
|
136
|
+
* RDR2-style volume ray-march. Clamped to camera.far at upload, so the default (Infinity) means
|
|
137
|
+
* "grid covers the whole range" and the far march is a no-op.
|
|
138
|
+
* @type {number}
|
|
139
|
+
*/
|
|
140
|
+
#froxel_far = Infinity;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* The froxel depth curve uploaded last frame. The TAA history is addressed on the curve it was
|
|
144
|
+
* accumulated with, so when the curve changes the history is wrong at every slice and must be
|
|
145
|
+
* dropped. Comparing the built parameters catches every input to the curve at once -- camera
|
|
146
|
+
* near, camera far, the froxel-far override -- rather than watching each separately.
|
|
147
|
+
* @type {Float32Array}
|
|
148
|
+
*/
|
|
149
|
+
#froxel_curve_previous = new Float32Array(3);
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* @returns {number}
|
|
153
|
+
*/
|
|
154
|
+
get froxel_far() {
|
|
155
|
+
return this.#froxel_far;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @param {number} v
|
|
160
|
+
*/
|
|
161
|
+
set froxel_far(v) {
|
|
162
|
+
this.#froxel_far = v;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Q-04. World -> volume and back for each cascade, innermost first, rebuilt every frame by
|
|
167
|
+
* {@link build_volumetrics_sun_shadow_transform} and uploaded in the metadata.
|
|
168
|
+
* @type {Float32Array[]}
|
|
169
|
+
*/
|
|
170
|
+
#sun_shadow_ws_to_uvw = Array.from({ length: VOLUMETRICS_SUN_SHADOW_CASCADE_COUNT }, () => new Float32Array(16));
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* @type {Float32Array[]}
|
|
174
|
+
*/
|
|
175
|
+
#sun_shadow_uvw_to_ws = Array.from({ length: VOLUMETRICS_SUN_SHADOW_CASCADE_COUNT }, () => new Float32Array(16));
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Voxel counts one sun optical-depth cascade is dispatched at this frame. Collapses to
|
|
179
|
+
* `[1, 1, 1]` when the froxels are marching instead, so the passes stay in the frame graph and
|
|
180
|
+
* cost nothing — see `graph_build_sun_shadow_volume` for why they stay rather than going.
|
|
181
|
+
* @type {number[]}
|
|
182
|
+
*/
|
|
183
|
+
#sun_shadow_resolution = [1, 1, 1];
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* World-space size of one voxel along the sun axis per cascade, i.e. each scan's step, packed
|
|
187
|
+
* for the `vec4f` the metadata carries.
|
|
188
|
+
* @type {Float32Array}
|
|
189
|
+
*/
|
|
190
|
+
#sun_shadow_step_ws = new Float32Array(4);
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Which directional light this frame's cascades were built for, or
|
|
194
|
+
* {@link VOLUMETRICS_SUN_SHADOW_LIGHT_NONE} when there was nothing to build for.
|
|
195
|
+
* @type {number}
|
|
196
|
+
*/
|
|
197
|
+
#sun_shadow_light_index = VOLUMETRICS_SUN_SHADOW_LIGHT_NONE;
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* View depth each cascade covers out to, packed the same way. Trailing lanes repeat the
|
|
201
|
+
* outermost so a read past the end of the ladder still lands on a real cascade.
|
|
202
|
+
* @type {Float32Array}
|
|
203
|
+
*/
|
|
204
|
+
#sun_shadow_cascade_far = new Float32Array(4);
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* What the fog integrates to through no medium: nothing in-scattered (.rgb 0), everything
|
|
208
|
+
* transmitted (.a 1). Handed out in place of the aerial LUT on a frame whose scene has no
|
|
209
|
+
* participating-media volume, so every surface that samples the fog keeps doing so and reads
|
|
210
|
+
* it as none, for one texel and no pass. Written once, at construction.
|
|
211
|
+
* @type {GPUTextureContext}
|
|
212
|
+
*/
|
|
213
|
+
#no_medium;
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* @type {GPUTypedBuffer}
|
|
217
|
+
*/
|
|
218
|
+
#metadata;
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* World-space irradiance clipmap (`GPUIrradianceClipmap`) used by particle lighting.
|
|
222
|
+
* @type {GPUIrradianceClipmap}
|
|
223
|
+
*/
|
|
224
|
+
#irradiance_clipmap;
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* @type {GPUDevice}
|
|
228
|
+
*/
|
|
229
|
+
#device
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
*
|
|
233
|
+
* @param {GPUDevice} device
|
|
234
|
+
*/
|
|
235
|
+
constructor(device) {
|
|
236
|
+
this.#device = device;
|
|
237
|
+
|
|
238
|
+
// initialize textures
|
|
239
|
+
for (let i = 0; i < 2; i++) {
|
|
240
|
+
const texture = new GPUTextureContext(device);
|
|
241
|
+
|
|
242
|
+
texture.descriptor.format = "rgba16float";
|
|
243
|
+
texture.descriptor.dimension = "3d";
|
|
244
|
+
texture.descriptor.usage = GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING;
|
|
245
|
+
|
|
246
|
+
this.#volumetrics_textures[i] = texture;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const no_medium = new GPUTextureContext(device);
|
|
250
|
+
no_medium.descriptor.label = "volumetrics/no medium";
|
|
251
|
+
no_medium.descriptor.format = "rgba16float";
|
|
252
|
+
no_medium.descriptor.dimension = "3d";
|
|
253
|
+
no_medium.descriptor.size = [1, 1, 1];
|
|
254
|
+
no_medium.descriptor.usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST;
|
|
255
|
+
|
|
256
|
+
device.queue.writeTexture(
|
|
257
|
+
{ texture: no_medium.gpu_texture },
|
|
258
|
+
new Float16Array([0, 0, 0, 1]),
|
|
259
|
+
{ bytesPerRow: 8, rowsPerImage: 1 },
|
|
260
|
+
[1, 1, 1]
|
|
261
|
+
);
|
|
262
|
+
no_medium.incrementVersion();
|
|
263
|
+
|
|
264
|
+
this.#no_medium = no_medium;
|
|
265
|
+
|
|
266
|
+
this.#metadata = GPUTypedBuffer.create({
|
|
267
|
+
type: VOLUMETRICS_METADATA_STRUCT,
|
|
268
|
+
device,
|
|
269
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
this.#irradiance_clipmap = new GPUIrradianceClipmap(device);
|
|
273
|
+
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* @returns {GPUIrradianceClipmap}
|
|
278
|
+
*/
|
|
279
|
+
get irradiance_clipmap() {
|
|
280
|
+
return this.#irradiance_clipmap;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Drop the TAA history, so the next frame integrates without blending against what came before
|
|
285
|
+
* it. For a cut the grid, the depth curve and the view cannot see: the camera jumped, or the
|
|
286
|
+
* scene changed significantly.
|
|
287
|
+
*/
|
|
288
|
+
reset_history() {
|
|
289
|
+
this.#reset_history = true;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
destroy() {
|
|
293
|
+
|
|
294
|
+
const textures = this.#volumetrics_textures;
|
|
295
|
+
|
|
296
|
+
for (let i = 0; i < 2; i++) {
|
|
297
|
+
|
|
298
|
+
textures[i].destroy();
|
|
299
|
+
|
|
300
|
+
textures[i] = null;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
this.#no_medium.destroy();
|
|
304
|
+
this.#irradiance_clipmap.destroy();
|
|
305
|
+
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* @param {GPUViewContext} view
|
|
310
|
+
*/
|
|
311
|
+
#update_resolution(view) {
|
|
312
|
+
|
|
313
|
+
const screen_resolution = view.resolution;
|
|
314
|
+
|
|
315
|
+
const output_resolution_x = Math.ceil(screen_resolution[0] / this.#tile_size);
|
|
316
|
+
const output_resolution_y = Math.ceil(screen_resolution[1] / this.#tile_size);
|
|
317
|
+
const output_resolution_z = this.#resolution_z;
|
|
318
|
+
|
|
319
|
+
const t_first = this.#volumetrics_textures[0];
|
|
320
|
+
|
|
321
|
+
if (t_first.width === output_resolution_x
|
|
322
|
+
&& t_first.height === output_resolution_y
|
|
323
|
+
&& t_first.depth === output_resolution_z
|
|
324
|
+
) {
|
|
325
|
+
// already at the right resolution
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
for (let i = 0; i < 2; i++) {
|
|
330
|
+
|
|
331
|
+
this.#volumetrics_textures[i].resize(
|
|
332
|
+
output_resolution_x,
|
|
333
|
+
output_resolution_y,
|
|
334
|
+
output_resolution_z
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// drop history
|
|
340
|
+
this.#reset_history = true;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Place this frame's sun optical-depth cascades, and say which light they cover.
|
|
345
|
+
*
|
|
346
|
+
* This is how the sun's media self-shadowing is computed — there is no second path to select
|
|
347
|
+
* between. The march in `chunk_integrate_optical_depth` remains for the lights the cascades do
|
|
348
|
+
* not cover: every point and spot light, whose rays are short enough that there is little to
|
|
349
|
+
* amortize, and any directional light past the first, since there is one ladder of boxes and it
|
|
350
|
+
* is aligned to one direction. Two situations leave the cascades covering nothing at all, and
|
|
351
|
+
* `VOLUMETRICS_SUN_SHADOW_LIGHT_NONE` is how they say so.
|
|
352
|
+
*
|
|
353
|
+
* The light is the first entry of the `light_directional` table, which is the first
|
|
354
|
+
* `DirectionalLight` in scene order — `GPULightCollection.#build` packs them in that order, so
|
|
355
|
+
* walking the same list here recovers the same index. In practice a scene has one sun.
|
|
356
|
+
*
|
|
357
|
+
* Every cascade is placed by the same call with a different `far`, which is what makes the
|
|
358
|
+
* stability argument transfer: each box is sized from the bounding sphere of the sub-frustum it
|
|
359
|
+
* covers, that sphere is a function of the projection alone, and the origin is snapped to whole
|
|
360
|
+
* voxels — so each cascade's world-space lattice is the same set of points from frame to frame
|
|
361
|
+
* no matter how the camera moves.
|
|
362
|
+
*
|
|
363
|
+
* @param {GPUViewContext} view
|
|
364
|
+
* @returns {number} the light index the cascades cover, or the sentinel
|
|
365
|
+
*/
|
|
366
|
+
#update_sun_shadow(view) {
|
|
367
|
+
const camera = view.camera.camera;
|
|
368
|
+
|
|
369
|
+
// One voxel per cascade rather than no pass at all: the lighting pass binds these textures
|
|
370
|
+
// unconditionally and WebGPU has no optional bindings. Nothing samples them, because the
|
|
371
|
+
// sentinel below matches no light index.
|
|
372
|
+
this.#sun_shadow_resolution = [1, 1, 1];
|
|
373
|
+
|
|
374
|
+
// an orthographic view has no fov to derive a bounding sphere from, and no volumetrics
|
|
375
|
+
// camera path that reaches here today — bail rather than invent one
|
|
376
|
+
if (typeof camera.fov !== "number") {
|
|
377
|
+
return VOLUMETRICS_SUN_SHADOW_LIGHT_NONE;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const lights = view.scene.lights.source.elements;
|
|
381
|
+
|
|
382
|
+
let sun = null;
|
|
383
|
+
|
|
384
|
+
for (let i = 0; i < lights.length; i++) {
|
|
385
|
+
if (lights[i].isDirectionalLight === true) {
|
|
386
|
+
sun = lights[i];
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (sun === null) {
|
|
392
|
+
return VOLUMETRICS_SUN_SHADOW_LIGHT_NONE;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const resolution = VOLUMETRICS_SUN_SHADOW_RESOLUTION;
|
|
396
|
+
|
|
397
|
+
// `forward` is the direction light TRAVELS — the same vector light_record_directional
|
|
398
|
+
// writes into the table, which the shader then negates to point at the light. Read off the
|
|
399
|
+
// CPU copy of the pose: the cascade is built here, on this side.
|
|
400
|
+
const direction = sun.transform.forward;
|
|
401
|
+
|
|
402
|
+
const view_matrix = camera.view_matrix;
|
|
403
|
+
const position = camera.transform;
|
|
404
|
+
|
|
405
|
+
// the medium lives in the froxel grid and the grid stops here, so there is nothing to
|
|
406
|
+
// integrate past it
|
|
407
|
+
const far = Math.min(this.#froxel_far, camera.far);
|
|
408
|
+
|
|
409
|
+
const cascade_count = VOLUMETRICS_SUN_SHADOW_CASCADE_COUNT;
|
|
410
|
+
const cascade_ratio = VOLUMETRICS_SUN_SHADOW_CASCADE_RATIO;
|
|
411
|
+
|
|
412
|
+
// the view matrix's z row, negated: view space looks down -Z, and the row is unit length
|
|
413
|
+
// because the linear part is a rotation
|
|
414
|
+
const camera_forward = [-view_matrix[2], -view_matrix[6], -view_matrix[10]];
|
|
415
|
+
|
|
416
|
+
for (let cascade = 0; cascade < cascade_count; cascade++) {
|
|
417
|
+
|
|
418
|
+
const cascade_far = volumetrics_sun_shadow_cascade_far(far, cascade, cascade_count, cascade_ratio);
|
|
419
|
+
|
|
420
|
+
this.#sun_shadow_step_ws[cascade] = build_volumetrics_sun_shadow_transform({
|
|
421
|
+
ws_to_uvw: this.#sun_shadow_ws_to_uvw[cascade],
|
|
422
|
+
uvw_to_ws: this.#sun_shadow_uvw_to_ws[cascade],
|
|
423
|
+
light_direction: [direction[0], direction[1], direction[2]],
|
|
424
|
+
camera_position: [position.translation_x, position.translation_y, position.translation_z],
|
|
425
|
+
camera_forward,
|
|
426
|
+
fov_y: camera.fov,
|
|
427
|
+
aspect: camera.aspect,
|
|
428
|
+
near: camera.near,
|
|
429
|
+
far: cascade_far,
|
|
430
|
+
resolution,
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
this.#sun_shadow_cascade_far[cascade] = cascade_far;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Trailing lanes repeat the outermost cascade rather than holding zero: the read side walks
|
|
437
|
+
// the ladder comparing view depth against these, and a zero would read as a cascade that
|
|
438
|
+
// ends at the camera.
|
|
439
|
+
for (let lane = cascade_count; lane < 4; lane++) {
|
|
440
|
+
this.#sun_shadow_cascade_far[lane] = far;
|
|
441
|
+
this.#sun_shadow_step_ws[lane] = this.#sun_shadow_step_ws[cascade_count - 1];
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
this.#sun_shadow_resolution = resolution;
|
|
445
|
+
|
|
446
|
+
return 0;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* @param {GPUViewContext} view
|
|
451
|
+
*/
|
|
452
|
+
#update_metadata(view) {
|
|
453
|
+
const camera = view.camera;
|
|
454
|
+
|
|
455
|
+
const resolution = this.#volumetrics_textures[0].size;
|
|
456
|
+
// the grid as allocated, so the curve always spans the slices the textures actually have
|
|
457
|
+
const resolution_z = resolution[2];
|
|
458
|
+
|
|
459
|
+
this.#sun_shadow_light_index = this.#update_sun_shadow(view);
|
|
460
|
+
|
|
461
|
+
const volumetrics_cluster_parameters = new Float32Array(3);
|
|
462
|
+
|
|
463
|
+
// The depth-curve `scale` must grow with the slice count. The hard-coded 4.06 was tuned for
|
|
464
|
+
// the light-cluster grid (24 slices); reusing it with 64 froxel slices collapses the log
|
|
465
|
+
// curve so ~half the slices bunch into the first fraction of a unit of depth, leaving almost
|
|
466
|
+
// none for the actual scene. Derive scale for a log-uniform distribution over
|
|
467
|
+
// [near + near_offset, far] instead (gives O ~= 0, i.e. depth ~ offset * (far/offset)^(s/(N-1))).
|
|
468
|
+
const NEAR_OFFSET = 0.1;
|
|
469
|
+
const depth_offset = camera.camera.near + NEAR_OFFSET;
|
|
470
|
+
const depth_scale = (resolution_z - 1) / Math.log2(camera.camera.far / depth_offset);
|
|
471
|
+
|
|
472
|
+
build_light_cluster_parameters(
|
|
473
|
+
volumetrics_cluster_parameters,
|
|
474
|
+
0,
|
|
475
|
+
resolution_z,
|
|
476
|
+
camera.camera.near,
|
|
477
|
+
camera.camera.far,
|
|
478
|
+
NEAR_OFFSET,
|
|
479
|
+
depth_scale
|
|
480
|
+
);
|
|
481
|
+
|
|
482
|
+
// Froxel-grid curve: same construction but ending at froxel_far (<= camera.far). The grid's
|
|
483
|
+
// slices pack into [near, froxel_far] for extra near detail; beyond froxel_far the aerial
|
|
484
|
+
// pass ray-marches the volumes. froxel_far == camera.far -> identical to the LUT curve (no-op).
|
|
485
|
+
const froxel_far = Math.min(this.#froxel_far, camera.camera.far);
|
|
486
|
+
const froxel_depth_scale = (resolution_z - 1) / Math.log2(froxel_far / depth_offset);
|
|
487
|
+
const volumetrics_froxel_cluster_parameters = new Float32Array(3);
|
|
488
|
+
build_light_cluster_parameters(
|
|
489
|
+
volumetrics_froxel_cluster_parameters,
|
|
490
|
+
0,
|
|
491
|
+
resolution_z,
|
|
492
|
+
camera.camera.near,
|
|
493
|
+
froxel_far,
|
|
494
|
+
NEAR_OFFSET,
|
|
495
|
+
froxel_depth_scale
|
|
496
|
+
);
|
|
497
|
+
|
|
498
|
+
// Recomputed deterministically, so a steady camera compares bitwise equal and never resets.
|
|
499
|
+
{
|
|
500
|
+
const curve = volumetrics_froxel_cluster_parameters;
|
|
501
|
+
const previous = this.#froxel_curve_previous;
|
|
502
|
+
|
|
503
|
+
if (curve[0] !== previous[0] || curve[1] !== previous[1] || curve[2] !== previous[2]) {
|
|
504
|
+
previous.set(curve);
|
|
505
|
+
this.#reset_history = true;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// The projection is infinite-far reverse-Z, so the camera frustum's far plane is zeroed and
|
|
510
|
+
// can't be sliced into froxel slabs. Rebuild a finite far plane at the logical far for the
|
|
511
|
+
// participating-media coverage test — exactly as the light-cluster assignment does.
|
|
512
|
+
const clipped_frustum = new Float32Array(24);
|
|
513
|
+
clipped_frustum.set(camera.camera.frustum);
|
|
514
|
+
update_frustum_far_plane(clipped_frustum, camera.camera.view_matrix, camera.camera.far);
|
|
515
|
+
|
|
516
|
+
this.#metadata.upload({
|
|
517
|
+
cluster_parameters: volumetrics_cluster_parameters,
|
|
518
|
+
froxel_cluster_parameters: volumetrics_froxel_cluster_parameters,
|
|
519
|
+
resolution,
|
|
520
|
+
frustum: slice_flat_array(clipped_frustum, 0, 4, 6),
|
|
521
|
+
history_reset: this.#reset_history ? 1 : 0,
|
|
522
|
+
froxel_far,
|
|
523
|
+
// multiscatter knobs (GUI-tunable via globals); engine defaults 6 / 0.5
|
|
524
|
+
ms_octave_count: globalThis.__VOL_MS_OCTAVES ?? 6,
|
|
525
|
+
ms_extinction_decay: globalThis.__VOL_MS_EXT_DECAY ?? 0.5,
|
|
526
|
+
// Q-04. The sun's media self-shadowing, out of a 16-step per-froxel march and into one
|
|
527
|
+
// fetch of the cascades built earlier this frame — see #update_sun_shadow.
|
|
528
|
+
sun_shadow_ws_to_uvw: this.#sun_shadow_ws_to_uvw,
|
|
529
|
+
sun_shadow_uvw_to_ws: this.#sun_shadow_uvw_to_ws,
|
|
530
|
+
sun_shadow_step_ws: this.#sun_shadow_step_ws,
|
|
531
|
+
sun_shadow_cascade_far: this.#sun_shadow_cascade_far,
|
|
532
|
+
sun_shadow_light_index: this.#sun_shadow_light_index,
|
|
533
|
+
}, this.#device.queue);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
*
|
|
538
|
+
* @param {Object} options
|
|
539
|
+
* @param {FrameGraph} options.graph
|
|
540
|
+
* @param {GPUViewContext} options.view what the fog is seen from: its resolution sizes the
|
|
541
|
+
* froxel grid, its camera places it, its scene fills it
|
|
542
|
+
* @param {number} options.light_cluster_data
|
|
543
|
+
* @param {number} options.light_cluster_lookup
|
|
544
|
+
* @param {number} options.light_cluster_parameters
|
|
545
|
+
* @param {number} options.shadowmap_atlas
|
|
546
|
+
* @param {ShaderSwitchSet} [options.shader_switches]
|
|
547
|
+
* @param {import("../global_illumination/brick4/Brick4LightMap.js").Brick4LightMap} [options.brick4]
|
|
548
|
+
* @param {import("../global_illumination/lpv/GPULightProbeVolume.js").GPULightProbeVolume} [options.lpv]
|
|
549
|
+
* @returns {{resolved: number, metadata: number, irradiance: {Y: number, CoCg: number, uniform: number}}}
|
|
550
|
+
*/
|
|
551
|
+
graph_update({
|
|
552
|
+
graph,
|
|
553
|
+
view,
|
|
554
|
+
light_cluster_data,
|
|
555
|
+
light_cluster_lookup,
|
|
556
|
+
light_cluster_parameters,
|
|
557
|
+
shadowmap_atlas,
|
|
558
|
+
shader_switches = ShaderSwitchSet.EMPTY,
|
|
559
|
+
brick4,
|
|
560
|
+
lpv,
|
|
561
|
+
}) {
|
|
562
|
+
|
|
563
|
+
assert.defined(view, 'view');
|
|
564
|
+
|
|
565
|
+
const lights = view.scene.lights;
|
|
566
|
+
const camera = view.camera;
|
|
567
|
+
|
|
568
|
+
const taa_requested = globalThis.__VOL_TAA_ENABLED;
|
|
569
|
+
if (taa_requested !== undefined && taa_requested !== this.#taa_enabled) {
|
|
570
|
+
this.taa_enabled = taa_requested;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const froxel_far_requested = globalThis.__VOL_FROXEL_FAR;
|
|
574
|
+
if (froxel_far_requested !== undefined && froxel_far_requested !== this.#froxel_far) {
|
|
575
|
+
this.#froxel_far = froxel_far_requested;
|
|
576
|
+
// the froxel grid's depth curve just changed; the TAA history is on the old curve, reset it
|
|
577
|
+
this.#reset_history = true;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (view.id !== this.#history_view_id) {
|
|
581
|
+
this.#history_view_id = view.id;
|
|
582
|
+
this.#reset_history = true;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
this.#update_resolution(view);
|
|
586
|
+
this.#update_metadata(view);
|
|
587
|
+
|
|
588
|
+
const gr_metadata = graph_import_buffer(graph, this.#metadata.buffer, "volumetrics metadata");
|
|
589
|
+
|
|
590
|
+
const resolution = this.#volumetrics_textures[0].size;
|
|
591
|
+
|
|
592
|
+
// World-space irradiance clipmap for particle lighting.
|
|
593
|
+
// Culled by the frame graph when no lit particles read it.
|
|
594
|
+
const irradiance = this.#irradiance_clipmap.graph_update({
|
|
595
|
+
graph,
|
|
596
|
+
camera,
|
|
597
|
+
scene_ctx: view.scene,
|
|
598
|
+
shadowmap_atlas,
|
|
599
|
+
shader_switches,
|
|
600
|
+
brick4,
|
|
601
|
+
lpv,
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
if (view.scene.volumetrics.source.volumes.length === 0) {
|
|
605
|
+
// No medium: nothing scatters and nothing absorbs, so the fog is the identity everywhere
|
|
606
|
+
// and the passes that would integrate it to that are not recorded. Nothing accumulates
|
|
607
|
+
// either, which leaves the history stale for whenever a volume arrives.
|
|
608
|
+
this.#reset_history = true;
|
|
609
|
+
|
|
610
|
+
return {
|
|
611
|
+
resolved: graph_import_texture(graph, this.#no_medium, "volumetrics no medium"),
|
|
612
|
+
metadata: gr_metadata,
|
|
613
|
+
irradiance,
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
let taa_history;
|
|
618
|
+
let taa_output;
|
|
619
|
+
|
|
620
|
+
if (this.#taa_enabled) {
|
|
621
|
+
const parity = view.frame_index & 1;
|
|
622
|
+
taa_history = graph_import_texture(graph, this.#volumetrics_textures[parity ^ 1], "volumetrics taa history");
|
|
623
|
+
taa_output = graph_import_texture(graph, this.#volumetrics_textures[parity], "volumetrics taa output");
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
const sky = view.scene.sky;
|
|
627
|
+
|
|
628
|
+
sky.update();
|
|
629
|
+
|
|
630
|
+
const { lut_aerial } = graph_build_volumetrics({
|
|
631
|
+
graph,
|
|
632
|
+
camera,
|
|
633
|
+
lights,
|
|
634
|
+
view,
|
|
635
|
+
sky,
|
|
636
|
+
light_cluster_data,
|
|
637
|
+
light_cluster_lookup,
|
|
638
|
+
light_cluster_parameters,
|
|
639
|
+
shadowmap_atlas,
|
|
640
|
+
|
|
641
|
+
volumetrics_metadata: gr_metadata,
|
|
642
|
+
volumetric_resolution: resolution,
|
|
643
|
+
sun_shadow_resolution: this.#sun_shadow_resolution,
|
|
644
|
+
|
|
645
|
+
taa_history,
|
|
646
|
+
taa_output,
|
|
647
|
+
camera_previous: view.gpu_previous_camera_state,
|
|
648
|
+
shader_switches,
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
this.#reset_history = false;
|
|
652
|
+
|
|
653
|
+
return {
|
|
654
|
+
resolved: lut_aerial,
|
|
655
|
+
metadata: gr_metadata,
|
|
656
|
+
irradiance,
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
}
|