@woosh/meep-engine 3.11.0 → 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 (86) 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 +191 -0
  15. package/src/shade/playground/volumetrics_froxel/index.html +270 -0
  16. package/src/shade/playground/volumetrics_froxel/main.d.ts +2 -0
  17. package/src/shade/playground/volumetrics_froxel/main.d.ts.map +1 -0
  18. package/src/shade/playground/volumetrics_froxel/main.js +917 -0
  19. package/src/shade/playground/volumetrics_froxel/measure.d.ts +119 -0
  20. package/src/shade/playground/volumetrics_froxel/measure.d.ts.map +1 -0
  21. package/src/shade/playground/volumetrics_froxel/measure.js +559 -0
  22. package/src/shade/renderer/Renderer.d.ts +13 -0
  23. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  24. package/src/shade/renderer/Renderer.js +45 -18
  25. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  26. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_point2.d.ts +14 -13
  27. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_point2.d.ts.map +1 -1
  28. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_point2.js +133 -129
  29. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.d.ts.map +1 -1
  30. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.js +480 -315
  31. package/src/shade/renderer/volumetrics/NOTES.md +691 -9
  32. package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.d.ts +5 -0
  33. package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.d.ts.map +1 -1
  34. package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.js +21 -2
  35. package/src/shade/renderer/volumetrics/SceneVolumetrics.d.ts +26 -0
  36. package/src/shade/renderer/volumetrics/SceneVolumetrics.d.ts.map +1 -1
  37. package/src/shade/renderer/volumetrics/SceneVolumetrics.js +64 -21
  38. package/src/shade/renderer/volumetrics/VOLUMETRICS_METADATA_STRUCT.d.ts.map +1 -1
  39. package/src/shade/renderer/volumetrics/VOLUMETRICS_METADATA_STRUCT.js +84 -28
  40. package/src/shade/renderer/volumetrics/VOLUMETRICS_SUN_SHADOW_RESOLUTION.d.ts +98 -0
  41. package/src/shade/renderer/volumetrics/VOLUMETRICS_SUN_SHADOW_RESOLUTION.d.ts.map +1 -0
  42. package/src/shade/renderer/volumetrics/VOLUMETRICS_SUN_SHADOW_RESOLUTION.js +111 -0
  43. package/src/shade/renderer/volumetrics/build_volumetrics_sun_shadow_transform.d.ts +84 -0
  44. package/src/shade/renderer/volumetrics/build_volumetrics_sun_shadow_transform.d.ts.map +1 -0
  45. package/src/shade/renderer/volumetrics/build_volumetrics_sun_shadow_transform.js +252 -0
  46. package/src/shade/renderer/volumetrics/chunk_camera_ray_planar_depth_scale.d.ts +21 -0
  47. package/src/shade/renderer/volumetrics/chunk_camera_ray_planar_depth_scale.d.ts.map +1 -0
  48. package/src/shade/renderer/volumetrics/chunk_camera_ray_planar_depth_scale.js +47 -0
  49. package/src/shade/renderer/volumetrics/chunk_integrate_optical_depth.d.ts.map +1 -1
  50. package/src/shade/renderer/volumetrics/chunk_integrate_optical_depth.js +75 -64
  51. package/src/shade/renderer/volumetrics/chunk_volumetrics_froxel_content_offset.d.ts +37 -0
  52. package/src/shade/renderer/volumetrics/chunk_volumetrics_froxel_content_offset.d.ts.map +1 -0
  53. package/src/shade/renderer/volumetrics/chunk_volumetrics_froxel_content_offset.js +56 -0
  54. package/src/shade/renderer/volumetrics/chunk_volumetrics_local_light_shadow.d.ts +43 -0
  55. package/src/shade/renderer/volumetrics/chunk_volumetrics_local_light_shadow.d.ts.map +1 -0
  56. package/src/shade/renderer/volumetrics/chunk_volumetrics_local_light_shadow.js +145 -0
  57. package/src/shade/renderer/volumetrics/chunk_volumetrics_sample_froxel_extinction.d.ts +20 -0
  58. package/src/shade/renderer/volumetrics/chunk_volumetrics_sample_froxel_extinction.d.ts.map +1 -0
  59. package/src/shade/renderer/volumetrics/chunk_volumetrics_sample_froxel_extinction.js +48 -0
  60. package/src/shade/renderer/volumetrics/chunk_volumetrics_sun_optical_depth.d.ts +38 -0
  61. package/src/shade/renderer/volumetrics/chunk_volumetrics_sun_optical_depth.d.ts.map +1 -0
  62. package/src/shade/renderer/volumetrics/chunk_volumetrics_sun_optical_depth.js +107 -0
  63. package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts +4 -1
  64. package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts.map +1 -1
  65. package/src/shade/renderer/volumetrics/graph_build_volumetrics.js +577 -465
  66. package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.d.ts.map +1 -1
  67. package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.js +418 -324
  68. package/src/shade/renderer/volumetrics/shader_volumetrics_build_scattering_lut.d.ts.map +1 -1
  69. package/src/shade/renderer/volumetrics/shader_volumetrics_build_scattering_lut.js +14 -3
  70. package/src/shade/renderer/volumetrics/shader_volumetrics_build_sun_shadow_volume.d.ts +8 -0
  71. package/src/shade/renderer/volumetrics/shader_volumetrics_build_sun_shadow_volume.d.ts.map +1 -0
  72. package/src/shade/renderer/volumetrics/shader_volumetrics_build_sun_shadow_volume.js +174 -0
  73. package/src/shade/renderer/volumetrics/volumetrics_position_world_to_froxel_uvw.d.ts.map +1 -1
  74. package/src/shade/renderer/volumetrics/volumetrics_position_world_to_froxel_uvw.js +48 -36
  75. package/src/shade/wgsl/emulator/CPUBitmapData.d.ts +25 -0
  76. package/src/shade/wgsl/emulator/CPUBitmapData.d.ts.map +1 -1
  77. package/src/shade/wgsl/emulator/CPUBitmapData.js +33 -0
  78. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +2221 -2215
  79. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
  80. package/src/shade/wgsl/emulator/WGSLLanguageCore.js +1297 -1117
  81. package/src/shade/wgsl/emulator/cpu_texture_gather.d.ts +69 -0
  82. package/src/shade/wgsl/emulator/cpu_texture_gather.d.ts.map +1 -0
  83. package/src/shade/wgsl/emulator/cpu_texture_gather.js +148 -0
  84. package/src/shade/wgsl/emulator/cpu_texture_sample.d.ts +16 -0
  85. package/src/shade/wgsl/emulator/cpu_texture_sample.d.ts.map +1 -1
  86. package/src/shade/wgsl/emulator/cpu_texture_sample.js +176 -172
