@driftengine/core 4.8.7 → 4.9.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 +61 -0
- package/README.md +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +7 -0
- package/dist/render/atmosphere.d.ts +13 -0
- package/dist/render/atmosphere.js +9 -1
- package/dist/render/backend/api.d.ts +9 -1
- package/dist/render/backend/createRenderer.d.ts +6 -0
- package/dist/render/backend/createRenderer.js +4 -1
- package/dist/render/backend/webgl2/renderer.d.ts +90 -2
- package/dist/render/backend/webgl2/renderer.js +297 -16
- package/dist/render/backend/webgpu/device.d.ts +7 -1
- package/dist/render/backend/webgpu/device.js +31 -3
- package/dist/render/backend/webgpu/flatPass.d.ts +16 -1
- package/dist/render/backend/webgpu/flatPass.js +58 -26
- package/dist/render/backend/webgpu/postPass.d.ts +51 -9
- package/dist/render/backend/webgpu/postPass.js +16 -2
- package/dist/render/backend/webgpu/reflectionSurfacePass.d.ts +30 -0
- package/dist/render/backend/webgpu/reflectionSurfacePass.js +100 -0
- package/dist/render/backend/webgpu/renderer.d.ts +131 -2
- package/dist/render/backend/webgpu/renderer.js +586 -70
- package/dist/render/displayRange.d.ts +31 -0
- package/dist/render/displayRange.js +47 -0
- package/dist/render/filmicCurve.d.ts +74 -0
- package/dist/render/filmicCurve.js +203 -0
- package/dist/render/fog.d.ts +4 -1
- package/dist/render/fog.js +12 -2
- package/dist/render/frame/resources.d.ts +2 -2
- package/dist/render/frame/resources.js +6 -0
- package/dist/render/frameReflections.d.ts +40 -0
- package/dist/render/frameReflections.js +48 -0
- package/dist/render/pass.d.ts +49 -0
- package/dist/render/reflectionSurfaceTargets.d.ts +28 -0
- package/dist/render/reflectionSurfaceTargets.js +99 -0
- package/dist/render/renderQuality.d.ts +39 -8
- package/dist/render/renderQuality.js +4 -0
- package/dist/render/sceneTarget.d.ts +5 -0
- package/dist/render/sceneTarget.js +24 -1
- package/dist/render/screenSpaceReflection.d.ts +5 -0
- package/dist/render/screenSpaceReflection.js +5 -0
- package/dist/render/shaders/arcane.d.ts +1 -1
- package/dist/render/shaders/bolt.d.ts +1 -1
- package/dist/render/shaders/caustics.d.ts +1 -1
- package/dist/render/shaders/film.d.ts +1 -1
- package/dist/render/shaders/filmLook.js +2 -1
- package/dist/render/shaders/fire.d.ts +1 -1
- package/dist/render/shaders/flat/glassTint.d.ts +1 -1
- package/dist/render/shaders/flat/glassTint.js +2 -0
- package/dist/render/shaders/flat/index.d.ts +10 -0
- package/dist/render/shaders/flat/index.js +6 -0
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +28 -0
- package/dist/render/shaders/flat/overlay.d.ts +16 -0
- package/dist/render/shaders/flat/overlay.js +117 -0
- package/dist/render/shaders/flat/pointShadow.js +1 -1
- package/dist/render/shaders/flat/reflectionSurface.d.ts +21 -0
- package/dist/render/shaders/flat/reflectionSurface.js +61 -0
- package/dist/render/shaders/fog.d.ts +1 -1
- package/dist/render/shaders/fog.js +9 -2
- package/dist/render/shaders/fringe.d.ts +29 -0
- package/dist/render/shaders/fringe.js +55 -0
- package/dist/render/shaders/generated/arcane.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/arcane.wgsl.js +1 -1
- package/dist/render/shaders/generated/bolt.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/bolt.wgsl.js +1 -1
- package/dist/render/shaders/generated/caustics.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/caustics.wgsl.js +1 -1
- package/dist/render/shaders/generated/film.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/film.wgsl.js +1 -1
- package/dist/render/shaders/generated/fire.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/fire.wgsl.js +1 -1
- package/dist/render/shaders/generated/flat.wgsl.d.ts +176 -16
- package/dist/render/shaders/generated/flat.wgsl.js +214 -54
- package/dist/render/shaders/generated/line.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/line.wgsl.js +1 -1
- package/dist/render/shaders/generated/particle.wgsl.d.ts +4 -4
- package/dist/render/shaders/generated/particle.wgsl.js +4 -4
- package/dist/render/shaders/generated/rush.wgsl.d.ts +56 -26
- package/dist/render/shaders/generated/rush.wgsl.js +56 -26
- package/dist/render/shaders/generated/scatter.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/scatter.wgsl.js +1 -1
- package/dist/render/shaders/generated/smoke.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/smoke.wgsl.js +1 -1
- package/dist/render/shaders/generated/ssrResolve.wgsl.d.ts +29 -0
- package/dist/render/shaders/generated/ssrResolve.wgsl.js +29 -0
- package/dist/render/shaders/generated/ssrTrace.wgsl.d.ts +22 -2
- package/dist/render/shaders/generated/ssrTrace.wgsl.js +22 -2
- package/dist/render/shaders/generated/water.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/water.wgsl.js +1 -1
- package/dist/render/shaders/gpudriven/fog.wgsl.d.ts +2 -2
- package/dist/render/shaders/gpudriven/fog.wgsl.js +11 -3
- package/dist/render/shaders/line.d.ts +1 -1
- package/dist/render/shaders/particle.d.ts +3 -3
- package/dist/render/shaders/rush.js +54 -11
- package/dist/render/shaders/scatter.d.ts +1 -1
- package/dist/render/shaders/smoke.d.ts +1 -1
- package/dist/render/shaders/ssrResolve.d.ts +17 -0
- package/dist/render/shaders/ssrResolve.js +51 -0
- package/dist/render/shaders/ssrTrace.js +26 -0
- package/dist/render/shaders/water.d.ts +1 -1
- package/dist/render/ssrPass.d.ts +16 -0
- package/dist/render/ssrPass.js +106 -1
- package/dist/render/surfaceOverlay.d.ts +132 -0
- package/dist/render/surfaceOverlay.js +85 -0
- package/dist/render/surfaceTexture.d.ts +19 -0
- package/dist/render/surfaceTexture.js +69 -0
- package/dist/render/temporalAa.d.ts +14 -0
- package/dist/render/temporalAa.js +20 -0
- package/dist/render/vertexDefaults.d.ts +17 -0
- package/dist/render/vertexDefaults.js +24 -0
- package/package.json +6 -6
- package/src/index.ts +16 -0
- package/src/render/atmosphere.ts +22 -1
- package/src/render/backend/api.ts +9 -0
- package/src/render/backend/createRenderer.ts +18 -1
- package/src/render/backend/webgl2/renderer.ts +357 -13
- package/src/render/backend/webgpu/device.ts +42 -3
- package/src/render/backend/webgpu/flatPass.ts +63 -26
- package/src/render/backend/webgpu/postPass.ts +18 -0
- package/src/render/backend/webgpu/reflectionSurfacePass.ts +114 -0
- package/src/render/backend/webgpu/renderer.ts +735 -83
- package/src/render/displayRange.ts +61 -0
- package/src/render/filmicCurve.ts +229 -0
- package/src/render/fog.ts +19 -3
- package/src/render/frame/resources.ts +6 -0
- package/src/render/frameReflections.ts +72 -0
- package/src/render/pass.ts +53 -1
- package/src/render/reflectionSurfaceTargets.ts +105 -0
- package/src/render/renderQuality.ts +44 -8
- package/src/render/sceneTarget.ts +30 -2
- package/src/render/screenSpaceReflection.ts +5 -0
- package/src/render/shaders/filmLook.ts +2 -1
- package/src/render/shaders/flat/glassTint.ts +5 -1
- package/src/render/shaders/flat/index.ts +16 -0
- package/src/render/shaders/flat/main.ts +28 -0
- package/src/render/shaders/flat/overlay.ts +117 -0
- package/src/render/shaders/flat/pointShadow.ts +1 -1
- package/src/render/shaders/flat/reflectionSurface.ts +61 -0
- package/src/render/shaders/fog.ts +9 -2
- package/src/render/shaders/fringe.ts +59 -0
- package/src/render/shaders/generated/arcane.wgsl.ts +1 -1
- package/src/render/shaders/generated/bolt.wgsl.ts +1 -1
- package/src/render/shaders/generated/caustics.wgsl.ts +1 -1
- package/src/render/shaders/generated/film.wgsl.ts +1 -1
- package/src/render/shaders/generated/fire.wgsl.ts +1 -1
- package/src/render/shaders/generated/flat.wgsl.ts +214 -54
- package/src/render/shaders/generated/line.wgsl.ts +1 -1
- package/src/render/shaders/generated/particle.wgsl.ts +4 -4
- package/src/render/shaders/generated/rush.wgsl.ts +56 -26
- package/src/render/shaders/generated/scatter.wgsl.ts +1 -1
- package/src/render/shaders/generated/smoke.wgsl.ts +1 -1
- package/src/render/shaders/generated/ssrResolve.wgsl.ts +30 -0
- package/src/render/shaders/generated/ssrTrace.wgsl.ts +22 -2
- package/src/render/shaders/generated/water.wgsl.ts +1 -1
- package/src/render/shaders/gpudriven/fog.wgsl.ts +11 -3
- package/src/render/shaders/rush.ts +54 -11
- package/src/render/shaders/ssrResolve.ts +52 -0
- package/src/render/shaders/ssrTrace.ts +26 -0
- package/src/render/ssrPass.ts +138 -1
- package/src/render/surfaceOverlay.ts +195 -0
- package/src/render/surfaceTexture.ts +67 -0
- package/src/render/temporalAa.ts +20 -0
- package/src/render/vertexDefaults.ts +28 -0
|
@@ -187,7 +187,19 @@ import {
|
|
|
187
187
|
packModel,
|
|
188
188
|
skinProfileDistance,
|
|
189
189
|
} from '../../surfaceModel.ts';
|
|
190
|
-
import type { SurfaceModel } from '../../surfaceModel.ts';
|
|
190
|
+
import type { SurfaceModel, SurfaceModelKind } from '../../surfaceModel.ts';
|
|
191
|
+
import {
|
|
192
|
+
OVERLAY_FLOATS,
|
|
193
|
+
overlayLays,
|
|
194
|
+
packSurfaceOverlay,
|
|
195
|
+
type SurfaceOverlay,
|
|
196
|
+
} from '../../surfaceOverlay.ts';
|
|
197
|
+
import {
|
|
198
|
+
frameReflectionsRefused,
|
|
199
|
+
resolveFrameReflections,
|
|
200
|
+
type ResolvedFrameReflections,
|
|
201
|
+
} from '../../frameReflections.ts';
|
|
202
|
+
import { ReflectionSurfacePass } from './reflectionSurfacePass.ts';
|
|
191
203
|
import { validateClothBinding } from '../../clothBindingData.ts';
|
|
192
204
|
import type { ClothBindingData } from '../../clothBindingData.ts';
|
|
193
205
|
import type { CompressedTextureFormat, SurfaceSource } from '../../compressedSource.ts';
|
|
@@ -216,7 +228,24 @@ import type { SurfaceMaterial, SurfaceTextureOptions } from '../../surfaceTextur
|
|
|
216
228
|
import type { RenderBackend } from '../api.ts';
|
|
217
229
|
import { reflectionTargetSize, type ReflectionSize } from '../../planarReflectionDraw.ts';
|
|
218
230
|
import { drawingBufferSize } from '../../drawingBuffer.ts';
|
|
219
|
-
import {
|
|
231
|
+
import {
|
|
232
|
+
MOTION_BLUR_MAX_UV,
|
|
233
|
+
OBJECT_BLUR_UNKEPT,
|
|
234
|
+
OUTPUT_TRANSFORM_CODE,
|
|
235
|
+
RUSH_REACH_UV,
|
|
236
|
+
clampMotionShare,
|
|
237
|
+
forwardTransformCode,
|
|
238
|
+
} from '../../vertexDefaults.ts';
|
|
239
|
+
import { FRINGE_FLOATS, resolveFringe } from '../../shaders/fringe.ts';
|
|
240
|
+
import { displayHeadroom, type DisplayRange } from '../../displayRange.ts';
|
|
241
|
+
import {
|
|
242
|
+
DEFAULT_FILMIC_CURVE,
|
|
243
|
+
FILMIC_CLAMPED,
|
|
244
|
+
FILMIC_CONSTANTS,
|
|
245
|
+
FILMIC_WITHOUT_COMPOSITE,
|
|
246
|
+
resolveFilmicCurve,
|
|
247
|
+
type FilmicCurve,
|
|
248
|
+
} from '../../filmicCurve.ts';
|
|
220
249
|
import { ExposurePass } from './exposurePass.ts';
|
|
221
250
|
import { AO_STORE } from '../../shaders/ambientOcclusion.ts';
|
|
222
251
|
import { clampAutoExposure } from '../../shaders/exposure.ts';
|
|
@@ -281,9 +310,15 @@ import {
|
|
|
281
310
|
DECAL_TEXTURES,
|
|
282
311
|
DECAL_UNIFORMS,
|
|
283
312
|
PREMULTIPLIED_OVER_BLEND,
|
|
313
|
+
FRAME_REFLECTION_BLEND,
|
|
284
314
|
REFLECTION_OVER_BLEND,
|
|
285
315
|
SSR_FRAG_FIELDS,
|
|
286
316
|
SSR_FRAG_SIZE,
|
|
317
|
+
SSR_MATERIAL_FIELDS,
|
|
318
|
+
SSR_MATERIAL_RESOLVE_FRAG_WGSL,
|
|
319
|
+
SSR_MATERIAL_SIZE,
|
|
320
|
+
SSR_MATERIAL_TEXTURES,
|
|
321
|
+
SSR_MATERIAL_UNIFORMS,
|
|
287
322
|
SSR_RESOLVE_FRAG_WGSL,
|
|
288
323
|
SSR_RESOLVE_TEXTURES,
|
|
289
324
|
SSR_TEXTURES,
|
|
@@ -392,6 +427,7 @@ import {
|
|
|
392
427
|
type FlatVariant,
|
|
393
428
|
flatPipeline,
|
|
394
429
|
flatPipelineAsync,
|
|
430
|
+
probeStageCeilings,
|
|
395
431
|
} from './flatPass.ts';
|
|
396
432
|
import type { OitTarget } from './flatPass.ts';
|
|
397
433
|
import {
|
|
@@ -694,34 +730,20 @@ const MAX_SKY_DRAWS = 64;
|
|
|
694
730
|
/**
|
|
695
731
|
* Whether the environment probe's permutation fits this device's per-stage binding limits.
|
|
696
732
|
*
|
|
697
|
-
*
|
|
698
|
-
*
|
|
699
|
-
*
|
|
700
|
-
*
|
|
733
|
+
* **Two ceilings, counted separately**: sampled textures and samplers, since textures that share a
|
|
734
|
+
* sampler (`// wgsl:share` in `flat/preamble.ts`) count once against the second. The widest variant
|
|
735
|
+
* binds sixteen textures and twelve samplers, which is exactly WebGPU's default texture ceiling, and
|
|
736
|
+
* `select.ts` asks for the adapter's own — 48 here — so the probe does not depend on that request.
|
|
701
737
|
*/
|
|
702
738
|
function probeFits(device: GPUDevice): boolean {
|
|
703
739
|
/*
|
|
704
|
-
* **
|
|
705
|
-
*
|
|
706
|
-
*
|
|
707
|
-
*
|
|
708
|
-
* it
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
*
|
|
712
|
-
* It used to declare seventeen *samplers* too, and that was the binding constraint: this
|
|
713
|
-
* adapter offers exactly sixteen and no request can raise it, so the probe stood down and
|
|
714
|
-
* reflective surfaces kept a gradient. The shadow bindings share one declaration — see
|
|
715
|
-
* `// wgsl:share shadow` in `flat/preamble.ts`, which costs nothing because they were already
|
|
716
|
-
* one object — so the count is four, and the fourth is the normal map's: it is a colour image
|
|
717
|
-
* and wants filtering, which a non-filtering shadow sampler cannot give it.
|
|
718
|
-
*
|
|
719
|
-
* **Counted from the generated bindings rather than typed**: `FLAT_BINDINGS.flatFrag`'s
|
|
720
|
-
* widest variant is `directionalShadows+environmentProbe+pointShadows`, seven textures and
|
|
721
|
-
* four samplers. A stale number here reads as a device declining a feature it can run.
|
|
722
|
-
*/
|
|
723
|
-
const TEXTURES = 7;
|
|
724
|
-
const SAMPLERS = 4;
|
|
740
|
+
* **Counted from the generated bindings rather than typed** (`probeStageCeilings`). It was typed
|
|
741
|
+
* here as seven textures and four samplers while the widest variant grew to sixteen and twelve
|
|
742
|
+
* with the material maps, glass, the fixture atlas and DriftLight: a stale number reads as a
|
|
743
|
+
* device declining a feature it can run, or, the way this one had drifted, as one admitted to a
|
|
744
|
+
* permutation it cannot bind.
|
|
745
|
+
*/
|
|
746
|
+
const { textures: TEXTURES, samplers: SAMPLERS } = probeStageCeilings();
|
|
725
747
|
const fits =
|
|
726
748
|
device.limits.maxSampledTexturesPerShaderStage >= TEXTURES &&
|
|
727
749
|
device.limits.maxSamplersPerShaderStage >= SAMPLERS;
|
|
@@ -789,6 +811,7 @@ const VERB_PASS = 3;
|
|
|
789
811
|
/** What the depth snapshot copies from, and what it copies into. */
|
|
790
812
|
const DEPTH_SOURCE = maskOf('sceneDepth');
|
|
791
813
|
const DEPTH_SNAPSHOT = maskOf('depthSnapshot');
|
|
814
|
+
const COLOR_SNAPSHOT = maskOf('colorSnapshot');
|
|
792
815
|
|
|
793
816
|
/** The mirror's own colour and depth. */
|
|
794
817
|
const MIRROR_TARGET = maskOf('mirrorColor', 'mirrorDepth');
|
|
@@ -1919,6 +1942,17 @@ export class WebGPURenderer implements RendererApi {
|
|
|
1919
1942
|
*/
|
|
1920
1943
|
private refractSnapshot: GPUTexture | null = null;
|
|
1921
1944
|
private refractSnapshotView: GPUTextureView | null = null;
|
|
1945
|
+
/**
|
|
1946
|
+
* The frame's colour as a registered pass reads it (`reads: ['colorSnapshot']`): the scene's own
|
|
1947
|
+
* size and format, refilled at each such pass's draw. Allocated only once a pass declares the
|
|
1948
|
+
* read, beside the frame's targets, and never in the frame loop. See `takePassColour`.
|
|
1949
|
+
*/
|
|
1950
|
+
private passColour: GPUTexture | null = null;
|
|
1951
|
+
private passColourView: GPUTextureView | null = null;
|
|
1952
|
+
/** Registered passes that read the colour, queued this frame for after the upscale. */
|
|
1953
|
+
private readonly latePasses: PassHandle[] = [];
|
|
1954
|
+
/** A late pass draws unjittered, as every late draw does. */
|
|
1955
|
+
private readonly latePassJitter = new Float32Array(2);
|
|
1922
1956
|
/** Whether this frame has taken it. One snapshot serves every refracting draw. */
|
|
1923
1957
|
private refractSnapshotTaken = false;
|
|
1924
1958
|
/**
|
|
@@ -2429,8 +2463,18 @@ export class WebGPURenderer implements RendererApi {
|
|
|
2429
2463
|
private readonly reflectionQueue = new ReflectionQueue();
|
|
2430
2464
|
private ssrLayout!: GPUBindGroupLayout;
|
|
2431
2465
|
private ssrResolveLayout!: GPUBindGroupLayout;
|
|
2466
|
+
/** The frame's materials' reflections, resolved; null where off. See `frameReflections.ts`. */
|
|
2467
|
+
private readonly frameReflections: ResolvedFrameReflections | null;
|
|
2468
|
+
/** Their surface half, null where they are off or cannot be drawn: `reflectionSurfacePass.ts`. */
|
|
2469
|
+
private readonly reflectionSurface: ReflectionSurfacePass | null;
|
|
2470
|
+
/** The material resolve: its layout, its one block (the blur) and its group over the three maps. */
|
|
2471
|
+
private ssrMaterialLayout!: GPUBindGroupLayout;
|
|
2472
|
+
private ssrMaterialUniforms!: GPUBuffer;
|
|
2473
|
+
private readonly ssrMaterialFloats = new Float32Array(SSR_MATERIAL_SIZE / 4);
|
|
2474
|
+
private ssrMaterialGroup: GPUBindGroup | null = null;
|
|
2432
2475
|
private ssrUniforms!: GPUBuffer;
|
|
2433
|
-
|
|
2476
|
+
/* One slot a surface, and one more for the frame's materials' trace. */
|
|
2477
|
+
private readonly ssrStaging = new ArrayBuffer(SSR_SLOT * (MAX_REFLECTIVE_SURFACES + 1));
|
|
2434
2478
|
private readonly ssrFloats = new Float32Array(this.ssrStaging);
|
|
2435
2479
|
private ssrReflection: GPUTexture | null = null;
|
|
2436
2480
|
private ssrReflectionView: GPUTextureView | null = null;
|
|
@@ -3000,8 +3044,15 @@ export class WebGPURenderer implements RendererApi {
|
|
|
3000
3044
|
const { device } = this.surface;
|
|
3001
3045
|
const { depth, colorMsaa } = target.attachments(this.samples, this.pipelines.format);
|
|
3002
3046
|
|
|
3003
|
-
|
|
3047
|
+
/*
|
|
3048
|
+
* **Flushed, and only then set aside.** The flush replays what the frame has recorded, and
|
|
3049
|
+
* replaying opens the frame's pass where none is open yet; saved before it, the pass given back
|
|
3050
|
+
* afterwards was the null that stood there, while the one the flush opened was left open on the
|
|
3051
|
+
* frame's encoder and every later pass on it was refused — a black frame for any capture taken
|
|
3052
|
+
* after the frame had drawn something. `scripts/capture-check.mjs` takes one at three points.
|
|
3053
|
+
*/
|
|
3004
3054
|
if (this.quality.frameGraph) this.flushGraph();
|
|
3055
|
+
const savedPass = this.pass;
|
|
3005
3056
|
const savedEncoder = this.encoder;
|
|
3006
3057
|
this.markRings();
|
|
3007
3058
|
const skinMark = this.skinPalettes.mark();
|
|
@@ -3105,6 +3156,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
3105
3156
|
if (this.ormMap === texture) this.ormMap = null;
|
|
3106
3157
|
if (this.emissiveMap === texture) this.emissiveMap = null;
|
|
3107
3158
|
if (this.modelMap === texture) this.modelMap = null;
|
|
3159
|
+
if (this.overlayMaps === texture) this.overlayMaps = null;
|
|
3108
3160
|
this.forgetBindingsOf(texture);
|
|
3109
3161
|
texture.dispose();
|
|
3110
3162
|
this.captureTargets.get(texture)?.dispose();
|
|
@@ -3123,6 +3175,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
3123
3175
|
/* Same reasoning, for whichever SDF labels were bound against this atlas. */
|
|
3124
3176
|
this.sdfTextBindGroups.delete(texture);
|
|
3125
3177
|
this.flatBindGroups.clear();
|
|
3178
|
+
this.overlayGroups.clear();
|
|
3126
3179
|
/*
|
|
3127
3180
|
* The blank one is built with the albedo held aside, because `buildFlatBindGroup` reads
|
|
3128
3181
|
* `this.albedo` for that slot — leaving it in place would put a real colour map into the
|
|
@@ -3516,6 +3569,20 @@ export class WebGPURenderer implements RendererApi {
|
|
|
3516
3569
|
this.material((f) => f.set(this.ambientSH, this.materialField('uAmbientSH')));
|
|
3517
3570
|
}
|
|
3518
3571
|
|
|
3572
|
+
/**
|
|
3573
|
+
* The overlay the following draws wear, or null for none: pass state, put back to none by
|
|
3574
|
+
* `bindMeshPass`. See `renderer.ts` and `surfaceOverlay.ts`. The numbers go in the material block,
|
|
3575
|
+
* which every variant declares; the atlas goes in a twin of each flat group (`overlayGroup`).
|
|
3576
|
+
*/
|
|
3577
|
+
setSurfaceOverlay(overlay: SurfaceOverlay | null): void {
|
|
3578
|
+
if (this.surface.lost) return;
|
|
3579
|
+
packSurfaceOverlay(overlay, this.overlayFloats);
|
|
3580
|
+
/* The first overlay laid is when the lit pipelines start carrying one. */
|
|
3581
|
+
if (overlayLays(overlay)) this.enableLit('SURFACE_OVERLAY');
|
|
3582
|
+
this.overlayMaps = (overlay?.maps ?? null) as GpuSurfaceTexture | null;
|
|
3583
|
+
this.material((f) => f.set(this.overlayFloats, this.materialField('uOverlay')));
|
|
3584
|
+
}
|
|
3585
|
+
|
|
3519
3586
|
/** How reflective the surfaces drawn next are. Clamped, as `renderer.ts` clamps it. */
|
|
3520
3587
|
setSurfaceReflectivity(amount: number): void {
|
|
3521
3588
|
this.material((f) => {
|
|
@@ -4260,7 +4327,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
4260
4327
|
*/
|
|
4261
4328
|
private gradeCode(): number {
|
|
4262
4329
|
return !this.hasComposite || !this.quality.hdrScene
|
|
4263
|
-
? (
|
|
4330
|
+
? forwardTransformCode(this.quality.outputTransform)
|
|
4264
4331
|
: 0;
|
|
4265
4332
|
}
|
|
4266
4333
|
|
|
@@ -6579,6 +6646,10 @@ export class WebGPURenderer implements RendererApi {
|
|
|
6579
6646
|
this.skinPalettes = new SkinPaletteRing(quality.drawsPerFrame);
|
|
6580
6647
|
this.maxDrawingBufferPixels = quality.maxDrawingBufferPixels;
|
|
6581
6648
|
this.exposure = quality.outputExposure;
|
|
6649
|
+
resolveFilmicCurve(DEFAULT_FILMIC_CURVE, this.filmConstants);
|
|
6650
|
+
if (quality.outputTransform === 'filmic' && !(quality.screenEffects && quality.hdrScene)) {
|
|
6651
|
+
console.warn(FILMIC_WITHOUT_COMPOSITE);
|
|
6652
|
+
}
|
|
6582
6653
|
this.flushSchedule =
|
|
6583
6654
|
quality.frameGraph && quality.identifierGraph
|
|
6584
6655
|
? createFlushSchedule(MAX_DRAWS_PER_FRAME)
|
|
@@ -6656,6 +6727,26 @@ export class WebGPURenderer implements RendererApi {
|
|
|
6656
6727
|
);
|
|
6657
6728
|
}
|
|
6658
6729
|
this.skinScatter = skinScreen ? new SkinScatterPass(device) : null;
|
|
6730
|
+
/*
|
|
6731
|
+
* The frame's materials' reflections, where the frame can carry them: scene light to swap the
|
|
6732
|
+
* probe's share in, and one sample, which the trace's depth copy and both maps are. Said once
|
|
6733
|
+
* where it cannot. See `frameReflections.ts`.
|
|
6734
|
+
*/
|
|
6735
|
+
this.frameReflections = resolveFrameReflections(quality.screenSpaceReflections);
|
|
6736
|
+
const reflectionsRefusal =
|
|
6737
|
+
this.frameReflections === null
|
|
6738
|
+
? null
|
|
6739
|
+
: !quality.screenEffects || !quality.hdrScene
|
|
6740
|
+
? 'the frame keeps no scene light to swap the probe in (`screenEffects` and `hdrScene`)'
|
|
6741
|
+
: this.samples > 1
|
|
6742
|
+
? 'a multisampled frame has no single depth or colour to march through'
|
|
6743
|
+
: null;
|
|
6744
|
+
if (reflectionsRefusal !== null)
|
|
6745
|
+
console.warn(`WebGPU: ${frameReflectionsRefused(reflectionsRefusal)}`);
|
|
6746
|
+
this.reflectionSurface =
|
|
6747
|
+
this.frameReflections !== null && reflectionsRefusal === null
|
|
6748
|
+
? new ReflectionSurfacePass(device)
|
|
6749
|
+
: null;
|
|
6659
6750
|
this.skinPipelines = skinScreen
|
|
6660
6751
|
? new PipelineCache(
|
|
6661
6752
|
device,
|
|
@@ -7046,6 +7137,11 @@ export class WebGPURenderer implements RendererApi {
|
|
|
7046
7137
|
addressModeU: 'clamp-to-edge',
|
|
7047
7138
|
addressModeV: 'clamp-to-edge',
|
|
7048
7139
|
});
|
|
7140
|
+
/* Or a surface overlay's atlas, in the twin a draw wearing one binds: `overlayGroup`. */
|
|
7141
|
+
const maps = this.overlayBinding ? this.overlayMaps : null;
|
|
7142
|
+
if (maps !== null) {
|
|
7143
|
+
return { view: maps.layerView() ?? blankView, sampler: this.refractSampler };
|
|
7144
|
+
}
|
|
7049
7145
|
return { view: this.refractSnapshotView ?? blankView, sampler: this.refractSampler };
|
|
7050
7146
|
}
|
|
7051
7147
|
/*
|
|
@@ -7222,6 +7318,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
7222
7318
|
{ binding: RUSH_TEXTURES.uExposureHeld },
|
|
7223
7319
|
/* Local exposure's grid, read filtered between tiles. See `localExposure.ts`. */
|
|
7224
7320
|
{ binding: RUSH_TEXTURES.uExposureLocal },
|
|
7321
|
+
/* Each mover's own motion: the reconstruction's target, or the scene where there is none. */
|
|
7322
|
+
{ binding: RUSH_TEXTURES.uMotion },
|
|
7225
7323
|
]);
|
|
7226
7324
|
this.rushUniforms = device.createBuffer({
|
|
7227
7325
|
label: 'post.rushUniforms',
|
|
@@ -7239,11 +7337,29 @@ export class WebGPURenderer implements RendererApi {
|
|
|
7239
7337
|
this.ssrLayout = createPostStageLayout(device, SSR_UNIFORMS, SSR_FRAG_SIZE, [
|
|
7240
7338
|
{ binding: SSR_TEXTURES.uSsrDepth, filterable: false },
|
|
7241
7339
|
{ binding: SSR_TEXTURES.uSsrScene },
|
|
7340
|
+
/* The frame's materials, for the trace that reads them: `frameReflections.ts`. */
|
|
7341
|
+
{ binding: SSR_TEXTURES.uSsrProbeMap },
|
|
7342
|
+
{ binding: SSR_TEXTURES.uSsrTintMap },
|
|
7242
7343
|
]);
|
|
7243
7344
|
this.ssrResolveLayout = createSsrResolveLayout(device);
|
|
7345
|
+
this.ssrMaterialLayout = createPostStageLayout(
|
|
7346
|
+
device,
|
|
7347
|
+
SSR_MATERIAL_UNIFORMS,
|
|
7348
|
+
SSR_MATERIAL_SIZE,
|
|
7349
|
+
[
|
|
7350
|
+
{ binding: SSR_MATERIAL_TEXTURES.uSsrReflection },
|
|
7351
|
+
{ binding: SSR_MATERIAL_TEXTURES.uSsrProbeMap },
|
|
7352
|
+
{ binding: SSR_MATERIAL_TEXTURES.uSsrTintMap },
|
|
7353
|
+
],
|
|
7354
|
+
);
|
|
7355
|
+
this.ssrMaterialUniforms = device.createBuffer({
|
|
7356
|
+
label: 'post.ssrMaterialUniforms',
|
|
7357
|
+
size: SSR_MATERIAL_SIZE,
|
|
7358
|
+
usage: USAGE_UNIFORM_DST,
|
|
7359
|
+
});
|
|
7244
7360
|
this.ssrUniforms = device.createBuffer({
|
|
7245
7361
|
label: 'post.ssrUniforms',
|
|
7246
|
-
size: SSR_SLOT * MAX_REFLECTIVE_SURFACES,
|
|
7362
|
+
size: SSR_SLOT * (MAX_REFLECTIVE_SURFACES + 1),
|
|
7247
7363
|
usage: USAGE_UNIFORM_DST,
|
|
7248
7364
|
});
|
|
7249
7365
|
this.decalUniforms = device.createBuffer({
|
|
@@ -7947,7 +8063,11 @@ export class WebGPURenderer implements RendererApi {
|
|
|
7947
8063
|
/* Last frame's snapshot is last frame's scene. */
|
|
7948
8064
|
this.refractSnapshotTaken = false;
|
|
7949
8065
|
this.skinScattered = false;
|
|
8066
|
+
/* One frame of the jitter's sequence however many times the frame binds its camera. */
|
|
8067
|
+
this.temporalHistory.nextFrame();
|
|
8068
|
+
this.latePasses.length = 0;
|
|
7950
8069
|
this.skinScatter?.reset();
|
|
8070
|
+
this.reflectionSurface?.reset();
|
|
7951
8071
|
this.plumeVerts.reset();
|
|
7952
8072
|
this.plumeFrags.reset();
|
|
7953
8073
|
this.waterVerts.reset();
|
|
@@ -8557,6 +8677,9 @@ export class WebGPURenderer implements RendererApi {
|
|
|
8557
8677
|
f[at('uWriteMode') + 1] = 0;
|
|
8558
8678
|
/* 1, not 0: a multiplier's identity is one. See `setEnvironmentGain`. */
|
|
8559
8679
|
f[at('uEnvironmentDials') + 2] = 1;
|
|
8680
|
+
/* No overlay until a draw sets one. See `setSurfaceOverlay`. */
|
|
8681
|
+
this.overlayFloats.fill(0);
|
|
8682
|
+
f.fill(0, at('uOverlay'), at('uOverlay') + OVERLAY_FLOATS);
|
|
8560
8683
|
/* The frame's own ambient until a draw asks for its own. See `setAmbientSH`. */
|
|
8561
8684
|
f.fill(0, at('uAmbientSH'), at('uAmbientSH') + AMBIENT_SH_FLOATS);
|
|
8562
8685
|
/*
|
|
@@ -8949,6 +9072,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
8949
9072
|
this.emissiveMap = null;
|
|
8950
9073
|
this.modelMap = null;
|
|
8951
9074
|
this.bindGroup = this.blankAlbedoBindGroup;
|
|
9075
|
+
/* Nor an overlay; its numbers are zeroed with the block's other defaults. */
|
|
9076
|
+
this.overlayMaps = null;
|
|
8952
9077
|
this.uvScale[0] = 1;
|
|
8953
9078
|
this.uvScale[1] = 1;
|
|
8954
9079
|
this.uvOffset[0] = 0;
|
|
@@ -9036,6 +9161,9 @@ export class WebGPURenderer implements RendererApi {
|
|
|
9036
9161
|
* where it happened to pass, write motion for a surface no pixel of the frame belongs to.
|
|
9037
9162
|
*/
|
|
9038
9163
|
if (this.tracksMotionNow()) this.recordDrawMotion(mesh, model, previousModel);
|
|
9164
|
+
else if (previousModel !== null && this.quality.cameraMotionBlur * this.motionBlurScale > 0) {
|
|
9165
|
+
this.warnObjectBlur();
|
|
9166
|
+
}
|
|
9039
9167
|
noteMapsWithoutUvs(mesh, mesh.hasUvs, this.materialHasMaps);
|
|
9040
9168
|
/* An opaque draw meets the medium as the dial says: `submitMesh` reads an absent `fog` as
|
|
9041
9169
|
true, which is a translucent draw's default and would put every opaque draw back in the fog
|
|
@@ -9455,6 +9583,26 @@ export class WebGPURenderer implements RendererApi {
|
|
|
9455
9583
|
material,
|
|
9456
9584
|
);
|
|
9457
9585
|
}
|
|
9586
|
+
/* And its surface half, for the frame's materials' reflections. See `keepReflectionSurface`. */
|
|
9587
|
+
if (!blend && lit && depthWrite && this.reflectionSurfaceKeeps()) {
|
|
9588
|
+
this.keepReflectionSurface(
|
|
9589
|
+
mesh,
|
|
9590
|
+
key,
|
|
9591
|
+
base,
|
|
9592
|
+
skinned,
|
|
9593
|
+
morphed,
|
|
9594
|
+
deltas,
|
|
9595
|
+
layer,
|
|
9596
|
+
eight,
|
|
9597
|
+
clothed,
|
|
9598
|
+
modelKind,
|
|
9599
|
+
slot,
|
|
9600
|
+
material,
|
|
9601
|
+
null,
|
|
9602
|
+
1,
|
|
9603
|
+
null,
|
|
9604
|
+
);
|
|
9605
|
+
}
|
|
9458
9606
|
}
|
|
9459
9607
|
/* Back to the defaults for whatever is drawn next, exactly as `renderer.ts` restores them —
|
|
9460
9608
|
unconditionally, because the scratch state above is shared by every draw after this one
|
|
@@ -9606,6 +9754,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
9606
9754
|
const refraction = options.refraction ?? 0;
|
|
9607
9755
|
const glassy = resolveGlass(options.glass, this.glassScratch);
|
|
9608
9756
|
if ((refraction <= 0 && !glassy) || !this.takeRefractSnapshot()) return false;
|
|
9757
|
+
this.seeThroughBound = true;
|
|
9609
9758
|
const glass = this.glassScratch;
|
|
9610
9759
|
/* Strength, path thickness, transmission, frost: one vector, for the budget the shader states. */
|
|
9611
9760
|
const seeAt = this.materialField('uSeeThrough');
|
|
@@ -9627,6 +9776,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
9627
9776
|
|
|
9628
9777
|
/** Put the scratch back after `bindSeeThrough`: refraction and glass off. */
|
|
9629
9778
|
private releaseSeeThrough(): void {
|
|
9779
|
+
this.seeThroughBound = false;
|
|
9630
9780
|
const seeAt = this.materialField('uSeeThrough');
|
|
9631
9781
|
this.perFrameFloats[seeAt] = 0;
|
|
9632
9782
|
this.perFrameFloats[seeAt + 2] = 0;
|
|
@@ -9827,6 +9977,26 @@ export class WebGPURenderer implements RendererApi {
|
|
|
9827
9977
|
if (cut === null) pass.drawIndexed(mesh.indexCount, count);
|
|
9828
9978
|
else pass.drawIndexedIndirect(cut.args, 0);
|
|
9829
9979
|
}
|
|
9980
|
+
/* And its surface half, for the frame's materials' reflections, with the same survivors. */
|
|
9981
|
+
if (!blend && lit && depthWrite && this.reflectionSurfaceKeeps()) {
|
|
9982
|
+
this.keepReflectionSurface(
|
|
9983
|
+
mesh,
|
|
9984
|
+
key,
|
|
9985
|
+
base,
|
|
9986
|
+
false,
|
|
9987
|
+
false,
|
|
9988
|
+
null,
|
|
9989
|
+
layer,
|
|
9990
|
+
false,
|
|
9991
|
+
false,
|
|
9992
|
+
modelKind,
|
|
9993
|
+
slot,
|
|
9994
|
+
material,
|
|
9995
|
+
instanceBuffer,
|
|
9996
|
+
count,
|
|
9997
|
+
cut === null ? null : cut.args,
|
|
9998
|
+
);
|
|
9999
|
+
}
|
|
9830
10000
|
}
|
|
9831
10001
|
/* Back to the defaults, unconditionally and for the reason `drawMesh` gives at length: this
|
|
9832
10002
|
scratch block is cumulative state shared by every draw after this one, and an early return
|
|
@@ -10534,6 +10704,20 @@ export class WebGPURenderer implements RendererApi {
|
|
|
10534
10704
|
private warnedNoPointShadowArray = false;
|
|
10535
10705
|
/** Said once, for the retired ambient dial. */
|
|
10536
10706
|
private warnedEnvironmentAmbient = false;
|
|
10707
|
+
/** The overlay the draws wear, `uOverlay`: see `setSurfaceOverlay`. */
|
|
10708
|
+
private readonly overlayFloats = new Float32Array(OVERLAY_FLOATS);
|
|
10709
|
+
/** Its atlas, bound where the refraction copy goes in a twin of each flat group. */
|
|
10710
|
+
private overlayMaps: GpuSurfaceTexture | null = null;
|
|
10711
|
+
/**
|
|
10712
|
+
* Each flat group's twin with the atlas in the refraction slot, by the group and the atlas, built
|
|
10713
|
+
* on the first draw that wears it and dropped wherever `flatBindGroups` is.
|
|
10714
|
+
*/
|
|
10715
|
+
private readonly overlayGroups = new Map<GPUBindGroup, Map<GpuSurfaceTexture, GPUBindGroup>>();
|
|
10716
|
+
/** True while a twin is built: what `flatTextures` hands the refraction slot then. */
|
|
10717
|
+
private overlayBinding = false;
|
|
10718
|
+
/** True between `bindSeeThrough` and `releaseSeeThrough`: that draw reads the copy, not the atlas. */
|
|
10719
|
+
private seeThroughBound = false;
|
|
10720
|
+
private warnedOverlayRefraction = false;
|
|
10537
10721
|
private readonly castingWorldIndices = new Int32Array(POINT_SHADOW_POOL).fill(-1);
|
|
10538
10722
|
private readonly resolvedPointShadows = createResolvedPointShadows(MAX_POINT_LIGHTS);
|
|
10539
10723
|
/** One face's view-projection, rebuilt per face rather than allocated. */
|
|
@@ -10982,6 +11166,43 @@ export class WebGPURenderer implements RendererApi {
|
|
|
10982
11166
|
);
|
|
10983
11167
|
}
|
|
10984
11168
|
|
|
11169
|
+
/**
|
|
11170
|
+
* The open material's flat group, or its twin with the surface overlay's atlas in the refraction
|
|
11171
|
+
* slot where the draw wears one and does not refract. A twin is built from the live material,
|
|
11172
|
+
* which `bindGroup` always is, and kept by the group and the atlas.
|
|
11173
|
+
*
|
|
11174
|
+
* **A draw that refracts, or is glass, keeps the frame's copy** and its overlay reads no image —
|
|
11175
|
+
* the shader asks the same of `uSeeThrough` (`overlayImagesBound`) — said once.
|
|
11176
|
+
*/
|
|
11177
|
+
private overlayGroup(): GPUBindGroup {
|
|
11178
|
+
const maps = this.overlayMaps;
|
|
11179
|
+
if (maps === null) return this.bindGroup;
|
|
11180
|
+
if (this.seeThroughBound) {
|
|
11181
|
+
if (!this.warnedOverlayRefraction) {
|
|
11182
|
+
this.warnedOverlayRefraction = true;
|
|
11183
|
+
console.warn(
|
|
11184
|
+
'WebGPU: a draw that refracts or is glass wears a surface overlay with maps. The frame ' +
|
|
11185
|
+
"copy it reads takes the overlay's slot, so its rim, dissolve and wrinkles read no image.",
|
|
11186
|
+
);
|
|
11187
|
+
}
|
|
11188
|
+
return this.bindGroup;
|
|
11189
|
+
}
|
|
11190
|
+
let byMaps = this.overlayGroups.get(this.bindGroup);
|
|
11191
|
+
if (byMaps === undefined) {
|
|
11192
|
+
byMaps = new Map();
|
|
11193
|
+
this.overlayGroups.set(this.bindGroup, byMaps);
|
|
11194
|
+
}
|
|
11195
|
+
let twin = byMaps.get(maps);
|
|
11196
|
+
if (twin === undefined) {
|
|
11197
|
+
this.overlayBinding = true;
|
|
11198
|
+
twin = this.buildFlatBindGroup();
|
|
11199
|
+
this.overlayBinding = false;
|
|
11200
|
+
this.bindGroupBudget.ask();
|
|
11201
|
+
byMaps.set(maps, twin);
|
|
11202
|
+
}
|
|
11203
|
+
return twin;
|
|
11204
|
+
}
|
|
11205
|
+
|
|
10985
11206
|
/**
|
|
10986
11207
|
* The bind group a draw uses, skinned or not.
|
|
10987
11208
|
*
|
|
@@ -10998,7 +11219,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
10998
11219
|
morphed: boolean,
|
|
10999
11220
|
deltas: GPUTextureView | null,
|
|
11000
11221
|
): GPUBindGroup {
|
|
11001
|
-
|
|
11222
|
+
const base = this.overlayGroup();
|
|
11223
|
+
if (!skinned && !morphed) return base;
|
|
11002
11224
|
const palette = skinned ? this.skinPalettes.view(this.skinPaletteSlot) : null;
|
|
11003
11225
|
/* Slot `k` caches at `k + 1`; index 0 is the morphed draw that is not skinned and has no
|
|
11004
11226
|
slot of its own. See `skinnedGroups`. */
|
|
@@ -11007,12 +11229,14 @@ export class WebGPURenderer implements RendererApi {
|
|
|
11007
11229
|
const cloth = skinned ? this.clothViews() : null;
|
|
11008
11230
|
if (
|
|
11009
11231
|
(this.skinnedGroups[at] ?? null) === null ||
|
|
11010
|
-
this.skinnedGroupsBeside[at] !==
|
|
11232
|
+
this.skinnedGroupsBeside[at] !== base ||
|
|
11011
11233
|
this.skinnedGroupsPalette[at] !== palette ||
|
|
11012
11234
|
this.skinnedGroupsDeltas[at] !== deltas ||
|
|
11013
11235
|
this.skinnedGroupsCloth[at] !== (cloth?.particles ?? null) ||
|
|
11014
11236
|
this.skinnedGroupsClothBinding[at] !== (cloth?.binding ?? null)
|
|
11015
11237
|
) {
|
|
11238
|
+
/* Built from the live material, with the overlay's atlas where `base` is its twin. */
|
|
11239
|
+
this.overlayBinding = base !== this.bindGroup;
|
|
11016
11240
|
this.skinnedGroups[at] = createFlatBindGroup(
|
|
11017
11241
|
this.surface.device,
|
|
11018
11242
|
this.flatLayoutFor(skinned, morphed),
|
|
@@ -11024,7 +11248,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
11024
11248
|
deltas,
|
|
11025
11249
|
cloth,
|
|
11026
11250
|
);
|
|
11027
|
-
this.
|
|
11251
|
+
this.overlayBinding = false;
|
|
11252
|
+
this.skinnedGroupsBeside[at] = base;
|
|
11028
11253
|
this.skinnedGroupsPalette[at] = palette;
|
|
11029
11254
|
this.skinnedGroupsDeltas[at] = deltas;
|
|
11030
11255
|
this.skinnedGroupsCloth[at] = cloth?.particles ?? null;
|
|
@@ -11090,6 +11315,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
11090
11315
|
*/
|
|
11091
11316
|
private rebuildFlatGroupsForNewRings(): void {
|
|
11092
11317
|
this.flatBindGroups.clear();
|
|
11318
|
+
this.overlayGroups.clear();
|
|
11093
11319
|
this.flatEnvironment = this.flatTextures('uEnvironment').view;
|
|
11094
11320
|
this.skinnedGroups.length = 0;
|
|
11095
11321
|
|
|
@@ -11153,6 +11379,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
11153
11379
|
*/
|
|
11154
11380
|
private rebuildFlatBindGroup(): void {
|
|
11155
11381
|
this.flatBindGroups.clear();
|
|
11382
|
+
this.overlayGroups.clear();
|
|
11156
11383
|
this.flatEnvironment = this.flatTextures('uEnvironment').view;
|
|
11157
11384
|
if (
|
|
11158
11385
|
this.albedo === null &&
|
|
@@ -11394,9 +11621,14 @@ export class WebGPURenderer implements RendererApi {
|
|
|
11394
11621
|
? (OUTPUT_TRANSFORM_CODE[this.quality.outputTransform] ?? 0)
|
|
11395
11622
|
: 0;
|
|
11396
11623
|
f[at('uOutputExposure')] = graded ? this.exposure : 1;
|
|
11624
|
+
f.set(this.filmA, at('uFilmA'));
|
|
11625
|
+
f.set(this.filmB, at('uFilmB'));
|
|
11626
|
+
f[at('uDisplayHeadroom')] = this.headroom;
|
|
11397
11627
|
f.set(this.reprojection, at('uReprojection'));
|
|
11398
11628
|
f[at('uMotionStrength')] = motionStrength;
|
|
11399
|
-
f[at('uMotionMax')] =
|
|
11629
|
+
f[at('uMotionMax')] = this.motionBlurMax;
|
|
11630
|
+
/* Each mover's own motion, where the reconstruction drew its target this frame. */
|
|
11631
|
+
f[at('uObjectMotion')] = this.reconstructing && this.reconMotionView !== null ? 1 : 0;
|
|
11400
11632
|
f[at('uAoStrength')] = this.frameProjection === null ? 0 : aoStrength;
|
|
11401
11633
|
/*
|
|
11402
11634
|
* The occlusion read where this frame's jitter put it (`uAoOffset`): measured from the jittered
|
|
@@ -11440,6 +11672,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
11440
11672
|
f[at('uGradeSize')] = this.gradeSize;
|
|
11441
11673
|
/* The lens and the print. See `setVignette` and `setFilmGrain`. */
|
|
11442
11674
|
f[at('uVignette')] = this.vignette;
|
|
11675
|
+
f.set(this.fringe, at('uFringe'));
|
|
11443
11676
|
f[at('uAutoExposure')] = adapting ? this.autoExposure : 0;
|
|
11444
11677
|
f[at('uLocalExposure')] = measuring && localising ? this.localExposure : 0;
|
|
11445
11678
|
f[at('uGrain')] = this.grain;
|
|
@@ -11485,7 +11718,11 @@ export class WebGPURenderer implements RendererApi {
|
|
|
11485
11718
|
private rebuildRushBindGroup(): void {
|
|
11486
11719
|
if (this.sceneColorView === null || this.resolvedDepthView === null) return;
|
|
11487
11720
|
if (this.aoTargetView === null) return;
|
|
11488
|
-
this.rushBindGroup = this.buildRushBindGroup(
|
|
11721
|
+
this.rushBindGroup = this.buildRushBindGroup(
|
|
11722
|
+
'post.rushBindGroup',
|
|
11723
|
+
this.sceneColorView,
|
|
11724
|
+
this.sceneColorView,
|
|
11725
|
+
);
|
|
11489
11726
|
/*
|
|
11490
11727
|
* **And the reconstruction's twin, from the same three call sites.** A colour grade replaces
|
|
11491
11728
|
* the lookup table and a resize replaces the depth and the occlusion target, and both groups
|
|
@@ -11493,12 +11730,21 @@ export class WebGPURenderer implements RendererApi {
|
|
|
11493
11730
|
* come to read a stale view on exactly the frames a consumer changed something.
|
|
11494
11731
|
*/
|
|
11495
11732
|
if (this.reconShownView !== null) {
|
|
11496
|
-
this.reconRushGroup = this.buildRushBindGroup(
|
|
11733
|
+
this.reconRushGroup = this.buildRushBindGroup(
|
|
11734
|
+
'recon.rushBindGroup',
|
|
11735
|
+
this.reconShownView,
|
|
11736
|
+
this.reconMotionView ?? this.reconShownView,
|
|
11737
|
+
);
|
|
11497
11738
|
}
|
|
11498
11739
|
}
|
|
11499
11740
|
|
|
11500
11741
|
/** The composite's group over one scene source. See `rebuildRushBindGroup`. */
|
|
11501
|
-
private buildRushBindGroup(
|
|
11742
|
+
private buildRushBindGroup(
|
|
11743
|
+
label: string,
|
|
11744
|
+
sceneView: GPUTextureView,
|
|
11745
|
+
/** Each mover's own motion, or a stand-in the shader is told not to read. */
|
|
11746
|
+
motionView: GPUTextureView,
|
|
11747
|
+
): GPUBindGroup | null {
|
|
11502
11748
|
const { device } = this.surface;
|
|
11503
11749
|
if (this.resolvedDepthView === null || this.aoTargetView === null) return null;
|
|
11504
11750
|
const entry = (
|
|
@@ -11530,6 +11776,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
11530
11776
|
and a declared binding still needs a complete texture, as the bloom fallback says. */
|
|
11531
11777
|
...entry(RUSH_TEXTURES.uExposureHeld, this.exposurePass?.heldView ?? sceneView),
|
|
11532
11778
|
...entry(RUSH_TEXTURES.uExposureLocal, this.exposurePass?.localView ?? sceneView),
|
|
11779
|
+
...entry(RUSH_TEXTURES.uMotion, motionView),
|
|
11533
11780
|
],
|
|
11534
11781
|
layout: this.rushLayout,
|
|
11535
11782
|
});
|
|
@@ -11813,7 +12060,11 @@ export class WebGPURenderer implements RendererApi {
|
|
|
11813
12060
|
);
|
|
11814
12061
|
}
|
|
11815
12062
|
/* The composite's own group, over the resolved picture instead of the scene target. */
|
|
11816
|
-
this.reconRushGroup = this.buildRushBindGroup(
|
|
12063
|
+
this.reconRushGroup = this.buildRushBindGroup(
|
|
12064
|
+
'recon.rushBindGroup',
|
|
12065
|
+
this.reconShownView,
|
|
12066
|
+
this.reconMotionView ?? this.reconShownView,
|
|
12067
|
+
);
|
|
11817
12068
|
/* And the scene's, because `ensureComposite` built it before this one existed. */
|
|
11818
12069
|
this.rebuildRushBindGroup();
|
|
11819
12070
|
/* And the bloom's, which blooms the resolved picture: see `rebuildBloomGroups`. */
|
|
@@ -11927,7 +12178,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
11927
12178
|
const depth = this.depthView;
|
|
11928
12179
|
if (!this.reconstructing || late === null || shown === null || depth === null) return;
|
|
11929
12180
|
const oitSet = this.oitActive && this.translucentQueue.length > 0;
|
|
11930
|
-
if (late.pending === 0 && !oitSet) return;
|
|
12181
|
+
if (late.pending === 0 && !oitSet && this.latePasses.length === 0) return;
|
|
11931
12182
|
const width = Math.max(1, this.surface.canvas.width);
|
|
11932
12183
|
const height = Math.max(1, this.surface.canvas.height);
|
|
11933
12184
|
/* The order-independent set tests against this depth too, so it is made for either. */
|
|
@@ -11951,20 +12202,64 @@ export class WebGPURenderer implements RendererApi {
|
|
|
11951
12202
|
this.refractSnapshotTaken = true;
|
|
11952
12203
|
}
|
|
11953
12204
|
this.lateRefractWanted = false;
|
|
11954
|
-
if (late.pending
|
|
11955
|
-
|
|
11956
|
-
|
|
11957
|
-
|
|
11958
|
-
|
|
11959
|
-
|
|
11960
|
-
|
|
11961
|
-
|
|
11962
|
-
|
|
11963
|
-
|
|
11964
|
-
|
|
11965
|
-
|
|
11966
|
-
|
|
11967
|
-
|
|
12205
|
+
if (late.pending > 0) {
|
|
12206
|
+
const pass = encoder.beginRenderPass({
|
|
12207
|
+
label: 'recon.late',
|
|
12208
|
+
timestampWrites: this.gpuTimer.writesFor(),
|
|
12209
|
+
colorAttachments: [{ view: shown, loadOp: 'load', storeOp: 'store' }],
|
|
12210
|
+
depthStencilAttachment: {
|
|
12211
|
+
view: late.depthView(width, height),
|
|
12212
|
+
depthLoadOp: 'load',
|
|
12213
|
+
/* Kept: the order-independent set replays against it after this pass. */
|
|
12214
|
+
depthStoreOp: 'store',
|
|
12215
|
+
},
|
|
12216
|
+
});
|
|
12217
|
+
late.replay(pass, this.issueBound);
|
|
12218
|
+
pass.end();
|
|
12219
|
+
}
|
|
12220
|
+
this.runLatePasses(encoder, shown, late.depthView(width, height));
|
|
12221
|
+
}
|
|
12222
|
+
|
|
12223
|
+
/**
|
|
12224
|
+
* The registered passes `drawPass` queued for after the upscale: each handed a fresh copy of the
|
|
12225
|
+
* reconstructed picture with every late draw in it, in the output-size texture the late
|
|
12226
|
+
* refraction keeps (its own reads are done by now: they were recorded before this copy), then
|
|
12227
|
+
* drawn over the picture in a pass of its own.
|
|
12228
|
+
*/
|
|
12229
|
+
private runLatePasses(
|
|
12230
|
+
encoder: GPUCommandEncoder,
|
|
12231
|
+
shown: GPUTextureView,
|
|
12232
|
+
depth: GPUTextureView,
|
|
12233
|
+
): void {
|
|
12234
|
+
const copy = this.refractSnapshot;
|
|
12235
|
+
const source = this.reconShown;
|
|
12236
|
+
for (const handle of this.latePasses) {
|
|
12237
|
+
const definition = passAt(this.passes, handle);
|
|
12238
|
+
if (definition === undefined) continue;
|
|
12239
|
+
if (copy !== null && source !== null) {
|
|
12240
|
+
encoder.copyTextureToTexture({ texture: source }, { texture: copy }, [
|
|
12241
|
+
copy.width,
|
|
12242
|
+
copy.height,
|
|
12243
|
+
]);
|
|
12244
|
+
}
|
|
12245
|
+
const pass = encoder.beginRenderPass({
|
|
12246
|
+
label: 'recon.latePass',
|
|
12247
|
+
timestampWrites: this.gpuTimer.writesFor(),
|
|
12248
|
+
colorAttachments: [{ view: shown, loadOp: 'load', storeOp: 'store' }],
|
|
12249
|
+
depthStencilAttachment: { view: depth, depthLoadOp: 'load', depthStoreOp: 'store' },
|
|
12250
|
+
});
|
|
12251
|
+
const context = this.passContext;
|
|
12252
|
+
context.pass = pass;
|
|
12253
|
+
context.outputTransform = this.gradeCode();
|
|
12254
|
+
context.outputExposure = this.gradeExposure();
|
|
12255
|
+
context.jitter = this.latePassJitter;
|
|
12256
|
+
context.format = RECON_HISTORY_FORMAT;
|
|
12257
|
+
context.depthFormat = DEPTH_FORMAT;
|
|
12258
|
+
context.samples = 1;
|
|
12259
|
+
definition.draw(context as PassContext);
|
|
12260
|
+
context.jitter = this.passJitter;
|
|
12261
|
+
pass.end();
|
|
12262
|
+
}
|
|
11968
12263
|
}
|
|
11969
12264
|
|
|
11970
12265
|
/** Whether this draw lands in the reconstructed picture. See `lateRouting.ts`. */
|
|
@@ -12391,7 +12686,10 @@ export class WebGPURenderer implements RendererApi {
|
|
|
12391
12686
|
* other is the disagreement the parity rule exists to prevent. Said once rather than dropped.
|
|
12392
12687
|
*/
|
|
12393
12688
|
private runReflections(encoder: GPUCommandEncoder): void {
|
|
12394
|
-
|
|
12689
|
+
const frameReflections = this.frameReflections;
|
|
12690
|
+
const surfaceHalf = this.reflectionSurface;
|
|
12691
|
+
const materials = frameReflections !== null && surfaceHalf !== null;
|
|
12692
|
+
if (this.reflectionQueue.length === 0 && !materials) return;
|
|
12395
12693
|
const scene = this.sceneColorView;
|
|
12396
12694
|
const reflection = this.ssrReflectionView;
|
|
12397
12695
|
const group = this.ssrBindGroup;
|
|
@@ -12436,9 +12734,81 @@ export class WebGPURenderer implements RendererApi {
|
|
|
12436
12734
|
f[base + at('uSsrThickness')] = surface.thicknessM;
|
|
12437
12735
|
f[base + at('uSsrSteps')] = surface.steps;
|
|
12438
12736
|
f[base + at('uSsrEdgeFade')] = REFLECTION_EDGE_FADE;
|
|
12737
|
+
f[base + at('uSsrMaterial')] = 0;
|
|
12439
12738
|
slot++;
|
|
12440
12739
|
});
|
|
12740
|
+
const regions = slot;
|
|
12741
|
+
/* The frame's materials' trace takes the slot after the surfaces': the box, its axis and its
|
|
12742
|
+
strength unread, the march and the ceiling its own. See `frameReflections.ts`. */
|
|
12743
|
+
if (materials) {
|
|
12744
|
+
const base = (slot * SSR_SLOT) / 4;
|
|
12745
|
+
f.set(this.ssrDepthToWorld, base + at('uSsrDepthToWorld'));
|
|
12746
|
+
f.set(this.ssrViewProj, base + at('uSsrViewProj'));
|
|
12747
|
+
f.set(this.frameEye, base + at('uSsrEye'));
|
|
12748
|
+
f.fill(0, base + at('uSsrAxis'), base + at('uSsrAxis') + 3);
|
|
12749
|
+
f.fill(1, base + at('uSsrTint'), base + at('uSsrTint') + 3);
|
|
12750
|
+
f[base + at('uSsrStrength')] = 1;
|
|
12751
|
+
f[base + at('uSsrFresnel')] = 0;
|
|
12752
|
+
f[base + at('uSsrFacingCos')] = -1;
|
|
12753
|
+
f[base + at('uSsrReach')] = frameReflections.reachM;
|
|
12754
|
+
f[base + at('uSsrThickness')] = frameReflections.thicknessM;
|
|
12755
|
+
f[base + at('uSsrSteps')] = frameReflections.steps;
|
|
12756
|
+
f[base + at('uSsrEdgeFade')] = REFLECTION_EDGE_FADE;
|
|
12757
|
+
f[base + at('uSsrMaterial')] = 1;
|
|
12758
|
+
f[base + at('uSsrMaxRoughness')] = frameReflections.maxRoughness;
|
|
12759
|
+
slot++;
|
|
12760
|
+
}
|
|
12441
12761
|
device.queue.writeBuffer(this.ssrUniforms, 0, this.ssrStaging, 0, slot * SSR_SLOT);
|
|
12762
|
+
const tracePipeline = postPipeline(
|
|
12763
|
+
this.pipelines,
|
|
12764
|
+
device,
|
|
12765
|
+
this.ssrLayout,
|
|
12766
|
+
'post.ssrTrace',
|
|
12767
|
+
SSR_TRACE_FRAG_WGSL,
|
|
12768
|
+
this.pipelines.format,
|
|
12769
|
+
PREMULTIPLIED_OVER_BLEND,
|
|
12770
|
+
);
|
|
12771
|
+
|
|
12772
|
+
if (materials && this.depthView !== null && this.ssrMaterialGroup !== null) {
|
|
12773
|
+
/* The frame's opaque lit draws again, into what the trace and the resolve read. */
|
|
12774
|
+
surfaceHalf.draw(encoder, this.depthView, this.issueBound);
|
|
12775
|
+
const trace = encoder.beginRenderPass({
|
|
12776
|
+
label: 'post.ssr.materialTrace',
|
|
12777
|
+
colorAttachments: [
|
|
12778
|
+
{ view: reflection, loadOp: 'clear', storeOp: 'store', clearValue: [0, 0, 0, 0] },
|
|
12779
|
+
],
|
|
12780
|
+
});
|
|
12781
|
+
trace.setPipeline(tracePipeline);
|
|
12782
|
+
trace.setBindGroup(0, group, [regions * SSR_SLOT]);
|
|
12783
|
+
trace.draw(3);
|
|
12784
|
+
trace.end();
|
|
12785
|
+
/* The blur's texel and its radius at roughness 1, as a share of the frame's height. */
|
|
12786
|
+
const m = this.ssrMaterialFloats;
|
|
12787
|
+
const blurAt = this.postField(SSR_MATERIAL_FIELDS, 'uSsrBlur');
|
|
12788
|
+
m[blurAt] = 1 / Math.max(width, 1);
|
|
12789
|
+
m[blurAt + 1] = 1 / Math.max(height, 1);
|
|
12790
|
+
m[blurAt + 2] = frameReflections.blur * height;
|
|
12791
|
+
device.queue.writeBuffer(this.ssrMaterialUniforms, 0, m);
|
|
12792
|
+
const resolve = encoder.beginRenderPass({
|
|
12793
|
+
label: 'post.ssr.materialResolve',
|
|
12794
|
+
colorAttachments: [{ view: scene, loadOp: 'load', storeOp: 'store' }],
|
|
12795
|
+
});
|
|
12796
|
+
resolve.setPipeline(
|
|
12797
|
+
postPipeline(
|
|
12798
|
+
this.pipelines,
|
|
12799
|
+
device,
|
|
12800
|
+
this.ssrMaterialLayout,
|
|
12801
|
+
'post.ssrMaterialResolve',
|
|
12802
|
+
SSR_MATERIAL_RESOLVE_FRAG_WGSL,
|
|
12803
|
+
this.pipelines.format,
|
|
12804
|
+
FRAME_REFLECTION_BLEND,
|
|
12805
|
+
),
|
|
12806
|
+
);
|
|
12807
|
+
resolve.setBindGroup(0, this.ssrMaterialGroup, [0]);
|
|
12808
|
+
resolve.draw(3);
|
|
12809
|
+
resolve.end();
|
|
12810
|
+
}
|
|
12811
|
+
if (regions === 0) return;
|
|
12442
12812
|
|
|
12443
12813
|
/* Cleared whole and then scissored per surface: the resolve reads every pixel, and one left
|
|
12444
12814
|
from the last frame would composite that reflection over this frame's scene. */
|
|
@@ -12448,17 +12818,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
12448
12818
|
{ view: reflection, loadOp: 'clear', storeOp: 'store', clearValue: [0, 0, 0, 0] },
|
|
12449
12819
|
],
|
|
12450
12820
|
});
|
|
12451
|
-
trace.setPipeline(
|
|
12452
|
-
postPipeline(
|
|
12453
|
-
this.pipelines,
|
|
12454
|
-
device,
|
|
12455
|
-
this.ssrLayout,
|
|
12456
|
-
'post.ssrTrace',
|
|
12457
|
-
SSR_TRACE_FRAG_WGSL,
|
|
12458
|
-
this.pipelines.format,
|
|
12459
|
-
PREMULTIPLIED_OVER_BLEND,
|
|
12460
|
-
),
|
|
12461
|
-
);
|
|
12821
|
+
trace.setPipeline(tracePipeline);
|
|
12462
12822
|
slot = 0;
|
|
12463
12823
|
this.reflectionQueue.replay((surface) => {
|
|
12464
12824
|
const index = slot++;
|
|
@@ -13222,6 +13582,90 @@ export class WebGPURenderer implements RendererApi {
|
|
|
13222
13582
|
);
|
|
13223
13583
|
}
|
|
13224
13584
|
|
|
13585
|
+
/** Whether a draw now is the frame's own, and so has a surface half to keep. */
|
|
13586
|
+
private reflectionSurfaceKeeps(): boolean {
|
|
13587
|
+
return (
|
|
13588
|
+
this.reflectionSurface !== null &&
|
|
13589
|
+
!this.reflectionPassActive &&
|
|
13590
|
+
!this.probePassActive &&
|
|
13591
|
+
!this.capturePassActive &&
|
|
13592
|
+
!this.framePresented &&
|
|
13593
|
+
!this.overlayActive &&
|
|
13594
|
+
this.oitMode === 'none'
|
|
13595
|
+
);
|
|
13596
|
+
}
|
|
13597
|
+
|
|
13598
|
+
/**
|
|
13599
|
+
* An opaque lit draw's surface half, kept for the frame's materials' reflections: the draw again,
|
|
13600
|
+
* with the same slots, buffers and bind group, through the pipeline with `REFLECTION_SURFACE` on —
|
|
13601
|
+
* which writes what the pixel reflects of its environment into the two targets
|
|
13602
|
+
* `reflectionSurfacePass.ts` holds and tests for the depth its frame draw wrote. Replayed when the
|
|
13603
|
+
* frame's pass has ended, before the trace reads them. See `shaders/flat/reflectionSurface.ts`.
|
|
13604
|
+
*/
|
|
13605
|
+
private keepReflectionSurface(
|
|
13606
|
+
mesh: GpuMesh,
|
|
13607
|
+
frameKey: string,
|
|
13608
|
+
base: string,
|
|
13609
|
+
skinned: boolean,
|
|
13610
|
+
morphed: boolean,
|
|
13611
|
+
deltas: GPUTextureView | null,
|
|
13612
|
+
layer: number,
|
|
13613
|
+
eight: boolean,
|
|
13614
|
+
clothed: boolean,
|
|
13615
|
+
modelKind: SurfaceModelKind | null,
|
|
13616
|
+
slot: number,
|
|
13617
|
+
material: number,
|
|
13618
|
+
instanceBuffer: GPUBuffer | null,
|
|
13619
|
+
instances: number,
|
|
13620
|
+
indirect: GPUBuffer | null,
|
|
13621
|
+
): void {
|
|
13622
|
+
const surface = this.reflectionSurface;
|
|
13623
|
+
const present = this.meshPresent.get(base);
|
|
13624
|
+
if (surface === null || present === undefined) return;
|
|
13625
|
+
const key = `${frameKey}|rs`;
|
|
13626
|
+
const pipeline =
|
|
13627
|
+
this.pipelines.peek(key) ??
|
|
13628
|
+
flatPipeline(
|
|
13629
|
+
this.pipelines,
|
|
13630
|
+
this.surface.device,
|
|
13631
|
+
this.flatLayoutFor(skinned, morphed),
|
|
13632
|
+
this.variant,
|
|
13633
|
+
key,
|
|
13634
|
+
present,
|
|
13635
|
+
false,
|
|
13636
|
+
skinned,
|
|
13637
|
+
morphed,
|
|
13638
|
+
true,
|
|
13639
|
+
layer,
|
|
13640
|
+
instanceBuffer !== null,
|
|
13641
|
+
'none',
|
|
13642
|
+
this.materialDoubleSided,
|
|
13643
|
+
false,
|
|
13644
|
+
eight,
|
|
13645
|
+
clothed,
|
|
13646
|
+
modelKind,
|
|
13647
|
+
'whole',
|
|
13648
|
+
true,
|
|
13649
|
+
);
|
|
13650
|
+
const command = surface.take();
|
|
13651
|
+
command.pipeline = pipeline;
|
|
13652
|
+
command.bindGroup = this.flatBindGroupFor(skinned, morphed, deltas);
|
|
13653
|
+
command.offsetA = slot;
|
|
13654
|
+
command.offsetB = material;
|
|
13655
|
+
command.offsetCount = 2;
|
|
13656
|
+
let index = 0;
|
|
13657
|
+
for (; index < mesh.vertexBuffers.length; index++) {
|
|
13658
|
+
command.vertexBuffers[index] = mesh.vertexBuffers[index] as GPUBuffer;
|
|
13659
|
+
}
|
|
13660
|
+
if (instanceBuffer !== null) command.vertexBuffers[index++] = instanceBuffer;
|
|
13661
|
+
command.vertexCount = index;
|
|
13662
|
+
command.indexBuffer = mesh.indexBuffer;
|
|
13663
|
+
command.indexed = true;
|
|
13664
|
+
command.count = mesh.indexCount;
|
|
13665
|
+
command.instances = instances;
|
|
13666
|
+
command.indirect = indirect;
|
|
13667
|
+
}
|
|
13668
|
+
|
|
13225
13669
|
/**
|
|
13226
13670
|
* A skin draw's diffuse and colour halves, kept for the spread: the frame's draw again, twice,
|
|
13227
13671
|
* with the same slots, buffers and bind group, through the pipelines that write the diffuse's
|
|
@@ -13301,11 +13745,17 @@ export class WebGPURenderer implements RendererApi {
|
|
|
13301
13745
|
private runSkinScatter(): void {
|
|
13302
13746
|
const skin = this.skinScatter;
|
|
13303
13747
|
if (skin === null || this.skinScattered) return;
|
|
13748
|
+
/*
|
|
13749
|
+
* **Not the frame's picture, so not the frame's moment**: a blended draw in a mirror, a probe
|
|
13750
|
+
* bake or a scene capture lands over none of the frame's skin. Latching there switched the
|
|
13751
|
+
* spread off for the frame, so a capture with a glowing sign in it left every face drawn after
|
|
13752
|
+
* it flat. `scripts/capture-check.mjs`.
|
|
13753
|
+
*/
|
|
13754
|
+
if (this.reflectionPassActive || this.probePassActive || this.capturePassActive) return;
|
|
13304
13755
|
/* Latched whether or not skin was kept: a skin drawn after this point is the whole surface. */
|
|
13305
13756
|
this.skinScattered = true;
|
|
13306
13757
|
if (!skin.pending) return;
|
|
13307
|
-
if (this.
|
|
13308
|
-
if (this.capturePassActive) return;
|
|
13758
|
+
if (this.oitReplaying) return;
|
|
13309
13759
|
const encoder = this.encoder;
|
|
13310
13760
|
const depth = this.depthView;
|
|
13311
13761
|
const resolved = this.resolvedDepthView;
|
|
@@ -13430,6 +13880,65 @@ export class WebGPURenderer implements RendererApi {
|
|
|
13430
13880
|
return true;
|
|
13431
13881
|
}
|
|
13432
13882
|
|
|
13883
|
+
/** The colour copy a registered pass reads, at the scene's size and format. See `passColour`. */
|
|
13884
|
+
private allocatePassColour(): void {
|
|
13885
|
+
const source = this.sceneColor;
|
|
13886
|
+
if (source === null || this.passColour !== null) return;
|
|
13887
|
+
this.passColour = this.surface.device.createTexture({
|
|
13888
|
+
label: 'pass.sceneColor',
|
|
13889
|
+
size: [source.width, source.height],
|
|
13890
|
+
format: this.pipelines.format,
|
|
13891
|
+
usage: 0x4 | 0x2, // TEXTURE_BINDING | COPY_DST
|
|
13892
|
+
});
|
|
13893
|
+
this.passColourView = this.passColour.createView();
|
|
13894
|
+
}
|
|
13895
|
+
|
|
13896
|
+
/**
|
|
13897
|
+
* The frame's colour so far, into `passColour`, for a registered pass that reads it: the
|
|
13898
|
+
* boundary `takeRefractSnapshot` describes — flush, end the pass, copy what its end resolved,
|
|
13899
|
+
* reopen with `load` — and refused where that one is, in a mirror, a probe bake or a capture,
|
|
13900
|
+
* whose pictures are not the frame's. **Not latched**, unlike refraction's: each reading pass
|
|
13901
|
+
* sees the passes drawn before it, at a full-frame copy each.
|
|
13902
|
+
*/
|
|
13903
|
+
private takePassColour(): void {
|
|
13904
|
+
if (this.reflectionPassActive || this.probePassActive || this.capturePassActive) return;
|
|
13905
|
+
const copy = this.passColour;
|
|
13906
|
+
const source = this.sceneColor;
|
|
13907
|
+
const encoder = this.encoder;
|
|
13908
|
+
if (copy === null || source === null || encoder === null || this.pass === null) return;
|
|
13909
|
+
if (this.quality.frameGraph) this.flushGraph();
|
|
13910
|
+
this.pass.end();
|
|
13911
|
+
encoder.copyTextureToTexture({ texture: source }, { texture: copy }, [copy.width, copy.height]);
|
|
13912
|
+
this.pass = this.reopenFramePass('pass.colourTaken');
|
|
13913
|
+
}
|
|
13914
|
+
|
|
13915
|
+
/**
|
|
13916
|
+
* The frame's pass again after a copy between passes, loading everything already drawn: the same
|
|
13917
|
+
* attachments the copy's caller ended, with nothing cleared.
|
|
13918
|
+
*/
|
|
13919
|
+
private reopenFramePass(label: string): GPURenderPassEncoder | null {
|
|
13920
|
+
const encoder = this.encoder;
|
|
13921
|
+
const target = this.compositeTarget();
|
|
13922
|
+
if (encoder === null || target === null) return null;
|
|
13923
|
+
const multisampled = this.compositeMsaa() !== null;
|
|
13924
|
+
return encoder.beginRenderPass({
|
|
13925
|
+
label,
|
|
13926
|
+
timestampWrites: this.gpuTimer.writesFor(),
|
|
13927
|
+
colorAttachments: [
|
|
13928
|
+
{
|
|
13929
|
+
view: this.compositeMsaa() ?? target,
|
|
13930
|
+
resolveTarget: multisampled ? target : undefined,
|
|
13931
|
+
loadOp: 'load',
|
|
13932
|
+
storeOp: resolvedStoreOp(multisampled, false, this.quality.discardResolvedAttachments),
|
|
13933
|
+
},
|
|
13934
|
+
],
|
|
13935
|
+
depthStencilAttachment:
|
|
13936
|
+
this.depthView === null
|
|
13937
|
+
? undefined
|
|
13938
|
+
: { view: this.depthView, depthLoadOp: 'load', depthStoreOp: 'store' },
|
|
13939
|
+
});
|
|
13940
|
+
}
|
|
13941
|
+
|
|
13433
13942
|
/**
|
|
13434
13943
|
* The pass to record into, reopening one on the canvas if the frame has already gone.
|
|
13435
13944
|
*
|
|
@@ -13621,6 +14130,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
13621
14130
|
/* And a march reads the same copy, for the same reason and with the same failure if it is
|
|
13622
14131
|
discarded: a depth of zero is the far plane reversed, so every ray finds nothing. */
|
|
13623
14132
|
this.reflectionQueue.length > 0 ||
|
|
14133
|
+
/* And the frame's materials' reflections, whose surface half tests against the depth too. */
|
|
14134
|
+
this.reflectionSurface !== null ||
|
|
13624
14135
|
/* And the medium's own march, which stops where the frame's depth says a surface is. Left
|
|
13625
14136
|
out of this, every ray runs to `maxDistance` through the walls and the fog is drawn on
|
|
13626
14137
|
top of the room rather than in it. */
|
|
@@ -14485,6 +14996,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
14485
14996
|
const reads = maskOf(...(definition.reads ?? []));
|
|
14486
14997
|
this.passReads.set(handle, reads);
|
|
14487
14998
|
this.passReadsUnion |= reads;
|
|
14999
|
+
/* The colour copy a reader needs, now rather than at its first draw: see `passColour`. */
|
|
15000
|
+
if ((reads & COLOR_SNAPSHOT) !== 0 && this.passColour === null) this.allocatePassColour();
|
|
14488
15001
|
if (definition.prepare !== undefined) this.preparingPasses++;
|
|
14489
15002
|
definition.init?.(this.passDevice());
|
|
14490
15003
|
return handle;
|
|
@@ -14504,12 +15017,16 @@ export class WebGPURenderer implements RendererApi {
|
|
|
14504
15017
|
environment: PassEnvironment | null;
|
|
14505
15018
|
distanceField: ComposedField | null;
|
|
14506
15019
|
jitter: Float32Array;
|
|
15020
|
+
sceneDepth: GPUTextureView | null;
|
|
15021
|
+
sceneColor: GPUTextureView | null;
|
|
14507
15022
|
} = {
|
|
14508
15023
|
backend: 'webgpu',
|
|
14509
15024
|
encoder: null,
|
|
14510
15025
|
environment: null,
|
|
14511
15026
|
distanceField: null,
|
|
14512
15027
|
jitter: this.passJitter,
|
|
15028
|
+
sceneDepth: null,
|
|
15029
|
+
sceneColor: null,
|
|
14513
15030
|
};
|
|
14514
15031
|
/**
|
|
14515
15032
|
* The object handed to a pass as `PrepareContext.environment`, rebuilt only when it changes.
|
|
@@ -14607,6 +15124,15 @@ export class WebGPURenderer implements RendererApi {
|
|
|
14607
15124
|
* than merely what happens.
|
|
14608
15125
|
*/
|
|
14609
15126
|
this.prepareContext.distanceField = this.fieldComposer?.field ?? null;
|
|
15127
|
+
/* Settled for the frame: `ensureComposite` has run, so the view stays until the pass draws. */
|
|
15128
|
+
this.prepareContext.sceneDepth = this.resolvedDepthView;
|
|
15129
|
+
/*
|
|
15130
|
+
* Under a reconstruction a colour reader draws after the upscale (see `drawPass`), so it reads
|
|
15131
|
+
* the output-size copy the late refraction keeps rather than the render's.
|
|
15132
|
+
*/
|
|
15133
|
+
this.prepareContext.sceneColor = this.reconstructionWanted
|
|
15134
|
+
? this.refractSnapshotView
|
|
15135
|
+
: this.passColourView;
|
|
14610
15136
|
for (const definition of this.passes.definitions) {
|
|
14611
15137
|
definition?.prepare?.(this.prepareContext as PrepareContext);
|
|
14612
15138
|
}
|
|
@@ -14623,20 +15149,42 @@ export class WebGPURenderer implements RendererApi {
|
|
|
14623
15149
|
if (!this.canDraw()) return;
|
|
14624
15150
|
const definition = passAt(this.passes, handle);
|
|
14625
15151
|
if (definition === undefined) return;
|
|
15152
|
+
const reads = this.passReads.get(handle) ?? 0;
|
|
15153
|
+
/*
|
|
15154
|
+
* **Under a reconstruction, a pass that reads the frame's colour draws after the blended draws
|
|
15155
|
+
* do.** Those land after the upscale, at the output's size, so a pass drawn among them at the
|
|
15156
|
+
* render's would read a frame without them and then be covered by them — a full-screen effect
|
|
15157
|
+
* with the hit sparks drawn sharp over it. Queued instead, and drawn by `runLate` over the
|
|
15158
|
+
* reconstructed picture, after every late draw, whatever order it was asked in.
|
|
15159
|
+
*/
|
|
15160
|
+
if ((reads & COLOR_SNAPSHOT) !== 0 && this.drawsLate()) {
|
|
15161
|
+
this.latePasses.push(handle);
|
|
15162
|
+
return;
|
|
15163
|
+
}
|
|
15164
|
+
/*
|
|
15165
|
+
* A pass that reads the depth copy gets it with everything drawn so far: the copy is taken
|
|
15166
|
+
* here, once a frame, as a soft-edged sprite takes it. Before `openPass` below, because taking
|
|
15167
|
+
* it ends the frame's pass and opens another.
|
|
15168
|
+
*/
|
|
15169
|
+
if ((reads & DEPTH_SNAPSHOT) !== 0 && !this.framePresented && this.openPass() !== null) {
|
|
15170
|
+
this.takeVolumeDepth();
|
|
15171
|
+
}
|
|
15172
|
+
/*
|
|
15173
|
+
* And one that reads the colour gets it afresh, with everything drawn before it — a full-screen
|
|
15174
|
+
* pass drawn last reads the whole frame and an effect drawn mid-frame reads the frame so far.
|
|
15175
|
+
* A copy a reading pass, where the depth above is one a frame: what a colour reader draws is
|
|
15176
|
+
* itself part of what the next one should see.
|
|
15177
|
+
*/
|
|
15178
|
+
if ((reads & COLOR_SNAPSHOT) !== 0 && !this.framePresented && this.openPass() !== null) {
|
|
15179
|
+
this.takePassColour();
|
|
15180
|
+
}
|
|
14626
15181
|
if (!this.quality.frameGraph) {
|
|
14627
15182
|
const pass = this.openPass();
|
|
14628
15183
|
if (pass === null) return;
|
|
14629
15184
|
definition.draw(this.fillPassContext(pass));
|
|
14630
15185
|
return;
|
|
14631
15186
|
}
|
|
14632
|
-
recordNode(
|
|
14633
|
-
this.arena,
|
|
14634
|
-
VERB_PASS,
|
|
14635
|
-
this.passReads.get(handle) ?? 0,
|
|
14636
|
-
this.currentTarget(),
|
|
14637
|
-
handle,
|
|
14638
|
-
0,
|
|
14639
|
-
);
|
|
15187
|
+
recordNode(this.arena, VERB_PASS, reads, this.currentTarget(), handle, 0);
|
|
14640
15188
|
if (this.framePresented) this.queueOverlayFlush();
|
|
14641
15189
|
}
|
|
14642
15190
|
|
|
@@ -14757,8 +15305,9 @@ export class WebGPURenderer implements RendererApi {
|
|
|
14757
15305
|
): void {
|
|
14758
15306
|
const binner = this.clusterBinner;
|
|
14759
15307
|
i[at('uClustered')] = binner === null ? 0 : 1;
|
|
14760
|
-
if (binner === null) return;
|
|
14761
15308
|
|
|
15309
|
+
/* Written whether or not the lights are clustered: a surface overlay's rim finds its place on
|
|
15310
|
+
the screen through the same two (\`overlayScreen\`). */
|
|
14762
15311
|
const p = camera.projection;
|
|
14763
15312
|
const tanHalfFovY = 1 / (p[5] ?? 1);
|
|
14764
15313
|
const aspect = (p[5] ?? 1) / (p[0] ?? 1);
|
|
@@ -14774,6 +15323,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
14774
15323
|
f[frustumAt + 2] = tanHalfFovY;
|
|
14775
15324
|
f[frustumAt + 3] = aspect;
|
|
14776
15325
|
f.set(camera.view as Float32Array, at('uView'));
|
|
15326
|
+
if (binner === null) return;
|
|
14777
15327
|
|
|
14778
15328
|
/* The rectangles past the fixed arm's go into the table. See `ClusterLightSet.areas`. */
|
|
14779
15329
|
const set = fillClusterLightSet(env, this.clusterLights, this.shadedAreaLights);
|
|
@@ -15098,6 +15648,49 @@ export class WebGPURenderer implements RendererApi {
|
|
|
15098
15648
|
/** This frame's exposure into the tone curve. Starts at the profile's own. */
|
|
15099
15649
|
private exposure: number;
|
|
15100
15650
|
|
|
15651
|
+
/**
|
|
15652
|
+
* The `filmic` curve every later frame is graded through, until it is set again. See
|
|
15653
|
+
* `RendererApi.setFilmicCurve`; the constants are solved here once, not per frame or per pixel.
|
|
15654
|
+
*/
|
|
15655
|
+
setFilmicCurve(curve: FilmicCurve): void {
|
|
15656
|
+
this.filmCurve = curve;
|
|
15657
|
+
if (resolveFilmicCurve(curve, this.filmConstants, this.headroom) && !this.warnedFilmClamped) {
|
|
15658
|
+
this.warnedFilmClamped = true;
|
|
15659
|
+
console.warn(FILMIC_CLAMPED);
|
|
15660
|
+
}
|
|
15661
|
+
}
|
|
15662
|
+
|
|
15663
|
+
private warnedFilmClamped = false;
|
|
15664
|
+
/** The curve last set, so a new headroom can solve it again. */
|
|
15665
|
+
private filmCurve: FilmicCurve = DEFAULT_FILMIC_CURVE;
|
|
15666
|
+
/** The display's peak over paper white; 1 on a standard range. See `setDisplayLuminance`. */
|
|
15667
|
+
private headroom = 1;
|
|
15668
|
+
|
|
15669
|
+
/** The range the frame goes out in. See `RendererApi.displayRange`. */
|
|
15670
|
+
get displayRange(): DisplayRange {
|
|
15671
|
+
return this.surface.displayRange;
|
|
15672
|
+
}
|
|
15673
|
+
|
|
15674
|
+
/** Why it is that range, in words. */
|
|
15675
|
+
get displayRangeReason(): string {
|
|
15676
|
+
return this.surface.displayRangeReason;
|
|
15677
|
+
}
|
|
15678
|
+
|
|
15679
|
+
/**
|
|
15680
|
+
* How bright paper white and the display's peak are, in any one unit (nits, usually), held until
|
|
15681
|
+
* changed: their ratio is how far above white the highlights may run on a high range display,
|
|
15682
|
+
* and nothing changes on a standard one. See `RendererApi.setDisplayLuminance`.
|
|
15683
|
+
*/
|
|
15684
|
+
setDisplayLuminance(paperWhite: number, peak: number): void {
|
|
15685
|
+
this.headroom = displayHeadroom(this.surface.displayRange, paperWhite, peak);
|
|
15686
|
+
resolveFilmicCurve(this.filmCurve, this.filmConstants, this.headroom);
|
|
15687
|
+
}
|
|
15688
|
+
|
|
15689
|
+
/** The curve's eight constants, and the two views the composite uploads them through. */
|
|
15690
|
+
private readonly filmConstants = new Float32Array(FILMIC_CONSTANTS);
|
|
15691
|
+
private readonly filmA = this.filmConstants.subarray(0, 4);
|
|
15692
|
+
private readonly filmB = this.filmConstants.subarray(4, 8);
|
|
15693
|
+
|
|
15101
15694
|
/*
|
|
15102
15695
|
* -----------------------------------------------------------------------------------------
|
|
15103
15696
|
* The three screen effects, which this backend cannot yet honour — and says so.
|
|
@@ -15142,6 +15735,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
15142
15735
|
private readonly veilColor: Vec3 = [0, 0, 0];
|
|
15143
15736
|
private veilAlpha = 0;
|
|
15144
15737
|
private vignette = 0;
|
|
15738
|
+
/** The lens's colour fringe: red's and green's pull and where it begins. See `fringe.ts`. */
|
|
15739
|
+
private readonly fringe = new Float32Array(FRINGE_FLOATS);
|
|
15145
15740
|
private grain = 0;
|
|
15146
15741
|
private grainSeed = 0;
|
|
15147
15742
|
private warnedLookWithoutComposite = false;
|
|
@@ -15165,6 +15760,21 @@ export class WebGPURenderer implements RendererApi {
|
|
|
15165
15760
|
this.vignette = clampVignette(strength);
|
|
15166
15761
|
}
|
|
15167
15762
|
|
|
15763
|
+
/**
|
|
15764
|
+
* The lens's colour fringe, held until changed: `intensity` is a percentage, 0 is none, and red
|
|
15765
|
+
* and green are pulled toward the centre by their wavelength's distance from blue, growing from
|
|
15766
|
+
* `start` — a share of the half-frame, 0 the centre — to the whole of it at the edge. Applied to
|
|
15767
|
+
* scene light at the composite's first read. Needs `screenEffects`, and says so once rather than
|
|
15768
|
+
* doing nothing. See `shaders/fringe.ts`.
|
|
15769
|
+
*/
|
|
15770
|
+
setChromaticAberration(intensity: number, start = 0): void {
|
|
15771
|
+
if (!this.hasComposite) {
|
|
15772
|
+
this.warnLookWithoutComposite(intensity);
|
|
15773
|
+
return;
|
|
15774
|
+
}
|
|
15775
|
+
resolveFringe(intensity, start, this.fringe);
|
|
15776
|
+
}
|
|
15777
|
+
|
|
15168
15778
|
/**
|
|
15169
15779
|
* Film grain, held until changed: an amplitude in display values (0 is none, 0.03 is a fine
|
|
15170
15780
|
* grain) and this frame's seed. **The seed is the caller's**, because the engine takes time from
|
|
@@ -15316,9 +15926,27 @@ export class WebGPURenderer implements RendererApi {
|
|
|
15316
15926
|
this.rushStrength = Math.min(Math.max(strength, 0), 1);
|
|
15317
15927
|
}
|
|
15318
15928
|
|
|
15319
|
-
/**
|
|
15320
|
-
|
|
15929
|
+
/**
|
|
15930
|
+
* How much of the frame's motion blur to apply, 0 to 1, and the longest smear as a share of the
|
|
15931
|
+
* frame, both held until changed. See `RendererApi.setCameraMotionBlur`.
|
|
15932
|
+
*/
|
|
15933
|
+
setCameraMotionBlur(scale: number, maxShare = MOTION_BLUR_MAX_UV): void {
|
|
15321
15934
|
this.motionBlurScale = Math.min(Math.max(scale, 0), 1);
|
|
15935
|
+
this.motionBlurMax = clampMotionShare(maxShare);
|
|
15936
|
+
}
|
|
15937
|
+
|
|
15938
|
+
/** The longest smear, as a share of the frame. See `setCameraMotionBlur`. */
|
|
15939
|
+
private motionBlurMax = MOTION_BLUR_MAX_UV;
|
|
15940
|
+
private warnedObjectBlur = false;
|
|
15941
|
+
|
|
15942
|
+
/**
|
|
15943
|
+
* Said once: a draw named where it was last frame while the frame blurs, and this frame keeps no
|
|
15944
|
+
* motion target to blur it by — the camera's motion alone smears it. See `OBJECT_BLUR_UNKEPT`.
|
|
15945
|
+
*/
|
|
15946
|
+
private warnObjectBlur(): void {
|
|
15947
|
+
if (this.warnedObjectBlur) return;
|
|
15948
|
+
this.warnedObjectBlur = true;
|
|
15949
|
+
console.warn(OBJECT_BLUR_UNKEPT);
|
|
15322
15950
|
}
|
|
15323
15951
|
/**
|
|
15324
15952
|
* The camera has cut: this frame is a new shot, not the next moment of the last one.
|
|
@@ -15927,17 +16555,18 @@ export class WebGPURenderer implements RendererApi {
|
|
|
15927
16555
|
* `bindMeshPass` and `drawMesh`, which record into whatever `this.pass` is. That is the same
|
|
15928
16556
|
* arrangement `bakeFace` uses for a point light's cube, and it is why a bake must not be
|
|
15929
16557
|
* opened inside a frame's pass by a caller who then expects the frame to continue.
|
|
15930
|
-
|
|
15931
|
-
const savedPass = this.pass;
|
|
15932
|
-
/*
|
|
16558
|
+
*
|
|
15933
16559
|
* **A bake is a boundary, and boundaries flush.** The mirror says why in its own words: what
|
|
15934
16560
|
* has been recorded and not yet replayed belongs to the pass that is about to end, and leaving
|
|
15935
16561
|
* it replays it into whichever pass opens next — a draw in the wrong target rather than a
|
|
15936
16562
|
* missing one. This is the fourth such boundary in the backend and the only one that was never
|
|
15937
16563
|
* given the treatment, which is what let a bake carry the frame's pending draws into a cube
|
|
15938
|
-
* face.
|
|
16564
|
+
* face. **The pass is saved after the flush**, which opens it where none was: saved before,
|
|
16565
|
+
* the null given back left the flushed pass open and the frame's encoder locked. See
|
|
16566
|
+
* `captureScene`.
|
|
15939
16567
|
*/
|
|
15940
16568
|
if (this.quality.frameGraph) this.flushGraph();
|
|
16569
|
+
const savedPass = this.pass;
|
|
15941
16570
|
const savedEncoder = this.encoder;
|
|
15942
16571
|
/*
|
|
15943
16572
|
* **The rings are marked here and given back after the submit below**, because this bake is its
|
|
@@ -16629,6 +17258,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
16629
17258
|
this.sceneColor,
|
|
16630
17259
|
this.sceneColorMsaa,
|
|
16631
17260
|
this.refractSnapshot,
|
|
17261
|
+
this.passColour,
|
|
16632
17262
|
this.resolvedDepth,
|
|
16633
17263
|
this.aoTarget,
|
|
16634
17264
|
this.aoScratch,
|
|
@@ -16698,6 +17328,9 @@ export class WebGPURenderer implements RendererApi {
|
|
|
16698
17328
|
usage: 0x4 | 0x2 | 0x10, // TEXTURE_BINDING | COPY_DST | RENDER_ATTACHMENT
|
|
16699
17329
|
});
|
|
16700
17330
|
this.refractSnapshotView = this.refractSnapshot.createView();
|
|
17331
|
+
this.passColour = null;
|
|
17332
|
+
this.passColourView = null;
|
|
17333
|
+
if ((this.passReadsUnion & COLOR_SNAPSHOT) !== 0) this.allocatePassColour();
|
|
16701
17334
|
this.refractMips = new SnapshotMips(
|
|
16702
17335
|
device,
|
|
16703
17336
|
this.pipelines,
|
|
@@ -16926,6 +17559,10 @@ export class WebGPURenderer implements RendererApi {
|
|
|
16926
17559
|
usage: USAGE,
|
|
16927
17560
|
});
|
|
16928
17561
|
this.ssrReflectionView = this.ssrReflection.createView();
|
|
17562
|
+
/* The frame's materials, at the scene's size; a stand-in where nothing traces them. */
|
|
17563
|
+
this.reflectionSurface?.size(width, height);
|
|
17564
|
+
const probeMap = this.reflectionSurface?.probeView ?? this.blankView;
|
|
17565
|
+
const tintMap = this.reflectionSurface?.tintView ?? this.blankView;
|
|
16929
17566
|
this.ssrBindGroup =
|
|
16930
17567
|
this.resolvedDepthView === null || this.sceneColorView === null
|
|
16931
17568
|
? null
|
|
@@ -16939,8 +17576,23 @@ export class WebGPURenderer implements RendererApi {
|
|
|
16939
17576
|
},
|
|
16940
17577
|
...entry(SSR_TEXTURES.uSsrDepth, this.resolvedDepthView, true),
|
|
16941
17578
|
...entry(SSR_TEXTURES.uSsrScene, this.sceneColorView),
|
|
17579
|
+
...entry(SSR_TEXTURES.uSsrProbeMap, probeMap),
|
|
17580
|
+
...entry(SSR_TEXTURES.uSsrTintMap, tintMap),
|
|
16942
17581
|
],
|
|
16943
17582
|
});
|
|
17583
|
+
this.ssrMaterialGroup = device.createBindGroup({
|
|
17584
|
+
label: 'post.ssrMaterialGroup',
|
|
17585
|
+
layout: this.ssrMaterialLayout,
|
|
17586
|
+
entries: [
|
|
17587
|
+
{
|
|
17588
|
+
binding: SSR_MATERIAL_UNIFORMS,
|
|
17589
|
+
resource: { buffer: this.ssrMaterialUniforms, size: SSR_MATERIAL_SIZE },
|
|
17590
|
+
},
|
|
17591
|
+
...entry(SSR_MATERIAL_TEXTURES.uSsrReflection, this.ssrReflectionView),
|
|
17592
|
+
...entry(SSR_MATERIAL_TEXTURES.uSsrProbeMap, probeMap),
|
|
17593
|
+
...entry(SSR_MATERIAL_TEXTURES.uSsrTintMap, tintMap),
|
|
17594
|
+
],
|
|
17595
|
+
});
|
|
16944
17596
|
this.ssrResolveGroup = device.createBindGroup({
|
|
16945
17597
|
label: 'post.ssrResolveGroup',
|
|
16946
17598
|
layout: this.ssrResolveLayout,
|