@driftengine/core 4.2.1 → 4.4.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/CHANGELOG.json +272 -0
- package/README.md +1 -1
- package/dist/cinematic/cameraPath.d.ts +53 -0
- package/dist/cinematic/cameraPath.js +118 -0
- package/dist/environment/daylight.d.ts +77 -0
- package/dist/environment/daylight.js +77 -0
- package/dist/geometry/meshConcat.d.ts +9 -0
- package/dist/geometry/meshConcat.js +101 -0
- package/dist/index.d.ts +12 -2
- package/dist/index.js +14 -1
- package/dist/render/backend/createRenderer.d.ts +13 -1
- package/dist/render/backend/createRenderer.js +6 -5
- package/dist/render/backend/webgl2/renderer.d.ts +166 -1
- package/dist/render/backend/webgl2/renderer.js +424 -23
- package/dist/render/backend/webgpu/buffers.js +46 -8
- package/dist/render/backend/webgpu/depthPass.d.ts +81 -3
- package/dist/render/backend/webgpu/depthPass.js +75 -14
- package/dist/render/backend/webgpu/exposurePass.d.ts +39 -0
- package/dist/render/backend/webgpu/exposurePass.js +190 -0
- package/dist/render/backend/webgpu/fieldCompose.d.ts +11 -0
- package/dist/render/backend/webgpu/fieldCompose.js +13 -0
- package/dist/render/backend/webgpu/flatPass.d.ts +3 -1
- package/dist/render/backend/webgpu/flatPass.js +12 -5
- package/dist/render/backend/webgpu/particlePass.d.ts +8 -0
- package/dist/render/backend/webgpu/particlePass.js +1 -0
- package/dist/render/backend/webgpu/postPass.d.ts +12 -2
- package/dist/render/backend/webgpu/probeBake.d.ts +59 -3
- package/dist/render/backend/webgpu/probeBake.js +97 -7
- package/dist/render/backend/webgpu/probeTargets.d.ts +65 -0
- package/dist/render/backend/webgpu/probeTargets.js +180 -0
- package/dist/render/backend/webgpu/renderer.d.ts +176 -11
- package/dist/render/backend/webgpu/renderer.js +680 -250
- package/dist/render/backend/webgpu/scatterPass.js +7 -7
- package/dist/render/backend/webgpu/shaders/clusterBin.wgsl.d.ts +1 -1
- package/dist/render/backend/webgpu/skinPaletteRing.d.ts +4 -0
- package/dist/render/backend/webgpu/skinPaletteRing.js +9 -0
- package/dist/render/backend/webgpu/skyPass.js +5 -1
- package/dist/render/backend/webgpu/uniformRing.d.ts +31 -1
- package/dist/render/backend/webgpu/uniformRing.js +53 -3
- package/dist/render/bloomChain.d.ts +6 -0
- package/dist/render/bloomChain.js +8 -0
- package/dist/render/clusteredLights.d.ts +19 -11
- package/dist/render/clusteredLights.js +96 -45
- package/dist/render/cutoutCaster.d.ts +23 -0
- package/dist/render/cutoutCaster.js +20 -0
- package/dist/render/driftLight/bake.d.ts +34 -0
- package/dist/render/driftLight/bake.js +95 -0
- package/dist/render/driftLight/falloff.d.ts +16 -0
- package/dist/render/driftLight/falloff.js +23 -0
- package/dist/render/driftLight/layout.d.ts +59 -0
- package/dist/render/driftLight/layout.js +138 -0
- package/dist/render/driftLight/lightField.d.ts +108 -0
- package/dist/render/driftLight/lightField.js +179 -0
- package/dist/render/driftLight/uniforms.d.ts +28 -0
- package/dist/render/driftLight/uniforms.js +41 -0
- package/dist/render/driftLight/visibility.d.ts +18 -0
- package/dist/render/driftLight/visibility.js +47 -0
- package/dist/render/emptyTexture.d.ts +6 -0
- package/dist/render/emptyTexture.js +19 -0
- package/dist/render/exposurePass.d.ts +48 -0
- package/dist/render/exposurePass.js +172 -0
- package/dist/render/flameLight.d.ts +71 -0
- package/dist/render/flameLight.js +63 -0
- package/dist/render/gi/bounceLights.d.ts +34 -0
- package/dist/render/gi/bounceLights.js +66 -0
- package/dist/render/gi/fieldReach.d.ts +12 -0
- package/dist/render/gi/fieldReach.js +62 -0
- package/dist/render/gi/globalField.d.ts +8 -0
- package/dist/render/gi/globalField.js +31 -0
- package/dist/render/gi/probeTrace.d.ts +3 -1
- package/dist/render/gi/probeTrace.js +3 -1
- package/dist/render/gpuCapability.js +19 -4
- package/dist/render/lightBudget.d.ts +11 -0
- package/dist/render/lightBudget.js +11 -0
- package/dist/render/pointLightSelection.d.ts +16 -0
- package/dist/render/pointLightSelection.js +178 -83
- package/dist/render/probeGrid.d.ts +11 -1
- package/dist/render/probeGrid.js +5 -2
- package/dist/render/probeSweeps.d.ts +45 -0
- package/dist/render/probeSweeps.js +77 -0
- package/dist/render/reflectionProbe.d.ts +43 -1
- package/dist/render/reflectionProbe.js +17 -3
- package/dist/render/renderQuality.d.ts +3 -1
- package/dist/render/rendererHarness.d.ts +9 -0
- package/dist/render/rendererHarness.js +18 -3
- package/dist/render/sceneTarget.d.ts +20 -0
- package/dist/render/sceneTarget.js +62 -1
- package/dist/render/shaders/agx.d.ts +31 -0
- package/dist/render/shaders/agx.js +81 -0
- package/dist/render/shaders/ambientOcclusion.d.ts +24 -5
- package/dist/render/shaders/ambientOcclusion.js +120 -63
- package/dist/render/shaders/cutoutCoverage.d.ts +31 -0
- package/dist/render/shaders/cutoutCoverage.js +40 -0
- package/dist/render/shaders/depth.d.ts +25 -0
- package/dist/render/shaders/depth.js +69 -3
- package/dist/render/shaders/exposure.d.ts +52 -0
- package/dist/render/shaders/exposure.js +124 -0
- package/dist/render/shaders/filmLook.d.ts +47 -0
- package/dist/render/shaders/filmLook.js +98 -0
- package/dist/render/shaders/flat/directionalShadow.js +35 -4
- package/dist/render/shaders/flat/driftLight.d.ts +29 -0
- package/dist/render/shaders/flat/driftLight.js +75 -0
- package/dist/render/shaders/flat/index.js +5 -0
- package/dist/render/shaders/flat/lobes.js +13 -0
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +68 -9
- package/dist/render/shaders/flat/preamble.js +23 -5
- package/dist/render/shaders/flat/probeGrid.js +46 -3
- package/dist/render/shaders/generated/ambientOcclusion.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/ambientOcclusion.wgsl.js +1 -1
- package/dist/render/shaders/generated/depth.wgsl.d.ts +100 -0
- package/dist/render/shaders/generated/depth.wgsl.js +100 -0
- package/dist/render/shaders/generated/exposure.wgsl.d.ts +42 -0
- package/dist/render/shaders/generated/exposure.wgsl.js +48 -0
- package/dist/render/shaders/generated/flat.wgsl.d.ts +1800 -1344
- package/dist/render/shaders/generated/flat.wgsl.js +1816 -1360
- package/dist/render/shaders/generated/localExposure.wgsl.d.ts +18 -0
- package/dist/render/shaders/generated/localExposure.wgsl.js +24 -0
- package/dist/render/shaders/generated/rush.wgsl.d.ts +40 -10
- package/dist/render/shaders/generated/rush.wgsl.js +40 -10
- package/dist/render/shaders/generated/temporalResolve.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/temporalResolve.wgsl.js +1 -1
- package/dist/render/shaders/gi/driftLight.wgsl.d.ts +16 -0
- package/dist/render/shaders/gi/driftLight.wgsl.js +49 -0
- package/dist/render/shaders/gi/probeBake.wgsl.d.ts +29 -4
- package/dist/render/shaders/gi/probeBake.wgsl.js +190 -20
- package/dist/render/shaders/globalMedium.js +4 -0
- package/dist/render/shaders/localExposure.d.ts +35 -0
- package/dist/render/shaders/localExposure.js +138 -0
- package/dist/render/shaders/prefilter.d.ts +1 -1
- package/dist/render/shaders/prefilter.js +6 -0
- package/dist/render/shaders/rush.js +33 -5
- package/dist/render/shaders/tangentFrame.d.ts +1 -1
- package/dist/render/shaders/tangentFrame.js +15 -3
- package/dist/render/shaders/temporalResolve.js +39 -1
- package/dist/render/shadowCasters.d.ts +5 -3
- package/dist/render/shadowMap.js +13 -0
- package/dist/render/surfaceTexture.d.ts +5 -0
- package/package.json +6 -6
- package/scripts/gpuCompute.mjs +24 -3
- package/scripts/png.mjs +23 -12
- package/src/cinematic/cameraPath.ts +178 -0
- package/src/environment/daylight.ts +152 -0
- package/src/geometry/meshConcat.ts +100 -0
- package/src/index.ts +32 -0
- package/src/render/backend/createRenderer.ts +22 -6
- package/src/render/backend/webgl2/renderer.ts +462 -20
- package/src/render/backend/webgpu/buffers.ts +51 -8
- package/src/render/backend/webgpu/depthPass.ts +78 -10
- package/src/render/backend/webgpu/exposurePass.ts +234 -0
- package/src/render/backend/webgpu/fieldCompose.ts +15 -0
- package/src/render/backend/webgpu/flatPass.ts +10 -2
- package/src/render/backend/webgpu/particlePass.ts +9 -0
- package/src/render/backend/webgpu/probeBake.ts +143 -9
- package/src/render/backend/webgpu/probeTargets.ts +211 -0
- package/src/render/backend/webgpu/renderer.ts +807 -271
- package/src/render/backend/webgpu/scatterPass.ts +7 -7
- package/src/render/backend/webgpu/skinPaletteRing.ts +10 -0
- package/src/render/backend/webgpu/skyPass.ts +5 -1
- package/src/render/backend/webgpu/uniformRing.ts +49 -3
- package/src/render/bloomChain.ts +9 -0
- package/src/render/clusteredLights.ts +108 -63
- package/src/render/cutoutCaster.ts +44 -0
- package/src/render/driftLight/bake.ts +125 -0
- package/src/render/driftLight/falloff.ts +26 -0
- package/src/render/driftLight/layout.ts +177 -0
- package/src/render/driftLight/lightField.ts +227 -0
- package/src/render/driftLight/uniforms.ts +61 -0
- package/src/render/driftLight/visibility.ts +58 -0
- package/src/render/emptyTexture.ts +41 -0
- package/src/render/exposurePass.ts +186 -0
- package/src/render/flameLight.ts +135 -0
- package/src/render/gi/bounceLights.ts +68 -0
- package/src/render/gi/fieldReach.ts +74 -0
- package/src/render/gi/globalField.ts +39 -0
- package/src/render/gi/probeTrace.ts +3 -1
- package/src/render/gpuCapability.ts +18 -5
- package/src/render/lightBudget.ts +12 -0
- package/src/render/pointLightSelection.ts +191 -79
- package/src/render/probeGrid.ts +13 -2
- package/src/render/probeSweeps.ts +80 -0
- package/src/render/reflectionProbe.ts +54 -2
- package/src/render/renderQuality.ts +3 -1
- package/src/render/rendererHarness.ts +25 -3
- package/src/render/sceneTarget.ts +75 -1
- package/src/render/shaders/ambientOcclusion.ts +125 -63
- package/src/render/shaders/cutoutCoverage.ts +43 -0
- package/src/render/shaders/depth.ts +80 -3
- package/src/render/shaders/exposure.ts +128 -0
- package/src/render/shaders/filmLook.ts +111 -0
- package/src/render/shaders/flat/directionalShadow.ts +35 -4
- package/src/render/shaders/flat/driftLight.ts +75 -0
- package/src/render/shaders/flat/index.ts +5 -0
- package/src/render/shaders/flat/lobes.ts +13 -0
- package/src/render/shaders/flat/main.ts +68 -9
- package/src/render/shaders/flat/preamble.ts +23 -5
- package/src/render/shaders/flat/probeGrid.ts +46 -3
- package/src/render/shaders/generated/ambientOcclusion.wgsl.ts +1 -1
- package/src/render/shaders/generated/depth.wgsl.ts +103 -0
- package/src/render/shaders/generated/exposure.wgsl.ts +51 -0
- package/src/render/shaders/generated/flat.wgsl.ts +1816 -1360
- package/src/render/shaders/generated/localExposure.wgsl.ts +26 -0
- package/src/render/shaders/generated/rush.wgsl.ts +40 -10
- package/src/render/shaders/generated/temporalResolve.wgsl.ts +1 -1
- package/src/render/shaders/gi/driftLight.wgsl.ts +49 -0
- package/src/render/shaders/gi/probeBake.wgsl.ts +191 -20
- package/src/render/shaders/globalMedium.ts +4 -0
- package/src/render/shaders/localExposure.ts +154 -0
- package/src/render/shaders/prefilter.ts +6 -0
- package/src/render/shaders/rush.ts +33 -5
- package/src/render/shaders/tangentFrame.ts +15 -3
- package/src/render/shaders/temporalResolve.ts +39 -1
- package/src/render/shadowCasters.ts +5 -3
- package/src/render/shadowMap.ts +14 -0
- package/src/render/surfaceTexture.ts +5 -0
|
@@ -42,16 +42,28 @@
|
|
|
42
42
|
*/
|
|
43
43
|
|
|
44
44
|
import { PROBE_DIRECTIONS } from '../../gi/probeTrace.ts';
|
|
45
|
+
import { BOUNCE_LIGHT_FLOATS } from '../../gi/bounceLights.ts';
|
|
45
46
|
import { probeUpdateSchedule } from '../../gi/probeVolume.ts';
|
|
47
|
+
import { probesTheFieldReaches } from '../../gi/fieldReach.ts';
|
|
46
48
|
import { irradianceLevelFor } from '../../prefilterEnvMap.ts';
|
|
47
49
|
import {
|
|
48
50
|
BAKE_COUNTS,
|
|
51
|
+
BAKE_DRIFT_LIGHT,
|
|
52
|
+
BAKE_DRIFT_ORIGIN,
|
|
49
53
|
BAKE_EDGE,
|
|
54
|
+
BAKE_FALLOFF,
|
|
55
|
+
BAKE_LIGHT_COUNT,
|
|
56
|
+
BOUNCE_LIGHT_ROWS,
|
|
50
57
|
BAKE_FRAME,
|
|
51
58
|
BAKE_ORIGIN,
|
|
52
59
|
BAKE_SCHEDULED,
|
|
53
60
|
BAKE_SPACING,
|
|
61
|
+
BAKE_HISTORY,
|
|
54
62
|
BAKE_SKY_COLOUR,
|
|
63
|
+
BAKE_SKY_DEEP,
|
|
64
|
+
BAKE_SKY_DRAWN,
|
|
65
|
+
BAKE_SKY_SUN,
|
|
66
|
+
BAKE_SKY_TOP,
|
|
55
67
|
BAKE_SUN_COLOUR,
|
|
56
68
|
BAKE_SUN_DIR,
|
|
57
69
|
PROBE_BAKE_FLOATS,
|
|
@@ -97,6 +109,20 @@ const VISIBILITY_COMPUTE = 0x4;
|
|
|
97
109
|
*/
|
|
98
110
|
export const PROBES_PER_FRAME = 5;
|
|
99
111
|
|
|
112
|
+
/**
|
|
113
|
+
* How much of what a probe held each refresh keeps, once the grid has been traced whole.
|
|
114
|
+
*
|
|
115
|
+
* Four fifths, so a probe is about the mean of its last five refreshes. The directions no longer
|
|
116
|
+
* turn, so a still scene gives the same estimate every time and this has nothing to average there;
|
|
117
|
+
* what it smooths is a light that moves, whose sun crosses from one direction of the set to the next
|
|
118
|
+
* in steps. What it gives up is how fast the bounce follows: a fifth of the way each time a probe
|
|
119
|
+
* comes round, which with 64
|
|
120
|
+
* probes at five a frame is a time constant of about half a second at 120 frames and a second at
|
|
121
|
+
* 60. It is counted in refreshes rather than seconds, because the renderer is given no clock; what
|
|
122
|
+
* would make it wrong is a scene whose light jumps and has to be followed at once.
|
|
123
|
+
*/
|
|
124
|
+
export const PROBE_HISTORY = 0.8;
|
|
125
|
+
|
|
100
126
|
export interface ProbeBakeTargets {
|
|
101
127
|
/** The array the shading samples, whose irradiance level this writes. */
|
|
102
128
|
readonly array: GPUTexture;
|
|
@@ -108,6 +134,27 @@ export interface ProbeBakeTargets {
|
|
|
108
134
|
readonly format: GPUTextureFormat;
|
|
109
135
|
}
|
|
110
136
|
|
|
137
|
+
/**
|
|
138
|
+
* A DriftLight field as the bake reads it: its two volumes and its two uniform rows, resolved for a
|
|
139
|
+
* probe by `resolveDriftLight` (radius zero, every fixed light through the volume).
|
|
140
|
+
*/
|
|
141
|
+
/**
|
|
142
|
+
* The frame's exact lights as the bake lights a hit with them: `resolveBounceLights`'s packing,
|
|
143
|
+
* how many it packed, and the frame's falloff code.
|
|
144
|
+
*/
|
|
145
|
+
export interface ProbeBakeLamps {
|
|
146
|
+
readonly data: Float32Array;
|
|
147
|
+
readonly count: number;
|
|
148
|
+
readonly falloff: number;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export interface ProbeBakeLightField {
|
|
152
|
+
readonly index: GPUTextureView;
|
|
153
|
+
readonly atlas: GPUTextureView;
|
|
154
|
+
readonly light: ArrayLike<number>;
|
|
155
|
+
readonly origin: ArrayLike<number>;
|
|
156
|
+
}
|
|
157
|
+
|
|
111
158
|
export class ProbeBaker {
|
|
112
159
|
private readonly device: GPUDevice;
|
|
113
160
|
private readonly targets: ProbeBakeTargets;
|
|
@@ -124,15 +171,22 @@ export class ProbeBaker {
|
|
|
124
171
|
private readonly marchBuffer: GPUBuffer;
|
|
125
172
|
private readonly bakeBuffer: GPUBuffer;
|
|
126
173
|
private readonly volumeBuffer: GPUBuffer;
|
|
174
|
+
private readonly lampBuffer: GPUBuffer;
|
|
127
175
|
private readonly scheduleBuffer: GPUBuffer;
|
|
128
176
|
private readonly rayBuffer: GPUBuffer;
|
|
129
177
|
private readonly irradianceBuffer: GPUBuffer;
|
|
130
178
|
private readonly blitParams: GPUBuffer;
|
|
131
179
|
|
|
132
180
|
private readonly blitGroup: GPUBindGroup;
|
|
133
|
-
/** Rebuilt only when the field it was built against
|
|
181
|
+
/** Rebuilt only when the field or the light field's volumes it was built against change. */
|
|
134
182
|
private computeGroup: GPUBindGroup | null = null;
|
|
135
183
|
private boundTo: PassDistanceField | null = null;
|
|
184
|
+
private boundAtlas: GPUTextureView | null = null;
|
|
185
|
+
/** DriftLight's volumes while a scene has none whole: one texel each, never read while off. */
|
|
186
|
+
private readonly emptyIndex: GPUTexture;
|
|
187
|
+
private readonly emptyAtlas: GPUTexture;
|
|
188
|
+
private readonly emptyIndexView: GPUTextureView;
|
|
189
|
+
private readonly emptyAtlasView: GPUTextureView;
|
|
136
190
|
|
|
137
191
|
private readonly marchScratch = new Float32Array(PROBE_MARCH_FLOATS);
|
|
138
192
|
private readonly marchInts = new Uint32Array(this.marchScratch.buffer);
|
|
@@ -200,8 +254,26 @@ export class ProbeBaker {
|
|
|
200
254
|
},
|
|
201
255
|
{ binding: 9, visibility: VISIBILITY_COMPUTE, sampler: { type: 'filtering' } },
|
|
202
256
|
{ binding: 10, visibility: VISIBILITY_COMPUTE, buffer: storageRead },
|
|
257
|
+
{
|
|
258
|
+
binding: 11,
|
|
259
|
+
visibility: VISIBILITY_COMPUTE,
|
|
260
|
+
texture: { sampleType: 'uint', viewDimension: '3d' },
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
binding: 12,
|
|
264
|
+
visibility: VISIBILITY_COMPUTE,
|
|
265
|
+
texture: { sampleType: 'float', viewDimension: '3d' },
|
|
266
|
+
},
|
|
267
|
+
{ binding: 13, visibility: VISIBILITY_COMPUTE, sampler: { type: 'filtering' } },
|
|
268
|
+
{ binding: 14, visibility: VISIBILITY_COMPUTE, buffer: { type: 'uniform' } },
|
|
203
269
|
],
|
|
204
270
|
});
|
|
271
|
+
const emptyVolume = (label: string, format: GPUTextureFormat): GPUTexture =>
|
|
272
|
+
device.createTexture({ label, size: [1, 1, 1], dimension: '3d', format, usage: 0x4 });
|
|
273
|
+
this.emptyIndex = emptyVolume('probe bake.emptyDriftLightIndex', 'r32uint');
|
|
274
|
+
this.emptyAtlas = emptyVolume('probe bake.emptyDriftLightAtlas', 'rgba16float');
|
|
275
|
+
this.emptyIndexView = this.emptyIndex.createView({ dimension: '3d' });
|
|
276
|
+
this.emptyAtlasView = this.emptyAtlas.createView({ dimension: '3d' });
|
|
205
277
|
const computeLayout = device.createPipelineLayout({
|
|
206
278
|
bindGroupLayouts: [this.computeLayout],
|
|
207
279
|
});
|
|
@@ -258,6 +330,7 @@ export class ProbeBaker {
|
|
|
258
330
|
this.marchBuffer = this.uniform('march', PROBE_MARCH_FLOATS * 4);
|
|
259
331
|
this.bakeBuffer = this.uniform('bake', PROBE_BAKE_FLOATS * 4);
|
|
260
332
|
this.volumeBuffer = this.uniform('volume', PROBE_VOLUME_FLOATS * 4);
|
|
333
|
+
this.lampBuffer = this.uniform('lamps', BOUNCE_LIGHT_ROWS * 16);
|
|
261
334
|
this.scheduleBuffer = this.storage('schedule', PROBES_PER_FRAME * 4);
|
|
262
335
|
this.rayBuffer = this.storage(
|
|
263
336
|
'rays',
|
|
@@ -419,6 +492,18 @@ export class ProbeBaker {
|
|
|
419
492
|
return true;
|
|
420
493
|
}
|
|
421
494
|
|
|
495
|
+
/**
|
|
496
|
+
* Whether the trace has written `layer`'s irradiance, after which that level is the trace's alone.
|
|
497
|
+
*
|
|
498
|
+
* A rasterised bake of a traced layer refreshes only its roughness chain. Two writers of one
|
|
499
|
+
* level is a layer that jumps to the raster's answer and is pulled back over the next refreshes,
|
|
500
|
+
* which on screen is light sweeping across a scene a probe at a time. What it gives up is the
|
|
501
|
+
* raster's own diffuse answer for that layer, which the trace replaces within a few frames anyway.
|
|
502
|
+
*/
|
|
503
|
+
traced(layer: number): boolean {
|
|
504
|
+
return this.filled[layer] === 1;
|
|
505
|
+
}
|
|
506
|
+
|
|
422
507
|
/**
|
|
423
508
|
* Refresh this frame's share of the grid.
|
|
424
509
|
*
|
|
@@ -429,18 +514,29 @@ export class ProbeBaker {
|
|
|
429
514
|
field: PassDistanceField,
|
|
430
515
|
grid: ProbeGrid,
|
|
431
516
|
seeded: boolean,
|
|
517
|
+
lightField: ProbeBakeLightField | null,
|
|
518
|
+
lamps: ProbeBakeLamps,
|
|
432
519
|
sun: {
|
|
433
520
|
readonly direction: ArrayLike<number>;
|
|
434
521
|
readonly colour: ArrayLike<number>;
|
|
435
|
-
|
|
522
|
+
/** Outside, flat, for a frame that drew no sky: the environment's ambient. */
|
|
523
|
+
readonly ambient: ArrayLike<number>;
|
|
524
|
+
/** The sky the frame drew, which an escaping ray reads when there is one. */
|
|
525
|
+
readonly sky: {
|
|
526
|
+
readonly top: ArrayLike<number>;
|
|
527
|
+
readonly horizon: ArrayLike<number>;
|
|
528
|
+
readonly deep: ArrayLike<number>;
|
|
529
|
+
readonly sunDir: ArrayLike<number>;
|
|
530
|
+
} | null;
|
|
436
531
|
},
|
|
437
532
|
): number {
|
|
438
533
|
if (this.layers !== grid.layers) this.setGrid(grid.layers);
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
this.frameIndex,
|
|
534
|
+
/* A probe the field does not reach keeps what it has. See `gi/fieldReach.ts`. */
|
|
535
|
+
const scheduled = probesTheFieldReaches(
|
|
536
|
+
grid,
|
|
443
537
|
this.scheduleScratch,
|
|
538
|
+
probeUpdateSchedule(grid.layers, PROBES_PER_FRAME, this.frameIndex, this.scheduleScratch),
|
|
539
|
+
field.outerBounds,
|
|
444
540
|
);
|
|
445
541
|
if (scheduled === 0) return 0;
|
|
446
542
|
|
|
@@ -476,8 +572,19 @@ export class ProbeBaker {
|
|
|
476
572
|
this.volumeScratch[VOLUME_COUNTS + axis] = grid.counts[axis] as number;
|
|
477
573
|
}
|
|
478
574
|
this.bakeScratch[BAKE_EDGE] = this.levelEdge;
|
|
479
|
-
|
|
575
|
+
/*
|
|
576
|
+
* **The same directions at every refresh.** The set used to turn with the frame so a probe
|
|
577
|
+
* would converge, but only a running mean converges and a blended history never stops moving:
|
|
578
|
+
* every refresh was a new estimate a few per cent from the last, and a courtyard held still
|
|
579
|
+
* flickered, 77% of its pixels moving by more than 3 levels over a dozen frames against 2.5%
|
|
580
|
+
* rasterised. Held, it is 4.8%. What it gives up is the quadrature error of one set of 256
|
|
581
|
+
* directions, which stays where it is rather than averaging away: the held frame read 2.5 levels
|
|
582
|
+
* brighter than the turning one's mean. What would make it wrong is a small bright source that
|
|
583
|
+
* falls between the directions of every probe that sees it.
|
|
584
|
+
*/
|
|
585
|
+
this.bakeScratch[BAKE_FRAME] = 0;
|
|
480
586
|
this.bakeScratch[BAKE_SCHEDULED] = scheduled;
|
|
587
|
+
this.bakeScratch[BAKE_HISTORY] = this.complete ? PROBE_HISTORY : 0;
|
|
481
588
|
/*
|
|
482
589
|
* **The sun, because the trace shades what it hits rather than reading a capture.** That is
|
|
483
590
|
* the whole of what a traced grid buys over a rasterised one: a light that moves changes the
|
|
@@ -486,7 +593,24 @@ export class ProbeBaker {
|
|
|
486
593
|
for (let axis = 0; axis < 3; axis += 1) {
|
|
487
594
|
this.bakeScratch[BAKE_SUN_DIR + axis] = sun.direction[axis] ?? 0;
|
|
488
595
|
this.bakeScratch[BAKE_SUN_COLOUR + axis] = sun.colour[axis] ?? 0;
|
|
489
|
-
|
|
596
|
+
const sky = sun.sky;
|
|
597
|
+
this.bakeScratch[BAKE_SKY_COLOUR + axis] =
|
|
598
|
+
sky === null ? (sun.ambient[axis] ?? 0) : (sky.horizon[axis] ?? 0);
|
|
599
|
+
this.bakeScratch[BAKE_SKY_TOP + axis] = sky === null ? 0 : (sky.top[axis] ?? 0);
|
|
600
|
+
this.bakeScratch[BAKE_SKY_DEEP + axis] = sky === null ? 0 : (sky.deep[axis] ?? 0);
|
|
601
|
+
this.bakeScratch[BAKE_SKY_SUN + axis] = sky === null ? 0 : (sky.sunDir[axis] ?? 0);
|
|
602
|
+
}
|
|
603
|
+
this.bakeScratch[BAKE_SKY_DRAWN] = sun.sky === null ? 0 : 1;
|
|
604
|
+
/* The summed light of the scene's fixed lights, or zeros that switch it off. */
|
|
605
|
+
for (let lane = 0; lane < 4; lane += 1) {
|
|
606
|
+
this.bakeScratch[BAKE_DRIFT_LIGHT + lane] = lightField?.light[lane] ?? 0;
|
|
607
|
+
this.bakeScratch[BAKE_DRIFT_ORIGIN + lane] = lightField?.origin[lane] ?? 0;
|
|
608
|
+
}
|
|
609
|
+
/* And the frame's exact lights, which light what a ray strikes as they light the frame. */
|
|
610
|
+
this.bakeScratch[BAKE_LIGHT_COUNT] = lamps.count;
|
|
611
|
+
this.bakeScratch[BAKE_FALLOFF] = lamps.falloff;
|
|
612
|
+
if (lamps.count > 0) {
|
|
613
|
+
queue.writeBuffer(this.lampBuffer, 0, lamps.data, 0, lamps.count * BOUNCE_LIGHT_FLOATS);
|
|
490
614
|
}
|
|
491
615
|
queue.writeBuffer(this.bakeBuffer, 0, this.bakeScratch);
|
|
492
616
|
|
|
@@ -501,7 +625,8 @@ export class ProbeBaker {
|
|
|
501
625
|
this.volumeScratch[VOLUME_RADIANCE_LEVEL] = 0;
|
|
502
626
|
queue.writeBuffer(this.volumeBuffer, 0, this.volumeScratch);
|
|
503
627
|
|
|
504
|
-
|
|
628
|
+
const atlas = lightField?.atlas ?? this.emptyAtlasView;
|
|
629
|
+
if (this.boundTo !== field || this.boundAtlas !== atlas || this.computeGroup === null) {
|
|
505
630
|
this.computeGroup = this.device.createBindGroup({
|
|
506
631
|
label: 'probe bake',
|
|
507
632
|
layout: this.computeLayout,
|
|
@@ -517,9 +642,14 @@ export class ProbeBaker {
|
|
|
517
642
|
{ binding: 8, resource: this.targets.view },
|
|
518
643
|
{ binding: 9, resource: this.targets.sampler },
|
|
519
644
|
{ binding: 10, resource: { buffer: field.albedo } },
|
|
645
|
+
{ binding: 11, resource: lightField?.index ?? this.emptyIndexView },
|
|
646
|
+
{ binding: 12, resource: atlas },
|
|
647
|
+
{ binding: 13, resource: this.targets.sampler },
|
|
648
|
+
{ binding: 14, resource: { buffer: this.lampBuffer } },
|
|
520
649
|
],
|
|
521
650
|
});
|
|
522
651
|
this.boundTo = field;
|
|
652
|
+
this.boundAtlas = atlas;
|
|
523
653
|
}
|
|
524
654
|
|
|
525
655
|
const pass = encoder.beginComputePass({
|
|
@@ -579,9 +709,13 @@ export class ProbeBaker {
|
|
|
579
709
|
this.rayBuffer.destroy();
|
|
580
710
|
this.irradianceBuffer.destroy();
|
|
581
711
|
this.blitParams.destroy();
|
|
712
|
+
this.lampBuffer.destroy();
|
|
713
|
+
this.emptyIndex.destroy();
|
|
714
|
+
this.emptyAtlas.destroy();
|
|
582
715
|
this.views = [];
|
|
583
716
|
this.computeGroup = null;
|
|
584
717
|
this.boundTo = null;
|
|
718
|
+
this.boundAtlas = null;
|
|
585
719
|
}
|
|
586
720
|
}
|
|
587
721
|
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a probe bake draws into and reads from, made once and kept rather than made per bake.
|
|
3
|
+
*
|
|
4
|
+
* **A bake is a per-frame path now, and it was written as a one-off.** It created a depth and a
|
|
5
|
+
* multisampled colour texture per call and destroyed them after, a view per face, and for each
|
|
6
|
+
* probe it completed a view and a bind group per level and face of the blur and the convolution.
|
|
7
|
+
* That was a load-time cost while a scene baked its probes once. Since a bake can be spread a few
|
|
8
|
+
* faces a frame (`ProbeBakeOptions.faces`) and a crossfading grid re-bakes as the light moves, it
|
|
9
|
+
* is every frame, and on the native host, where each object costs most, a courtyard with the sun
|
|
10
|
+
* moving spent a third of its CPU in bakes. The capture cube lives as long as the renderer, so
|
|
11
|
+
* every view of it can too; the array is replaced only when the grid is, and `forgetArray` drops
|
|
12
|
+
* what was made against the old one.
|
|
13
|
+
*
|
|
14
|
+
* What it gives up is holding the attachments between bakes: a depth and a colour texture the size
|
|
15
|
+
* of one face, at the scene's sample count, kept for as long as the renderer is.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { DEPTH_FORMAT } from '../../depthConvention.ts';
|
|
19
|
+
import {
|
|
20
|
+
PREFILTER_SAMPLER_BINDING,
|
|
21
|
+
PREFILTER_TEXTURE_BINDING,
|
|
22
|
+
PREFILTER_UNIFORM_BINDING,
|
|
23
|
+
PROBE_FACE_BASIS,
|
|
24
|
+
PROBE_MIP_UNIFORM_SIZE,
|
|
25
|
+
probeLevels,
|
|
26
|
+
} from './probePass.ts';
|
|
27
|
+
|
|
28
|
+
export class ProbeTargets {
|
|
29
|
+
private readonly device: GPUDevice;
|
|
30
|
+
private readonly probe: GPUTexture;
|
|
31
|
+
private readonly size: number;
|
|
32
|
+
/** Level 0 of each face, which the bake's passes resolve into. */
|
|
33
|
+
readonly faceViews: readonly GPUTextureView[];
|
|
34
|
+
/** The whole chain as a cube, which the convolution samples. */
|
|
35
|
+
readonly wholeCube: GPUTextureView;
|
|
36
|
+
/** Level `l - 1` as a cube, which the blur into level `l` samples; index 0 unused. */
|
|
37
|
+
private readonly chainSources: GPUTextureView[] = [];
|
|
38
|
+
/** Level `l` of face `f`, at `l * faces + f`, which the blur writes. */
|
|
39
|
+
private readonly chainTargets: GPUTextureView[] = [];
|
|
40
|
+
private depth: GPUTexture | null = null;
|
|
41
|
+
private depthView: GPUTextureView | null = null;
|
|
42
|
+
private colorMsaa: GPUTexture | null = null;
|
|
43
|
+
private colorMsaaView: GPUTextureView | null = null;
|
|
44
|
+
private attachmentKey = '';
|
|
45
|
+
/* The array's level-and-layer views and the groups over this ring's buffer, each made once. */
|
|
46
|
+
private readonly arrayViews = new Map<number, GPUTextureView>();
|
|
47
|
+
private chainGroups: GPUBindGroup[] = [];
|
|
48
|
+
private prefilterGroup: GPUBindGroup | null = null;
|
|
49
|
+
private groupsBuffer: GPUBuffer | null = null;
|
|
50
|
+
|
|
51
|
+
constructor(device: GPUDevice, probe: GPUTexture, size: number) {
|
|
52
|
+
this.device = device;
|
|
53
|
+
this.probe = probe;
|
|
54
|
+
this.size = size;
|
|
55
|
+
const faces: GPUTextureView[] = [];
|
|
56
|
+
for (let face = 0; face < PROBE_FACE_BASIS.length; face++) {
|
|
57
|
+
faces.push(this.faceLevel(face, 0));
|
|
58
|
+
}
|
|
59
|
+
this.faceViews = faces;
|
|
60
|
+
this.wholeCube = probe.createView({ dimension: 'cube' });
|
|
61
|
+
const levels = probeLevels(size);
|
|
62
|
+
for (let level = 1; level < levels; level++) {
|
|
63
|
+
this.chainSources[level] = probe.createView({
|
|
64
|
+
dimension: 'cube',
|
|
65
|
+
baseMipLevel: level - 1,
|
|
66
|
+
mipLevelCount: 1,
|
|
67
|
+
});
|
|
68
|
+
for (let face = 0; face < PROBE_FACE_BASIS.length; face++) {
|
|
69
|
+
this.chainTargets[level * PROBE_FACE_BASIS.length + face] = this.faceLevel(face, level);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The depth and, above one sample, the multisampled colour a face is drawn into: made on the first
|
|
76
|
+
* bake and again only if the sample count or the format has changed since.
|
|
77
|
+
*/
|
|
78
|
+
attachments(
|
|
79
|
+
samples: number,
|
|
80
|
+
format: GPUTextureFormat,
|
|
81
|
+
): { depth: GPUTextureView; colorMsaa: GPUTextureView | null } {
|
|
82
|
+
const key = `${samples}:${format}`;
|
|
83
|
+
if (this.depthView === null || key !== this.attachmentKey) {
|
|
84
|
+
this.destroyAttachments();
|
|
85
|
+
this.attachmentKey = key;
|
|
86
|
+
this.depth = this.device.createTexture({
|
|
87
|
+
label: 'probe.depth',
|
|
88
|
+
size: [this.size, this.size],
|
|
89
|
+
format: DEPTH_FORMAT,
|
|
90
|
+
/* The world's sample count, because a bake draws the world with the world's pipelines. */
|
|
91
|
+
sampleCount: samples,
|
|
92
|
+
usage: 0x10, // RENDER_ATTACHMENT
|
|
93
|
+
});
|
|
94
|
+
this.depthView = this.depth.createView();
|
|
95
|
+
this.colorMsaa =
|
|
96
|
+
samples > 1
|
|
97
|
+
? this.device.createTexture({
|
|
98
|
+
label: 'probe.colorMsaa',
|
|
99
|
+
size: [this.size, this.size],
|
|
100
|
+
format,
|
|
101
|
+
sampleCount: samples,
|
|
102
|
+
usage: 0x10, // RENDER_ATTACHMENT
|
|
103
|
+
})
|
|
104
|
+
: null;
|
|
105
|
+
this.colorMsaaView = this.colorMsaa?.createView() ?? null;
|
|
106
|
+
}
|
|
107
|
+
return { depth: this.depthView as GPUTextureView, colorMsaa: this.colorMsaaView };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** The blur's target for `level` of `face`. */
|
|
111
|
+
chainTarget(level: number, face: number): GPUTextureView {
|
|
112
|
+
return this.chainTargets[level * PROBE_FACE_BASIS.length + face] as GPUTextureView;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The blur's group for `level`, over `buffer`: the uniforms, the level above as a cube, the
|
|
117
|
+
* sampler. Remade only if the ring's buffer has been replaced.
|
|
118
|
+
*/
|
|
119
|
+
chainGroup(
|
|
120
|
+
layout: GPUBindGroupLayout,
|
|
121
|
+
buffer: GPUBuffer,
|
|
122
|
+
sampler: GPUSampler,
|
|
123
|
+
level: number,
|
|
124
|
+
): GPUBindGroup {
|
|
125
|
+
this.forBuffer(buffer);
|
|
126
|
+
let group = this.chainGroups[level];
|
|
127
|
+
if (group === undefined) {
|
|
128
|
+
group = this.device.createBindGroup({
|
|
129
|
+
layout,
|
|
130
|
+
entries: [
|
|
131
|
+
{ binding: 0, resource: { buffer, size: PROBE_MIP_UNIFORM_SIZE } },
|
|
132
|
+
{ binding: 1, resource: this.chainSources[level] as GPUTextureView },
|
|
133
|
+
{ binding: 2, resource: sampler },
|
|
134
|
+
],
|
|
135
|
+
});
|
|
136
|
+
this.chainGroups[level] = group;
|
|
137
|
+
}
|
|
138
|
+
return group;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** The convolution's group over `buffer`: the uniforms, the whole chain, the sampler. */
|
|
142
|
+
prefilterGroupFor(
|
|
143
|
+
layout: GPUBindGroupLayout,
|
|
144
|
+
buffer: GPUBuffer,
|
|
145
|
+
sampler: GPUSampler,
|
|
146
|
+
): GPUBindGroup {
|
|
147
|
+
this.forBuffer(buffer);
|
|
148
|
+
this.prefilterGroup ??= this.device.createBindGroup({
|
|
149
|
+
layout,
|
|
150
|
+
entries: [
|
|
151
|
+
{ binding: PREFILTER_UNIFORM_BINDING, resource: { buffer, size: PROBE_MIP_UNIFORM_SIZE } },
|
|
152
|
+
{ binding: PREFILTER_TEXTURE_BINDING, resource: this.wholeCube },
|
|
153
|
+
{ binding: PREFILTER_SAMPLER_BINDING, resource: sampler },
|
|
154
|
+
],
|
|
155
|
+
});
|
|
156
|
+
return this.prefilterGroup;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** `level` of `layer` of the grid's array, which the convolution writes. */
|
|
160
|
+
arrayView(array: GPUTexture, layer: number, level: number): GPUTextureView {
|
|
161
|
+
const key = layer * 32 + level;
|
|
162
|
+
let view = this.arrayViews.get(key);
|
|
163
|
+
if (view === undefined) {
|
|
164
|
+
view = array.createView({
|
|
165
|
+
dimension: '2d',
|
|
166
|
+
baseArrayLayer: layer,
|
|
167
|
+
arrayLayerCount: 1,
|
|
168
|
+
baseMipLevel: level,
|
|
169
|
+
mipLevelCount: 1,
|
|
170
|
+
});
|
|
171
|
+
this.arrayViews.set(key, view);
|
|
172
|
+
}
|
|
173
|
+
return view;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** The grid's array has been replaced: nothing made against the old one may be handed out. */
|
|
177
|
+
forgetArray(): void {
|
|
178
|
+
this.arrayViews.clear();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
dispose(): void {
|
|
182
|
+
this.destroyAttachments();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private faceLevel(face: number, level: number): GPUTextureView {
|
|
186
|
+
return this.probe.createView({
|
|
187
|
+
dimension: '2d',
|
|
188
|
+
baseArrayLayer: face,
|
|
189
|
+
arrayLayerCount: 1,
|
|
190
|
+
baseMipLevel: level,
|
|
191
|
+
mipLevelCount: 1,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** Groups hold the ring's buffer, so a replaced buffer makes every one of them stale. */
|
|
196
|
+
private forBuffer(buffer: GPUBuffer): void {
|
|
197
|
+
if (buffer === this.groupsBuffer) return;
|
|
198
|
+
this.groupsBuffer = buffer;
|
|
199
|
+
this.chainGroups = [];
|
|
200
|
+
this.prefilterGroup = null;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private destroyAttachments(): void {
|
|
204
|
+
this.depth?.destroy();
|
|
205
|
+
this.colorMsaa?.destroy();
|
|
206
|
+
this.depth = null;
|
|
207
|
+
this.depthView = null;
|
|
208
|
+
this.colorMsaa = null;
|
|
209
|
+
this.colorMsaaView = null;
|
|
210
|
+
}
|
|
211
|
+
}
|