@@ -1 +1 @@
1
- {"version":3,"file":"shader_volumetrics_build_scattering_lut.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/volumetrics/shader_volumetrics_build_scattering_lut.js"],"names":[],"mappings":"AAqPA,oEAIG;8BAhP2B,4BAA4B"}
1
+ {"version":3,"file":"shader_volumetrics_build_scattering_lut.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/volumetrics/shader_volumetrics_build_scattering_lut.js"],"names":[],"mappings":"AAgQA,oEAIG;8BA3P2B,4BAA4B"}
@@ -19,6 +19,7 @@ import { chunk_directional_lights_iteration_mask } from "../shader/chunk/light/i
19
19
  import { chunk_get_directional_light_info_by_index } from "../shader/chunk/light/io/chunk_get_directional_light_info_by_index.js";
20
20
  import { chunk_sky_lut_compute_uv } from "../atmosphere/chunk_sky_lut_compute_uv.js";
21
21
  import { chunk_calculate_multiscattering } from "./chunk_calculate_multiscattering.js";
22
+ import { chunk_camera_ray_planar_depth_scale } from "./chunk_camera_ray_planar_depth_scale.js";
22
23
  import { PARTICIPATING_MEDIA_VOLUME_STRUCT } from "./PARTICIPATING_MEDIA_VOLUME_STRUCT.js";
23
24
  import { SCENE_VOLUMETRICS_METADATA_STRUCT } from "./SCENE_VOLUMETRICS_METADATA_STRUCT.js";
