@woosh/meep-engine 3.60.1 → 3.60.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/shade/playground/sh_exponential/main.js +2 -2
- package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_CLIPMAP_PROPOSAL_2026_10_27.md +4 -0
- package/src/shade/renderer/light/GPULightCollection.d.ts.map +1 -1
- package/src/shade/renderer/light/GPULightCollection.js +3 -5
- package/src/shade/renderer/light/environment/execute_filter_environment_map.d.ts +13 -5
- 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 +133 -7
- package/src/shade/renderer/light/environment/graph_filter_environment_map.d.ts +9 -0
- package/src/shade/renderer/light/environment/graph_filter_environment_map.d.ts.map +1 -1
- package/src/shade/renderer/light/environment/graph_filter_environment_map.js +33 -4
- package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.d.ts +13 -0
- package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.js +169 -0
- package/src/shade/renderer/light/environment/shader_downsample_environment_map.d.ts +3 -0
- package/src/shade/renderer/light/environment/shader_downsample_environment_map.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/shader_downsample_environment_map.js +105 -0
- package/src/shade/renderer/light/environment/sphere_probe_lod_to_roughness.d.ts +10 -0
- package/src/shade/renderer/light/environment/sphere_probe_lod_to_roughness.d.ts.map +1 -0
- package/src/shade/renderer/light/environment/sphere_probe_lod_to_roughness.js +23 -0
- package/src/shade/renderer/shader/chunk/light/probe/chunk_probe_visibility_weight.d.ts +8 -4
- package/src/shade/renderer/shader/chunk/light/probe/chunk_probe_visibility_weight.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/light/probe/chunk_probe_visibility_weight.js +9 -5
- package/src/view/elements/progress/segmented/GhostTrail.d.ts +6 -0
- package/src/view/elements/progress/segmented/GhostTrail.d.ts.map +1 -1
- package/src/view/elements/progress/segmented/GhostTrail.js +14 -0
- package/src/view/elements/progress/segmented/SegmentedResourceBarView.d.ts +55 -19
- package/src/view/elements/progress/segmented/SegmentedResourceBarView.d.ts.map +1 -1
- package/src/view/elements/progress/segmented/SegmentedResourceBarView.js +173 -120
- package/src/view/elements/progress/segmented/playground/main.js +18 -11
- package/src/view/elements/progress/segmented/segment_cell_extent.d.ts +14 -0
- package/src/view/elements/progress/segmented/segment_cell_extent.d.ts.map +1 -0
- package/src/view/elements/progress/segmented/segment_cell_extent.js +44 -0
- package/src/view/elements/progress/segmented/segment_mask_url.d.ts +24 -0
- package/src/view/elements/progress/segmented/segment_mask_url.d.ts.map +1 -0
- package/src/view/elements/progress/segmented/segment_mask_url.js +74 -0
- package/src/view/elements/progress/segmented/segment_mask_width.d.ts +18 -0
- package/src/view/elements/progress/segmented/segment_mask_width.d.ts.map +1 -0
- package/src/view/elements/progress/segmented/segment_mask_width.js +46 -0
- package/src/view/elements/progress/segmented/segmented-resource-bar.css +6 -10
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ShadeGPUCommandContext } from "../../device/ShadeGPUCommandContext.js";
|
|
2
2
|
import { GPUFrameRecorder } from "../../device/timing/profile/GPUFrameRecorder.js";
|
|
3
3
|
import { GraphicsContext } from "../../renderer/GraphicsContext.js";
|
|
4
|
-
import {
|
|
4
|
+
import { encode_filter_environment_map } from "../../renderer/light/environment/graph_filter_environment_map.js";
|
|
5
5
|
import { load_environment_map, ProjectionMappingType } from "../../renderer/light/environment/rgbe/load_environment_map.js";
|
|
6
6
|
import { SH_EXP_ENCODE_PARAMETERS_STRUCT } from "../../renderer/light/environment/sh_exponential/SH_EXP_ENCODE_PARAMETERS_STRUCT.js";
|
|
7
7
|
import { SH_EXP_P4Q2_SETTINGS, SHExpEncoder } from "../../renderer/light/environment/sh_exponential/SHExpEncoder.js";
|
|
@@ -289,7 +289,7 @@ async function main() {
|
|
|
289
289
|
const cmd = ShadeGPUCommandContext.create(graphics, 'SH exponential playground / environment');
|
|
290
290
|
|
|
291
291
|
// the engine IBL row reads the prefiltered levels, exactly as the forward path does
|
|
292
|
-
|
|
292
|
+
encode_filter_environment_map(environment, cmd);
|
|
293
293
|
|
|
294
294
|
// the reference's own grid, finer than the encoder's
|
|
295
295
|
shader_sh_exp_environment_cells.dispatch({
|
package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_CLIPMAP_PROPOSAL_2026_10_27.md
CHANGED
|
@@ -3230,6 +3230,10 @@ a per-invocation function instead. Each is tier 2 whole, and no thread's loop ru
|
|
|
3230
3230
|
- one bilinear depth tap toward p_b at distance `|p_b − probe|/s_c`;
|
|
3231
3231
|
- weight = trilinear × `probe_visibility_weight`: the wrap back face, Chebyshev from the tap, and
|
|
3232
3232
|
the crush.
|
|
3233
|
+
- The wrap is `((n·ω+1)/2)²`, without DDGI's + 0.2. With it, a probe behind Sponza's thin outer
|
|
3234
|
+
wall, which Chebyshev passes at 0.94–0.97 through the grazing texels of an 8² depth map, held ≈0.25,
|
|
3235
|
+
above the crush: 11% of the wall's pick, 32% in cascade 1, where the wall sits 0.15 of a spacing past
|
|
3236
|
+
the lattice plane, a dark lattice of blotches. Without it, 0.1% and 0.6%.
|
|
3233
3237
|
|
|
3234
3238
|
No SH and no radiance is read here.
|
|
3235
3239
|
4. **Pick two**, the extracted `pick2_wrs_mat2x4`, which is Brick4's selection:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GPULightCollection.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/light/GPULightCollection.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"GPULightCollection.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/light/GPULightCollection.js"],"names":[],"mappings":"AAoDA;IAEI;;;;;OAKG;IACH,iCAFW,eAAe,QAOzB;IA6KD;;;;;;OAMG;IACH,sBALW,eAAe,aACf,eAAe,EA2CzB;IAnND;;;OAGG;IACH,6DAEC;IAeD;;OAEG;IACH,4BAEC;IAED;;;;;;OAMG;IACH,6BAEC;IAED;;;;;;;OAOG;IACH,qFAEC;IAWD;;OAEG;IACH,+CAEC;IA6DD;;;OAGG;IACH,qCAEC;IAED;;;;;;OAMG;IACH,gCAEC;IAED;;;;;;OAMG;IACH,yCAEC;IAGD,4BAMC;IAoMD;;;OAGG;IACH,YAFW,eAAe,QAoCzB;IAED;;;;;;;;;OASG;IACH,gBAFW,OAAO,+BAA+B,EAAE,YAAY,QAkC9D;IAED;;;;;;OAMG;IACH,uBAHW,sBAAsB,mCA6BhC;IAwED,gBAUC;;CACJ;8BA1lBY,OAAO,0CAA0C,EAAE,eAAe;8BAClE,OAAO,uBAAuB,EAAE,eAAe;8BAC/C,OAAO,6BAA6B,EAAE,eAAe;8BACrD,OAAO,sBAAsB,EAAE,eAAe;4BA7B/B,gCAAgC;yCACnB,2CAA2C;kCAClD,iCAAiC;uCAH5B,wCAAwC"}
|
|
@@ -7,10 +7,8 @@ import { GPUSceneShadowmapContext } from "../shadow/map/GPUSceneShadowmapContext
|
|
|
7
7
|
import { GPUTextureContext } from "../texture/GPUTextureContext.js";
|
|
8
8
|
import { texture_mip_level_count } from "../../util/texture_mip_extent.js";
|
|
9
9
|
import { texture_write_to_gpu } from "../texture/texture_write_to_gpu.js";
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
execute_filter_environment_map
|
|
13
|
-
} from "./environment/execute_filter_environment_map.js";
|
|
10
|
+
import { enquire_filter_environment_map_pipelines } from "./environment/execute_filter_environment_map.js";
|
|
11
|
+
import { encode_filter_environment_map } from "./environment/graph_filter_environment_map.js";
|
|
14
12
|
import { SPHERE_PROBE_MIPMAP_LEVELS } from "./environment/DEFINES.js";
|
|
15
13
|
import { octahedral_skirt_matches } from "./environment/octahedral_skirt_matches.js";
|
|
16
14
|
import { ENVIRONMENT_SH_TYPE, shader_project_environment_sh } from "./environment/shader_project_environment_sh.js";
|
|
@@ -398,7 +396,7 @@ export class GPULightCollection {
|
|
|
398
396
|
// convolve
|
|
399
397
|
const cmd = ShadeGPUCommandContext.create(ctx);
|
|
400
398
|
|
|
401
|
-
|
|
399
|
+
encode_filter_environment_map(gpu_ctx, cmd);
|
|
402
400
|
|
|
403
401
|
// after the prefilter, whose roughest levels are what it reads
|
|
404
402
|
shader_project_environment_sh.dispatch({
|
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @
|
|
3
|
-
*/
|
|
4
|
-
/**
|
|
5
|
-
* Offer the pipeline {@link execute_filter_environment_map} dispatches — see {@link PipelineEnquiry}.
|
|
2
|
+
* Offer the pipelines {@link execute_filter_environment_map} dispatches — see {@link PipelineEnquiry}.
|
|
6
3
|
*
|
|
7
4
|
* @param {PipelineEnquiry} enquiry
|
|
8
5
|
*/
|
|
9
6
|
export function enquire_filter_environment_map_pipelines(enquiry: PipelineEnquiry): void;
|
|
7
|
+
/**
|
|
8
|
+
* The plain averages of a map's level 0 the prefilter's level 1 samples from: level 1 down as far as
|
|
9
|
+
* its samples reach, as a texture of their own, so the map keeps its levels 1.. for the filtered
|
|
10
|
+
* result. Nothing reads them after the prefilter, so a frame graph holds them transiently.
|
|
11
|
+
*
|
|
12
|
+
* @param {number} map_size side of the map's level 0
|
|
13
|
+
* @returns {TextureResourceDescriptor}
|
|
14
|
+
*/
|
|
15
|
+
export function environment_map_averages_descriptor(map_size: number): TextureResourceDescriptor;
|
|
10
16
|
/**
|
|
11
17
|
*
|
|
12
18
|
* @param {GPUTextureContext} map
|
|
19
|
+
* @param {GPUTextureContext} averages described by {@link environment_map_averages_descriptor}
|
|
13
20
|
* @param {ShadeGPUCommandContext} encoder
|
|
14
21
|
*/
|
|
15
|
-
export function execute_filter_environment_map(map: GPUTextureContext, encoder: ShadeGPUCommandContext): void;
|
|
22
|
+
export function execute_filter_environment_map(map: GPUTextureContext, averages: GPUTextureContext, encoder: ShadeGPUCommandContext): void;
|
|
16
23
|
export type PipelineEnquiry = import("../../../device/pipeline/PipelineEnquiry.js").PipelineEnquiry;
|
|
24
|
+
import { TextureResourceDescriptor } from "../../../device/graph/TextureResourceDescriptor.js";
|
|
17
25
|
//# sourceMappingURL=execute_filter_environment_map.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execute_filter_environment_map.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/light/environment/execute_filter_environment_map.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"execute_filter_environment_map.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/light/environment/execute_filter_environment_map.js"],"names":[],"mappings":"AAuBA;;;;GAIG;AACH,kEAFW,eAAe,QAMzB;AAsBD;;;;;;;GAOG;AACH,8DAHW,MAAM,GACJ,yBAAyB,CAyBrC;AAED;;;;;GAKG;AACH,2IA0GC;8BAzLY,OAAO,6CAA6C,EAAE,eAAe;0CAZxC,oDAAoD"}
|
|
@@ -1,31 +1,103 @@
|
|
|
1
1
|
import { assert } from "../../../../core/assert.js";
|
|
2
2
|
import { TextureViewDescriptor } from "../../../descriptor/texture/TextureViewDescriptor.js";
|
|
3
|
-
import {
|
|
3
|
+
import { TextureResourceDescriptor } from "../../../device/graph/TextureResourceDescriptor.js";
|
|
4
|
+
import { cone_cosine_from_roughness } from "../../../../core/math/physics/brdf/cone_cosine_from_roughness.js";
|
|
5
|
+
import { BSDF_ROUGHNESS_MINIMUM, SPHERE_PROBE_MIPMAP_LEVELS } from "./DEFINES.js";
|
|
4
6
|
import { shader_convolve_reflection_map } from "./shader_convolve_reflection_map.js";
|
|
7
|
+
import {
|
|
8
|
+
CONVOLVE_FROM_AVERAGES_SAMPLE_COUNT,
|
|
9
|
+
shader_convolve_reflection_map_from_averages
|
|
10
|
+
} from "./shader_convolve_reflection_map_from_averages.js";
|
|
11
|
+
import { shader_downsample_environment_map } from "./shader_downsample_environment_map.js";
|
|
12
|
+
import { sphere_probe_lod_to_roughness } from "./sphere_probe_lod_to_roughness.js";
|
|
5
13
|
|
|
6
14
|
/**
|
|
7
15
|
* @typedef {import("../../../device/pipeline/PipelineEnquiry.js").PipelineEnquiry} PipelineEnquiry
|
|
8
16
|
*/
|
|
9
17
|
|
|
10
18
|
/**
|
|
11
|
-
*
|
|
19
|
+
* The smallest side a skirted level of the averages goes down to: two interior texels.
|
|
20
|
+
* @type {number}
|
|
21
|
+
*/
|
|
22
|
+
const AVERAGES_MIN_SIZE = 4;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Offer the pipelines {@link execute_filter_environment_map} dispatches — see {@link PipelineEnquiry}.
|
|
12
26
|
*
|
|
13
27
|
* @param {PipelineEnquiry} enquiry
|
|
14
28
|
*/
|
|
15
29
|
export function enquire_filter_environment_map_pipelines(enquiry) {
|
|
30
|
+
enquiry.compute.push(shader_downsample_environment_map.pipeline);
|
|
31
|
+
enquiry.compute.push(shader_convolve_reflection_map_from_averages.pipeline);
|
|
16
32
|
enquiry.compute.push(shader_convolve_reflection_map.pipeline);
|
|
17
33
|
}
|
|
18
34
|
|
|
35
|
+
/**
|
|
36
|
+
* How many averages level 1's samples reach. A sample reads the level whose texel is the solid angle
|
|
37
|
+
* it stands for, and the coarsest it asks for is where the octahedron's texels are smallest
|
|
38
|
+
* (|d|_1 = 1): the lod `shader_convolve_reflection_map_from_averages` takes, at its largest. 3 for a
|
|
39
|
+
* 1024 map, one more for each doubling.
|
|
40
|
+
*
|
|
41
|
+
* @param {number} map_size side of the map's level 0
|
|
42
|
+
* @returns {number}
|
|
43
|
+
*/
|
|
44
|
+
function averages_read_by_level_1(map_size) {
|
|
45
|
+
const roughness = Math.max(sphere_probe_lod_to_roughness(1), BSDF_ROUGHNESS_MINIMUM);
|
|
46
|
+
const cone_solid_angle = 2 * Math.PI * (1 - cone_cosine_from_roughness(roughness));
|
|
47
|
+
const sample_solid_angle = cone_solid_angle / CONVOLVE_FROM_AVERAGES_SAMPLE_COUNT;
|
|
48
|
+
|
|
49
|
+
const interior = map_size - 2;
|
|
50
|
+
const smallest_texel_solid_angle = 4 / (interior * interior);
|
|
51
|
+
|
|
52
|
+
return Math.max(1, Math.ceil(0.5 * Math.log2(sample_solid_angle / smallest_texel_solid_angle)));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The plain averages of a map's level 0 the prefilter's level 1 samples from: level 1 down as far as
|
|
57
|
+
* its samples reach, as a texture of their own, so the map keeps its levels 1.. for the filtered
|
|
58
|
+
* result. Nothing reads them after the prefilter, so a frame graph holds them transiently.
|
|
59
|
+
*
|
|
60
|
+
* @param {number} map_size side of the map's level 0
|
|
61
|
+
* @returns {TextureResourceDescriptor}
|
|
62
|
+
*/
|
|
63
|
+
export function environment_map_averages_descriptor(map_size) {
|
|
64
|
+
assert.isNonNegativeInteger(map_size, 'map_size');
|
|
65
|
+
|
|
66
|
+
const size = map_size >> 1;
|
|
67
|
+
|
|
68
|
+
let available = 0;
|
|
69
|
+
for (let s = size; s >= AVERAGES_MIN_SIZE; s >>= 1) {
|
|
70
|
+
available++;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
assert.greaterThanOrEqual(available, 1, 'environment map too small to average');
|
|
74
|
+
|
|
75
|
+
const mipLevelCount = Math.min(available, averages_read_by_level_1(map_size));
|
|
76
|
+
|
|
77
|
+
const descriptor = TextureResourceDescriptor.from({
|
|
78
|
+
resolution: [size, size],
|
|
79
|
+
format: "rgba16float",
|
|
80
|
+
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.STORAGE_BINDING,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
descriptor.mipLevelCount = mipLevelCount;
|
|
84
|
+
|
|
85
|
+
return descriptor;
|
|
86
|
+
}
|
|
87
|
+
|
|
19
88
|
/**
|
|
20
89
|
*
|
|
21
90
|
* @param {GPUTextureContext} map
|
|
91
|
+
* @param {GPUTextureContext} averages described by {@link environment_map_averages_descriptor}
|
|
22
92
|
* @param {ShadeGPUCommandContext} encoder
|
|
23
93
|
*/
|
|
24
94
|
export function execute_filter_environment_map(
|
|
25
95
|
map,
|
|
96
|
+
averages,
|
|
26
97
|
encoder
|
|
27
98
|
) {
|
|
28
99
|
assert.defined(map);
|
|
100
|
+
assert.defined(averages);
|
|
29
101
|
assert.defined(encoder);
|
|
30
102
|
|
|
31
103
|
/*
|
|
@@ -45,6 +117,39 @@ export function execute_filter_environment_map(
|
|
|
45
117
|
*/
|
|
46
118
|
const mips = Math.min(map.descriptor.mipLevelCount, SPHERE_PROBE_MIPMAP_LEVELS);
|
|
47
119
|
|
|
120
|
+
if (mips < 2) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const level_0 = map.obtainView(TextureViewDescriptor.from({
|
|
125
|
+
baseMipLevel: 0,
|
|
126
|
+
mipLevelCount: 1,
|
|
127
|
+
}));
|
|
128
|
+
|
|
129
|
+
// the averages, each from the one above it, the first from level 0
|
|
130
|
+
const average_levels = averages.descriptor.mipLevelCount;
|
|
131
|
+
|
|
132
|
+
for (let i = 0; i < average_levels; i++) {
|
|
133
|
+
const target_size = averages.size[0] >> i;
|
|
134
|
+
const group_count = Math.max(1, Math.ceil(target_size / 16));
|
|
135
|
+
|
|
136
|
+
shader_downsample_environment_map.dispatch({
|
|
137
|
+
encoder,
|
|
138
|
+
group_count_x: group_count,
|
|
139
|
+
group_count_y: group_count,
|
|
140
|
+
bindings: {
|
|
141
|
+
input: i === 0 ? level_0 : averages.obtainView(TextureViewDescriptor.from({
|
|
142
|
+
baseMipLevel: i - 1,
|
|
143
|
+
mipLevelCount: 1,
|
|
144
|
+
})),
|
|
145
|
+
output: averages.obtainView(TextureViewDescriptor.from({
|
|
146
|
+
baseMipLevel: i,
|
|
147
|
+
mipLevelCount: 1
|
|
148
|
+
}))
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
48
153
|
for (let i = 1; i < mips; i++) {
|
|
49
154
|
const target_width = map.size[0] >> i;
|
|
50
155
|
const target_height = map.size[1] >> i;
|
|
@@ -54,6 +159,30 @@ export function execute_filter_environment_map(
|
|
|
54
159
|
const dispatch_x = Math.max(1, Math.ceil(target_width / 16));
|
|
55
160
|
const dispatch_y = Math.max(1, Math.ceil(target_height / 16));
|
|
56
161
|
|
|
162
|
+
const output = map.obtainView(TextureViewDescriptor.from({
|
|
163
|
+
baseMipLevel: i,
|
|
164
|
+
mipLevelCount: 1
|
|
165
|
+
}));
|
|
166
|
+
|
|
167
|
+
if (i === 1) {
|
|
168
|
+
// from level 0 and its averages: level 0 is too fine for the taps that cover level 1's cone
|
|
169
|
+
shader_convolve_reflection_map_from_averages.dispatch({
|
|
170
|
+
encoder,
|
|
171
|
+
group_count_x: dispatch_x,
|
|
172
|
+
group_count_y: dispatch_y,
|
|
173
|
+
bindings: {
|
|
174
|
+
level_0,
|
|
175
|
+
averages: averages.obtainView(TextureViewDescriptor.from({
|
|
176
|
+
baseMipLevel: 0,
|
|
177
|
+
mipLevelCount: average_levels,
|
|
178
|
+
})),
|
|
179
|
+
output
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
|
|
57
186
|
shader_convolve_reflection_map.dispatch({
|
|
58
187
|
encoder,
|
|
59
188
|
group_count_x: dispatch_x,
|
|
@@ -64,11 +193,8 @@ export function execute_filter_environment_map(
|
|
|
64
193
|
baseMipLevel: i - 1,
|
|
65
194
|
mipLevelCount: 1,
|
|
66
195
|
})),
|
|
67
|
-
output
|
|
68
|
-
baseMipLevel: i,
|
|
69
|
-
mipLevelCount: 1
|
|
70
|
-
}))
|
|
196
|
+
output
|
|
71
197
|
}
|
|
72
198
|
});
|
|
73
199
|
}
|
|
74
|
-
}
|
|
200
|
+
}
|
|
@@ -9,4 +9,13 @@ export function graph_filter_environment_map({ graph, map, }: {
|
|
|
9
9
|
graph: FrameGraph;
|
|
10
10
|
map: number;
|
|
11
11
|
}): number;
|
|
12
|
+
/**
|
|
13
|
+
* Prefilter a map outside a frame graph of the caller's: one of its own, so the averages are still
|
|
14
|
+
* transient.
|
|
15
|
+
*
|
|
16
|
+
* @param {GPUTextureContext} map
|
|
17
|
+
* @param {ShadeGPUCommandContext} encoder
|
|
18
|
+
*/
|
|
19
|
+
export function encode_filter_environment_map(map: GPUTextureContext, encoder: ShadeGPUCommandContext): void;
|
|
20
|
+
import { FrameGraph } from "../../../../engine/graphics/render/frame_graph/FrameGraph.js";
|
|
12
21
|
//# sourceMappingURL=graph_filter_environment_map.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph_filter_environment_map.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/light/environment/graph_filter_environment_map.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"graph_filter_environment_map.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/light/environment/graph_filter_environment_map.js"],"names":[],"mappings":"AAOA;;;;;;GAMG;AACH;IAJ+B,KAAK,EAAzB,UAAU;IACM,GAAG,EAAnB,MAAM;IACJ,MAAM,CAmClB;AAED;;;;;;GAMG;AACH,6GAYC;2BApE0B,8DAA8D"}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FrameGraph } from "../../../../engine/graphics/render/frame_graph/FrameGraph.js";
|
|
2
|
+
import { TextureResourceDescriptor } from "../../../device/graph/TextureResourceDescriptor.js";
|
|
3
|
+
import {
|
|
4
|
+
environment_map_averages_descriptor,
|
|
5
|
+
execute_filter_environment_map
|
|
6
|
+
} from "./execute_filter_environment_map.js";
|
|
2
7
|
|
|
3
8
|
/**
|
|
4
9
|
* @see https://github.com/mrdoob/three.js/blob/efb7510951afff90c90fd2c969675285e374b0be/src/extras/PMREMGenerator.js#L32
|
|
@@ -27,14 +32,38 @@ export function graph_filter_environment_map({
|
|
|
27
32
|
*/
|
|
28
33
|
const map = resources.get(data.map);
|
|
29
34
|
|
|
30
|
-
execute_filter_environment_map(map, context.encoder);
|
|
35
|
+
execute_filter_environment_map(map, resources.get(data.averages), context.encoder);
|
|
31
36
|
|
|
32
37
|
}
|
|
33
38
|
|
|
34
39
|
const builder = graph.add("filter env map", data, execute);
|
|
35
40
|
|
|
36
|
-
|
|
37
41
|
data.map = builder.read(map);
|
|
42
|
+
data.averages = builder.create(
|
|
43
|
+
"env map averages",
|
|
44
|
+
environment_map_averages_descriptor(graph.getDescriptor(map).resolution[0])
|
|
45
|
+
);
|
|
38
46
|
|
|
39
47
|
return builder.write(map);
|
|
40
|
-
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Prefilter a map outside a frame graph of the caller's: one of its own, so the averages are still
|
|
52
|
+
* transient.
|
|
53
|
+
*
|
|
54
|
+
* @param {GPUTextureContext} map
|
|
55
|
+
* @param {ShadeGPUCommandContext} encoder
|
|
56
|
+
*/
|
|
57
|
+
export function encode_filter_environment_map(map, encoder) {
|
|
58
|
+
const graph = new FrameGraph("environment map prefilter");
|
|
59
|
+
|
|
60
|
+
const imported = graph.import_resource(
|
|
61
|
+
"environment map",
|
|
62
|
+
TextureResourceDescriptor.fromTexture(map.gpu_texture),
|
|
63
|
+
map
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
graph_filter_environment_map({ graph, map: imported });
|
|
67
|
+
|
|
68
|
+
encoder.encodeGraph(graph);
|
|
69
|
+
}
|
package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Samples per texel. Each reads the average whose texel matches the solid angle it stands for, so
|
|
3
|
+
* the count sets the noise of the lobe's shape, not whether a bright texel is counted. Measured on a
|
|
4
|
+
* single bright texel of a 1024 map at 48 places, level 1 over level 0 in solid-angle energy:
|
|
5
|
+
* 32 → 0.995 ± 0.014, 64 → 0.998 ± 0.008 (at most 1.005), 196 → 1.001 ± 0.019 — 196 asks for a
|
|
6
|
+
* finer average than the 2x2 one, so it point-samples level 0 again. UE filters with 32 below
|
|
7
|
+
* roughness 0.1 and 64 above.
|
|
8
|
+
* @type {number}
|
|
9
|
+
*/
|
|
10
|
+
export const CONVOLVE_FROM_AVERAGES_SAMPLE_COUNT: number;
|
|
11
|
+
export const shader_convolve_reflection_map_from_averages: ComputeShader;
|
|
12
|
+
import { ComputeShader } from "../../shader/ComputeShader.js";
|
|
13
|
+
//# sourceMappingURL=shader_convolve_reflection_map_from_averages.d.ts.map
|
package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shader_convolve_reflection_map_from_averages.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.js"],"names":[],"mappings":"AAoCA;;;;;;;;GAQG;AACH,kDAFU,MAAM,CAEsC;AAuHtD,yEAII;8BAtJ0B,+BAA+B"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { chunk_cone_cosine_from_roughness } from "../../shader/chunk/brdf/chunk_cone_cosine_from_roughness.js";
|
|
2
|
+
import {
|
|
3
|
+
chunk_build_orthonormal_matrix_n
|
|
4
|
+
} from "../../shader/chunk/geometry/normal/chunk_build_orthonormal_matrix_n.js";
|
|
5
|
+
import {
|
|
6
|
+
chunk_uv_octahedral_unit_decode
|
|
7
|
+
} from "../../shader/chunk/geometry/octahedron/chunk_uv_octahedral_unit_decode.js";
|
|
8
|
+
import {
|
|
9
|
+
chunk_uv_octahedral_unit_encode
|
|
10
|
+
} from "../../shader/chunk/geometry/octahedron/chunk_uv_octahedral_unit_encode.js";
|
|
11
|
+
import { chunk_uniform_sample_cone } from "../../shader/chunk/geometry/ray/chunk_uniform_sample_cone.js";
|
|
12
|
+
import { chunk_safe_rcp } from "../../shader/chunk/math/chunk_safe_rcp.js";
|
|
13
|
+
import { chunk_hammersley_2d } from "../../shader/chunk/noise/chunk_hammersley_2d.js";
|
|
14
|
+
import {
|
|
15
|
+
chunk_texture_octahedral_skirt_texel_uv
|
|
16
|
+
} from "../../shader/chunk/texture/octahedral/chunk_texture_octahedral_skirt_texel_uv.js";
|
|
17
|
+
import { chunk_texture_octahedral_skirt_uv } from "../../shader/chunk/texture/octahedral/chunk_texture_octahedral_skirt_uv.js";
|
|
18
|
+
import { CodeChunk } from "../../shader/compiler/CodeChunk.js";
|
|
19
|
+
import { ComputeShader } from "../../shader/ComputeShader.js";
|
|
20
|
+
import { ShaderResourceSetDescriptor } from "../../shader/resource/ShaderResourceSetDescriptor.js";
|
|
21
|
+
import { ShaderDescriptor } from "../../shader/ShaderDescriptor.js";
|
|
22
|
+
import { chunk_reflection_sample_weight } from "./chunk_reflection_sample_weight.js";
|
|
23
|
+
import { chunk_sphere_probe_lod_to_roughness } from "./chunk_sphere_probe_lod_to_roughness.js";
|
|
24
|
+
import { BSDF_ROUGHNESS_MINIMUM } from "./DEFINES.js";
|
|
25
|
+
import { ENVIRONMENT_MAP_SAMPLER_RESOURCE } from "./ENVIRONMENT_MAP_SAMPLER_RESOURCE.js";
|
|
26
|
+
|
|
27
|
+
const resources = new ShaderResourceSetDescriptor();
|
|
28
|
+
|
|
29
|
+
resources.createGroup()
|
|
30
|
+
// the map's level 0, and its plain averages from level 1 down (see shader_downsample_environment_map)
|
|
31
|
+
.addTexture("level_0", "float")
|
|
32
|
+
.addTexture("averages", "float")
|
|
33
|
+
.addStorageTexture("output", "rgba16float")
|
|
34
|
+
.add(ENVIRONMENT_MAP_SAMPLER_RESOURCE)
|
|
35
|
+
;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Samples per texel. Each reads the average whose texel matches the solid angle it stands for, so
|
|
39
|
+
* the count sets the noise of the lobe's shape, not whether a bright texel is counted. Measured on a
|
|
40
|
+
* single bright texel of a 1024 map at 48 places, level 1 over level 0 in solid-angle energy:
|
|
41
|
+
* 32 → 0.995 ± 0.014, 64 → 0.998 ± 0.008 (at most 1.005), 196 → 1.001 ± 0.019 — 196 asks for a
|
|
42
|
+
* finer average than the 2x2 one, so it point-samples level 0 again. UE filters with 32 below
|
|
43
|
+
* roughness 0.1 and 64 above.
|
|
44
|
+
* @type {number}
|
|
45
|
+
*/
|
|
46
|
+
export const CONVOLVE_FROM_AVERAGES_SAMPLE_COUNT = 64;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Level 1 of the prefilter, by filtered importance sampling: each sample reads the plain average of
|
|
50
|
+
* level 0 whose texel is the solid angle the sample stands for, so a texel brighter than its
|
|
51
|
+
* neighbours is averaged into the coarser texel the sample reads instead of being hit or missed by
|
|
52
|
+
* the sample pattern. Point taps into level 0 — 196 of them over a cone of 639 level-0 texels — put
|
|
53
|
+
* one bright texel's share of level 1 anywhere from 0.79 to 1.21 of its energy, and an HDR sun
|
|
54
|
+
* came out 0.66% brighter than it went in.
|
|
55
|
+
*
|
|
56
|
+
* Level 1 only. The levels after it read the level above, already smooth at their sample spacing
|
|
57
|
+
* (see shader_convolve_reflection_map). Read from level 0 instead, their samples land on averages
|
|
58
|
+
* 8 to 16 texels a side, too coarse an octahedral grid to keep a bright texel's energy: ±11% at
|
|
59
|
+
* level 4, and a sky 11% brighter.
|
|
60
|
+
*
|
|
61
|
+
* @see https://developer.nvidia.com/gpugems/gpugems3/part-iii-rendering/chapter-20-gpu-based-importance-sampling
|
|
62
|
+
* @see https://github.com/EpicGames/UnrealEngine/blob/5.4/Engine/Shaders/Private/ReflectionEnvironmentShaders.usf FilterPS
|
|
63
|
+
* @see https://projects.blender.org/blender/blender/src/commit/866ef5b468589587395b1ee7480ea75b4c0fbc26/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_sphere_convolve_comp.glsl for the lobe
|
|
64
|
+
* @type {CodeChunk}
|
|
65
|
+
*/
|
|
66
|
+
const body = CodeChunk.from(
|
|
67
|
+
//language=WGSL
|
|
68
|
+
`
|
|
69
|
+
/// level 0 is the map's own, the levels below it are the averages
|
|
70
|
+
fn sample_source_level(direction: vec3<f32>, level: u32) -> vec4<f32> {
|
|
71
|
+
let uv = uv_octahedral_unit_encode(direction);
|
|
72
|
+
|
|
73
|
+
if (level == 0u) {
|
|
74
|
+
let size = f32(textureDimensions(level_0).x);
|
|
75
|
+
|
|
76
|
+
return textureSampleLevel(level_0, sEnvironment, texture_octahedral_skirt_uv(uv, size), 0.0);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let average_level = level - 1u;
|
|
80
|
+
let size = f32(textureDimensions(averages, average_level).x);
|
|
81
|
+
|
|
82
|
+
return textureSampleLevel(averages, sEnvironment, texture_octahedral_skirt_uv(uv, size), f32(average_level));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@compute @workgroup_size(16,16,1)
|
|
86
|
+
fn main(
|
|
87
|
+
@builtin(global_invocation_id) global_invocation_id : vec3<u32>,
|
|
88
|
+
){
|
|
89
|
+
let output_size = textureDimensions(output);
|
|
90
|
+
|
|
91
|
+
let output_texel_i = global_invocation_id.xy;
|
|
92
|
+
|
|
93
|
+
if(any(output_texel_i >= output_size)){
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* From mip to linear roughness (same as UI). */
|
|
98
|
+
let roughness = max(sphere_probe_lod_to_roughness(1.0), ${BSDF_ROUGHNESS_MINIMUM});
|
|
99
|
+
let cone_cos = cone_cosine_from_roughness(roughness);
|
|
100
|
+
|
|
101
|
+
// the skirt too: a skirt texel takes its interior twin's uv, so it is that texel, computed again
|
|
102
|
+
let out_uv = texture_octahedral_skirt_texel_uv(output_texel_i, output_size.x);
|
|
103
|
+
let out_direction = uv_octahedral_unit_decode( out_uv );
|
|
104
|
+
|
|
105
|
+
let basis = build_orthonormal_matrix_n(out_direction);
|
|
106
|
+
|
|
107
|
+
var weight_accum = 0.0;
|
|
108
|
+
var radiance_accum = vec4(0.0);
|
|
109
|
+
|
|
110
|
+
const sample_count = ${CONVOLVE_FROM_AVERAGES_SAMPLE_COUNT};
|
|
111
|
+
|
|
112
|
+
// uniform in the cone, so every sample stands for the same solid angle
|
|
113
|
+
let sample_solid_angle = 2.0 * PI * (1.0 - cone_cos) / f32(sample_count);
|
|
114
|
+
|
|
115
|
+
// level 0's octahedral square, n texels a side, spans 2 in octahedron coordinates; a texel there
|
|
116
|
+
// covers (2/n)^2 of it, and the octahedron's solid angle per unit area is |d|_1^3 along unit d
|
|
117
|
+
let level_0_interior = f32(textureDimensions(level_0).x) - 2.0;
|
|
118
|
+
let level_0_texel_area = 4.0 / (level_0_interior * level_0_interior);
|
|
119
|
+
|
|
120
|
+
let last_level = f32(textureNumLevels(averages));
|
|
121
|
+
|
|
122
|
+
for (var i = 0u; i < sample_count; i++) {
|
|
123
|
+
let rand = hammersley_2d(i, sample_count);
|
|
124
|
+
|
|
125
|
+
let in_direction = normalize(basis * uniform_sample_cone(rand, cone_cos));
|
|
126
|
+
|
|
127
|
+
let l1 = dot(abs(in_direction), vec3(1.0));
|
|
128
|
+
let texel_solid_angle = level_0_texel_area * l1 * l1 * l1;
|
|
129
|
+
|
|
130
|
+
let lod = clamp(0.5 * log2(sample_solid_angle / texel_solid_angle), 0.0, last_level);
|
|
131
|
+
let lod_floor = floor(lod);
|
|
132
|
+
let level = u32(lod_floor);
|
|
133
|
+
|
|
134
|
+
let radiance = mix(
|
|
135
|
+
sample_source_level(in_direction, level),
|
|
136
|
+
sample_source_level(in_direction, min(level + 1u, u32(last_level))),
|
|
137
|
+
lod - lod_floor
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
let weight = reflection_sample_weight(out_direction, in_direction, roughness);
|
|
141
|
+
|
|
142
|
+
radiance_accum += radiance * weight;
|
|
143
|
+
weight_accum += weight;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
let out_radiance = radiance_accum * safe_rcp(weight_accum);
|
|
147
|
+
|
|
148
|
+
textureStore(output, output_texel_i, out_radiance);
|
|
149
|
+
}
|
|
150
|
+
`, [
|
|
151
|
+
chunk_sphere_probe_lod_to_roughness,
|
|
152
|
+
chunk_hammersley_2d,
|
|
153
|
+
chunk_build_orthonormal_matrix_n,
|
|
154
|
+
chunk_safe_rcp,
|
|
155
|
+
chunk_uniform_sample_cone,
|
|
156
|
+
chunk_texture_octahedral_skirt_texel_uv,
|
|
157
|
+
chunk_texture_octahedral_skirt_uv,
|
|
158
|
+
chunk_uv_octahedral_unit_decode,
|
|
159
|
+
chunk_uv_octahedral_unit_encode,
|
|
160
|
+
chunk_reflection_sample_weight,
|
|
161
|
+
chunk_cone_cosine_from_roughness,
|
|
162
|
+
]
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
export const shader_convolve_reflection_map_from_averages = new ComputeShader(ShaderDescriptor.from({
|
|
166
|
+
label: 'Convolve reflection map from averages',
|
|
167
|
+
body,
|
|
168
|
+
resources
|
|
169
|
+
}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shader_downsample_environment_map.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/light/environment/shader_downsample_environment_map.js"],"names":[],"mappings":"AAoGA,8DAII;8BAjG0B,+BAA+B"}
|