@woosh/meep-engine 3.50.0 → 3.51.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/graphics3/make_default_environment.d.ts.map +1 -1
- package/src/engine/graphics3/make_default_environment.js +11 -5
- package/src/shade/playground/particle_ecs/moonlit_environment.d.ts.map +1 -1
- package/src/shade/playground/particle_ecs/moonlit_environment.js +11 -5
- package/src/shade/playground/sh_exponential/sh_exp_playground_shaders.d.ts.map +1 -1
- package/src/shade/playground/sh_exponential/sh_exp_playground_shaders.js +3 -1
- package/src/shade/renderer/atmosphere/chunk_sample_environment_color.d.ts +3 -0
- package/src/shade/renderer/atmosphere/chunk_sample_environment_color.d.ts.map +1 -1
- package/src/shade/renderer/atmosphere/chunk_sample_environment_color.js +11 -8
- package/src/shade/renderer/atmosphere/shader_sky_irradiance_lut.d.ts +6 -0
- package/src/shade/renderer/atmosphere/shader_sky_irradiance_lut.d.ts.map +1 -1
- package/src/shade/renderer/atmosphere/shader_sky_irradiance_lut.js +227 -215
- package/src/shade/renderer/deferred/SHADING_LIGHT_RESOURCE_GROUP.d.ts.map +1 -1
- package/src/shade/renderer/deferred/SHADING_LIGHT_RESOURCE_GROUP.js +2 -0
- package/src/shade/renderer/deferred/indirect/chunk_get_ibl_irradiance.d.ts.map +1 -1
- package/src/shade/renderer/deferred/indirect/chunk_get_ibl_irradiance.js +4 -2
- package/src/shade/renderer/deferred/indirect/chunk_get_ibl_radiance.d.ts.map +1 -1
- package/src/shade/renderer/deferred/indirect/chunk_get_ibl_radiance.js +4 -2
- package/src/shade/renderer/deferred/shader_background.d.ts.map +1 -1
- package/src/shade/renderer/deferred/shader_background.js +4 -2
- package/src/shade/renderer/global_illumination/brick4/gpu/bake/shader_brick4_bake_probes.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/brick4/gpu/bake/shader_brick4_bake_probes.js +3 -1
- package/src/shade/renderer/global_illumination/ibl/shader_diffuse_IBL.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/ibl/shader_diffuse_IBL.js +3 -1
- package/src/shade/renderer/global_illumination/ibl/shader_specular_IBL.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/ibl/shader_specular_IBL.js +3 -1
- package/src/shade/renderer/global_illumination/lpv/deprecated/probe_trace_compute_shader_code.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/lpv/deprecated/probe_trace_compute_shader_code.js +3 -1
- package/src/shade/renderer/global_illumination/probe/octahedral/shader_lpv_build_gbuffer.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/probe/octahedral/shader_lpv_build_gbuffer.js +4 -2
- package/src/shade/renderer/light/GPULightCollection.d.ts.map +1 -1
- package/src/shade/renderer/light/GPULightCollection.js +44 -0
- package/src/shade/renderer/light/environment/ENVIRONMENT_MAP_RESOURCE.d.ts +2 -1
- package/src/shade/renderer/light/environment/ENVIRONMENT_MAP_RESOURCE.d.ts.map +1 -1
- package/src/shade/renderer/light/environment/ENVIRONMENT_MAP_RESOURCE.js +3 -2
- package/src/shade/renderer/light/environment/ENVIRONMENT_MAP_SAMPLER_RESOURCE.d.ts +14 -0
- package/src/shade/renderer/light/environment/ENVIRONMENT_MAP_SAMPLER_RESOURCE.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/ENVIRONMENT_MAP_SAMPLER_RESOURCE.js +25 -0
- package/src/shade/renderer/light/environment/execute_filter_environment_map.d.ts.map +1 -1
- package/src/shade/renderer/light/environment/execute_filter_environment_map.js +3 -4
- package/src/shade/renderer/light/environment/octahedral_skirt_matches.d.ts +19 -0
- package/src/shade/renderer/light/environment/octahedral_skirt_matches.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/octahedral_skirt_matches.js +67 -0
- package/src/shade/renderer/light/environment/octahedral_skirt_source_texel.d.ts +16 -0
- package/src/shade/renderer/light/environment/octahedral_skirt_source_texel.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/octahedral_skirt_source_texel.js +35 -0
- package/src/shade/renderer/light/environment/resample_cube_to_octahedral.d.ts +7 -2
- package/src/shade/renderer/light/environment/resample_cube_to_octahedral.d.ts.map +1 -1
- package/src/shade/renderer/light/environment/resample_cube_to_octahedral.js +23 -7
- package/src/shade/renderer/light/environment/resample_equirectangular_to_octahedral.d.ts +8 -2
- package/src/shade/renderer/light/environment/resample_equirectangular_to_octahedral.d.ts.map +1 -1
- package/src/shade/renderer/light/environment/resample_equirectangular_to_octahedral.js +24 -7
- package/src/shade/renderer/light/environment/sampler2d_octahedral_skirt_fill.d.ts +12 -0
- package/src/shade/renderer/light/environment/sampler2d_octahedral_skirt_fill.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/sampler2d_octahedral_skirt_fill.js +54 -0
- package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_environment_cells.d.ts +2 -2
- package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_environment_cells.d.ts.map +1 -1
- package/src/shade/renderer/light/environment/sh_exponential/shader_sh_exp_environment_cells.js +5 -4
- package/src/shade/renderer/light/environment/shader_convolve_reflection_map.d.ts.map +1 -1
- package/src/shade/renderer/light/environment/shader_convolve_reflection_map.js +13 -9
- package/src/shade/renderer/light/environment/shader_project_environment_sh.d.ts.map +1 -1
- package/src/shade/renderer/light/environment/shader_project_environment_sh.js +3 -1
- package/src/shade/renderer/lightmap/bake/shader_lightmap_viz_to_irradiance_flat.d.ts.map +1 -1
- package/src/shade/renderer/lightmap/bake/shader_lightmap_viz_to_irradiance_flat.js +3 -1
- package/src/shade/renderer/lightmap/bake/shader_lightmap_viz_to_sh.d.ts.map +1 -1
- package/src/shade/renderer/lightmap/bake/shader_lightmap_viz_to_sh.js +3 -1
- package/src/shade/renderer/path_tracer/accumulating/shader_accumulating_path_tracer.d.ts.map +1 -1
- package/src/shade/renderer/path_tracer/accumulating/shader_accumulating_path_tracer.js +3 -1
- package/src/shade/renderer/path_tracer/graph_path_trace_scene.d.ts.map +1 -1
- package/src/shade/renderer/path_tracer/graph_path_trace_scene.js +3 -1
- package/src/shade/renderer/path_tracer/shader/chunk_render_trace_path.js +1 -1
- package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader.js +3 -1
- package/src/shade/renderer/rasterize/native/avboit/AVBOIT_CONSTANTS.d.ts +0 -5
- package/src/shade/renderer/rasterize/native/avboit/AVBOIT_CONSTANTS.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/avboit/AVBOIT_CONSTANTS.js +0 -6
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_counts_to_extinction.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_counts_to_extinction.js +4 -1
- package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.js +3 -0
- package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.js +3 -0
- package/src/shade/renderer/shader/chunk/color/tonemap/chunk_PAPER_WHITE_NITS.js +1 -1
- package/src/shade/renderer/shader/chunk/texture/octahedral/chunk_texture_octahedral_skirt_texel_uv.d.ts +15 -0
- package/src/shade/renderer/shader/chunk/texture/octahedral/chunk_texture_octahedral_skirt_texel_uv.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/texture/octahedral/chunk_texture_octahedral_skirt_texel_uv.js +27 -0
- package/src/shade/renderer/shader/chunk/texture/octahedral/chunk_texture_octahedral_skirt_uv.d.ts +18 -0
- package/src/shade/renderer/shader/chunk/texture/octahedral/chunk_texture_octahedral_skirt_uv.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/texture/octahedral/chunk_texture_octahedral_skirt_uv.js +24 -0
- package/src/shade/renderer/shader/chunk/texture/sample/chunk_sample_environment_map.d.ts +18 -1
- package/src/shade/renderer/shader/chunk/texture/sample/chunk_sample_environment_map.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/texture/sample/chunk_sample_environment_map.js +45 -27
- package/src/shade/renderer/view/GPUViewSkyContext.js +169 -169
- package/src/shade/renderer/volumetrics/NOTES.md +209 -0
- package/src/shade/renderer/volumetrics/VOLUMETRICS_METADATA_STRUCT.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/VOLUMETRICS_METADATA_STRUCT.js +1 -1
- package/src/shade/renderer/volumetrics/chunk_volumetrics_media_group_list.d.ts +36 -0
- package/src/shade/renderer/volumetrics/chunk_volumetrics_media_group_list.d.ts.map +1 -0
- package/src/shade/renderer/volumetrics/chunk_volumetrics_media_group_list.js +129 -0
- package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/graph_build_volumetrics.js +75 -0
- package/src/shade/renderer/volumetrics/shader_volumetrics_bin_participating_media.d.ts +26 -0
- package/src/shade/renderer/volumetrics/shader_volumetrics_bin_participating_media.d.ts.map +1 -0
- package/src/shade/renderer/volumetrics/shader_volumetrics_bin_participating_media.js +110 -0
- package/src/shade/renderer/volumetrics/shader_volumetrics_build_participating_media.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/shader_volumetrics_build_participating_media.js +38 -14
- package/src/shade/renderer/volumetrics/shader_volumetrics_build_scattering_lut.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/shader_volumetrics_build_scattering_lut.js +9 -4
- package/src/shade/renderer/volumetrics/shader_volumetrics_split_participating_media.d.ts +31 -0
- package/src/shade/renderer/volumetrics/shader_volumetrics_split_participating_media.d.ts.map +1 -0
- package/src/shade/renderer/volumetrics/shader_volumetrics_split_participating_media.js +139 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The participating-media build's workgroup, in froxels: flat in z, one slice a group. Read by its
|
|
3
|
+
* `@workgroup_size`, by the bin that pre-culls the volumes for each of those groups, and by the
|
|
4
|
+
* dispatches of both.
|
|
5
|
+
* @type {number[]}
|
|
6
|
+
*/
|
|
7
|
+
export const VOLUMETRICS_MEDIA_WORKGROUP_RESOLUTION: number[];
|
|
8
|
+
/**
|
|
9
|
+
* The most volumes of its group's list a froxel of the participating-media build walks. Chosen, not
|
|
10
|
+
* derived. Past it the build drops the group's highest-indexed volumes, whatever each froxel would
|
|
11
|
+
* have made of them.
|
|
12
|
+
* @type {number}
|
|
13
|
+
*/
|
|
14
|
+
export const VOLUMETRICS_MEDIA_GROUP_VOLUME_BUDGET: number;
|
|
15
|
+
/**
|
|
16
|
+
* The volumes each workgroup of the participating-media build may see, as one list per group: a
|
|
17
|
+
* bitmask over the volumes' rows, `list_words` words a group, the groups in `grid3d_to_index` order.
|
|
18
|
+
* The bin writes it, and the build walks its own group's list in row order instead of every volume.
|
|
19
|
+
*
|
|
20
|
+
* - `volumetrics_media_groups` is the build's workgroup grid over a froxel grid.
|
|
21
|
+
* - `volumetrics_media_list_words` is the words of one group's list: as many as the volumes need, and
|
|
22
|
+
* never more than the buffer holds for each group, so a list sized from a count the host had
|
|
23
|
+
* before a volume arrived drops that volume for a frame rather than overrun.
|
|
24
|
+
* - `volumetrics_froxel_planes` is the metadata frustum with its Y pair turned to the froxel grid's
|
|
25
|
+
* rows (screen Y runs down, world Y up).
|
|
26
|
+
* - `volumetrics_froxel_slab` is the frustum of a box of froxels: `uv` over the grid's columns and
|
|
27
|
+
* rows, `slice` over its depth curve, clamped to the camera's logical near and far.
|
|
28
|
+
* `volumetrics_froxel_slab_between` is the same with the depths given, inside `[near, far]`.
|
|
29
|
+
* - `volumetrics_volume_in_slab` is the build's own rejection of a volume by a slab: its unit box
|
|
30
|
+
* against the slab's planes taken into the volume's frame.
|
|
31
|
+
*
|
|
32
|
+
* @type {CodeChunk}
|
|
33
|
+
*/
|
|
34
|
+
export const chunk_volumetrics_media_group_list: CodeChunk;
|
|
35
|
+
import { CodeChunk } from "../shader/compiler/CodeChunk.js";
|
|
36
|
+
//# sourceMappingURL=chunk_volumetrics_media_group_list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk_volumetrics_media_group_list.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/volumetrics/chunk_volumetrics_media_group_list.js"],"names":[],"mappings":"AAWA;;;;;GAKG;AACH,qDAFU,MAAM,EAAE,CAEgD;AAElE;;;;;GAKG;AACH,oDAFU,MAAM,CAEwC;AAExD;;;;;;;;;;;;;;;;;;GAkBG;AACH,iDAFU,SAAS,CAoFjB;0BAzHwB,iCAAiC"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { AABB3_STRUCT } from "../shader/chunk/geometry/aabb3/AABB3_STRUCT.js";
|
|
2
|
+
import { chunk_aabb3_intersects_frustum } from "../shader/chunk/geometry/aabb3/chunk_aabb3_intersects_frustum.js";
|
|
3
|
+
import { chunk_frustum_slice } from "../shader/chunk/geometry/frustum/chunk_frustum_slice.js";
|
|
4
|
+
import {
|
|
5
|
+
chunk_frustum_transform_plane_matrix
|
|
6
|
+
} from "../shader/chunk/geometry/frustum/chunk_frustum_transform_plane_matrix.js";
|
|
7
|
+
import { chunk_grid3d_to_index } from "../shader/chunk/grid/chunk_grid3d_to_index.js";
|
|
8
|
+
import { CodeChunk } from "../shader/compiler/CodeChunk.js";
|
|
9
|
+
import { chunk_cluster_depth_from_z_slice } from "../light/cluster/chunk_cluster_depth_from_z_slice.js";
|
|
10
|
+
import { PARTICIPATING_MEDIA_VOLUME_STRUCT } from "./PARTICIPATING_MEDIA_VOLUME_STRUCT.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The participating-media build's workgroup, in froxels: flat in z, one slice a group. Read by its
|
|
14
|
+
* `@workgroup_size`, by the bin that pre-culls the volumes for each of those groups, and by the
|
|
15
|
+
* dispatches of both.
|
|
16
|
+
* @type {number[]}
|
|
17
|
+
*/
|
|
18
|
+
export const VOLUMETRICS_MEDIA_WORKGROUP_RESOLUTION = [16, 16, 1];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The most volumes of its group's list a froxel of the participating-media build walks. Chosen, not
|
|
22
|
+
* derived. Past it the build drops the group's highest-indexed volumes, whatever each froxel would
|
|
23
|
+
* have made of them.
|
|
24
|
+
* @type {number}
|
|
25
|
+
*/
|
|
26
|
+
export const VOLUMETRICS_MEDIA_GROUP_VOLUME_BUDGET = 32;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The volumes each workgroup of the participating-media build may see, as one list per group: a
|
|
30
|
+
* bitmask over the volumes' rows, `list_words` words a group, the groups in `grid3d_to_index` order.
|
|
31
|
+
* The bin writes it, and the build walks its own group's list in row order instead of every volume.
|
|
32
|
+
*
|
|
33
|
+
* - `volumetrics_media_groups` is the build's workgroup grid over a froxel grid.
|
|
34
|
+
* - `volumetrics_media_list_words` is the words of one group's list: as many as the volumes need, and
|
|
35
|
+
* never more than the buffer holds for each group, so a list sized from a count the host had
|
|
36
|
+
* before a volume arrived drops that volume for a frame rather than overrun.
|
|
37
|
+
* - `volumetrics_froxel_planes` is the metadata frustum with its Y pair turned to the froxel grid's
|
|
38
|
+
* rows (screen Y runs down, world Y up).
|
|
39
|
+
* - `volumetrics_froxel_slab` is the frustum of a box of froxels: `uv` over the grid's columns and
|
|
40
|
+
* rows, `slice` over its depth curve, clamped to the camera's logical near and far.
|
|
41
|
+
* `volumetrics_froxel_slab_between` is the same with the depths given, inside `[near, far]`.
|
|
42
|
+
* - `volumetrics_volume_in_slab` is the build's own rejection of a volume by a slab: its unit box
|
|
43
|
+
* against the slab's planes taken into the volume's frame.
|
|
44
|
+
*
|
|
45
|
+
* @type {CodeChunk}
|
|
46
|
+
*/
|
|
47
|
+
export const chunk_volumetrics_media_group_list = CodeChunk.from(
|
|
48
|
+
//language=WGSL
|
|
49
|
+
`
|
|
50
|
+
const VOLUMETRICS_MEDIA_GROUP_XY = vec2<u32>(${VOLUMETRICS_MEDIA_WORKGROUP_RESOLUTION[0]}u, ${VOLUMETRICS_MEDIA_WORKGROUP_RESOLUTION[1]}u);
|
|
51
|
+
const VOLUMETRICS_MEDIA_GROUP_VOLUME_BUDGET: u32 = ${VOLUMETRICS_MEDIA_GROUP_VOLUME_BUDGET}u;
|
|
52
|
+
|
|
53
|
+
fn volumetrics_media_groups(resolution: vec3<u32>) -> vec3<u32> {
|
|
54
|
+
return vec3<u32>((resolution.xy + VOLUMETRICS_MEDIA_GROUP_XY - vec2<u32>(1u)) / VOLUMETRICS_MEDIA_GROUP_XY, resolution.z);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
fn volumetrics_media_list_words(volume_count: u32, buffer_words: u32, groups: vec3<u32>) -> u32 {
|
|
58
|
+
return min((volume_count + 31u) / 32u, buffer_words / (groups.x * groups.y * groups.z));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
fn volumetrics_media_list_offset(group: vec3<u32>, groups: vec3<u32>, list_words: u32) -> u32 {
|
|
62
|
+
return grid3d_to_index(group, groups.xy) * list_words;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
fn volumetrics_froxel_planes(frustum: array<vec4<f32>, 6>) -> array<vec4<f32>, 6> {
|
|
66
|
+
// Frustum planes are [LEFT, RIGHT, BOTTOM, TOP, NEAR, FAR] (see Camera.construct_frustum_planes),
|
|
67
|
+
// every one facing in. The grid's rows run down the screen while world Y runs up, so the Y pair
|
|
68
|
+
// trades places, and stays facing in: frustum_slice takes the plane at uv 0 as it is and the one
|
|
69
|
+
// at uv 1 as it is, so swapped and kept they bound the slab from the inside, as X and Z do.
|
|
70
|
+
var planes = frustum;
|
|
71
|
+
let t = planes[2];
|
|
72
|
+
planes[2] = planes[3];
|
|
73
|
+
planes[3] = t;
|
|
74
|
+
return planes;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
fn volumetrics_froxel_slab(
|
|
78
|
+
planes: array<vec4<f32>, 6>,
|
|
79
|
+
uv_min: vec2<f32>,
|
|
80
|
+
uv_max: vec2<f32>,
|
|
81
|
+
slice_0: f32,
|
|
82
|
+
slice_1: f32,
|
|
83
|
+
near_far: vec2<f32>,
|
|
84
|
+
curve: vec3<f32>,
|
|
85
|
+
slices: f32,
|
|
86
|
+
) -> array<vec4<f32>, 6> {
|
|
87
|
+
let z0 = max(near_far.x, cluster_depth_from_z_slice(slice_0, curve, slices));
|
|
88
|
+
let z1 = min(near_far.y, cluster_depth_from_z_slice(slice_1, curve, slices));
|
|
89
|
+
|
|
90
|
+
return volumetrics_froxel_slab_between(planes, uv_min, uv_max, z0, z1, near_far);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
fn volumetrics_froxel_slab_between(
|
|
94
|
+
planes: array<vec4<f32>, 6>,
|
|
95
|
+
uv_min: vec2<f32>,
|
|
96
|
+
uv_max: vec2<f32>,
|
|
97
|
+
z0: f32,
|
|
98
|
+
z1: f32,
|
|
99
|
+
near_far: vec2<f32>,
|
|
100
|
+
) -> array<vec4<f32>, 6> {
|
|
101
|
+
let z0_norm = (z0 - near_far.x) / (near_far.y - near_far.x);
|
|
102
|
+
let z1_norm = (z1 - near_far.x) / (near_far.y - near_far.x);
|
|
103
|
+
|
|
104
|
+
return frustum_slice(planes, vec3(uv_min, z0_norm), vec3(uv_max, z1_norm));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
fn volumetrics_volume_in_slab(slab: array<vec4<f32>, 6>, volume: ${PARTICIPATING_MEDIA_VOLUME_STRUCT.wgsl_ref}) -> bool {
|
|
108
|
+
// Points go world -> local through transform_inverse, so planes go by the transpose of *its*
|
|
109
|
+
// inverse, which is just transpose(transform) — already resident.
|
|
110
|
+
let local = frustum_transform_plane_matrix(slab, transpose(volume.transform));
|
|
111
|
+
|
|
112
|
+
const unit_bounds = ${AABB3_STRUCT.make({
|
|
113
|
+
min: "vec3(-0.5)",
|
|
114
|
+
max: "vec3(0.5)",
|
|
115
|
+
})};
|
|
116
|
+
|
|
117
|
+
return aabb3_intersects_frustum(unit_bounds, local);
|
|
118
|
+
}
|
|
119
|
+
`,
|
|
120
|
+
[
|
|
121
|
+
PARTICIPATING_MEDIA_VOLUME_STRUCT.declaration_chunk,
|
|
122
|
+
AABB3_STRUCT.declaration_chunk,
|
|
123
|
+
chunk_aabb3_intersects_frustum,
|
|
124
|
+
chunk_frustum_slice,
|
|
125
|
+
chunk_frustum_transform_plane_matrix,
|
|
126
|
+
chunk_cluster_depth_from_z_slice,
|
|
127
|
+
chunk_grid3d_to_index,
|
|
128
|
+
]
|
|
129
|
+
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph_build_volumetrics.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/volumetrics/graph_build_volumetrics.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"graph_build_volumetrics.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/volumetrics/graph_build_volumetrics.js"],"names":[],"mappings":"AA+aA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH;IAtB+B,KAAK;IACC,MAAM;IACJ,MAAM;IACV,IAAI;IACZ,GAAG;IAIH,kBAAkB,EAAlC,MAAM;IACU,oBAAoB,EAApC,MAAM;IACU,wBAAwB,EAAxC,MAAM;IACU,eAAe,EAA/B,MAAM;IACY,qBAAqB,EAAvC,MAAM,EAAE;IACU,qBAAqB,EAAvC,MAAM,EAAE;IAGQ,oBAAoB,EAApC,MAAM;IACW,WAAW,GAA5B,MAAM;IACW,UAAU,GAA3B,MAAM;IACqB,eAAe;IAChB,eAAe,GAAzC,eAAe;;;;;;EAsLzB;gCA3nB+B,uCAAuC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { assert } from "../../../core/assert.js";
|
|
2
2
|
import { SamplerDescriptor } from "../../descriptor/texture/SamplerDescriptor.js";
|
|
3
|
+
import { BufferResourceDescriptor } from "../../device/graph/BufferResourceDescriptor.js";
|
|
3
4
|
import { TextureResourceDescriptor } from "../../device/graph/TextureResourceDescriptor.js";
|
|
4
5
|
import { ShaderSwitchSet } from "../shader/compiler/ShaderSwitchSet.js";
|
|
5
6
|
import { graph_compute_pass } from "../shader/graph/compute/graph_compute_pass.js";
|
|
@@ -11,6 +12,12 @@ import { DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR } from "../texture/DEFAULT_RENDER
|
|
|
11
12
|
import { WIND_FIELD_UNIFORM } from "../wind/WIND_FIELD_UNIFORM.js";
|
|
12
13
|
import { volumetrics_sun_shadow_texture_name } from "./chunk_volumetrics_sun_optical_depth.js";
|
|
13
14
|
import { PARTICIPATING_MEDIA_NOISE_SAMPLER_DESCRIPTOR } from "./noise/PARTICIPATING_MEDIA_NOISE.js";
|
|
15
|
+
import { VOLUMETRICS_MEDIA_WORKGROUP_RESOLUTION } from "./chunk_volumetrics_media_group_list.js";
|
|
16
|
+
import { shader_volumetrics_bin_participating_media } from "./shader_volumetrics_bin_participating_media.js";
|
|
17
|
+
import {
|
|
18
|
+
shader_volumetrics_split_participating_media,
|
|
19
|
+
VOLUMETRICS_MEDIA_SPLIT_LANES
|
|
20
|
+
} from "./shader_volumetrics_split_participating_media.js";
|
|
14
21
|
import { shader_volumetrics_build_lighting } from "./shader_volumetrics_build_lighting.js";
|
|
15
22
|
import { shader_volumetrics_build_participating_media } from "./shader_volumetrics_build_participating_media.js";
|
|
16
23
|
import { shader_volumetrics_build_scattering_lut } from "./shader_volumetrics_build_scattering_lut.js";
|
|
@@ -48,6 +55,21 @@ function graph_integrate_participating_media({
|
|
|
48
55
|
|
|
49
56
|
const data = {};
|
|
50
57
|
|
|
58
|
+
// the media build's workgroups, each of which the bin gives a list of the volumes it may see
|
|
59
|
+
const groups = [
|
|
60
|
+
Math.ceil(resolution[0] / VOLUMETRICS_MEDIA_WORKGROUP_RESOLUTION[0]),
|
|
61
|
+
Math.ceil(resolution[1] / VOLUMETRICS_MEDIA_WORKGROUP_RESOLUTION[1]),
|
|
62
|
+
resolution[2],
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
// A bit a volume. Sized from the scene's volumes, which the table's rows never outnumber; the
|
|
66
|
+
// shaders take the volume count off the table and the words a list gets off this buffer.
|
|
67
|
+
const volume_count = scene.volumetrics.source.volumes.length;
|
|
68
|
+
const list_words = Math.ceil(volume_count / 32);
|
|
69
|
+
const group_lists_bytes = 4 * Math.max(4, groups[0] * groups[1] * groups[2] * list_words);
|
|
70
|
+
|
|
71
|
+
// the near and the far field: a count, then a row for every volume either may hold
|
|
72
|
+
const field_bytes = 4 * Math.max(4, 1 + volume_count);
|
|
51
73
|
|
|
52
74
|
const pass = graph.add(
|
|
53
75
|
"aerial lut",
|
|
@@ -69,10 +91,40 @@ function graph_integrate_participating_media({
|
|
|
69
91
|
*/
|
|
70
92
|
const output1 = resources.get(data.output1);
|
|
71
93
|
|
|
94
|
+
const group_lists = resources.get(data.group_lists);
|
|
95
|
+
const near_field = resources.get(data.near_field);
|
|
96
|
+
const far_field = resources.get(data.far_field);
|
|
97
|
+
|
|
72
98
|
const encoder = context.encoder;
|
|
73
99
|
|
|
74
100
|
const volumetrics_metadata = resources.get(data.volumetrics_metadata);
|
|
75
101
|
|
|
102
|
+
shader_volumetrics_split_participating_media.dispatch({
|
|
103
|
+
encoder,
|
|
104
|
+
bindings: {
|
|
105
|
+
camera: camera.buffer.buffer,
|
|
106
|
+
volumetrics_metadata,
|
|
107
|
+
scene_database: scene.scene_database_buffer,
|
|
108
|
+
near_field,
|
|
109
|
+
far_field,
|
|
110
|
+
},
|
|
111
|
+
group_count_x: Math.max(1, Math.ceil(volume_count / VOLUMETRICS_MEDIA_SPLIT_LANES)),
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
shader_volumetrics_bin_participating_media.dispatch({
|
|
115
|
+
encoder,
|
|
116
|
+
bindings: {
|
|
117
|
+
camera: camera.buffer.buffer,
|
|
118
|
+
volumetrics_metadata,
|
|
119
|
+
scene_database: scene.scene_database_buffer,
|
|
120
|
+
near_field,
|
|
121
|
+
group_lists,
|
|
122
|
+
},
|
|
123
|
+
group_count_x: groups[0],
|
|
124
|
+
group_count_y: groups[1],
|
|
125
|
+
group_count_z: groups[2],
|
|
126
|
+
});
|
|
127
|
+
|
|
76
128
|
/**
|
|
77
129
|
* The scene's noise atlas, imported this frame so a grow between frames is seen.
|
|
78
130
|
* @type {GPUTextureContext}
|
|
@@ -92,6 +144,7 @@ function graph_integrate_participating_media({
|
|
|
92
144
|
sParticipatingMediaNoise: context.graphics.samplers.obtain(PARTICIPATING_MEDIA_NOISE_SAMPLER_DESCRIPTOR),
|
|
93
145
|
tOutput0: output0.obtainView(DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR),
|
|
94
146
|
tOutput1: output1.obtainView(DEFAULT_RENDER_TARGET_VIEW_DESCRIPTOR),
|
|
147
|
+
group_lists,
|
|
95
148
|
};
|
|
96
149
|
|
|
97
150
|
// taken this frame rather than held: a producer that grows the field replaces its atlas
|
|
@@ -113,6 +166,23 @@ function graph_integrate_participating_media({
|
|
|
113
166
|
});
|
|
114
167
|
|
|
115
168
|
data.volumetrics_metadata = pass.read(volumetrics_metadata);
|
|
169
|
+
// the split appends to lists counted from zero
|
|
170
|
+
data.near_field = pass.create("participating media near field", BufferResourceDescriptor.from(
|
|
171
|
+
field_bytes,
|
|
172
|
+
GPUBufferUsage.STORAGE,
|
|
173
|
+
0, 4,
|
|
174
|
+
));
|
|
175
|
+
data.far_field = pass.create("participating media far field", BufferResourceDescriptor.from(
|
|
176
|
+
field_bytes,
|
|
177
|
+
GPUBufferUsage.STORAGE,
|
|
178
|
+
0, 4,
|
|
179
|
+
));
|
|
180
|
+
// the bin ORs bits into zeroed lists
|
|
181
|
+
data.group_lists = pass.create("participating media group lists", BufferResourceDescriptor.from(
|
|
182
|
+
group_lists_bytes,
|
|
183
|
+
GPUBufferUsage.STORAGE,
|
|
184
|
+
0, group_lists_bytes,
|
|
185
|
+
));
|
|
116
186
|
data.noise_atlas = pass.read(graph_import_texture(graph, scene.volumetrics.noise_atlas.texture, "volumetrics noise atlas"));
|
|
117
187
|
|
|
118
188
|
data.output0 = pass.create("Participating Media 0", TextureResourceDescriptor.from({
|
|
@@ -132,6 +202,7 @@ function graph_integrate_participating_media({
|
|
|
132
202
|
participating_media0: data.output0,
|
|
133
203
|
participating_media1: data.output1,
|
|
134
204
|
volumetrics_metadata: data.volumetrics_metadata,
|
|
205
|
+
far_field: data.far_field,
|
|
135
206
|
};
|
|
136
207
|
}
|
|
137
208
|
|
|
@@ -421,6 +492,7 @@ export function graph_build_volumetrics({
|
|
|
421
492
|
const {
|
|
422
493
|
participating_media0,
|
|
423
494
|
participating_media1,
|
|
495
|
+
far_field,
|
|
424
496
|
} = graph_integrate_participating_media({
|
|
425
497
|
graph,
|
|
426
498
|
scene: view.scene,
|
|
@@ -497,6 +569,7 @@ export function graph_build_volumetrics({
|
|
|
497
569
|
|
|
498
570
|
const light_scattering_lut = resources.get(data.light_scattering_lut);
|
|
499
571
|
const sun_transmittance_lut = resources.get(data.sun_transmittance_lut);
|
|
572
|
+
const far_field = resources.get(data.far_field);
|
|
500
573
|
|
|
501
574
|
const encoder = context.encoder;
|
|
502
575
|
|
|
@@ -526,6 +599,7 @@ export function graph_build_volumetrics({
|
|
|
526
599
|
|
|
527
600
|
// RDR2-style far-field march inputs (used beyond froxel_far)
|
|
528
601
|
scene_database: view.scene.scene_database_buffer,
|
|
602
|
+
far_field,
|
|
529
603
|
light_data: lights.buffer_data,
|
|
530
604
|
tSunTransmittance: sun_transmittance_lut.obtainView(),
|
|
531
605
|
|
|
@@ -540,6 +614,7 @@ export function graph_build_volumetrics({
|
|
|
540
614
|
a_data.participating_media1 = a_pass.read(participating_media1);
|
|
541
615
|
a_data.volumetrics_metadata = a_pass.read(volumetrics_metadata);
|
|
542
616
|
a_data.sun_transmittance_lut = a_pass.read(transmittance_lut);
|
|
617
|
+
a_data.far_field = a_pass.read(far_field);
|
|
543
618
|
|
|
544
619
|
a_data.output = a_pass.create("Integrated Volumetrics", TextureResourceDescriptor.from({
|
|
545
620
|
resolution: volumetric_resolution,
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lanes of a bin workgroup. Small on purpose: a lane tests one volume at a time, so at a handful of
|
|
3
|
+
* volumes most lanes of any group idle, and the fewer there are the less the bin launches for
|
|
4
|
+
* nothing.
|
|
5
|
+
* @type {number}
|
|
6
|
+
*/
|
|
7
|
+
export const VOLUMETRICS_MEDIA_BIN_LANES: number;
|
|
8
|
+
/**
|
|
9
|
+
* Pre-culls the scene's participating-media volumes for each workgroup of the participating-media
|
|
10
|
+
* build: one workgroup per build group, which tests every volume of the near field (see
|
|
11
|
+
* {@link shader_volumetrics_split_participating_media}) against the group's slab of froxels and
|
|
12
|
+
* sets the bit of each one it keeps in that group's list (see {@link chunk_volumetrics_media_group_list}).
|
|
13
|
+
* The lists start zeroed; the graph clears them.
|
|
14
|
+
*
|
|
15
|
+
* The slab is the union of every froxel of the group as the build slices it, widened so that
|
|
16
|
+
* rounding can never make the group stricter than a froxel. In xy that is a quarter of a froxel
|
|
17
|
+
* past the group's columns and rows. In z it is the column jitter, which moves a froxel's slab by
|
|
18
|
+
* up to half a slice either way, and a quarter of a slice beyond that. A volume a froxel would
|
|
19
|
+
* cover therefore never misses its group's list, and the build reads, through the list, exactly
|
|
20
|
+
* what it read walking every volume.
|
|
21
|
+
*
|
|
22
|
+
* Budget: a lane tests ⌈near-field volumes / 64⌉ volumes. Nothing is past it.
|
|
23
|
+
*/
|
|
24
|
+
export const shader_volumetrics_bin_participating_media: ComputeShader;
|
|
25
|
+
import { ComputeShader } from "../shader/ComputeShader.js";
|
|
26
|
+
//# sourceMappingURL=shader_volumetrics_bin_participating_media.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shader_volumetrics_bin_participating_media.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/volumetrics/shader_volumetrics_bin_participating_media.js"],"names":[],"mappings":"AAWA;;;;;GAKG;AACH,0CAFU,MAAM,CAE8B;AAY9C;;;;;;;;;;;;;;;GAeG;AACH,uEAgEG;8BA3G2B,4BAA4B"}
|
|
@@ -0,0 +1,110 @@
|
|
|
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 { atomic_u32 } from "../shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
|
|
6
|
+
import { WebGPUArray } from "../shader/type/WebGPUArray.js";
|
|
7
|
+
import { chunk_participating_media_volumes_iterate } from "./chunk_participating_media_volumes_iterate.js";
|
|
8
|
+
import { chunk_volumetrics_media_group_list } from "./chunk_volumetrics_media_group_list.js";
|
|
9
|
+
import { VOLUMETRICS_MEDIA_VOLUME_LIST } from "./shader_volumetrics_split_participating_media.js";
|
|
10
|
+
import { VOLUMETRICS_METADATA_STRUCT } from "./VOLUMETRICS_METADATA_STRUCT.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Lanes of a bin workgroup. Small on purpose: a lane tests one volume at a time, so at a handful of
|
|
14
|
+
* volumes most lanes of any group idle, and the fewer there are the less the bin launches for
|
|
15
|
+
* nothing.
|
|
16
|
+
* @type {number}
|
|
17
|
+
*/
|
|
18
|
+
export const VOLUMETRICS_MEDIA_BIN_LANES = 64;
|
|
19
|
+
|
|
20
|
+
const resources = new ShaderResourceSetDescriptor();
|
|
21
|
+
|
|
22
|
+
resources.createGroup()
|
|
23
|
+
.addUniform("camera", CAMERA_UNIFORM_STRUCT)
|
|
24
|
+
.addUniform("volumetrics_metadata", VOLUMETRICS_METADATA_STRUCT)
|
|
25
|
+
.addStorageBuffer("scene_database", WebGPUArray.u32)
|
|
26
|
+
.addStorageBuffer("near_field", VOLUMETRICS_MEDIA_VOLUME_LIST)
|
|
27
|
+
.addStorageBuffer("group_lists", WebGPUArray.from(atomic_u32), true)
|
|
28
|
+
;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Pre-culls the scene's participating-media volumes for each workgroup of the participating-media
|
|
32
|
+
* build: one workgroup per build group, which tests every volume of the near field (see
|
|
33
|
+
* {@link shader_volumetrics_split_participating_media}) against the group's slab of froxels and
|
|
34
|
+
* sets the bit of each one it keeps in that group's list (see {@link chunk_volumetrics_media_group_list}).
|
|
35
|
+
* The lists start zeroed; the graph clears them.
|
|
36
|
+
*
|
|
37
|
+
* The slab is the union of every froxel of the group as the build slices it, widened so that
|
|
38
|
+
* rounding can never make the group stricter than a froxel. In xy that is a quarter of a froxel
|
|
39
|
+
* past the group's columns and rows. In z it is the column jitter, which moves a froxel's slab by
|
|
40
|
+
* up to half a slice either way, and a quarter of a slice beyond that. A volume a froxel would
|
|
41
|
+
* cover therefore never misses its group's list, and the build reads, through the list, exactly
|
|
42
|
+
* what it read walking every volume.
|
|
43
|
+
*
|
|
44
|
+
* Budget: a lane tests ⌈near-field volumes / 64⌉ volumes. Nothing is past it.
|
|
45
|
+
*/
|
|
46
|
+
export const shader_volumetrics_bin_participating_media = ComputeShader.from({
|
|
47
|
+
label: "Volumetrics / bin participating media",
|
|
48
|
+
resources,
|
|
49
|
+
body: CodeChunk.from(
|
|
50
|
+
//language=WGSL
|
|
51
|
+
`
|
|
52
|
+
@compute @workgroup_size(${VOLUMETRICS_MEDIA_BIN_LANES})
|
|
53
|
+
fn main(
|
|
54
|
+
@builtin(workgroup_id) group: vec3<u32>,
|
|
55
|
+
@builtin(local_invocation_index) lane: u32,
|
|
56
|
+
) {
|
|
57
|
+
let resolution = volumetrics_metadata.resolution;
|
|
58
|
+
let groups = volumetrics_media_groups(resolution);
|
|
59
|
+
|
|
60
|
+
if (any(group >= groups)) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let volume_count = participating_media_volumes_count(&scene_database);
|
|
65
|
+
let list_words = volumetrics_media_list_words(volume_count, arrayLength(&group_lists), groups);
|
|
66
|
+
let listed_count = min(volume_count, list_words * 32u);
|
|
67
|
+
let near_count = min(near_field.count, arrayLength(&near_field.indices));
|
|
68
|
+
|
|
69
|
+
if (lane >= near_count) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
let near_far = camera.clip_near_far;
|
|
74
|
+
|
|
75
|
+
let column_0 = group.xy * VOLUMETRICS_MEDIA_GROUP_XY;
|
|
76
|
+
let column_1 = min(column_0 + VOLUMETRICS_MEDIA_GROUP_XY, resolution.xy);
|
|
77
|
+
|
|
78
|
+
let slab = volumetrics_froxel_slab(
|
|
79
|
+
volumetrics_froxel_planes(volumetrics_metadata.frustum),
|
|
80
|
+
(vec2<f32>(column_0) - 0.25) / vec2<f32>(resolution.xy),
|
|
81
|
+
(vec2<f32>(column_1) + 0.25) / vec2<f32>(resolution.xy),
|
|
82
|
+
f32(group.z) - 0.75,
|
|
83
|
+
f32(group.z + 1u) + 0.75,
|
|
84
|
+
near_far,
|
|
85
|
+
volumetrics_metadata.froxel_cluster_parameters,
|
|
86
|
+
f32(resolution.z),
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
let list = volumetrics_media_list_offset(group, groups, list_words);
|
|
90
|
+
|
|
91
|
+
for (var i = lane; i < near_count; i += ${VOLUMETRICS_MEDIA_BIN_LANES}u) {
|
|
92
|
+
let v = near_field.indices[i];
|
|
93
|
+
|
|
94
|
+
if (v >= listed_count) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let volume = participating_media_volume_read(&scene_database, v);
|
|
99
|
+
|
|
100
|
+
if (volumetrics_volume_in_slab(slab, volume)) {
|
|
101
|
+
atomicOr(&group_lists[list + (v >> 5u)], 1u << (v & 31u));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
`, [
|
|
106
|
+
chunk_participating_media_volumes_iterate,
|
|
107
|
+
chunk_volumetrics_media_group_list,
|
|
108
|
+
]
|
|
109
|
+
),
|
|
110
|
+
});
|
package/src/shade/renderer/volumetrics/shader_volumetrics_build_participating_media.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shader_volumetrics_build_participating_media.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/volumetrics/shader_volumetrics_build_participating_media.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"shader_volumetrics_build_participating_media.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/volumetrics/shader_volumetrics_build_participating_media.js"],"names":[],"mappings":"AA8QA,yEAIE;8BA3P4B,4BAA4B"}
|
|
@@ -28,6 +28,10 @@ import { chunk_cluster_depth_from_z_slice } from "../light/cluster/chunk_cluster
|
|
|
28
28
|
import { chunk_compute_volume_fade_factor } from "./chunk_compute_volume_fade_factor.js";
|
|
29
29
|
import { chunk_frustum_unit_box_coverage_multisample } from "./chunk_frustum_unit_box_coverage_multisample.js";
|
|
30
30
|
import { chunk_volumetrics_column_jitter } from "./chunk_volumetrics_column_jitter.js";
|
|
31
|
+
import {
|
|
32
|
+
chunk_volumetrics_media_group_list,
|
|
33
|
+
VOLUMETRICS_MEDIA_WORKGROUP_RESOLUTION
|
|
34
|
+
} from "./chunk_volumetrics_media_group_list.js";
|
|
31
35
|
import { chunk_participating_media_volumes_iterate } from "./chunk_participating_media_volumes_iterate.js";
|
|
32
36
|
import { chunk_participating_media_noise } from "./noise/chunk_participating_media_noise.js";
|
|
33
37
|
import { PARTICIPATING_MEDIA_NOISE_SAMPLER_DESCRIPTOR } from "./noise/PARTICIPATING_MEDIA_NOISE.js";
|
|
@@ -47,12 +51,13 @@ wind_field_add_resources(resources.createGroup()
|
|
|
47
51
|
.addTexture("tParticipatingMediaNoise", "float", "3d")
|
|
48
52
|
.addSampler("sParticipatingMediaNoise", "filtering", PARTICIPATING_MEDIA_NOISE_SAMPLER_DESCRIPTOR)
|
|
49
53
|
.addStorageTexture("tOutput0", "rgba16float", "3d", "write-only")
|
|
50
|
-
.addStorageTexture("tOutput1", "rgba16float", "3d", "write-only")
|
|
54
|
+
.addStorageTexture("tOutput1", "rgba16float", "3d", "write-only")
|
|
55
|
+
.addStorageBuffer("group_lists", WebGPUArray.u32));
|
|
51
56
|
|
|
52
57
|
const body = CodeChunk.from(
|
|
53
58
|
//language=WGSL
|
|
54
59
|
`
|
|
55
|
-
@compute @workgroup_size(
|
|
60
|
+
@compute @workgroup_size(${VOLUMETRICS_MEDIA_WORKGROUP_RESOLUTION.join(",")})
|
|
56
61
|
fn main(
|
|
57
62
|
@builtin(global_invocation_id) voxel_coord : vec3<u32>
|
|
58
63
|
){
|
|
@@ -86,22 +91,17 @@ fn main(
|
|
|
86
91
|
let z1_norm = (z1 - near_far.x) / (near_far.y - near_far.x);
|
|
87
92
|
|
|
88
93
|
// Frustum planes are [LEFT, RIGHT, BOTTOM, TOP, NEAR, FAR] (see Camera.construct_frustum_planes).
|
|
89
|
-
// Only the Y plane pair
|
|
90
|
-
//
|
|
94
|
+
// Only the Y plane pair changes: screen-Y is top-down but world-Y is bottom-up (the visible
|
|
95
|
+
// "above/below" swap), so the pair trades places and keeps facing in, as the bin's slabs do.
|
|
96
|
+
// Negated as well, the pair faced out, and the box test below kept a volume only where it
|
|
97
|
+
// spanned the froxel's whole row. X already matches (screen-left -> LEFT plane). Z must NOT be
|
|
91
98
|
// flipped: we slice the metadata frustum, which has a real FINITE far plane (rebuilt at the
|
|
92
99
|
// logical far, since the camera's infinite projection zeroes its own far plane). With a real
|
|
93
100
|
// far plane the near/far slab maps to z0_norm/z1_norm directly; swapping it inverts and
|
|
94
101
|
// far-skews the slab depth (frustum_slice would place froxels near far - z*(far-near)),
|
|
95
102
|
// which made fog density depend on camera.far and miss the actual volume.
|
|
96
|
-
var input_planes = volumetrics_metadata.frustum;
|
|
97
|
-
{
|
|
98
|
-
var t = input_planes[2];
|
|
99
|
-
input_planes[2] = -input_planes[3];
|
|
100
|
-
input_planes[3] = -t;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
103
|
let froxel_frustum = frustum_slice(
|
|
104
|
-
|
|
104
|
+
volumetrics_froxel_planes(volumetrics_metadata.frustum),
|
|
105
105
|
vec3(vec2f(voxel_coord.xy + 0u) / vec2f(resolution.xy), z0_norm),
|
|
106
106
|
vec3(vec2f(voxel_coord.xy + 1u) / vec2f(resolution.xy), z1_norm),
|
|
107
107
|
);
|
|
@@ -132,9 +132,32 @@ fn main(
|
|
|
132
132
|
var noise_wind = wind_field.far_velocity;
|
|
133
133
|
var noise_wind_read = false;
|
|
134
134
|
|
|
135
|
-
|
|
135
|
+
// The volumes the bin kept for this froxel's workgroup, in row order: the order the walk over
|
|
136
|
+
// every volume had, so the sums below add up exactly as they did. The list is the group's, so
|
|
137
|
+
// every lane of a workgroup walks the same one.
|
|
138
|
+
let groups = volumetrics_media_groups(resolution);
|
|
139
|
+
let list_words = volumetrics_media_list_words(participating_media_volumes_count(&scene_database), arrayLength(&group_lists), groups);
|
|
140
|
+
let list = volumetrics_media_list_offset(vec3<u32>(voxel_coord.xy / VOLUMETRICS_MEDIA_GROUP_XY, voxel_coord.z), groups, list_words);
|
|
141
|
+
|
|
142
|
+
var bits = 0u;
|
|
143
|
+
var word = 0u;
|
|
144
|
+
|
|
145
|
+
// Budget: VOLUMETRICS_MEDIA_GROUP_VOLUME_BUDGET listed volumes, and a load for each of the
|
|
146
|
+
// list's words, ⌈volumes / 32⌉, an empty word costing the one load. Past the budget the group's
|
|
147
|
+
// highest-indexed volumes are dropped.
|
|
148
|
+
for (var listed = 0u; listed < VOLUMETRICS_MEDIA_GROUP_VOLUME_BUDGET; listed++) {
|
|
149
|
+
while (bits == 0u && word < list_words) {
|
|
150
|
+
bits = group_lists[list + word];
|
|
151
|
+
word++;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (bits == 0u) {
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
let v = (word - 1u) * 32u + countTrailingZeros(bits);
|
|
159
|
+
bits &= bits - 1u;
|
|
136
160
|
|
|
137
|
-
for (var v = 0u; v < volume_count; v++) {
|
|
138
161
|
let volume = participating_media_volume_read(&scene_database, v);
|
|
139
162
|
|
|
140
163
|
// Transform froxel into volume's coordinate space. Points go world -> local through
|
|
@@ -241,6 +264,7 @@ fn main(
|
|
|
241
264
|
chunk_stbn_sample_vec3,
|
|
242
265
|
chunk_wind_field_sample,
|
|
243
266
|
chunk_participating_media_noise,
|
|
267
|
+
chunk_volumetrics_media_group_list,
|
|
244
268
|
]
|
|
245
269
|
)
|
|
246
270
|
|
|
@@ -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":"
|
|
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":"AAiRA,oEAIG;8BA5Q2B,4BAA4B"}
|
|
@@ -21,6 +21,7 @@ import { chunk_sky_lut_compute_uv } from "../atmosphere/chunk_sky_lut_compute_uv
|
|
|
21
21
|
import { chunk_calculate_multiscattering } from "./chunk_calculate_multiscattering.js";
|
|
22
22
|
import { chunk_camera_ray_planar_depth_scale } from "./chunk_camera_ray_planar_depth_scale.js";
|
|
23
23
|
import { chunk_participating_media_volumes_iterate } from "./chunk_participating_media_volumes_iterate.js";
|
|
24
|
+
import { VOLUMETRICS_MEDIA_VOLUME_LIST } from "./shader_volumetrics_split_participating_media.js";
|
|
24
25
|
|
|
25
26
|
const resources = new ShaderResourceSetDescriptor();
|
|
26
27
|
|
|
@@ -35,6 +36,8 @@ resources.createGroup()
|
|
|
35
36
|
// RDR2-style far field: beyond froxel_far the pass ray-marches the scene volumes directly,
|
|
36
37
|
// lit by the directional sun + atmosphere (single scattering, no self-shadow).
|
|
37
38
|
.addStorageBuffer("scene_database", WebGPUArray.u32)
|
|
39
|
+
// the volumes any part of which lies beyond froxel_far, as the split lists them
|
|
40
|
+
.addStorageBuffer("far_field", VOLUMETRICS_MEDIA_VOLUME_LIST)
|
|
38
41
|
.addStorageBuffer("light_data", WebGPUArray.u32)
|
|
39
42
|
.addTexture("tSunTransmittance", "float", "2d")
|
|
40
43
|
.addSampler("sLinear")
|
|
@@ -56,17 +59,19 @@ struct FarVolumeSample {
|
|
|
56
59
|
}
|
|
57
60
|
|
|
58
61
|
// Point-sample the scene participating-media volumes (coarse: full density inside the box, no edge
|
|
59
|
-
// fade — that is a near-field nicety). Used only beyond the froxel grid
|
|
62
|
+
// fade — that is a near-field nicety). Used only beyond the froxel grid, so only the far field's
|
|
63
|
+
// volumes are walked. The split lists them in no particular order, so the sums below round
|
|
64
|
+
// differently from frame to frame; the volumetric TAA and the final image's TAA absorb that.
|
|
60
65
|
fn far_sample_volumes(world_pos: vec3<f32>) -> FarVolumeSample {
|
|
61
66
|
var scattering = vec3<f32>(0.0);
|
|
62
67
|
var extinction = vec3<f32>(0.0);
|
|
63
68
|
var g_weighted = 0.0;
|
|
64
69
|
var scattering_luma = 0.0;
|
|
65
70
|
|
|
66
|
-
let
|
|
71
|
+
let far_count = min(far_field.count, arrayLength(&far_field.indices));
|
|
67
72
|
|
|
68
|
-
for (var
|
|
69
|
-
let volume = participating_media_volume_read(&scene_database,
|
|
73
|
+
for (var i = 0u; i < far_count; i++) {
|
|
74
|
+
let volume = participating_media_volume_read(&scene_database, far_field.indices[i]);
|
|
70
75
|
let local = (volume.transform_inverse * vec4<f32>(world_pos, 1.0)).xyz;
|
|
71
76
|
if (any(abs(local) > vec3<f32>(0.5))) {
|
|
72
77
|
continue;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Some of the scene's participating-media volumes, as their rows in the scene database: `indices[0,
|
|
3
|
+
* count)`, in no particular order.
|
|
4
|
+
*/
|
|
5
|
+
export const VOLUMETRICS_MEDIA_VOLUME_LIST: WebGPUStruct;
|
|
6
|
+
/**
|
|
7
|
+
* Threads of a split workgroup, each taking one volume.
|
|
8
|
+
* @type {number}
|
|
9
|
+
*/
|
|
10
|
+
export const VOLUMETRICS_MEDIA_SPLIT_LANES: number;
|
|
11
|
+
/**
|
|
12
|
+
* Splits the scene's participating-media volumes between the near field, the froxel grid's depths
|
|
13
|
+
* up to `froxel_far`, and the far field beyond it, where the aerial pass reads the volumes
|
|
14
|
+
* themselves. One thread a volume. A volume outside any of the grid's four side planes or its near
|
|
15
|
+
* plane goes to neither. Of the rest, a volume any part of which is nearer than `froxel_far` goes to
|
|
16
|
+
* the near field, and one any part of which is beyond goes to the far field, so a volume across
|
|
17
|
+
* `froxel_far` goes to both.
|
|
18
|
+
*
|
|
19
|
+
* The side planes are the grid's, widened a quarter froxel as the bin widens its groups'. The near
|
|
20
|
+
* plane is the one the bin's first groups start at. With `froxel_far` at its default, the camera's
|
|
21
|
+
* far, the discriminator is where the bin's last groups end, so any volume a group of the bin
|
|
22
|
+
* would keep is in the near field, and the bin reads only the near field. With `froxel_far` pulled
|
|
23
|
+
* nearer, the grid's last slice still reaches the camera's far. A volume wholly between the two then
|
|
24
|
+
* belongs to the far field alone and stops reaching that slice.
|
|
25
|
+
*
|
|
26
|
+
* The lists start at a count of zero; the graph clears them. Budget: one volume a thread.
|
|
27
|
+
*/
|
|
28
|
+
export const shader_volumetrics_split_participating_media: ComputeShader;
|
|
29
|
+
import { WebGPUStruct } from "../shader/type/WebGPUStruct.js";
|
|
30
|
+
import { ComputeShader } from "../shader/ComputeShader.js";
|
|
31
|
+
//# sourceMappingURL=shader_volumetrics_split_participating_media.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shader_volumetrics_split_participating_media.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/volumetrics/shader_volumetrics_split_participating_media.js"],"names":[],"mappings":"AAgBA;;;GAGG;AACH,yDAGiC;AAUjC;;;GAGG;AACH,4CAFU,MAAM,CAEgC;AAYhD;;;;;;;;;;;;;;;;GAgBG;AACH,yEAwEG;6BA/H0B,gCAAgC;8BAJ/B,4BAA4B"}
|