24
25
 
@@ -145,6 +146,14 @@ fn main(
145
146
  var inScatter = vec3<f32>(0.0);
146
147
  var throughput = vec3<f32>(1.0);
147
148
 
149
+ // Every t below is a PLANAR view-space depth off the froxel curve, but the loop uses their
150
+ // difference as a segment length in Beer's law and walks them along a unit ray to place world
151
+ // positions. Both want arc length, which is longer by 1/cos(theta). Uncorrected, the optical
152
+ // path is under-integrated by cos(theta) and fog thins toward the frame edges.
153
+ //
154
+ // Constant for the whole column, so this is one reciprocal per invocation rather than per slice.
155
+ let depth_to_distance = camera_ray_planar_depth_scale(view_ray.direction, camera);
156
+
148
157
  // start at the near plane
149
158
  var t_prev = camera_near_far.x;
150
159
 
@@ -154,7 +163,7 @@ fn main(
154
163
 
155
164
  // in-scatter (.rgb) and the TAA-accumulated mono extinction coefficient (.a) now share the
156
165
  // volume-lighting texture, so a single sample provides both.
157
- let vl_start = far_sample_lighting(camera_near_far.x, uvw_start, view_ray.origin + view_ray.direction * camera_near_far.x, view_ray.direction);
166
+ let vl_start = far_sample_lighting(camera_near_far.x, uvw_start, view_ray.origin + view_ray.direction * (camera_near_far.x * depth_to_distance), view_ray.direction);
158
167
  var light_prev = vl_start.rgb;
159
168
  var extinction_prev = vl_start.a;
160
169
 
@@ -187,12 +196,13 @@ fn main(
187
196
  f32(resolution.z)
188
197
  ));
189
198
 
190
- let dt = max(0.0, t_next - t_prev);
199
+ // planar depth difference -> arc length along the view ray (C-01b)
200
+ let dt = max(0.0, t_next - t_prev) * depth_to_distance;
191
201
 
192
202
  // 2. Fetch "Next" Value (The end of this segment)
193
203
  let uvw_next = vec3<f32>(tile_uv, next_index_dithered / f32(resolution.z));
194
204
 
195
- let world_pos_next = view_ray.origin + view_ray.direction * t_next;
205
+ let world_pos_next = view_ray.origin + view_ray.direction * (t_next * depth_to_distance);
196
206
  let vl_next = far_sample_lighting(t_next, uvw_next, world_pos_next, view_ray.direction);
197
207
  let media_extinction = vl_next.a;
198
208
  let light_next = vl_next.rgb;
@@ -236,6 +246,7 @@ fn main(
236
246
  chunk_cluster_depth_to_z_slice,
237
247
  chunk_stbn_sample_vec3,
238
248
  chunk_camera_ray_from_uv,
249
+ chunk_camera_ray_planar_depth_scale,
239
250
  chunk_directional_lights_iteration_mask,
240
251
  chunk_get_directional_light_info_by_index,
241
252
  chunk_sky_lut_compute_uv,
@@ -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));
@@ -1 +1 @@
1
- {"version":3,"file":"volumetrics_position_world_to_froxel_uvw.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/volumetrics/volumetrics_position_world_to_froxel_uvw.js"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,uDAFU,SAAS,CA8BlB;0BAnCyB,iCAAiC"}
1
+ {"version":3,"file":"volumetrics_position_world_to_froxel_uvw.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/volumetrics/volumetrics_position_world_to_froxel_uvw.js"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,uDAFU,SAAS,CAyClB;0BA/CyB,iCAAiC"}
@@ -1,37 +1,49 @@
1
- import { chunk_ndc_to_uv } from "../shader/chunk/texture/chunk_ndc_to_uv.js";
2
- import { CodeChunk } from "../shader/compiler/CodeChunk.js";
3
-
4
- /**
5
- * NOTE: we don't clamp final slice coordinate, this is intentional, we let the linear texture sampler take care of that.
6
- * See {@link build_light_cluster_parameters}
7
- * @type {CodeChunk}
8
- */
9
- export const volumetrics_position_world_to_froxel_uvw = CodeChunk.from(
10
- //language=WGSL
11
- `
12
- fn volumetrics_position_world_to_froxel_uvw(
13
- position_ws: vec3<f32>
14
- ) -> vec3<f32>{
15
-
16
- let position_vs = camera.view_matrix * vec4(position_ws, 1.0);
17
- let position_clip = camera.projection_matrix * position_vs;
18
-
19
- let uv = ndc_to_uv(position_clip.xy / position_clip.w);
20
-
21
- let view_depth = -position_vs.z;
22
- let cluster_parameters = volumetrics_metadata.cluster_parameters;
23
-
24
- // see 'cluster_depth_to_z_slice' for explanation
25
- let slice_position:f32 = log2(fma(view_depth, cluster_parameters.x, cluster_parameters.y)) * cluster_parameters.z;
26
-
27
- let w = (slice_position + 0.5) / f32(volumetrics_metadata.resolution.z);
28
-
29
- return vec3(
30
- uv,
31
- w
32
- );
33
- }
34
- `, [
35
- chunk_ndc_to_uv,
36
- ]
1
+ import { chunk_ndc_to_uv } from "../shader/chunk/texture/chunk_ndc_to_uv.js";
2
+ import { CodeChunk } from "../shader/compiler/CodeChunk.js";
3
+ import { chunk_volumetrics_froxel_content_offset } from "./chunk_volumetrics_froxel_content_offset.js";
4
+
5
+ /**
6
+ * NOTE: we don't clamp final slice coordinate, this is intentional, we let the linear texture sampler take care of that.
7
+ * See {@link build_light_cluster_parameters}
8
+ * @type {CodeChunk}
9
+ */
10
+ export const volumetrics_position_world_to_froxel_uvw = CodeChunk.from(
11
+ //language=WGSL
12
+ `
13
+ fn volumetrics_position_world_to_froxel_uvw(
14
+ position_ws: vec3<f32>
15
+ ) -> vec3<f32>{
16
+
17
+ let position_vs = camera.view_matrix * vec4(position_ws, 1.0);
18
+ let position_clip = camera.projection_matrix * position_vs;
19
+
20
+ let uv = ndc_to_uv(position_clip.xy / position_clip.w);
21
+
22
+ let view_depth = -position_vs.z;
23
+
24
+ // froxel_cluster_parameters, NOT cluster_parameters: this addresses the FROXEL grid, which the
25
+ // participating-media pass builds on that curve. cluster_parameters is the aerial LUT's. The
26
+ // two are identical while froxel_far is unset — which is why the mix-up went unnoticed — and
27
+ // diverge as soon as the grid is packed into a shorter range.
28
+ let cluster_parameters = volumetrics_metadata.froxel_cluster_parameters;
29
+
30
+ // see 'cluster_depth_to_z_slice' for explanation
31
+ let slice_position:f32 = log2(fma(view_depth, cluster_parameters.x, cluster_parameters.y)) * cluster_parameters.z;
32
+
33
+ // A cell-averaged grid: texel k holds the slab [k+j, k+1+j], not the point at slice k, so the
34
+ // texel-centre convention alone reads about half a froxel too far out. See
35
+ // chunk_volumetrics_froxel_content_offset for where the ~0.53 comes from.
36
+ let content_offset = volumetrics_froxel_content_offset(cluster_parameters.z);
37
+
38
+ let w = (slice_position + 0.5 - content_offset) / f32(volumetrics_metadata.resolution.z);
39
+
40
+ return vec3(
41
+ uv,
42
+ w
43
+ );
44
+ }
45
+ `, [
46
+ chunk_ndc_to_uv,
47
+ chunk_volumetrics_froxel_content_offset,
48
+ ]
37
49
  )
@@ -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;IA6HI;;;;;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;IAvLD;;;;;;OAMG;IACH,kBANW,UAAU,MAAM,CAAC,aACjB,MAAM,SACN,MAAM,WACN,MAAM,UACN,MAAM,EAmBhB;IA3CD;;;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;IA4BL,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;CAuFJ"}
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
  }