@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.
- package/package.json +1 -1
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +294 -290
- package/src/engine/graphics3/ParticipatingMedia.d.ts +111 -0
- package/src/engine/graphics3/ParticipatingMedia.d.ts.map +1 -0
- package/src/engine/graphics3/ParticipatingMedia.js +203 -0
- package/src/engine/graphics3/ParticipatingMediaSerializationAdapter.d.ts +34 -0
- package/src/engine/graphics3/ParticipatingMediaSerializationAdapter.d.ts.map +1 -0
- package/src/engine/graphics3/ParticipatingMediaSerializationAdapter.js +73 -0
- package/src/engine/graphics3/ParticipatingMediaSystem3.d.ts +87 -0
- package/src/engine/graphics3/ParticipatingMediaSystem3.d.ts.map +1 -0
- package/src/engine/graphics3/ParticipatingMediaSystem3.js +184 -0
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/shade/playground/volumetrics_froxel/README.md +104 -1
- package/src/shade/playground/volumetrics_froxel/index.html +20 -1
- package/src/shade/playground/volumetrics_froxel/main.d.ts.map +1 -1
- package/src/shade/playground/volumetrics_froxel/main.js +298 -0
- package/src/shade/playground/volumetrics_froxel/measure.d.ts +119 -0
- package/src/shade/playground/volumetrics_froxel/measure.d.ts.map +1 -0
- package/src/shade/playground/volumetrics_froxel/measure.js +559 -0
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +22 -18
- package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
- package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_point2.d.ts +14 -13
- package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_point2.d.ts.map +1 -1
- package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_point2.js +133 -129
- package/src/shade/renderer/volumetrics/GPUViewVolumetrics.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/GPUViewVolumetrics.js +480 -315
- package/src/shade/renderer/volumetrics/NOTES.md +649 -8
- package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.d.ts +5 -0
- package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.js +21 -2
- package/src/shade/renderer/volumetrics/SceneVolumetrics.d.ts +26 -0
- package/src/shade/renderer/volumetrics/SceneVolumetrics.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/SceneVolumetrics.js +64 -21
- package/src/shade/renderer/volumetrics/VOLUMETRICS_METADATA_STRUCT.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/VOLUMETRICS_METADATA_STRUCT.js +84 -28
- package/src/shade/renderer/volumetrics/VOLUMETRICS_SUN_SHADOW_RESOLUTION.d.ts +98 -0
- package/src/shade/renderer/volumetrics/VOLUMETRICS_SUN_SHADOW_RESOLUTION.d.ts.map +1 -0
- package/src/shade/renderer/volumetrics/VOLUMETRICS_SUN_SHADOW_RESOLUTION.js +111 -0
- package/src/shade/renderer/volumetrics/build_volumetrics_sun_shadow_transform.d.ts +84 -0
- package/src/shade/renderer/volumetrics/build_volumetrics_sun_shadow_transform.d.ts.map +1 -0
- package/src/shade/renderer/volumetrics/build_volumetrics_sun_shadow_transform.js +252 -0
- package/src/shade/renderer/volumetrics/chunk_integrate_optical_depth.js +75 -75
- package/src/shade/renderer/volumetrics/chunk_volumetrics_local_light_shadow.d.ts +43 -0
- package/src/shade/renderer/volumetrics/chunk_volumetrics_local_light_shadow.d.ts.map +1 -0
- package/src/shade/renderer/volumetrics/chunk_volumetrics_local_light_shadow.js +145 -0
- package/src/shade/renderer/volumetrics/chunk_volumetrics_sample_froxel_extinction.d.ts +20 -0
- package/src/shade/renderer/volumetrics/chunk_volumetrics_sample_froxel_extinction.d.ts.map +1 -0
- package/src/shade/renderer/volumetrics/chunk_volumetrics_sample_froxel_extinction.js +48 -0
- package/src/shade/renderer/volumetrics/chunk_volumetrics_sun_optical_depth.d.ts +38 -0
- package/src/shade/renderer/volumetrics/chunk_volumetrics_sun_optical_depth.d.ts.map +1 -0
- package/src/shade/renderer/volumetrics/chunk_volumetrics_sun_optical_depth.js +107 -0
- package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts +4 -1
- package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/graph_build_volumetrics.js +577 -471
- package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.js +64 -13
- package/src/shade/renderer/volumetrics/shader_volumetrics_build_sun_shadow_volume.d.ts +8 -0
- package/src/shade/renderer/volumetrics/shader_volumetrics_build_sun_shadow_volume.d.ts.map +1 -0
- package/src/shade/renderer/volumetrics/shader_volumetrics_build_sun_shadow_volume.js +174 -0
- package/src/shade/wgsl/emulator/CPUBitmapData.d.ts +25 -0
- package/src/shade/wgsl/emulator/CPUBitmapData.d.ts.map +1 -1
- package/src/shade/wgsl/emulator/CPUBitmapData.js +33 -0
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +2221 -2215
- package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
- package/src/shade/wgsl/emulator/WGSLLanguageCore.js +1297 -1117
- package/src/shade/wgsl/emulator/cpu_texture_gather.d.ts +69 -0
- package/src/shade/wgsl/emulator/cpu_texture_gather.d.ts.map +1 -0
- package/src/shade/wgsl/emulator/cpu_texture_gather.js +148 -0
- package/src/shade/wgsl/emulator/cpu_texture_sample.d.ts +16 -0
- package/src/shade/wgsl/emulator/cpu_texture_sample.d.ts.map +1 -1
- package/src/shade/wgsl/emulator/cpu_texture_sample.js +176 -172
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shader_volumetrics_build_lighting.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"shader_volumetrics_build_lighting.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.js"],"names":[],"mappings":"AA6ZA,8DAIE;8BA7Y4B,4BAA4B"}
|
|
@@ -34,12 +34,18 @@ import { chunk_calculate_multiscattering } from "./chunk_calculate_multiscatteri
|
|
|
34
34
|
import { chunk_camera_ray_planar_depth_scale } from "./chunk_camera_ray_planar_depth_scale.js";
|
|
35
35
|
import { chunk_volumetrics_froxel_content_offset } from "./chunk_volumetrics_froxel_content_offset.js";
|
|
36
36
|
import { chunk_integrate_optical_depth } from "./chunk_integrate_optical_depth.js";
|
|
37
|
+
import {
|
|
38
|
+
chunk_volumetrics_sun_optical_depth,
|
|
39
|
+
volumetrics_sun_shadow_texture_name
|
|
40
|
+
} from "./chunk_volumetrics_sun_optical_depth.js";
|
|
41
|
+
import { VOLUMETRICS_SUN_SHADOW_CASCADE_COUNT } from "./VOLUMETRICS_SUN_SHADOW_RESOLUTION.js";
|
|
37
42
|
import { chunk_volumetrics_column_jitter } from "./chunk_volumetrics_column_jitter.js";
|
|
43
|
+
import { chunk_volumetrics_local_light_shadow } from "./chunk_volumetrics_local_light_shadow.js";
|
|
38
44
|
import { VOLUMETRICS_METADATA_STRUCT } from "./VOLUMETRICS_METADATA_STRUCT.js";
|
|
39
45
|
|
|
40
46
|
const resources = new ShaderResourceSetDescriptor();
|
|
41
47
|
|
|
42
|
-
resources.createGroup()
|
|
48
|
+
const primary_group = resources.createGroup()
|
|
43
49
|
.addUniform("volumetrics_metadata", VOLUMETRICS_METADATA_STRUCT)
|
|
44
50
|
.addUniform("camera", CAMERA_UNIFORM_STRUCT)
|
|
45
51
|
.addUniform("view", VIEW_UNIFORM_STRUCT)
|
|
@@ -48,8 +54,16 @@ resources.createGroup()
|
|
|
48
54
|
.addSampler("sLinear", "filtering", SAMPLER_LINEAR_UPSCALE)
|
|
49
55
|
.addTexture("tSTBN_vec3", "unfilterable-float", "3d")
|
|
50
56
|
.addTexture("tParticipatingMedia0", "float", "3d")
|
|
51
|
-
.addTexture("tParticipatingMedia1", "float", "3d")
|
|
52
|
-
|
|
57
|
+
.addTexture("tParticipatingMedia1", "float", "3d");
|
|
58
|
+
|
|
59
|
+
// Q-04: the sun's optical depth, pre-integrated along the light axis by
|
|
60
|
+
// shader_volumetrics_build_sun_shadow_volume, one cascade per texture and innermost first.
|
|
61
|
+
// Sampled, not marched.
|
|
62
|
+
for (let cascade = 0; cascade < VOLUMETRICS_SUN_SHADOW_CASCADE_COUNT; cascade++) {
|
|
63
|
+
primary_group.addTexture(volumetrics_sun_shadow_texture_name(cascade), "float", "3d");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
primary_group.addStorageTexture("tLighting", "rgba16float", "3d", "write-only")
|
|
53
67
|
|
|
54
68
|
// Lighting information
|
|
55
69
|
resources.createGroup()
|
|
@@ -222,14 +236,37 @@ fn main(
|
|
|
222
236
|
sun_lut_uv, 0.0
|
|
223
237
|
).rgb;
|
|
224
238
|
|
|
225
|
-
// .
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
239
|
+
// Q-04. Every froxel on this sun ray is asking for the same integral, one increment
|
|
240
|
+
// apart, and the march recomputes all of it from scratch for each: O(froxels * steps)
|
|
241
|
+
// to produce O(froxels) of information. The sun is where that is worst, because its
|
|
242
|
+
// tmax is the distance to the sun clipped to the frustum, so the march spans the whole
|
|
243
|
+
// grid rather than the few metres a lamp's does.
|
|
244
|
+
//
|
|
245
|
+
// So read the answer out of the cascades built once this frame with their axis on the
|
|
246
|
+
// sun, where the increment relation IS the storage — see
|
|
247
|
+
// shader_volumetrics_build_sun_shadow_volume. Same quantity, same units, one fetch.
|
|
248
|
+
//
|
|
249
|
+
// The march below is not an alternative to it, it is what is left for the lights the
|
|
250
|
+
// cascades do not cover: a second directional light, since there is one ladder and it
|
|
251
|
+
// is aligned to one direction, and the two cases that cannot place a box at all, which
|
|
252
|
+
// sun_shadow_light_index reports by holding a value no light index can equal.
|
|
253
|
+
var optical_depth: vec3<f32>;
|
|
254
|
+
|
|
255
|
+
if (directional_index == volumetrics_metadata.sun_shadow_light_index) {
|
|
256
|
+
|
|
257
|
+
optical_depth = volumetrics_sun_optical_depth(position_ws);
|
|
258
|
+
|
|
259
|
+
} else {
|
|
260
|
+
|
|
261
|
+
// .direction points TOWARD the light; march the optical-depth ray toward the light (+direction).
|
|
262
|
+
let shadow_ray = ${RAY_STRUCT.make({
|
|
263
|
+
origin: "position_ws",
|
|
264
|
+
direction: "sun.direction",
|
|
265
|
+
tmax: "sun.distance",
|
|
266
|
+
})};
|
|
267
|
+
|
|
268
|
+
optical_depth = integrate_optical_depth(shadow_ray);
|
|
269
|
+
}
|
|
233
270
|
|
|
234
271
|
let ms = calculate_multiscattering(
|
|
235
272
|
optical_depth,
|
|
@@ -284,7 +321,16 @@ fn main(
|
|
|
284
321
|
volumetrics_metadata.ms_extinction_decay
|
|
285
322
|
);
|
|
286
323
|
|
|
287
|
-
|
|
324
|
+
// Before this, integrate_optical_depth was the ONLY thing occluding a local light:
|
|
325
|
+
// the medium shadowed itself and geometry did not shadow it at all, so a torch behind a
|
|
326
|
+
// wall lit the fog through the wall and a light through a window cast no shaft. The
|
|
327
|
+
// directional loop above has always multiplied its shadow map in; this is the same
|
|
328
|
+
// operation for the clustered lights, which is what Frostbite and UE both do in the
|
|
329
|
+
// froxel pass. One nearest atlas tap rather than the deferred path's filter kernel —
|
|
330
|
+
// see the chunk for why that is not a compromise here.
|
|
331
|
+
let shadow_term = volumetrics_point_light_shadow(&light_data, light_index, position_ws);
|
|
332
|
+
|
|
333
|
+
inScatteringSum += shadow_term * ms * direct_light.color;
|
|
288
334
|
}
|
|
289
335
|
|
|
290
336
|
for (var k = 0u; k < spot_count; k++) {
|
|
@@ -316,7 +362,10 @@ fn main(
|
|
|
316
362
|
volumetrics_metadata.ms_extinction_decay
|
|
317
363
|
);
|
|
318
364
|
|
|
319
|
-
|
|
365
|
+
// see the point loop above
|
|
366
|
+
let shadow_term = volumetrics_spot_light_shadow(&light_data, light_index, position_ws);
|
|
367
|
+
|
|
368
|
+
inScatteringSum += shadow_term * ms * direct_light.color;
|
|
320
369
|
}
|
|
321
370
|
}
|
|
322
371
|
|
|
@@ -350,10 +399,12 @@ fn main(
|
|
|
350
399
|
chunk_get_point_light_info_by_index,
|
|
351
400
|
chunk_get_spot_light_info_by_index,
|
|
352
401
|
chunk_shadowmap_get_light_visibility,
|
|
402
|
+
chunk_volumetrics_local_light_shadow,
|
|
353
403
|
chunk_sky_lut_compute_uv,
|
|
354
404
|
|
|
355
405
|
RAY_STRUCT.declaration_chunk,
|
|
356
406
|
chunk_integrate_optical_depth,
|
|
407
|
+
chunk_volumetrics_sun_optical_depth,
|
|
357
408
|
chunk_calculate_multiscattering,
|
|
358
409
|
chunk_rgb_to_luminance,
|
|
359
410
|
chunk_volumetrics_column_jitter,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One build shader per cascade, innermost first.
|
|
3
|
+
*
|
|
4
|
+
* @type {ComputeShader[]}
|
|
5
|
+
*/
|
|
6
|
+
export const shader_volumetrics_build_sun_shadow_volume: ComputeShader[];
|
|
7
|
+
import { ComputeShader } from "../shader/ComputeShader.js";
|
|
8
|
+
//# sourceMappingURL=shader_volumetrics_build_sun_shadow_volume.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shader_volumetrics_build_sun_shadow_volume.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/volumetrics/shader_volumetrics_build_sun_shadow_volume.js"],"names":[],"mappings":"AAuKA;;;;GAIG;AACH,yDAFU,aAAa,EAAE,CAG0E;8BA3KrE,4BAA4B"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { CAMERA_UNIFORM_STRUCT } from "../camera/CAMERA_UNIFORM_STRUCT.js";
|
|
2
|
+
import { CodeChunk } from "../shader/compiler/CodeChunk.js";
|
|
3
|
+
import { ComputeShader } from "../shader/ComputeShader.js";
|
|
4
|
+
import { ShaderResourceSetDescriptor } from "../shader/resource/ShaderResourceSetDescriptor.js";
|
|
5
|
+
import { SAMPLER_LINEAR_UPSCALE } from "../texture/SAMPLER_LINEAR_UPSCALE.js";
|
|
6
|
+
import { chunk_volumetrics_sample_froxel_extinction } from "./chunk_volumetrics_sample_froxel_extinction.js";
|
|
7
|
+
import {
|
|
8
|
+
VOLUMETRICS_SUN_SHADOW_CASCADE_COUNT,
|
|
9
|
+
VOLUMETRICS_SUN_SHADOW_WORKGROUP_RESOLUTION,
|
|
10
|
+
volumetrics_sun_shadow_sub_steps
|
|
11
|
+
} from "./VOLUMETRICS_SUN_SHADOW_RESOLUTION.js";
|
|
12
|
+
import { VOLUMETRICS_METADATA_STRUCT } from "./VOLUMETRICS_METADATA_STRUCT.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Q-04. Builds one cascade of the sun's optical-depth volume so the lighting pass can read a
|
|
16
|
+
* froxel's media self-shadowing in one fetch instead of marching 16 steps toward the sun for it.
|
|
17
|
+
*
|
|
18
|
+
* The structure is a light-aligned voxel grid holding a prefix sum of extinction along the sun
|
|
19
|
+
* axis — in the shadow-map taxonomy an opacity shadow map (Kim & Neumann 2001) with enough slices
|
|
20
|
+
* and hardware trilinear reconstruction that the slice artefacts it is remembered for do not
|
|
21
|
+
* appear, and exact rather than approximate for the piecewise-constant field a voxel grid is.
|
|
22
|
+
* Enshrouded (GPC 2024) ships the same thing, cascaded: 3D volumes with their z axis on the primary
|
|
23
|
+
* light, initialised from the participating media and then integrated by a 2D dispatch per column,
|
|
24
|
+
* each cascade seeded from the one outside it. Frostbite (SIGGRAPH 2015) cascades its extinction
|
|
25
|
+
* volume the same way, three clip-map levels of it, for the same reason.
|
|
26
|
+
*
|
|
27
|
+
* Fourier Opacity Mapping is the better-known answer and is the wrong one here. It exists because
|
|
28
|
+
* the medium usually arrives as unordered rasterized particles, where the only thing you can do is
|
|
29
|
+
* accumulate a fixed-size, order-independent, additively-blendable summary — and it pays for that
|
|
30
|
+
* with ringing at sharp density transitions. This engine's medium is already a 3D texture with a
|
|
31
|
+
* defined order along any axis, so the scan is available, exact, and has nothing to ring.
|
|
32
|
+
*
|
|
33
|
+
* One shader per cascade rather than one shader taking a cascade index, because the two things that
|
|
34
|
+
* vary — which cascade's transform to read, and whether there is an outer cascade to start from —
|
|
35
|
+
* both change the *bindings*, and a variant that declares an outer texture it never samples would
|
|
36
|
+
* have to be handed one.
|
|
37
|
+
*
|
|
38
|
+
* @param {number} index innermost cascade is 0
|
|
39
|
+
* @returns {ComputeShader}
|
|
40
|
+
* @see chunk_volumetrics_sun_optical_depth for the read side
|
|
41
|
+
* @see build_volumetrics_sun_shadow_transform for where each box is placed, and why it is stable
|
|
42
|
+
*/
|
|
43
|
+
function make_shader(index) {
|
|
44
|
+
|
|
45
|
+
const seeded = index < VOLUMETRICS_SUN_SHADOW_CASCADE_COUNT - 1;
|
|
46
|
+
|
|
47
|
+
const sub_steps = volumetrics_sun_shadow_sub_steps(index);
|
|
48
|
+
|
|
49
|
+
const resources = new ShaderResourceSetDescriptor();
|
|
50
|
+
|
|
51
|
+
const group = resources.createGroup()
|
|
52
|
+
.addUniform("volumetrics_metadata", VOLUMETRICS_METADATA_STRUCT)
|
|
53
|
+
.addUniform("camera", CAMERA_UNIFORM_STRUCT)
|
|
54
|
+
.addSampler("sLinear", "filtering", SAMPLER_LINEAR_UPSCALE)
|
|
55
|
+
.addTexture("tParticipatingMedia1", "float", "3d");
|
|
56
|
+
|
|
57
|
+
if (seeded) {
|
|
58
|
+
group.addTexture("tSunShadowOuter", "float", "3d");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
group.addStorageTexture("tSunShadowVolume", "rgba16float", "3d", "write-only");
|
|
62
|
+
|
|
63
|
+
const body = CodeChunk.from(
|
|
64
|
+
//language=WGSL
|
|
65
|
+
`
|
|
66
|
+
@compute @workgroup_size(${VOLUMETRICS_SUN_SHADOW_WORKGROUP_RESOLUTION.join(",")})
|
|
67
|
+
fn main(
|
|
68
|
+
@builtin(global_invocation_id) column : vec3<u32>
|
|
69
|
+
){
|
|
70
|
+
let resolution = textureDimensions(tSunShadowVolume);
|
|
71
|
+
|
|
72
|
+
if(any(column.xy >= resolution.xy)){
|
|
73
|
+
// overflow
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// The volume's z axis is the direction light TRAVELS, so this column is one sun ray and slice 0
|
|
78
|
+
// is its end nearest the sun. Walking it in order turns the per-froxel integral into a running
|
|
79
|
+
// sum: every voxel's optical depth is its predecessor's plus one slab. That is the whole point
|
|
80
|
+
// of the pass — the same information the 16-step march produces, in O(voxels) instead of
|
|
81
|
+
// O(froxels * steps), and produced once rather than once per froxel that happens to lie on the
|
|
82
|
+
// ray.
|
|
83
|
+
//
|
|
84
|
+
// One invocation per column rather than a cooperative scan. The dependency chain is one
|
|
85
|
+
// cascade's depth long and each link is a texture fetch, so a Hillis-Steele or Blinn-Sengupta
|
|
86
|
+
// scan would trade this pass's perfectly coherent, register-resident accumulator for log(n)
|
|
87
|
+
// rounds of shared memory and barriers to speed up something that is already bandwidth-bound.
|
|
88
|
+
// The aerial pass has the same shape for the same reason.
|
|
89
|
+
const CASCADE = ${index}u;
|
|
90
|
+
|
|
91
|
+
let uvw_to_ws = volumetrics_metadata.sun_shadow_uvw_to_ws[CASCADE];
|
|
92
|
+
|
|
93
|
+
// What a voxel must hold is the medium's INTEGRAL across its slab, not the medium's value at
|
|
94
|
+
// its centre, and one point sample per voxel is not that. A fog volume three units thick
|
|
95
|
+
// crossed by a 2.3-unit lattice has its entire optical depth resting on one or two samples:
|
|
96
|
+
// whether they land inside it, on its fade, or just outside swings the answer by the whole
|
|
97
|
+
// shadow, and the per-frame column jitter under them turns that into a strobe.
|
|
98
|
+
//
|
|
99
|
+
// So sample the slab several times and store the storage-resolution result. The sampling rate
|
|
100
|
+
// and the resolution are separate knobs and only the first one has to beat the medium's
|
|
101
|
+
// features. The count is per cascade — see volumetrics_sun_shadow_sub_steps — so that every
|
|
102
|
+
// cascade samples at the same world-space spacing rather than at the same count per voxel.
|
|
103
|
+
const SUB_STEPS = ${sub_steps}u;
|
|
104
|
+
|
|
105
|
+
let sub_step_ws = volumetrics_metadata.sun_shadow_step_ws[CASCADE] / f32(SUB_STEPS);
|
|
106
|
+
|
|
107
|
+
let column_uv = vec2<f32>(
|
|
108
|
+
(f32(column.x) + 0.5) / f32(resolution.x),
|
|
109
|
+
(f32(column.y) + 0.5) / f32(resolution.y)
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
${seeded
|
|
113
|
+
? ` // Seeded from the cascade outside this one, at the world position where this column enters
|
|
114
|
+
// this box. Everything between the sun and that plane has already been integrated out there, at
|
|
115
|
+
// that cascade's resolution, so the scan here starts from it rather than from zero and an inner
|
|
116
|
+
// cascade is a refinement of the outer rather than a truncation of the medium. Enshrouded
|
|
117
|
+
// describes exactly this: "each cascade as an initial shadow value reads the upper cascade".
|
|
118
|
+
//
|
|
119
|
+
// The clamp is a statement about the domain rather than a guard that fires: the outer box
|
|
120
|
+
// contains the whole frustum and this box's entry plane is a face of a box sized from a
|
|
121
|
+
// sub-frustum of it.
|
|
122
|
+
let entry_ws = (uvw_to_ws * vec4<f32>(column_uv, 0.0, 1.0)).xyz;
|
|
123
|
+
|
|
124
|
+
let outer_uvw = (volumetrics_metadata.sun_shadow_ws_to_uvw[CASCADE + 1u] * vec4<f32>(entry_ws, 1.0)).xyz;
|
|
125
|
+
|
|
126
|
+
var optical_depth = textureSampleLevel(tSunShadowOuter, sLinear, saturate(outer_uvw), 0.0).rgb;`
|
|
127
|
+
: ` // The outermost cascade's box contains the whole frustum, and the medium exists only inside
|
|
128
|
+
// the frustum, so the slab its scan starts on is empty and zero is the whole of the truth about
|
|
129
|
+
// what is between it and the sun.
|
|
130
|
+
var optical_depth = vec3<f32>(0.0);`}
|
|
131
|
+
|
|
132
|
+
for(var slice = 0u; slice < resolution.z; slice++){
|
|
133
|
+
|
|
134
|
+
let voxel = vec3<u32>(column.xy, slice);
|
|
135
|
+
|
|
136
|
+
for(var sub = 0u; sub < SUB_STEPS; sub++){
|
|
137
|
+
|
|
138
|
+
// Halfway through the slab, so the value stored at slice k is the integral from the
|
|
139
|
+
// box's sun-side face to the voxel's CENTRE. Linear interpolation between two stored
|
|
140
|
+
// values then reproduces the true integral exactly wherever extinction is constant
|
|
141
|
+
// across the pair. Storing the integral to the far face instead — a right-endpoint sum,
|
|
142
|
+
// which is what the march this replaces does — biases every reading by half a slab.
|
|
143
|
+
if(sub == SUB_STEPS / 2u){
|
|
144
|
+
textureStore(tSunShadowVolume, voxel, vec4<f32>(optical_depth, 0.0));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
let w = (f32(slice) + (f32(sub) + 0.5) / f32(SUB_STEPS)) / f32(resolution.z);
|
|
148
|
+
|
|
149
|
+
let position_ws = (uvw_to_ws * vec4<f32>(column_uv, w, 1.0)).xyz;
|
|
150
|
+
|
|
151
|
+
optical_depth += volumetrics_sample_froxel_extinction(position_ws) * sub_step_ws;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
`, [
|
|
156
|
+
CAMERA_UNIFORM_STRUCT.declaration_chunk,
|
|
157
|
+
chunk_volumetrics_sample_froxel_extinction,
|
|
158
|
+
]
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
return ComputeShader.from({
|
|
162
|
+
label: `Volumetrics / build sun shadow volume ${index}`,
|
|
163
|
+
resources,
|
|
164
|
+
body
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* One build shader per cascade, innermost first.
|
|
170
|
+
*
|
|
171
|
+
* @type {ComputeShader[]}
|
|
172
|
+
*/
|
|
173
|
+
export const shader_volumetrics_build_sun_shadow_volume =
|
|
174
|
+
Array.from({ length: VOLUMETRICS_SUN_SHADOW_CASCADE_COUNT }, (_, index) => make_shader(index));
|
|
@@ -77,6 +77,16 @@ export class CPUBitmapData {
|
|
|
77
77
|
* @param {number} [depth]
|
|
78
78
|
*/
|
|
79
79
|
static float32(item_size: number, width: number, height?: number, depth?: number): CPUBitmapData;
|
|
80
|
+
/**
|
|
81
|
+
* Single-channel f32 bitmap flagged as a `texture_depth_*` binding — see {@link is_depth}.
|
|
82
|
+
* `textureLoad` on it yields a scalar, the way it does on a device.
|
|
83
|
+
*
|
|
84
|
+
* @param {number} width
|
|
85
|
+
* @param {number} [height]
|
|
86
|
+
* @param {number} [depth]
|
|
87
|
+
* @returns {CPUBitmapData}
|
|
88
|
+
*/
|
|
89
|
+
static depth32(width: number, height?: number, depth?: number): CPUBitmapData;
|
|
80
90
|
/**
|
|
81
91
|
* @param {ArrayLike<number>} data
|
|
82
92
|
* @param {number} item_size
|
|
@@ -101,6 +111,21 @@ export class CPUBitmapData {
|
|
|
101
111
|
* @type {Uint32Array}
|
|
102
112
|
*/
|
|
103
113
|
size: Uint32Array;
|
|
114
|
+
/**
|
|
115
|
+
* Marks this as backing a `texture_depth_*` binding rather than a `texture_2d<f32>`.
|
|
116
|
+
*
|
|
117
|
+
* The two differ in WGSL at the call site, not in storage: `textureLoad` on a depth texture
|
|
118
|
+
* returns a bare `f32`, while on a colour texture it returns `vec4<f32>`. Shader code written
|
|
119
|
+
* against a depth texture therefore uses the result as a scalar, and an emulator that always
|
|
120
|
+
* hands back a vec4 turns every such expression into a silent component-wise broadcast —
|
|
121
|
+
* arithmetic that neither throws nor resembles what the GPU does.
|
|
122
|
+
*
|
|
123
|
+
* Off by default, so nothing that does not opt in changes behaviour. Set it, or use
|
|
124
|
+
* {@link CPUBitmapData.depth32}, when binding a texture the shader declares as `depth`.
|
|
125
|
+
*
|
|
126
|
+
* @type {boolean}
|
|
127
|
+
*/
|
|
128
|
+
is_depth: boolean;
|
|
104
129
|
/** @returns {number} */
|
|
105
130
|
get width(): number;
|
|
106
131
|
/** @returns {number} */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CPUBitmapData.d.ts","sourceRoot":"","sources":["../../../../../src/shade/wgsl/emulator/CPUBitmapData.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH;
|
|
1
|
+
{"version":3,"file":"CPUBitmapData.d.ts","sourceRoot":"","sources":["../../../../../src/shade/wgsl/emulator/CPUBitmapData.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH;IA6II;;;;;OAKG;IACH,wBALW,MAAM,SACN,MAAM,WACN,MAAM,UACN,MAAM,iBAIhB;IAED;;;;;OAKG;IACH,yBALW,MAAM,SACN,MAAM,WACN,MAAM,UACN,MAAM,iBAIhB;IAED;;;;;OAKG;IACH,yBALW,MAAM,SACN,MAAM,WACN,MAAM,UACN,MAAM,iBAIhB;IAED;;;;;OAKG;IACH,uBALW,MAAM,SACN,MAAM,WACN,MAAM,UACN,MAAM,iBAIhB;IAED;;;;;OAKG;IACH,wBALW,MAAM,SACN,MAAM,WACN,MAAM,UACN,MAAM,iBAIhB;IAED;;;;;OAKG;IACH,wBALW,MAAM,SACN,MAAM,WACN,MAAM,UACN,MAAM,iBAIhB;IAED;;;;;OAKG;IACH,0BALW,MAAM,SACN,MAAM,WACN,MAAM,UACN,MAAM,iBAIhB;IAED;;;;;OAKG;IACH,0BALW,MAAM,SACN,MAAM,WACN,MAAM,UACN,MAAM,iBAIhB;IAED;;;;;;;;OAQG;IACH,sBALW,MAAM,WACN,MAAM,UACN,MAAM,GACJ,aAAa,CAQzB;IAxMD;;;;;;OAMG;IACH,kBANW,UAAU,MAAM,CAAC,aACjB,MAAM,SACN,MAAM,WACN,MAAM,UACN,MAAM,EAmBhB;IA3DD;;;OAGG;IACH,MAFU,YAAY,kBAAkB,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,UAAU,GAAG,WAAW,GAAG,WAAW,GAAG,MAAM,EAAE,CAE1H;IAEL;;;OAGG;IACH,WAFU,MAAM,CAEN;IAEV;;;;OAIG;IACH,MAFU,WAAW,CAEhB;IAEL;;;;;;;;;;;;;OAaG;IACH,UAFU,OAAO,CAEA;IA4BjB,wBAAwB;IACxB,oBAAoC;IACpC,wBAAwB;IACxB,qBAAqC;IACrC,wBAAwB;IACxB,oBAAoC;IAEpC;;;;;;OAMG;IACH,gCAIC;IAED;;;;;;;OAOG;IACH,gBALW,MAAM,KACN,MAAM,KACN,MAAM,GACJ,MAAM,CAMlB;IAED;;;;;;;;;OASG;IACH,QANW,MAAM,KACN,MAAM,KACN,MAAM;;oBAEN,MAAM,QAWhB;IAED;;;;;;;;;OASG;IACH,SANW,MAAM,KACN,MAAM,KACN,MAAM,OACN,UAAU,MAAM,CAAC,eACjB,MAAM,QAQhB;CAwGJ"}
|
|
@@ -42,6 +42,22 @@ export class CPUBitmapData {
|
|
|
42
42
|
*/
|
|
43
43
|
size;
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Marks this as backing a `texture_depth_*` binding rather than a `texture_2d<f32>`.
|
|
47
|
+
*
|
|
48
|
+
* The two differ in WGSL at the call site, not in storage: `textureLoad` on a depth texture
|
|
49
|
+
* returns a bare `f32`, while on a colour texture it returns `vec4<f32>`. Shader code written
|
|
50
|
+
* against a depth texture therefore uses the result as a scalar, and an emulator that always
|
|
51
|
+
* hands back a vec4 turns every such expression into a silent component-wise broadcast —
|
|
52
|
+
* arithmetic that neither throws nor resembles what the GPU does.
|
|
53
|
+
*
|
|
54
|
+
* Off by default, so nothing that does not opt in changes behaviour. Set it, or use
|
|
55
|
+
* {@link CPUBitmapData.depth32}, when binding a texture the shader declares as `depth`.
|
|
56
|
+
*
|
|
57
|
+
* @type {boolean}
|
|
58
|
+
*/
|
|
59
|
+
is_depth = false;
|
|
60
|
+
|
|
45
61
|
/**
|
|
46
62
|
* @param {ArrayLike<number>} data
|
|
47
63
|
* @param {number} item_size
|
|
@@ -226,4 +242,21 @@ export class CPUBitmapData {
|
|
|
226
242
|
static float32(item_size, width, height = 1, depth = 1) {
|
|
227
243
|
return new CPUBitmapData(new Float32Array(item_size * width * height * depth), item_size, width, height, depth);
|
|
228
244
|
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Single-channel f32 bitmap flagged as a `texture_depth_*` binding — see {@link is_depth}.
|
|
248
|
+
* `textureLoad` on it yields a scalar, the way it does on a device.
|
|
249
|
+
*
|
|
250
|
+
* @param {number} width
|
|
251
|
+
* @param {number} [height]
|
|
252
|
+
* @param {number} [depth]
|
|
253
|
+
* @returns {CPUBitmapData}
|
|
254
|
+
*/
|
|
255
|
+
static depth32(width, height = 1, depth = 1) {
|
|
256
|
+
const result = CPUBitmapData.float32(1, width, height, depth);
|
|
257
|
+
|
|
258
|
+
result.is_depth = true;
|
|
259
|
+
|
|
260
|
+
return result;
|
|
261
|
+
}
|
|
229
262
|
}
|