@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
|
@@ -59,17 +59,23 @@ import { sceneCaptureTexels } from '../../sceneCapture.js';
|
|
|
59
59
|
import { gpuCompressedFormats } from './compressedUpload.js';
|
|
60
60
|
import { GpuClothBinding, GpuClothParticles, clothStandIn } from './clothTextures.js';
|
|
61
61
|
import { MODEL_PARAM_FLOATS, eyeAxisInWorld, packModel, skinProfileDistance, } from '../../surfaceModel.js';
|
|
62
|
+
import { OVERLAY_FLOATS, overlayLays, packSurfaceOverlay, } from '../../surfaceOverlay.js';
|
|
63
|
+
import { frameReflectionsRefused, resolveFrameReflections, } from '../../frameReflections.js';
|
|
64
|
+
import { ReflectionSurfacePass } from './reflectionSurfacePass.js';
|
|
62
65
|
import { validateClothBinding } from '../../clothBindingData.js';
|
|
63
66
|
import { createSdfTextBindGroup, createSdfTextBindGroupLayout, GpuSdfText, sdfTextPipeline, SDF_TEXT_FRAG_FIELDS, SDF_TEXT_FRAG_SIZE, SDF_TEXT_VERT_FIELDS, SDF_TEXT_VERT_SIZE, } from './sdfTextPass.js';
|
|
64
67
|
import { createFilmBindGroup, createFilmBindGroupLayout, filmPipeline, FILM_FRAG_FIELDS, FILM_FRAG_SIZE, FILM_VERT_FIELDS, FILM_VERT_SIZE, } from './filmPass.js';
|
|
65
68
|
import { reflectionTargetSize } from '../../planarReflectionDraw.js';
|
|
66
69
|
import { drawingBufferSize } from '../../drawingBuffer.js';
|
|
67
|
-
import { MOTION_BLUR_MAX_UV, OUTPUT_TRANSFORM_CODE, RUSH_REACH_UV } from '../../vertexDefaults.js';
|
|
70
|
+
import { MOTION_BLUR_MAX_UV, OBJECT_BLUR_UNKEPT, OUTPUT_TRANSFORM_CODE, RUSH_REACH_UV, clampMotionShare, forwardTransformCode, } from '../../vertexDefaults.js';
|
|
71
|
+
import { FRINGE_FLOATS, resolveFringe } from '../../shaders/fringe.js';
|
|
72
|
+
import { displayHeadroom } from '../../displayRange.js';
|
|
73
|
+
import { DEFAULT_FILMIC_CURVE, FILMIC_CLAMPED, FILMIC_CONSTANTS, FILMIC_WITHOUT_COMPOSITE, resolveFilmicCurve, } from '../../filmicCurve.js';
|
|
68
74
|
import { ExposurePass } from './exposurePass.js';
|
|
69
75
|
import { AO_STORE } from '../../shaders/ambientOcclusion.js';
|
|
70
76
|
import { clampAutoExposure } from '../../shaders/exposure.js';
|
|
71
77
|
import { clampLocalExposure } from '../../shaders/localExposure.js';
|
|
72
|
-
import { createDepthResolveLayout, createPostStageLayout, depthResolvePipeline, postPipeline, postPipelineTargets, sceneColorFormat, AO_BLUR_FIELDS, AO_BLUR_FRAG_WGSL, AO_BLUR_SIZE, AO_BLUR_TEXTURES, AO_BLUR_UNIFORMS, AO_FRAG_FIELDS, AO_FRAG_SIZE, AO_FRAG_WGSL, AO_TEXTURES, AO_UNIFORMS, BLOOM_DOWNSAMPLE_FIELDS, BLOOM_DOWNSAMPLE_FRAG_WGSL, BLOOM_PREFILTER_FIELDS, BLOOM_PREFILTER_FRAG_WGSL, BLOOM_STAGE_SIZE, BLOOM_TEXTURES, BLOOM_UNIFORMS, BLOOM_UPSAMPLE_FIELDS, TINTED_ADD_BLEND, BLOOM_UPSAMPLE_FRAG_WGSL, RESOLVED_DEPTH_FORMAT, RUSH_FRAG_FIELDS, RUSH_FRAG_SIZE, RUSH_FRAG_WGSL, RUSH_TEXTURES, RUSH_UNIFORMS, TAA_FRAG_FIELDS, TAA_FRAG_SIZE, TAA_TEXTURES, TAA_UNIFORMS, TEMPORAL_RESOLVE_FRAG_WGSL, MEDIUM_FRAG_FIELDS, MEDIUM_FRAG_SIZE, MEDIUM_FRAG_WGSL, MEDIUM_TEXTURES, MEDIUM_TRANSMIT_BLEND, MEDIUM_UNIFORMS, MEDIUM_UPSAMPLE_FIELDS, MEDIUM_UPSAMPLE_FRAG_WGSL, MEDIUM_UPSAMPLE_SIZE, MEDIUM_UPSAMPLE_TEXTURES, MEDIUM_UPSAMPLE_UNIFORMS, OIT_COMPOSITE_BLEND, OIT_RESOLVE_FRAG_WGSL, OIT_TEXTURES, DECAL_FRAG_FIELDS, DECAL_FRAG_SIZE, DECAL_MULTIPLY_BLEND, DECAL_PROJECT_FRAG_WGSL, DECAL_TEXTURES, DECAL_UNIFORMS, PREMULTIPLIED_OVER_BLEND, REFLECTION_OVER_BLEND, SSR_FRAG_FIELDS, SSR_FRAG_SIZE, SSR_RESOLVE_FRAG_WGSL, SSR_RESOLVE_TEXTURES, SSR_TEXTURES, SSR_TRACE_FRAG_WGSL, SSR_UNIFORMS, createOitResolveLayout, createSsrResolveLayout, } from './postPass.js';
|
|
78
|
+
import { createDepthResolveLayout, createPostStageLayout, depthResolvePipeline, postPipeline, postPipelineTargets, sceneColorFormat, AO_BLUR_FIELDS, AO_BLUR_FRAG_WGSL, AO_BLUR_SIZE, AO_BLUR_TEXTURES, AO_BLUR_UNIFORMS, AO_FRAG_FIELDS, AO_FRAG_SIZE, AO_FRAG_WGSL, AO_TEXTURES, AO_UNIFORMS, BLOOM_DOWNSAMPLE_FIELDS, BLOOM_DOWNSAMPLE_FRAG_WGSL, BLOOM_PREFILTER_FIELDS, BLOOM_PREFILTER_FRAG_WGSL, BLOOM_STAGE_SIZE, BLOOM_TEXTURES, BLOOM_UNIFORMS, BLOOM_UPSAMPLE_FIELDS, TINTED_ADD_BLEND, BLOOM_UPSAMPLE_FRAG_WGSL, RESOLVED_DEPTH_FORMAT, RUSH_FRAG_FIELDS, RUSH_FRAG_SIZE, RUSH_FRAG_WGSL, RUSH_TEXTURES, RUSH_UNIFORMS, TAA_FRAG_FIELDS, TAA_FRAG_SIZE, TAA_TEXTURES, TAA_UNIFORMS, TEMPORAL_RESOLVE_FRAG_WGSL, MEDIUM_FRAG_FIELDS, MEDIUM_FRAG_SIZE, MEDIUM_FRAG_WGSL, MEDIUM_TEXTURES, MEDIUM_TRANSMIT_BLEND, MEDIUM_UNIFORMS, MEDIUM_UPSAMPLE_FIELDS, MEDIUM_UPSAMPLE_FRAG_WGSL, MEDIUM_UPSAMPLE_SIZE, MEDIUM_UPSAMPLE_TEXTURES, MEDIUM_UPSAMPLE_UNIFORMS, OIT_COMPOSITE_BLEND, OIT_RESOLVE_FRAG_WGSL, OIT_TEXTURES, DECAL_FRAG_FIELDS, DECAL_FRAG_SIZE, DECAL_MULTIPLY_BLEND, DECAL_PROJECT_FRAG_WGSL, DECAL_TEXTURES, DECAL_UNIFORMS, PREMULTIPLIED_OVER_BLEND, FRAME_REFLECTION_BLEND, REFLECTION_OVER_BLEND, SSR_FRAG_FIELDS, SSR_FRAG_SIZE, SSR_MATERIAL_FIELDS, SSR_MATERIAL_RESOLVE_FRAG_WGSL, SSR_MATERIAL_SIZE, SSR_MATERIAL_TEXTURES, SSR_MATERIAL_UNIFORMS, SSR_RESOLVE_FRAG_WGSL, SSR_RESOLVE_TEXTURES, SSR_TEXTURES, SSR_TRACE_FRAG_WGSL, SSR_UNIFORMS, createOitResolveLayout, createSsrResolveLayout, } from './postPass.js';
|
|
73
79
|
import { GRADE_PLACEHOLDER_SIZE, identityGradeLut, validateGradeLut, } from '../../colourGrade.js';
|
|
74
80
|
import { FILM_LOOK_WITHOUT_COMPOSITE, clampGrain, clampVignette, grainSeed, } from '../../shaders/filmLook.js';
|
|
75
81
|
import { createProbeMipBindGroupLayout, createProbePrefilterBindGroupLayout, PREFILTER_UNIFORM_OFFSETS, probeLevels, probeMipPipeline, probePrefilterPipeline, PROBE_FACES, PROBE_FACE_BASIS, PROBE_FAR_M, PROBE_FOV_DEG, PROBE_MIP_UNIFORM_SIZE, PROBE_NEAR_M, } from './probePass.js';
|
|
@@ -93,7 +99,7 @@ import { GpuGlassTint } from './glassTintPass.js';
|
|
|
93
99
|
import { SUN_DYNAMIC_LAYER, SUN_GLASS_MOVING_LAYER, SUN_GLASS_STATIC_LAYER, SUN_PEELED_LAYER, SUN_STATIC_LAYER, } from '../../shadowMap.js';
|
|
94
100
|
import { VERTEX_LAYOUT, createGpuMesh, createGpuMeshIncremental } from './buffers.js';
|
|
95
101
|
import { SkinPaletteRing } from './skinPaletteRing.js';
|
|
96
|
-
import { DEPTH_FORMAT, FLAT_VERT_FIELDS, FLAT_VERT_SIZE, createFlatBindGroup, createFlatBindGroupLayout, flatFragmentBindings, flatVariant, flatVertexBindings, flatPipeline, flatPipelineAsync, } from './flatPass.js';
|
|
102
|
+
import { DEPTH_FORMAT, FLAT_VERT_FIELDS, FLAT_VERT_SIZE, createFlatBindGroup, createFlatBindGroupLayout, flatFragmentBindings, flatVariant, flatVertexBindings, flatPipeline, flatPipelineAsync, probeStageCeilings, } from './flatPass.js';
|
|
97
103
|
import { DEPTH_FRAG_BINDING, DEPTH_FRAG_SIZE, DEPTH_FRAG_WGSL, DEPTH_PREVIOUS_BINDING, DEPTH_CUTOUT_VERT_FIELDS, DEPTH_CUTOUT_VERT_SIZE, DEPTH_INSTANCED_CUTOUT_VERT_FIELDS, DEPTH_VERT_FIELDS, DEPTH_VERT_SIZE, SHADOW_FORMAT, createDepthBindGroup, createDepthBindGroupLayout, depthPipeline, } from './depthPass.js';
|
|
98
104
|
import { PipelineCache } from './pipelineCache.js';
|
|
99
105
|
import { SKIN_TARGET_FORMAT, SkinScatterPass } from './skinScatterPass.js';
|
|
@@ -215,34 +221,20 @@ const MAX_SKY_DRAWS = 64;
|
|
|
215
221
|
/**
|
|
216
222
|
* Whether the environment probe's permutation fits this device's per-stage binding limits.
|
|
217
223
|
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
224
|
+
* **Two ceilings, counted separately**: sampled textures and samplers, since textures that share a
|
|
225
|
+
* sampler (`// wgsl:share` in `flat/preamble.ts`) count once against the second. The widest variant
|
|
226
|
+
* binds sixteen textures and twelve samplers, which is exactly WebGPU's default texture ceiling, and
|
|
227
|
+
* `select.ts` asks for the adapter's own — 48 here — so the probe does not depend on that request.
|
|
222
228
|
*/
|
|
223
229
|
function probeFits(device) {
|
|
224
230
|
/*
|
|
225
|
-
* **
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
* it
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
*
|
|
233
|
-
* It used to declare seventeen *samplers* too, and that was the binding constraint: this
|
|
234
|
-
* adapter offers exactly sixteen and no request can raise it, so the probe stood down and
|
|
235
|
-
* reflective surfaces kept a gradient. The shadow bindings share one declaration — see
|
|
236
|
-
* `// wgsl:share shadow` in `flat/preamble.ts`, which costs nothing because they were already
|
|
237
|
-
* one object — so the count is four, and the fourth is the normal map's: it is a colour image
|
|
238
|
-
* and wants filtering, which a non-filtering shadow sampler cannot give it.
|
|
239
|
-
*
|
|
240
|
-
* **Counted from the generated bindings rather than typed**: `FLAT_BINDINGS.flatFrag`'s
|
|
241
|
-
* widest variant is `directionalShadows+environmentProbe+pointShadows`, seven textures and
|
|
242
|
-
* four samplers. A stale number here reads as a device declining a feature it can run.
|
|
243
|
-
*/
|
|
244
|
-
const TEXTURES = 7;
|
|
245
|
-
const SAMPLERS = 4;
|
|
231
|
+
* **Counted from the generated bindings rather than typed** (`probeStageCeilings`). It was typed
|
|
232
|
+
* here as seven textures and four samplers while the widest variant grew to sixteen and twelve
|
|
233
|
+
* with the material maps, glass, the fixture atlas and DriftLight: a stale number reads as a
|
|
234
|
+
* device declining a feature it can run, or, the way this one had drifted, as one admitted to a
|
|
235
|
+
* permutation it cannot bind.
|
|
236
|
+
*/
|
|
237
|
+
const { textures: TEXTURES, samplers: SAMPLERS } = probeStageCeilings();
|
|
246
238
|
const fits = device.limits.maxSampledTexturesPerShaderStage >= TEXTURES &&
|
|
247
239
|
device.limits.maxSamplersPerShaderStage >= SAMPLERS;
|
|
248
240
|
if (!fits) {
|
|
@@ -279,6 +271,7 @@ const VERB_PASS = 3;
|
|
|
279
271
|
/** What the depth snapshot copies from, and what it copies into. */
|
|
280
272
|
const DEPTH_SOURCE = maskOf('sceneDepth');
|
|
281
273
|
const DEPTH_SNAPSHOT = maskOf('depthSnapshot');
|
|
274
|
+
const COLOR_SNAPSHOT = maskOf('colorSnapshot');
|
|
282
275
|
/** The mirror's own colour and depth. */
|
|
283
276
|
const MIRROR_TARGET = maskOf('mirrorColor', 'mirrorDepth');
|
|
284
277
|
/**
|
|
@@ -1280,6 +1273,17 @@ export class WebGPURenderer {
|
|
|
1280
1273
|
*/
|
|
1281
1274
|
refractSnapshot = null;
|
|
1282
1275
|
refractSnapshotView = null;
|
|
1276
|
+
/**
|
|
1277
|
+
* The frame's colour as a registered pass reads it (`reads: ['colorSnapshot']`): the scene's own
|
|
1278
|
+
* size and format, refilled at each such pass's draw. Allocated only once a pass declares the
|
|
1279
|
+
* read, beside the frame's targets, and never in the frame loop. See `takePassColour`.
|
|
1280
|
+
*/
|
|
1281
|
+
passColour = null;
|
|
1282
|
+
passColourView = null;
|
|
1283
|
+
/** Registered passes that read the colour, queued this frame for after the upscale. */
|
|
1284
|
+
latePasses = [];
|
|
1285
|
+
/** A late pass draws unjittered, as every late draw does. */
|
|
1286
|
+
latePassJitter = new Float32Array(2);
|
|
1283
1287
|
/** Whether this frame has taken it. One snapshot serves every refracting draw. */
|
|
1284
1288
|
refractSnapshotTaken = false;
|
|
1285
1289
|
/**
|
|
@@ -1782,8 +1786,18 @@ export class WebGPURenderer {
|
|
|
1782
1786
|
reflectionQueue = new ReflectionQueue();
|
|
1783
1787
|
ssrLayout;
|
|
1784
1788
|
ssrResolveLayout;
|
|
1789
|
+
/** The frame's materials' reflections, resolved; null where off. See `frameReflections.ts`. */
|
|
1790
|
+
frameReflections;
|
|
1791
|
+
/** Their surface half, null where they are off or cannot be drawn: `reflectionSurfacePass.ts`. */
|
|
1792
|
+
reflectionSurface;
|
|
1793
|
+
/** The material resolve: its layout, its one block (the blur) and its group over the three maps. */
|
|
1794
|
+
ssrMaterialLayout;
|
|
1795
|
+
ssrMaterialUniforms;
|
|
1796
|
+
ssrMaterialFloats = new Float32Array(SSR_MATERIAL_SIZE / 4);
|
|
1797
|
+
ssrMaterialGroup = null;
|
|
1785
1798
|
ssrUniforms;
|
|
1786
|
-
|
|
1799
|
+
/* One slot a surface, and one more for the frame's materials' trace. */
|
|
1800
|
+
ssrStaging = new ArrayBuffer(SSR_SLOT * (MAX_REFLECTIVE_SURFACES + 1));
|
|
1787
1801
|
ssrFloats = new Float32Array(this.ssrStaging);
|
|
1788
1802
|
ssrReflection = null;
|
|
1789
1803
|
ssrReflectionView = null;
|
|
@@ -2263,9 +2277,16 @@ export class WebGPURenderer {
|
|
|
2263
2277
|
camera.updateMatrices(target.width / target.height);
|
|
2264
2278
|
const { device } = this.surface;
|
|
2265
2279
|
const { depth, colorMsaa } = target.attachments(this.samples, this.pipelines.format);
|
|
2266
|
-
|
|
2280
|
+
/*
|
|
2281
|
+
* **Flushed, and only then set aside.** The flush replays what the frame has recorded, and
|
|
2282
|
+
* replaying opens the frame's pass where none is open yet; saved before it, the pass given back
|
|
2283
|
+
* afterwards was the null that stood there, while the one the flush opened was left open on the
|
|
2284
|
+
* frame's encoder and every later pass on it was refused — a black frame for any capture taken
|
|
2285
|
+
* after the frame had drawn something. `scripts/capture-check.mjs` takes one at three points.
|
|
2286
|
+
*/
|
|
2267
2287
|
if (this.quality.frameGraph)
|
|
2268
2288
|
this.flushGraph();
|
|
2289
|
+
const savedPass = this.pass;
|
|
2269
2290
|
const savedEncoder = this.encoder;
|
|
2270
2291
|
this.markRings();
|
|
2271
2292
|
const skinMark = this.skinPalettes.mark();
|
|
@@ -2371,6 +2392,8 @@ export class WebGPURenderer {
|
|
|
2371
2392
|
this.emissiveMap = null;
|
|
2372
2393
|
if (this.modelMap === texture)
|
|
2373
2394
|
this.modelMap = null;
|
|
2395
|
+
if (this.overlayMaps === texture)
|
|
2396
|
+
this.overlayMaps = null;
|
|
2374
2397
|
this.forgetBindingsOf(texture);
|
|
2375
2398
|
texture.dispose();
|
|
2376
2399
|
this.captureTargets.get(texture)?.dispose();
|
|
@@ -2388,6 +2411,7 @@ export class WebGPURenderer {
|
|
|
2388
2411
|
/* Same reasoning, for whichever SDF labels were bound against this atlas. */
|
|
2389
2412
|
this.sdfTextBindGroups.delete(texture);
|
|
2390
2413
|
this.flatBindGroups.clear();
|
|
2414
|
+
this.overlayGroups.clear();
|
|
2391
2415
|
/*
|
|
2392
2416
|
* The blank one is built with the albedo held aside, because `buildFlatBindGroup` reads
|
|
2393
2417
|
* `this.albedo` for that slot — leaving it in place would put a real colour map into the
|
|
@@ -2723,6 +2747,21 @@ export class WebGPURenderer {
|
|
|
2723
2747
|
packAmbientSH(coefficients, this.ambientSH);
|
|
2724
2748
|
this.material((f) => f.set(this.ambientSH, this.materialField('uAmbientSH')));
|
|
2725
2749
|
}
|
|
2750
|
+
/**
|
|
2751
|
+
* The overlay the following draws wear, or null for none: pass state, put back to none by
|
|
2752
|
+
* `bindMeshPass`. See `renderer.ts` and `surfaceOverlay.ts`. The numbers go in the material block,
|
|
2753
|
+
* which every variant declares; the atlas goes in a twin of each flat group (`overlayGroup`).
|
|
2754
|
+
*/
|
|
2755
|
+
setSurfaceOverlay(overlay) {
|
|
2756
|
+
if (this.surface.lost)
|
|
2757
|
+
return;
|
|
2758
|
+
packSurfaceOverlay(overlay, this.overlayFloats);
|
|
2759
|
+
/* The first overlay laid is when the lit pipelines start carrying one. */
|
|
2760
|
+
if (overlayLays(overlay))
|
|
2761
|
+
this.enableLit('SURFACE_OVERLAY');
|
|
2762
|
+
this.overlayMaps = (overlay?.maps ?? null);
|
|
2763
|
+
this.material((f) => f.set(this.overlayFloats, this.materialField('uOverlay')));
|
|
2764
|
+
}
|
|
2726
2765
|
/** How reflective the surfaces drawn next are. Clamped, as `renderer.ts` clamps it. */
|
|
2727
2766
|
setSurfaceReflectivity(amount) {
|
|
2728
2767
|
this.material((f) => {
|
|
@@ -3393,7 +3432,7 @@ export class WebGPURenderer {
|
|
|
3393
3432
|
*/
|
|
3394
3433
|
gradeCode() {
|
|
3395
3434
|
return !this.hasComposite || !this.quality.hdrScene
|
|
3396
|
-
? (
|
|
3435
|
+
? forwardTransformCode(this.quality.outputTransform)
|
|
3397
3436
|
: 0;
|
|
3398
3437
|
}
|
|
3399
3438
|
gradeExposure() {
|
|
@@ -5313,6 +5352,10 @@ export class WebGPURenderer {
|
|
|
5313
5352
|
this.skinPalettes = new SkinPaletteRing(quality.drawsPerFrame);
|
|
5314
5353
|
this.maxDrawingBufferPixels = quality.maxDrawingBufferPixels;
|
|
5315
5354
|
this.exposure = quality.outputExposure;
|
|
5355
|
+
resolveFilmicCurve(DEFAULT_FILMIC_CURVE, this.filmConstants);
|
|
5356
|
+
if (quality.outputTransform === 'filmic' && !(quality.screenEffects && quality.hdrScene)) {
|
|
5357
|
+
console.warn(FILMIC_WITHOUT_COMPOSITE);
|
|
5358
|
+
}
|
|
5316
5359
|
this.flushSchedule =
|
|
5317
5360
|
quality.frameGraph && quality.identifierGraph
|
|
5318
5361
|
? createFlushSchedule(MAX_DRAWS_PER_FRAME)
|
|
@@ -5382,6 +5425,25 @@ export class WebGPURenderer {
|
|
|
5382
5425
|
console.warn("WebGPU: skinScattering 'screen-space' needs screenEffects to add into; skin is pre-integrated");
|
|
5383
5426
|
}
|
|
5384
5427
|
this.skinScatter = skinScreen ? new SkinScatterPass(device) : null;
|
|
5428
|
+
/*
|
|
5429
|
+
* The frame's materials' reflections, where the frame can carry them: scene light to swap the
|
|
5430
|
+
* probe's share in, and one sample, which the trace's depth copy and both maps are. Said once
|
|
5431
|
+
* where it cannot. See `frameReflections.ts`.
|
|
5432
|
+
*/
|
|
5433
|
+
this.frameReflections = resolveFrameReflections(quality.screenSpaceReflections);
|
|
5434
|
+
const reflectionsRefusal = this.frameReflections === null
|
|
5435
|
+
? null
|
|
5436
|
+
: !quality.screenEffects || !quality.hdrScene
|
|
5437
|
+
? 'the frame keeps no scene light to swap the probe in (`screenEffects` and `hdrScene`)'
|
|
5438
|
+
: this.samples > 1
|
|
5439
|
+
? 'a multisampled frame has no single depth or colour to march through'
|
|
5440
|
+
: null;
|
|
5441
|
+
if (reflectionsRefusal !== null)
|
|
5442
|
+
console.warn(`WebGPU: ${frameReflectionsRefused(reflectionsRefusal)}`);
|
|
5443
|
+
this.reflectionSurface =
|
|
5444
|
+
this.frameReflections !== null && reflectionsRefusal === null
|
|
5445
|
+
? new ReflectionSurfacePass(device)
|
|
5446
|
+
: null;
|
|
5385
5447
|
this.skinPipelines = skinScreen
|
|
5386
5448
|
? new PipelineCache(device, SKIN_TARGET_FORMAT, this.samples, glassShadows, quality.clusteredLights)
|
|
5387
5449
|
: null;
|
|
@@ -5737,6 +5799,11 @@ export class WebGPURenderer {
|
|
|
5737
5799
|
addressModeU: 'clamp-to-edge',
|
|
5738
5800
|
addressModeV: 'clamp-to-edge',
|
|
5739
5801
|
});
|
|
5802
|
+
/* Or a surface overlay's atlas, in the twin a draw wearing one binds: `overlayGroup`. */
|
|
5803
|
+
const maps = this.overlayBinding ? this.overlayMaps : null;
|
|
5804
|
+
if (maps !== null) {
|
|
5805
|
+
return { view: maps.layerView() ?? blankView, sampler: this.refractSampler };
|
|
5806
|
+
}
|
|
5740
5807
|
return { view: this.refractSnapshotView ?? blankView, sampler: this.refractSampler };
|
|
5741
5808
|
}
|
|
5742
5809
|
/*
|
|
@@ -5867,6 +5934,8 @@ export class WebGPURenderer {
|
|
|
5867
5934
|
{ binding: RUSH_TEXTURES.uExposureHeld },
|
|
5868
5935
|
/* Local exposure's grid, read filtered between tiles. See `localExposure.ts`. */
|
|
5869
5936
|
{ binding: RUSH_TEXTURES.uExposureLocal },
|
|
5937
|
+
/* Each mover's own motion: the reconstruction's target, or the scene where there is none. */
|
|
5938
|
+
{ binding: RUSH_TEXTURES.uMotion },
|
|
5870
5939
|
]);
|
|
5871
5940
|
this.rushUniforms = device.createBuffer({
|
|
5872
5941
|
label: 'post.rushUniforms',
|
|
@@ -5884,11 +5953,24 @@ export class WebGPURenderer {
|
|
|
5884
5953
|
this.ssrLayout = createPostStageLayout(device, SSR_UNIFORMS, SSR_FRAG_SIZE, [
|
|
5885
5954
|
{ binding: SSR_TEXTURES.uSsrDepth, filterable: false },
|
|
5886
5955
|
{ binding: SSR_TEXTURES.uSsrScene },
|
|
5956
|
+
/* The frame's materials, for the trace that reads them: `frameReflections.ts`. */
|
|
5957
|
+
{ binding: SSR_TEXTURES.uSsrProbeMap },
|
|
5958
|
+
{ binding: SSR_TEXTURES.uSsrTintMap },
|
|
5887
5959
|
]);
|
|
5888
5960
|
this.ssrResolveLayout = createSsrResolveLayout(device);
|
|
5961
|
+
this.ssrMaterialLayout = createPostStageLayout(device, SSR_MATERIAL_UNIFORMS, SSR_MATERIAL_SIZE, [
|
|
5962
|
+
{ binding: SSR_MATERIAL_TEXTURES.uSsrReflection },
|
|
5963
|
+
{ binding: SSR_MATERIAL_TEXTURES.uSsrProbeMap },
|
|
5964
|
+
{ binding: SSR_MATERIAL_TEXTURES.uSsrTintMap },
|
|
5965
|
+
]);
|
|
5966
|
+
this.ssrMaterialUniforms = device.createBuffer({
|
|
5967
|
+
label: 'post.ssrMaterialUniforms',
|
|
5968
|
+
size: SSR_MATERIAL_SIZE,
|
|
5969
|
+
usage: USAGE_UNIFORM_DST,
|
|
5970
|
+
});
|
|
5889
5971
|
this.ssrUniforms = device.createBuffer({
|
|
5890
5972
|
label: 'post.ssrUniforms',
|
|
5891
|
-
size: SSR_SLOT * MAX_REFLECTIVE_SURFACES,
|
|
5973
|
+
size: SSR_SLOT * (MAX_REFLECTIVE_SURFACES + 1),
|
|
5892
5974
|
usage: USAGE_UNIFORM_DST,
|
|
5893
5975
|
});
|
|
5894
5976
|
this.decalUniforms = device.createBuffer({
|
|
@@ -6304,7 +6386,11 @@ export class WebGPURenderer {
|
|
|
6304
6386
|
/* Last frame's snapshot is last frame's scene. */
|
|
6305
6387
|
this.refractSnapshotTaken = false;
|
|
6306
6388
|
this.skinScattered = false;
|
|
6389
|
+
/* One frame of the jitter's sequence however many times the frame binds its camera. */
|
|
6390
|
+
this.temporalHistory.nextFrame();
|
|
6391
|
+
this.latePasses.length = 0;
|
|
6307
6392
|
this.skinScatter?.reset();
|
|
6393
|
+
this.reflectionSurface?.reset();
|
|
6308
6394
|
this.plumeVerts.reset();
|
|
6309
6395
|
this.plumeFrags.reset();
|
|
6310
6396
|
this.waterVerts.reset();
|
|
@@ -6861,6 +6947,9 @@ export class WebGPURenderer {
|
|
|
6861
6947
|
f[at('uWriteMode') + 1] = 0;
|
|
6862
6948
|
/* 1, not 0: a multiplier's identity is one. See `setEnvironmentGain`. */
|
|
6863
6949
|
f[at('uEnvironmentDials') + 2] = 1;
|
|
6950
|
+
/* No overlay until a draw sets one. See `setSurfaceOverlay`. */
|
|
6951
|
+
this.overlayFloats.fill(0);
|
|
6952
|
+
f.fill(0, at('uOverlay'), at('uOverlay') + OVERLAY_FLOATS);
|
|
6864
6953
|
/* The frame's own ambient until a draw asks for its own. See `setAmbientSH`. */
|
|
6865
6954
|
f.fill(0, at('uAmbientSH'), at('uAmbientSH') + AMBIENT_SH_FLOATS);
|
|
6866
6955
|
/*
|
|
@@ -7229,6 +7318,8 @@ export class WebGPURenderer {
|
|
|
7229
7318
|
this.emissiveMap = null;
|
|
7230
7319
|
this.modelMap = null;
|
|
7231
7320
|
this.bindGroup = this.blankAlbedoBindGroup;
|
|
7321
|
+
/* Nor an overlay; its numbers are zeroed with the block's other defaults. */
|
|
7322
|
+
this.overlayMaps = null;
|
|
7232
7323
|
this.uvScale[0] = 1;
|
|
7233
7324
|
this.uvScale[1] = 1;
|
|
7234
7325
|
this.uvOffset[0] = 0;
|
|
@@ -7310,6 +7401,9 @@ export class WebGPURenderer {
|
|
|
7310
7401
|
*/
|
|
7311
7402
|
if (this.tracksMotionNow())
|
|
7312
7403
|
this.recordDrawMotion(mesh, model, previousModel);
|
|
7404
|
+
else if (previousModel !== null && this.quality.cameraMotionBlur * this.motionBlurScale > 0) {
|
|
7405
|
+
this.warnObjectBlur();
|
|
7406
|
+
}
|
|
7313
7407
|
noteMapsWithoutUvs(mesh, mesh.hasUvs, this.materialHasMaps);
|
|
7314
7408
|
/* An opaque draw meets the medium as the dial says: `submitMesh` reads an absent `fog` as
|
|
7315
7409
|
true, which is a translucent draw's default and would put every opaque draw back in the fog
|
|
@@ -7665,6 +7759,10 @@ export class WebGPURenderer {
|
|
|
7665
7759
|
if (splits) {
|
|
7666
7760
|
this.keepSkinDiffuse(mesh, key, base, skinned, morphed, deltas, layer, eight, clothed, slot, material);
|
|
7667
7761
|
}
|
|
7762
|
+
/* And its surface half, for the frame's materials' reflections. See `keepReflectionSurface`. */
|
|
7763
|
+
if (!blend && lit && depthWrite && this.reflectionSurfaceKeeps()) {
|
|
7764
|
+
this.keepReflectionSurface(mesh, key, base, skinned, morphed, deltas, layer, eight, clothed, modelKind, slot, material, null, 1, null);
|
|
7765
|
+
}
|
|
7668
7766
|
}
|
|
7669
7767
|
/* Back to the defaults for whatever is drawn next, exactly as `renderer.ts` restores them —
|
|
7670
7768
|
unconditionally, because the scratch state above is shared by every draw after this one
|
|
@@ -7794,6 +7892,7 @@ export class WebGPURenderer {
|
|
|
7794
7892
|
const glassy = resolveGlass(options.glass, this.glassScratch);
|
|
7795
7893
|
if ((refraction <= 0 && !glassy) || !this.takeRefractSnapshot())
|
|
7796
7894
|
return false;
|
|
7895
|
+
this.seeThroughBound = true;
|
|
7797
7896
|
const glass = this.glassScratch;
|
|
7798
7897
|
/* Strength, path thickness, transmission, frost: one vector, for the budget the shader states. */
|
|
7799
7898
|
const seeAt = this.materialField('uSeeThrough');
|
|
@@ -7814,6 +7913,7 @@ export class WebGPURenderer {
|
|
|
7814
7913
|
}
|
|
7815
7914
|
/** Put the scratch back after `bindSeeThrough`: refraction and glass off. */
|
|
7816
7915
|
releaseSeeThrough() {
|
|
7916
|
+
this.seeThroughBound = false;
|
|
7817
7917
|
const seeAt = this.materialField('uSeeThrough');
|
|
7818
7918
|
this.perFrameFloats[seeAt] = 0;
|
|
7819
7919
|
this.perFrameFloats[seeAt + 2] = 0;
|
|
@@ -7978,6 +8078,10 @@ export class WebGPURenderer {
|
|
|
7978
8078
|
else
|
|
7979
8079
|
pass.drawIndexedIndirect(cut.args, 0);
|
|
7980
8080
|
}
|
|
8081
|
+
/* And its surface half, for the frame's materials' reflections, with the same survivors. */
|
|
8082
|
+
if (!blend && lit && depthWrite && this.reflectionSurfaceKeeps()) {
|
|
8083
|
+
this.keepReflectionSurface(mesh, key, base, false, false, null, layer, false, false, modelKind, slot, material, instanceBuffer, count, cut === null ? null : cut.args);
|
|
8084
|
+
}
|
|
7981
8085
|
}
|
|
7982
8086
|
/* Back to the defaults, unconditionally and for the reason `drawMesh` gives at length: this
|
|
7983
8087
|
scratch block is cumulative state shared by every draw after this one, and an early return
|
|
@@ -8606,6 +8710,20 @@ export class WebGPURenderer {
|
|
|
8606
8710
|
warnedNoPointShadowArray = false;
|
|
8607
8711
|
/** Said once, for the retired ambient dial. */
|
|
8608
8712
|
warnedEnvironmentAmbient = false;
|
|
8713
|
+
/** The overlay the draws wear, `uOverlay`: see `setSurfaceOverlay`. */
|
|
8714
|
+
overlayFloats = new Float32Array(OVERLAY_FLOATS);
|
|
8715
|
+
/** Its atlas, bound where the refraction copy goes in a twin of each flat group. */
|
|
8716
|
+
overlayMaps = null;
|
|
8717
|
+
/**
|
|
8718
|
+
* Each flat group's twin with the atlas in the refraction slot, by the group and the atlas, built
|
|
8719
|
+
* on the first draw that wears it and dropped wherever `flatBindGroups` is.
|
|
8720
|
+
*/
|
|
8721
|
+
overlayGroups = new Map();
|
|
8722
|
+
/** True while a twin is built: what `flatTextures` hands the refraction slot then. */
|
|
8723
|
+
overlayBinding = false;
|
|
8724
|
+
/** True between `bindSeeThrough` and `releaseSeeThrough`: that draw reads the copy, not the atlas. */
|
|
8725
|
+
seeThroughBound = false;
|
|
8726
|
+
warnedOverlayRefraction = false;
|
|
8609
8727
|
castingWorldIndices = new Int32Array(POINT_SHADOW_POOL).fill(-1);
|
|
8610
8728
|
resolvedPointShadows = createResolvedPointShadows(MAX_POINT_LIGHTS);
|
|
8611
8729
|
/** One face's view-projection, rebuilt per face rather than allocated. */
|
|
@@ -8956,6 +9074,41 @@ export class WebGPURenderer {
|
|
|
8956
9074
|
buildFlatBindGroup() {
|
|
8957
9075
|
return createFlatBindGroup(this.surface.device, this.bindGroupLayout, this.variant, this.perDraw.buffer, this.perFrame.buffer, this.flatTextures);
|
|
8958
9076
|
}
|
|
9077
|
+
/**
|
|
9078
|
+
* The open material's flat group, or its twin with the surface overlay's atlas in the refraction
|
|
9079
|
+
* slot where the draw wears one and does not refract. A twin is built from the live material,
|
|
9080
|
+
* which `bindGroup` always is, and kept by the group and the atlas.
|
|
9081
|
+
*
|
|
9082
|
+
* **A draw that refracts, or is glass, keeps the frame's copy** and its overlay reads no image —
|
|
9083
|
+
* the shader asks the same of `uSeeThrough` (`overlayImagesBound`) — said once.
|
|
9084
|
+
*/
|
|
9085
|
+
overlayGroup() {
|
|
9086
|
+
const maps = this.overlayMaps;
|
|
9087
|
+
if (maps === null)
|
|
9088
|
+
return this.bindGroup;
|
|
9089
|
+
if (this.seeThroughBound) {
|
|
9090
|
+
if (!this.warnedOverlayRefraction) {
|
|
9091
|
+
this.warnedOverlayRefraction = true;
|
|
9092
|
+
console.warn('WebGPU: a draw that refracts or is glass wears a surface overlay with maps. The frame ' +
|
|
9093
|
+
"copy it reads takes the overlay's slot, so its rim, dissolve and wrinkles read no image.");
|
|
9094
|
+
}
|
|
9095
|
+
return this.bindGroup;
|
|
9096
|
+
}
|
|
9097
|
+
let byMaps = this.overlayGroups.get(this.bindGroup);
|
|
9098
|
+
if (byMaps === undefined) {
|
|
9099
|
+
byMaps = new Map();
|
|
9100
|
+
this.overlayGroups.set(this.bindGroup, byMaps);
|
|
9101
|
+
}
|
|
9102
|
+
let twin = byMaps.get(maps);
|
|
9103
|
+
if (twin === undefined) {
|
|
9104
|
+
this.overlayBinding = true;
|
|
9105
|
+
twin = this.buildFlatBindGroup();
|
|
9106
|
+
this.overlayBinding = false;
|
|
9107
|
+
this.bindGroupBudget.ask();
|
|
9108
|
+
byMaps.set(maps, twin);
|
|
9109
|
+
}
|
|
9110
|
+
return twin;
|
|
9111
|
+
}
|
|
8959
9112
|
/**
|
|
8960
9113
|
* The bind group a draw uses, skinned or not.
|
|
8961
9114
|
*
|
|
@@ -8968,8 +9121,9 @@ export class WebGPURenderer {
|
|
|
8968
9121
|
* rebuild this every draw.
|
|
8969
9122
|
*/
|
|
8970
9123
|
flatBindGroupFor(skinned, morphed, deltas) {
|
|
9124
|
+
const base = this.overlayGroup();
|
|
8971
9125
|
if (!skinned && !morphed)
|
|
8972
|
-
return
|
|
9126
|
+
return base;
|
|
8973
9127
|
const palette = skinned ? this.skinPalettes.view(this.skinPaletteSlot) : null;
|
|
8974
9128
|
/* Slot `k` caches at `k + 1`; index 0 is the morphed draw that is not skinned and has no
|
|
8975
9129
|
slot of its own. See `skinnedGroups`. */
|
|
@@ -8977,13 +9131,16 @@ export class WebGPURenderer {
|
|
|
8977
9131
|
/* A skinned group carries the cloth's views, or the stand-in's: see `clothViews`. */
|
|
8978
9132
|
const cloth = skinned ? this.clothViews() : null;
|
|
8979
9133
|
if ((this.skinnedGroups[at] ?? null) === null ||
|
|
8980
|
-
this.skinnedGroupsBeside[at] !==
|
|
9134
|
+
this.skinnedGroupsBeside[at] !== base ||
|
|
8981
9135
|
this.skinnedGroupsPalette[at] !== palette ||
|
|
8982
9136
|
this.skinnedGroupsDeltas[at] !== deltas ||
|
|
8983
9137
|
this.skinnedGroupsCloth[at] !== (cloth?.particles ?? null) ||
|
|
8984
9138
|
this.skinnedGroupsClothBinding[at] !== (cloth?.binding ?? null)) {
|
|
9139
|
+
/* Built from the live material, with the overlay's atlas where `base` is its twin. */
|
|
9140
|
+
this.overlayBinding = base !== this.bindGroup;
|
|
8985
9141
|
this.skinnedGroups[at] = createFlatBindGroup(this.surface.device, this.flatLayoutFor(skinned, morphed), this.variant, this.perDraw.buffer, this.perFrame.buffer, this.flatTextures, palette, deltas, cloth);
|
|
8986
|
-
this.
|
|
9142
|
+
this.overlayBinding = false;
|
|
9143
|
+
this.skinnedGroupsBeside[at] = base;
|
|
8987
9144
|
this.skinnedGroupsPalette[at] = palette;
|
|
8988
9145
|
this.skinnedGroupsDeltas[at] = deltas;
|
|
8989
9146
|
this.skinnedGroupsCloth[at] = cloth?.particles ?? null;
|
|
@@ -9050,6 +9207,7 @@ export class WebGPURenderer {
|
|
|
9050
9207
|
*/
|
|
9051
9208
|
rebuildFlatGroupsForNewRings() {
|
|
9052
9209
|
this.flatBindGroups.clear();
|
|
9210
|
+
this.overlayGroups.clear();
|
|
9053
9211
|
this.flatEnvironment = this.flatTextures('uEnvironment').view;
|
|
9054
9212
|
this.skinnedGroups.length = 0;
|
|
9055
9213
|
const albedo = this.albedo;
|
|
@@ -9102,6 +9260,7 @@ export class WebGPURenderer {
|
|
|
9102
9260
|
*/
|
|
9103
9261
|
rebuildFlatBindGroup() {
|
|
9104
9262
|
this.flatBindGroups.clear();
|
|
9263
|
+
this.overlayGroups.clear();
|
|
9105
9264
|
this.flatEnvironment = this.flatTextures('uEnvironment').view;
|
|
9106
9265
|
if (this.albedo === null &&
|
|
9107
9266
|
this.normalMap === null &&
|
|
@@ -9327,9 +9486,14 @@ export class WebGPURenderer {
|
|
|
9327
9486
|
? (OUTPUT_TRANSFORM_CODE[this.quality.outputTransform] ?? 0)
|
|
9328
9487
|
: 0;
|
|
9329
9488
|
f[at('uOutputExposure')] = graded ? this.exposure : 1;
|
|
9489
|
+
f.set(this.filmA, at('uFilmA'));
|
|
9490
|
+
f.set(this.filmB, at('uFilmB'));
|
|
9491
|
+
f[at('uDisplayHeadroom')] = this.headroom;
|
|
9330
9492
|
f.set(this.reprojection, at('uReprojection'));
|
|
9331
9493
|
f[at('uMotionStrength')] = motionStrength;
|
|
9332
|
-
f[at('uMotionMax')] =
|
|
9494
|
+
f[at('uMotionMax')] = this.motionBlurMax;
|
|
9495
|
+
/* Each mover's own motion, where the reconstruction drew its target this frame. */
|
|
9496
|
+
f[at('uObjectMotion')] = this.reconstructing && this.reconMotionView !== null ? 1 : 0;
|
|
9333
9497
|
f[at('uAoStrength')] = this.frameProjection === null ? 0 : aoStrength;
|
|
9334
9498
|
/*
|
|
9335
9499
|
* The occlusion read where this frame's jitter put it (`uAoOffset`): measured from the jittered
|
|
@@ -9373,6 +9537,7 @@ export class WebGPURenderer {
|
|
|
9373
9537
|
f[at('uGradeSize')] = this.gradeSize;
|
|
9374
9538
|
/* The lens and the print. See `setVignette` and `setFilmGrain`. */
|
|
9375
9539
|
f[at('uVignette')] = this.vignette;
|
|
9540
|
+
f.set(this.fringe, at('uFringe'));
|
|
9376
9541
|
f[at('uAutoExposure')] = adapting ? this.autoExposure : 0;
|
|
9377
9542
|
f[at('uLocalExposure')] = measuring && localising ? this.localExposure : 0;
|
|
9378
9543
|
f[at('uGrain')] = this.grain;
|
|
@@ -9410,7 +9575,7 @@ export class WebGPURenderer {
|
|
|
9410
9575
|
return;
|
|
9411
9576
|
if (this.aoTargetView === null)
|
|
9412
9577
|
return;
|
|
9413
|
-
this.rushBindGroup = this.buildRushBindGroup('post.rushBindGroup', this.sceneColorView);
|
|
9578
|
+
this.rushBindGroup = this.buildRushBindGroup('post.rushBindGroup', this.sceneColorView, this.sceneColorView);
|
|
9414
9579
|
/*
|
|
9415
9580
|
* **And the reconstruction's twin, from the same three call sites.** A colour grade replaces
|
|
9416
9581
|
* the lookup table and a resize replaces the depth and the occlusion target, and both groups
|
|
@@ -9418,11 +9583,13 @@ export class WebGPURenderer {
|
|
|
9418
9583
|
* come to read a stale view on exactly the frames a consumer changed something.
|
|
9419
9584
|
*/
|
|
9420
9585
|
if (this.reconShownView !== null) {
|
|
9421
|
-
this.reconRushGroup = this.buildRushBindGroup('recon.rushBindGroup', this.reconShownView);
|
|
9586
|
+
this.reconRushGroup = this.buildRushBindGroup('recon.rushBindGroup', this.reconShownView, this.reconMotionView ?? this.reconShownView);
|
|
9422
9587
|
}
|
|
9423
9588
|
}
|
|
9424
9589
|
/** The composite's group over one scene source. See `rebuildRushBindGroup`. */
|
|
9425
|
-
buildRushBindGroup(label, sceneView
|
|
9590
|
+
buildRushBindGroup(label, sceneView,
|
|
9591
|
+
/** Each mover's own motion, or a stand-in the shader is told not to read. */
|
|
9592
|
+
motionView) {
|
|
9426
9593
|
const { device } = this.surface;
|
|
9427
9594
|
if (this.resolvedDepthView === null || this.aoTargetView === null)
|
|
9428
9595
|
return null;
|
|
@@ -9451,6 +9618,7 @@ export class WebGPURenderer {
|
|
|
9451
9618
|
and a declared binding still needs a complete texture, as the bloom fallback says. */
|
|
9452
9619
|
...entry(RUSH_TEXTURES.uExposureHeld, this.exposurePass?.heldView ?? sceneView),
|
|
9453
9620
|
...entry(RUSH_TEXTURES.uExposureLocal, this.exposurePass?.localView ?? sceneView),
|
|
9621
|
+
...entry(RUSH_TEXTURES.uMotion, motionView),
|
|
9454
9622
|
],
|
|
9455
9623
|
layout: this.rushLayout,
|
|
9456
9624
|
});
|
|
@@ -9707,7 +9875,7 @@ export class WebGPURenderer {
|
|
|
9707
9875
|
this.reconSharpenGroups[write] = build(`recon.sharpen${String(write)}`, target, this.reconShownView, records, nextRecords, swings, nextSwings);
|
|
9708
9876
|
}
|
|
9709
9877
|
/* The composite's own group, over the resolved picture instead of the scene target. */
|
|
9710
|
-
this.reconRushGroup = this.buildRushBindGroup('recon.rushBindGroup', this.reconShownView);
|
|
9878
|
+
this.reconRushGroup = this.buildRushBindGroup('recon.rushBindGroup', this.reconShownView, this.reconMotionView ?? this.reconShownView);
|
|
9711
9879
|
/* And the scene's, because `ensureComposite` built it before this one existed. */
|
|
9712
9880
|
this.rebuildRushBindGroup();
|
|
9713
9881
|
/* And the bloom's, which blooms the resolved picture: see `rebuildBloomGroups`. */
|
|
@@ -9796,7 +9964,7 @@ export class WebGPURenderer {
|
|
|
9796
9964
|
if (!this.reconstructing || late === null || shown === null || depth === null)
|
|
9797
9965
|
return;
|
|
9798
9966
|
const oitSet = this.oitActive && this.translucentQueue.length > 0;
|
|
9799
|
-
if (late.pending === 0 && !oitSet)
|
|
9967
|
+
if (late.pending === 0 && !oitSet && this.latePasses.length === 0)
|
|
9800
9968
|
return;
|
|
9801
9969
|
const width = Math.max(1, this.surface.canvas.width);
|
|
9802
9970
|
const height = Math.max(1, this.surface.canvas.height);
|
|
@@ -9808,21 +9976,60 @@ export class WebGPURenderer {
|
|
|
9808
9976
|
this.refractSnapshotTaken = true;
|
|
9809
9977
|
}
|
|
9810
9978
|
this.lateRefractWanted = false;
|
|
9811
|
-
if (late.pending
|
|
9812
|
-
|
|
9813
|
-
|
|
9814
|
-
|
|
9815
|
-
|
|
9816
|
-
|
|
9817
|
-
|
|
9818
|
-
|
|
9819
|
-
|
|
9820
|
-
|
|
9821
|
-
|
|
9822
|
-
}
|
|
9823
|
-
|
|
9824
|
-
|
|
9825
|
-
|
|
9979
|
+
if (late.pending > 0) {
|
|
9980
|
+
const pass = encoder.beginRenderPass({
|
|
9981
|
+
label: 'recon.late',
|
|
9982
|
+
timestampWrites: this.gpuTimer.writesFor(),
|
|
9983
|
+
colorAttachments: [{ view: shown, loadOp: 'load', storeOp: 'store' }],
|
|
9984
|
+
depthStencilAttachment: {
|
|
9985
|
+
view: late.depthView(width, height),
|
|
9986
|
+
depthLoadOp: 'load',
|
|
9987
|
+
/* Kept: the order-independent set replays against it after this pass. */
|
|
9988
|
+
depthStoreOp: 'store',
|
|
9989
|
+
},
|
|
9990
|
+
});
|
|
9991
|
+
late.replay(pass, this.issueBound);
|
|
9992
|
+
pass.end();
|
|
9993
|
+
}
|
|
9994
|
+
this.runLatePasses(encoder, shown, late.depthView(width, height));
|
|
9995
|
+
}
|
|
9996
|
+
/**
|
|
9997
|
+
* The registered passes `drawPass` queued for after the upscale: each handed a fresh copy of the
|
|
9998
|
+
* reconstructed picture with every late draw in it, in the output-size texture the late
|
|
9999
|
+
* refraction keeps (its own reads are done by now: they were recorded before this copy), then
|
|
10000
|
+
* drawn over the picture in a pass of its own.
|
|
10001
|
+
*/
|
|
10002
|
+
runLatePasses(encoder, shown, depth) {
|
|
10003
|
+
const copy = this.refractSnapshot;
|
|
10004
|
+
const source = this.reconShown;
|
|
10005
|
+
for (const handle of this.latePasses) {
|
|
10006
|
+
const definition = passAt(this.passes, handle);
|
|
10007
|
+
if (definition === undefined)
|
|
10008
|
+
continue;
|
|
10009
|
+
if (copy !== null && source !== null) {
|
|
10010
|
+
encoder.copyTextureToTexture({ texture: source }, { texture: copy }, [
|
|
10011
|
+
copy.width,
|
|
10012
|
+
copy.height,
|
|
10013
|
+
]);
|
|
10014
|
+
}
|
|
10015
|
+
const pass = encoder.beginRenderPass({
|
|
10016
|
+
label: 'recon.latePass',
|
|
10017
|
+
timestampWrites: this.gpuTimer.writesFor(),
|
|
10018
|
+
colorAttachments: [{ view: shown, loadOp: 'load', storeOp: 'store' }],
|
|
10019
|
+
depthStencilAttachment: { view: depth, depthLoadOp: 'load', depthStoreOp: 'store' },
|
|
10020
|
+
});
|
|
10021
|
+
const context = this.passContext;
|
|
10022
|
+
context.pass = pass;
|
|
10023
|
+
context.outputTransform = this.gradeCode();
|
|
10024
|
+
context.outputExposure = this.gradeExposure();
|
|
10025
|
+
context.jitter = this.latePassJitter;
|
|
10026
|
+
context.format = RECON_HISTORY_FORMAT;
|
|
10027
|
+
context.depthFormat = DEPTH_FORMAT;
|
|
10028
|
+
context.samples = 1;
|
|
10029
|
+
definition.draw(context);
|
|
10030
|
+
context.jitter = this.passJitter;
|
|
10031
|
+
pass.end();
|
|
10032
|
+
}
|
|
9826
10033
|
}
|
|
9827
10034
|
/** Whether this draw lands in the reconstructed picture. See `lateRouting.ts`. */
|
|
9828
10035
|
tracksMotionNow() {
|
|
@@ -10189,7 +10396,10 @@ export class WebGPURenderer {
|
|
|
10189
10396
|
* other is the disagreement the parity rule exists to prevent. Said once rather than dropped.
|
|
10190
10397
|
*/
|
|
10191
10398
|
runReflections(encoder) {
|
|
10192
|
-
|
|
10399
|
+
const frameReflections = this.frameReflections;
|
|
10400
|
+
const surfaceHalf = this.reflectionSurface;
|
|
10401
|
+
const materials = frameReflections !== null && surfaceHalf !== null;
|
|
10402
|
+
if (this.reflectionQueue.length === 0 && !materials)
|
|
10193
10403
|
return;
|
|
10194
10404
|
const scene = this.sceneColorView;
|
|
10195
10405
|
const reflection = this.ssrReflectionView;
|
|
@@ -10234,9 +10444,63 @@ export class WebGPURenderer {
|
|
|
10234
10444
|
f[base + at('uSsrThickness')] = surface.thicknessM;
|
|
10235
10445
|
f[base + at('uSsrSteps')] = surface.steps;
|
|
10236
10446
|
f[base + at('uSsrEdgeFade')] = REFLECTION_EDGE_FADE;
|
|
10447
|
+
f[base + at('uSsrMaterial')] = 0;
|
|
10237
10448
|
slot++;
|
|
10238
10449
|
});
|
|
10450
|
+
const regions = slot;
|
|
10451
|
+
/* The frame's materials' trace takes the slot after the surfaces': the box, its axis and its
|
|
10452
|
+
strength unread, the march and the ceiling its own. See `frameReflections.ts`. */
|
|
10453
|
+
if (materials) {
|
|
10454
|
+
const base = (slot * SSR_SLOT) / 4;
|
|
10455
|
+
f.set(this.ssrDepthToWorld, base + at('uSsrDepthToWorld'));
|
|
10456
|
+
f.set(this.ssrViewProj, base + at('uSsrViewProj'));
|
|
10457
|
+
f.set(this.frameEye, base + at('uSsrEye'));
|
|
10458
|
+
f.fill(0, base + at('uSsrAxis'), base + at('uSsrAxis') + 3);
|
|
10459
|
+
f.fill(1, base + at('uSsrTint'), base + at('uSsrTint') + 3);
|
|
10460
|
+
f[base + at('uSsrStrength')] = 1;
|
|
10461
|
+
f[base + at('uSsrFresnel')] = 0;
|
|
10462
|
+
f[base + at('uSsrFacingCos')] = -1;
|
|
10463
|
+
f[base + at('uSsrReach')] = frameReflections.reachM;
|
|
10464
|
+
f[base + at('uSsrThickness')] = frameReflections.thicknessM;
|
|
10465
|
+
f[base + at('uSsrSteps')] = frameReflections.steps;
|
|
10466
|
+
f[base + at('uSsrEdgeFade')] = REFLECTION_EDGE_FADE;
|
|
10467
|
+
f[base + at('uSsrMaterial')] = 1;
|
|
10468
|
+
f[base + at('uSsrMaxRoughness')] = frameReflections.maxRoughness;
|
|
10469
|
+
slot++;
|
|
10470
|
+
}
|
|
10239
10471
|
device.queue.writeBuffer(this.ssrUniforms, 0, this.ssrStaging, 0, slot * SSR_SLOT);
|
|
10472
|
+
const tracePipeline = postPipeline(this.pipelines, device, this.ssrLayout, 'post.ssrTrace', SSR_TRACE_FRAG_WGSL, this.pipelines.format, PREMULTIPLIED_OVER_BLEND);
|
|
10473
|
+
if (materials && this.depthView !== null && this.ssrMaterialGroup !== null) {
|
|
10474
|
+
/* The frame's opaque lit draws again, into what the trace and the resolve read. */
|
|
10475
|
+
surfaceHalf.draw(encoder, this.depthView, this.issueBound);
|
|
10476
|
+
const trace = encoder.beginRenderPass({
|
|
10477
|
+
label: 'post.ssr.materialTrace',
|
|
10478
|
+
colorAttachments: [
|
|
10479
|
+
{ view: reflection, loadOp: 'clear', storeOp: 'store', clearValue: [0, 0, 0, 0] },
|
|
10480
|
+
],
|
|
10481
|
+
});
|
|
10482
|
+
trace.setPipeline(tracePipeline);
|
|
10483
|
+
trace.setBindGroup(0, group, [regions * SSR_SLOT]);
|
|
10484
|
+
trace.draw(3);
|
|
10485
|
+
trace.end();
|
|
10486
|
+
/* The blur's texel and its radius at roughness 1, as a share of the frame's height. */
|
|
10487
|
+
const m = this.ssrMaterialFloats;
|
|
10488
|
+
const blurAt = this.postField(SSR_MATERIAL_FIELDS, 'uSsrBlur');
|
|
10489
|
+
m[blurAt] = 1 / Math.max(width, 1);
|
|
10490
|
+
m[blurAt + 1] = 1 / Math.max(height, 1);
|
|
10491
|
+
m[blurAt + 2] = frameReflections.blur * height;
|
|
10492
|
+
device.queue.writeBuffer(this.ssrMaterialUniforms, 0, m);
|
|
10493
|
+
const resolve = encoder.beginRenderPass({
|
|
10494
|
+
label: 'post.ssr.materialResolve',
|
|
10495
|
+
colorAttachments: [{ view: scene, loadOp: 'load', storeOp: 'store' }],
|
|
10496
|
+
});
|
|
10497
|
+
resolve.setPipeline(postPipeline(this.pipelines, device, this.ssrMaterialLayout, 'post.ssrMaterialResolve', SSR_MATERIAL_RESOLVE_FRAG_WGSL, this.pipelines.format, FRAME_REFLECTION_BLEND));
|
|
10498
|
+
resolve.setBindGroup(0, this.ssrMaterialGroup, [0]);
|
|
10499
|
+
resolve.draw(3);
|
|
10500
|
+
resolve.end();
|
|
10501
|
+
}
|
|
10502
|
+
if (regions === 0)
|
|
10503
|
+
return;
|
|
10240
10504
|
/* Cleared whole and then scissored per surface: the resolve reads every pixel, and one left
|
|
10241
10505
|
from the last frame would composite that reflection over this frame's scene. */
|
|
10242
10506
|
const trace = encoder.beginRenderPass({
|
|
@@ -10245,7 +10509,7 @@ export class WebGPURenderer {
|
|
|
10245
10509
|
{ view: reflection, loadOp: 'clear', storeOp: 'store', clearValue: [0, 0, 0, 0] },
|
|
10246
10510
|
],
|
|
10247
10511
|
});
|
|
10248
|
-
trace.setPipeline(
|
|
10512
|
+
trace.setPipeline(tracePipeline);
|
|
10249
10513
|
slot = 0;
|
|
10250
10514
|
this.reflectionQueue.replay((surface) => {
|
|
10251
10515
|
const index = slot++;
|
|
@@ -10877,6 +11141,50 @@ export class WebGPURenderer {
|
|
|
10877
11141
|
!this.overlayActive &&
|
|
10878
11142
|
this.oitMode === 'none');
|
|
10879
11143
|
}
|
|
11144
|
+
/** Whether a draw now is the frame's own, and so has a surface half to keep. */
|
|
11145
|
+
reflectionSurfaceKeeps() {
|
|
11146
|
+
return (this.reflectionSurface !== null &&
|
|
11147
|
+
!this.reflectionPassActive &&
|
|
11148
|
+
!this.probePassActive &&
|
|
11149
|
+
!this.capturePassActive &&
|
|
11150
|
+
!this.framePresented &&
|
|
11151
|
+
!this.overlayActive &&
|
|
11152
|
+
this.oitMode === 'none');
|
|
11153
|
+
}
|
|
11154
|
+
/**
|
|
11155
|
+
* An opaque lit draw's surface half, kept for the frame's materials' reflections: the draw again,
|
|
11156
|
+
* with the same slots, buffers and bind group, through the pipeline with `REFLECTION_SURFACE` on —
|
|
11157
|
+
* which writes what the pixel reflects of its environment into the two targets
|
|
11158
|
+
* `reflectionSurfacePass.ts` holds and tests for the depth its frame draw wrote. Replayed when the
|
|
11159
|
+
* frame's pass has ended, before the trace reads them. See `shaders/flat/reflectionSurface.ts`.
|
|
11160
|
+
*/
|
|
11161
|
+
keepReflectionSurface(mesh, frameKey, base, skinned, morphed, deltas, layer, eight, clothed, modelKind, slot, material, instanceBuffer, instances, indirect) {
|
|
11162
|
+
const surface = this.reflectionSurface;
|
|
11163
|
+
const present = this.meshPresent.get(base);
|
|
11164
|
+
if (surface === null || present === undefined)
|
|
11165
|
+
return;
|
|
11166
|
+
const key = `${frameKey}|rs`;
|
|
11167
|
+
const pipeline = this.pipelines.peek(key) ??
|
|
11168
|
+
flatPipeline(this.pipelines, this.surface.device, this.flatLayoutFor(skinned, morphed), this.variant, key, present, false, skinned, morphed, true, layer, instanceBuffer !== null, 'none', this.materialDoubleSided, false, eight, clothed, modelKind, 'whole', true);
|
|
11169
|
+
const command = surface.take();
|
|
11170
|
+
command.pipeline = pipeline;
|
|
11171
|
+
command.bindGroup = this.flatBindGroupFor(skinned, morphed, deltas);
|
|
11172
|
+
command.offsetA = slot;
|
|
11173
|
+
command.offsetB = material;
|
|
11174
|
+
command.offsetCount = 2;
|
|
11175
|
+
let index = 0;
|
|
11176
|
+
for (; index < mesh.vertexBuffers.length; index++) {
|
|
11177
|
+
command.vertexBuffers[index] = mesh.vertexBuffers[index];
|
|
11178
|
+
}
|
|
11179
|
+
if (instanceBuffer !== null)
|
|
11180
|
+
command.vertexBuffers[index++] = instanceBuffer;
|
|
11181
|
+
command.vertexCount = index;
|
|
11182
|
+
command.indexBuffer = mesh.indexBuffer;
|
|
11183
|
+
command.indexed = true;
|
|
11184
|
+
command.count = mesh.indexCount;
|
|
11185
|
+
command.instances = instances;
|
|
11186
|
+
command.indirect = indirect;
|
|
11187
|
+
}
|
|
10880
11188
|
/**
|
|
10881
11189
|
* A skin draw's diffuse and colour halves, kept for the spread: the frame's draw again, twice,
|
|
10882
11190
|
* with the same slots, buffers and bind group, through the pipelines that write the diffuse's
|
|
@@ -10924,13 +11232,19 @@ export class WebGPURenderer {
|
|
|
10924
11232
|
const skin = this.skinScatter;
|
|
10925
11233
|
if (skin === null || this.skinScattered)
|
|
10926
11234
|
return;
|
|
11235
|
+
/*
|
|
11236
|
+
* **Not the frame's picture, so not the frame's moment**: a blended draw in a mirror, a probe
|
|
11237
|
+
* bake or a scene capture lands over none of the frame's skin. Latching there switched the
|
|
11238
|
+
* spread off for the frame, so a capture with a glowing sign in it left every face drawn after
|
|
11239
|
+
* it flat. `scripts/capture-check.mjs`.
|
|
11240
|
+
*/
|
|
11241
|
+
if (this.reflectionPassActive || this.probePassActive || this.capturePassActive)
|
|
11242
|
+
return;
|
|
10927
11243
|
/* Latched whether or not skin was kept: a skin drawn after this point is the whole surface. */
|
|
10928
11244
|
this.skinScattered = true;
|
|
10929
11245
|
if (!skin.pending)
|
|
10930
11246
|
return;
|
|
10931
|
-
if (this.
|
|
10932
|
-
return;
|
|
10933
|
-
if (this.capturePassActive)
|
|
11247
|
+
if (this.oitReplaying)
|
|
10934
11248
|
return;
|
|
10935
11249
|
const encoder = this.encoder;
|
|
10936
11250
|
const depth = this.depthView;
|
|
@@ -11045,6 +11359,66 @@ export class WebGPURenderer {
|
|
|
11045
11359
|
this.volumeDepthTaken = true;
|
|
11046
11360
|
return true;
|
|
11047
11361
|
}
|
|
11362
|
+
/** The colour copy a registered pass reads, at the scene's size and format. See `passColour`. */
|
|
11363
|
+
allocatePassColour() {
|
|
11364
|
+
const source = this.sceneColor;
|
|
11365
|
+
if (source === null || this.passColour !== null)
|
|
11366
|
+
return;
|
|
11367
|
+
this.passColour = this.surface.device.createTexture({
|
|
11368
|
+
label: 'pass.sceneColor',
|
|
11369
|
+
size: [source.width, source.height],
|
|
11370
|
+
format: this.pipelines.format,
|
|
11371
|
+
usage: 0x4 | 0x2, // TEXTURE_BINDING | COPY_DST
|
|
11372
|
+
});
|
|
11373
|
+
this.passColourView = this.passColour.createView();
|
|
11374
|
+
}
|
|
11375
|
+
/**
|
|
11376
|
+
* The frame's colour so far, into `passColour`, for a registered pass that reads it: the
|
|
11377
|
+
* boundary `takeRefractSnapshot` describes — flush, end the pass, copy what its end resolved,
|
|
11378
|
+
* reopen with `load` — and refused where that one is, in a mirror, a probe bake or a capture,
|
|
11379
|
+
* whose pictures are not the frame's. **Not latched**, unlike refraction's: each reading pass
|
|
11380
|
+
* sees the passes drawn before it, at a full-frame copy each.
|
|
11381
|
+
*/
|
|
11382
|
+
takePassColour() {
|
|
11383
|
+
if (this.reflectionPassActive || this.probePassActive || this.capturePassActive)
|
|
11384
|
+
return;
|
|
11385
|
+
const copy = this.passColour;
|
|
11386
|
+
const source = this.sceneColor;
|
|
11387
|
+
const encoder = this.encoder;
|
|
11388
|
+
if (copy === null || source === null || encoder === null || this.pass === null)
|
|
11389
|
+
return;
|
|
11390
|
+
if (this.quality.frameGraph)
|
|
11391
|
+
this.flushGraph();
|
|
11392
|
+
this.pass.end();
|
|
11393
|
+
encoder.copyTextureToTexture({ texture: source }, { texture: copy }, [copy.width, copy.height]);
|
|
11394
|
+
this.pass = this.reopenFramePass('pass.colourTaken');
|
|
11395
|
+
}
|
|
11396
|
+
/**
|
|
11397
|
+
* The frame's pass again after a copy between passes, loading everything already drawn: the same
|
|
11398
|
+
* attachments the copy's caller ended, with nothing cleared.
|
|
11399
|
+
*/
|
|
11400
|
+
reopenFramePass(label) {
|
|
11401
|
+
const encoder = this.encoder;
|
|
11402
|
+
const target = this.compositeTarget();
|
|
11403
|
+
if (encoder === null || target === null)
|
|
11404
|
+
return null;
|
|
11405
|
+
const multisampled = this.compositeMsaa() !== null;
|
|
11406
|
+
return encoder.beginRenderPass({
|
|
11407
|
+
label,
|
|
11408
|
+
timestampWrites: this.gpuTimer.writesFor(),
|
|
11409
|
+
colorAttachments: [
|
|
11410
|
+
{
|
|
11411
|
+
view: this.compositeMsaa() ?? target,
|
|
11412
|
+
resolveTarget: multisampled ? target : undefined,
|
|
11413
|
+
loadOp: 'load',
|
|
11414
|
+
storeOp: resolvedStoreOp(multisampled, false, this.quality.discardResolvedAttachments),
|
|
11415
|
+
},
|
|
11416
|
+
],
|
|
11417
|
+
depthStencilAttachment: this.depthView === null
|
|
11418
|
+
? undefined
|
|
11419
|
+
: { view: this.depthView, depthLoadOp: 'load', depthStoreOp: 'store' },
|
|
11420
|
+
});
|
|
11421
|
+
}
|
|
11048
11422
|
/**
|
|
11049
11423
|
* The pass to record into, reopening one on the canvas if the frame has already gone.
|
|
11050
11424
|
*
|
|
@@ -11226,6 +11600,8 @@ export class WebGPURenderer {
|
|
|
11226
11600
|
/* And a march reads the same copy, for the same reason and with the same failure if it is
|
|
11227
11601
|
discarded: a depth of zero is the far plane reversed, so every ray finds nothing. */
|
|
11228
11602
|
this.reflectionQueue.length > 0 ||
|
|
11603
|
+
/* And the frame's materials' reflections, whose surface half tests against the depth too. */
|
|
11604
|
+
this.reflectionSurface !== null ||
|
|
11229
11605
|
/* And the medium's own march, which stops where the frame's depth says a surface is. Left
|
|
11230
11606
|
out of this, every ray runs to `maxDistance` through the walls and the fog is drawn on
|
|
11231
11607
|
top of the room rather than in it. */
|
|
@@ -12042,6 +12418,9 @@ export class WebGPURenderer {
|
|
|
12042
12418
|
const reads = maskOf(...(definition.reads ?? []));
|
|
12043
12419
|
this.passReads.set(handle, reads);
|
|
12044
12420
|
this.passReadsUnion |= reads;
|
|
12421
|
+
/* The colour copy a reader needs, now rather than at its first draw: see `passColour`. */
|
|
12422
|
+
if ((reads & COLOR_SNAPSHOT) !== 0 && this.passColour === null)
|
|
12423
|
+
this.allocatePassColour();
|
|
12045
12424
|
if (definition.prepare !== undefined)
|
|
12046
12425
|
this.preparingPasses++;
|
|
12047
12426
|
definition.init?.(this.passDevice());
|
|
@@ -12061,6 +12440,8 @@ export class WebGPURenderer {
|
|
|
12061
12440
|
environment: null,
|
|
12062
12441
|
distanceField: null,
|
|
12063
12442
|
jitter: this.passJitter,
|
|
12443
|
+
sceneDepth: null,
|
|
12444
|
+
sceneColor: null,
|
|
12064
12445
|
};
|
|
12065
12446
|
/**
|
|
12066
12447
|
* The object handed to a pass as `PrepareContext.environment`, rebuilt only when it changes.
|
|
@@ -12146,6 +12527,15 @@ export class WebGPURenderer {
|
|
|
12146
12527
|
* than merely what happens.
|
|
12147
12528
|
*/
|
|
12148
12529
|
this.prepareContext.distanceField = this.fieldComposer?.field ?? null;
|
|
12530
|
+
/* Settled for the frame: `ensureComposite` has run, so the view stays until the pass draws. */
|
|
12531
|
+
this.prepareContext.sceneDepth = this.resolvedDepthView;
|
|
12532
|
+
/*
|
|
12533
|
+
* Under a reconstruction a colour reader draws after the upscale (see `drawPass`), so it reads
|
|
12534
|
+
* the output-size copy the late refraction keeps rather than the render's.
|
|
12535
|
+
*/
|
|
12536
|
+
this.prepareContext.sceneColor = this.reconstructionWanted
|
|
12537
|
+
? this.refractSnapshotView
|
|
12538
|
+
: this.passColourView;
|
|
12149
12539
|
for (const definition of this.passes.definitions) {
|
|
12150
12540
|
definition?.prepare?.(this.prepareContext);
|
|
12151
12541
|
}
|
|
@@ -12163,6 +12553,35 @@ export class WebGPURenderer {
|
|
|
12163
12553
|
const definition = passAt(this.passes, handle);
|
|
12164
12554
|
if (definition === undefined)
|
|
12165
12555
|
return;
|
|
12556
|
+
const reads = this.passReads.get(handle) ?? 0;
|
|
12557
|
+
/*
|
|
12558
|
+
* **Under a reconstruction, a pass that reads the frame's colour draws after the blended draws
|
|
12559
|
+
* do.** Those land after the upscale, at the output's size, so a pass drawn among them at the
|
|
12560
|
+
* render's would read a frame without them and then be covered by them — a full-screen effect
|
|
12561
|
+
* with the hit sparks drawn sharp over it. Queued instead, and drawn by `runLate` over the
|
|
12562
|
+
* reconstructed picture, after every late draw, whatever order it was asked in.
|
|
12563
|
+
*/
|
|
12564
|
+
if ((reads & COLOR_SNAPSHOT) !== 0 && this.drawsLate()) {
|
|
12565
|
+
this.latePasses.push(handle);
|
|
12566
|
+
return;
|
|
12567
|
+
}
|
|
12568
|
+
/*
|
|
12569
|
+
* A pass that reads the depth copy gets it with everything drawn so far: the copy is taken
|
|
12570
|
+
* here, once a frame, as a soft-edged sprite takes it. Before `openPass` below, because taking
|
|
12571
|
+
* it ends the frame's pass and opens another.
|
|
12572
|
+
*/
|
|
12573
|
+
if ((reads & DEPTH_SNAPSHOT) !== 0 && !this.framePresented && this.openPass() !== null) {
|
|
12574
|
+
this.takeVolumeDepth();
|
|
12575
|
+
}
|
|
12576
|
+
/*
|
|
12577
|
+
* And one that reads the colour gets it afresh, with everything drawn before it — a full-screen
|
|
12578
|
+
* pass drawn last reads the whole frame and an effect drawn mid-frame reads the frame so far.
|
|
12579
|
+
* A copy a reading pass, where the depth above is one a frame: what a colour reader draws is
|
|
12580
|
+
* itself part of what the next one should see.
|
|
12581
|
+
*/
|
|
12582
|
+
if ((reads & COLOR_SNAPSHOT) !== 0 && !this.framePresented && this.openPass() !== null) {
|
|
12583
|
+
this.takePassColour();
|
|
12584
|
+
}
|
|
12166
12585
|
if (!this.quality.frameGraph) {
|
|
12167
12586
|
const pass = this.openPass();
|
|
12168
12587
|
if (pass === null)
|
|
@@ -12170,7 +12589,7 @@ export class WebGPURenderer {
|
|
|
12170
12589
|
definition.draw(this.fillPassContext(pass));
|
|
12171
12590
|
return;
|
|
12172
12591
|
}
|
|
12173
|
-
recordNode(this.arena, VERB_PASS,
|
|
12592
|
+
recordNode(this.arena, VERB_PASS, reads, this.currentTarget(), handle, 0);
|
|
12174
12593
|
if (this.framePresented)
|
|
12175
12594
|
this.queueOverlayFlush();
|
|
12176
12595
|
}
|
|
@@ -12279,8 +12698,8 @@ export class WebGPURenderer {
|
|
|
12279
12698
|
bindClusters(camera, env, f, i, at) {
|
|
12280
12699
|
const binner = this.clusterBinner;
|
|
12281
12700
|
i[at('uClustered')] = binner === null ? 0 : 1;
|
|
12282
|
-
|
|
12283
|
-
|
|
12701
|
+
/* Written whether or not the lights are clustered: a surface overlay's rim finds its place on
|
|
12702
|
+
the screen through the same two (\`overlayScreen\`). */
|
|
12284
12703
|
const p = camera.projection;
|
|
12285
12704
|
const tanHalfFovY = 1 / (p[5] ?? 1);
|
|
12286
12705
|
const aspect = (p[5] ?? 1) / (p[0] ?? 1);
|
|
@@ -12296,6 +12715,8 @@ export class WebGPURenderer {
|
|
|
12296
12715
|
f[frustumAt + 2] = tanHalfFovY;
|
|
12297
12716
|
f[frustumAt + 3] = aspect;
|
|
12298
12717
|
f.set(camera.view, at('uView'));
|
|
12718
|
+
if (binner === null)
|
|
12719
|
+
return;
|
|
12299
12720
|
/* The rectangles past the fixed arm's go into the table. See `ClusterLightSet.areas`. */
|
|
12300
12721
|
const set = fillClusterLightSet(env, this.clusterLights, this.shadedAreaLights);
|
|
12301
12722
|
/* A slot is read only under `POINT_SHADOWS`; see `lightHasFixture`. */
|
|
@@ -12596,6 +13017,43 @@ export class WebGPURenderer {
|
|
|
12596
13017
|
}
|
|
12597
13018
|
/** This frame's exposure into the tone curve. Starts at the profile's own. */
|
|
12598
13019
|
exposure;
|
|
13020
|
+
/**
|
|
13021
|
+
* The `filmic` curve every later frame is graded through, until it is set again. See
|
|
13022
|
+
* `RendererApi.setFilmicCurve`; the constants are solved here once, not per frame or per pixel.
|
|
13023
|
+
*/
|
|
13024
|
+
setFilmicCurve(curve) {
|
|
13025
|
+
this.filmCurve = curve;
|
|
13026
|
+
if (resolveFilmicCurve(curve, this.filmConstants, this.headroom) && !this.warnedFilmClamped) {
|
|
13027
|
+
this.warnedFilmClamped = true;
|
|
13028
|
+
console.warn(FILMIC_CLAMPED);
|
|
13029
|
+
}
|
|
13030
|
+
}
|
|
13031
|
+
warnedFilmClamped = false;
|
|
13032
|
+
/** The curve last set, so a new headroom can solve it again. */
|
|
13033
|
+
filmCurve = DEFAULT_FILMIC_CURVE;
|
|
13034
|
+
/** The display's peak over paper white; 1 on a standard range. See `setDisplayLuminance`. */
|
|
13035
|
+
headroom = 1;
|
|
13036
|
+
/** The range the frame goes out in. See `RendererApi.displayRange`. */
|
|
13037
|
+
get displayRange() {
|
|
13038
|
+
return this.surface.displayRange;
|
|
13039
|
+
}
|
|
13040
|
+
/** Why it is that range, in words. */
|
|
13041
|
+
get displayRangeReason() {
|
|
13042
|
+
return this.surface.displayRangeReason;
|
|
13043
|
+
}
|
|
13044
|
+
/**
|
|
13045
|
+
* How bright paper white and the display's peak are, in any one unit (nits, usually), held until
|
|
13046
|
+
* changed: their ratio is how far above white the highlights may run on a high range display,
|
|
13047
|
+
* and nothing changes on a standard one. See `RendererApi.setDisplayLuminance`.
|
|
13048
|
+
*/
|
|
13049
|
+
setDisplayLuminance(paperWhite, peak) {
|
|
13050
|
+
this.headroom = displayHeadroom(this.surface.displayRange, paperWhite, peak);
|
|
13051
|
+
resolveFilmicCurve(this.filmCurve, this.filmConstants, this.headroom);
|
|
13052
|
+
}
|
|
13053
|
+
/** The curve's eight constants, and the two views the composite uploads them through. */
|
|
13054
|
+
filmConstants = new Float32Array(FILMIC_CONSTANTS);
|
|
13055
|
+
filmA = this.filmConstants.subarray(0, 4);
|
|
13056
|
+
filmB = this.filmConstants.subarray(4, 8);
|
|
12599
13057
|
/*
|
|
12600
13058
|
* -----------------------------------------------------------------------------------------
|
|
12601
13059
|
* The three screen effects, which this backend cannot yet honour — and says so.
|
|
@@ -12639,6 +13097,8 @@ export class WebGPURenderer {
|
|
|
12639
13097
|
veilColor = [0, 0, 0];
|
|
12640
13098
|
veilAlpha = 0;
|
|
12641
13099
|
vignette = 0;
|
|
13100
|
+
/** The lens's colour fringe: red's and green's pull and where it begins. See `fringe.ts`. */
|
|
13101
|
+
fringe = new Float32Array(FRINGE_FLOATS);
|
|
12642
13102
|
grain = 0;
|
|
12643
13103
|
grainSeed = 0;
|
|
12644
13104
|
warnedLookWithoutComposite = false;
|
|
@@ -12660,6 +13120,20 @@ export class WebGPURenderer {
|
|
|
12660
13120
|
}
|
|
12661
13121
|
this.vignette = clampVignette(strength);
|
|
12662
13122
|
}
|
|
13123
|
+
/**
|
|
13124
|
+
* The lens's colour fringe, held until changed: `intensity` is a percentage, 0 is none, and red
|
|
13125
|
+
* and green are pulled toward the centre by their wavelength's distance from blue, growing from
|
|
13126
|
+
* `start` — a share of the half-frame, 0 the centre — to the whole of it at the edge. Applied to
|
|
13127
|
+
* scene light at the composite's first read. Needs `screenEffects`, and says so once rather than
|
|
13128
|
+
* doing nothing. See `shaders/fringe.ts`.
|
|
13129
|
+
*/
|
|
13130
|
+
setChromaticAberration(intensity, start = 0) {
|
|
13131
|
+
if (!this.hasComposite) {
|
|
13132
|
+
this.warnLookWithoutComposite(intensity);
|
|
13133
|
+
return;
|
|
13134
|
+
}
|
|
13135
|
+
resolveFringe(intensity, start, this.fringe);
|
|
13136
|
+
}
|
|
12663
13137
|
/**
|
|
12664
13138
|
* Film grain, held until changed: an amplitude in display values (0 is none, 0.03 is a fine
|
|
12665
13139
|
* grain) and this frame's seed. **The seed is the caller's**, because the engine takes time from
|
|
@@ -12793,9 +13267,26 @@ export class WebGPURenderer {
|
|
|
12793
13267
|
setSpeedRush(strength) {
|
|
12794
13268
|
this.rushStrength = Math.min(Math.max(strength, 0), 1);
|
|
12795
13269
|
}
|
|
12796
|
-
/**
|
|
12797
|
-
|
|
13270
|
+
/**
|
|
13271
|
+
* How much of the frame's motion blur to apply, 0 to 1, and the longest smear as a share of the
|
|
13272
|
+
* frame, both held until changed. See `RendererApi.setCameraMotionBlur`.
|
|
13273
|
+
*/
|
|
13274
|
+
setCameraMotionBlur(scale, maxShare = MOTION_BLUR_MAX_UV) {
|
|
12798
13275
|
this.motionBlurScale = Math.min(Math.max(scale, 0), 1);
|
|
13276
|
+
this.motionBlurMax = clampMotionShare(maxShare);
|
|
13277
|
+
}
|
|
13278
|
+
/** The longest smear, as a share of the frame. See `setCameraMotionBlur`. */
|
|
13279
|
+
motionBlurMax = MOTION_BLUR_MAX_UV;
|
|
13280
|
+
warnedObjectBlur = false;
|
|
13281
|
+
/**
|
|
13282
|
+
* Said once: a draw named where it was last frame while the frame blurs, and this frame keeps no
|
|
13283
|
+
* motion target to blur it by — the camera's motion alone smears it. See `OBJECT_BLUR_UNKEPT`.
|
|
13284
|
+
*/
|
|
13285
|
+
warnObjectBlur() {
|
|
13286
|
+
if (this.warnedObjectBlur)
|
|
13287
|
+
return;
|
|
13288
|
+
this.warnedObjectBlur = true;
|
|
13289
|
+
console.warn(OBJECT_BLUR_UNKEPT);
|
|
12799
13290
|
}
|
|
12800
13291
|
/**
|
|
12801
13292
|
* The camera has cut: this frame is a new shot, not the next moment of the last one.
|
|
@@ -13342,18 +13833,19 @@ export class WebGPURenderer {
|
|
|
13342
13833
|
* `bindMeshPass` and `drawMesh`, which record into whatever `this.pass` is. That is the same
|
|
13343
13834
|
* arrangement `bakeFace` uses for a point light's cube, and it is why a bake must not be
|
|
13344
13835
|
* opened inside a frame's pass by a caller who then expects the frame to continue.
|
|
13345
|
-
|
|
13346
|
-
const savedPass = this.pass;
|
|
13347
|
-
/*
|
|
13836
|
+
*
|
|
13348
13837
|
* **A bake is a boundary, and boundaries flush.** The mirror says why in its own words: what
|
|
13349
13838
|
* has been recorded and not yet replayed belongs to the pass that is about to end, and leaving
|
|
13350
13839
|
* it replays it into whichever pass opens next — a draw in the wrong target rather than a
|
|
13351
13840
|
* missing one. This is the fourth such boundary in the backend and the only one that was never
|
|
13352
13841
|
* given the treatment, which is what let a bake carry the frame's pending draws into a cube
|
|
13353
|
-
* face.
|
|
13842
|
+
* face. **The pass is saved after the flush**, which opens it where none was: saved before,
|
|
13843
|
+
* the null given back left the flushed pass open and the frame's encoder locked. See
|
|
13844
|
+
* `captureScene`.
|
|
13354
13845
|
*/
|
|
13355
13846
|
if (this.quality.frameGraph)
|
|
13356
13847
|
this.flushGraph();
|
|
13848
|
+
const savedPass = this.pass;
|
|
13357
13849
|
const savedEncoder = this.encoder;
|
|
13358
13850
|
/*
|
|
13359
13851
|
* **The rings are marked here and given back after the submit below**, because this bake is its
|
|
@@ -13938,6 +14430,7 @@ export class WebGPURenderer {
|
|
|
13938
14430
|
this.sceneColor,
|
|
13939
14431
|
this.sceneColorMsaa,
|
|
13940
14432
|
this.refractSnapshot,
|
|
14433
|
+
this.passColour,
|
|
13941
14434
|
this.resolvedDepth,
|
|
13942
14435
|
this.aoTarget,
|
|
13943
14436
|
this.aoScratch,
|
|
@@ -14003,6 +14496,10 @@ export class WebGPURenderer {
|
|
|
14003
14496
|
usage: 0x4 | 0x2 | 0x10, // TEXTURE_BINDING | COPY_DST | RENDER_ATTACHMENT
|
|
14004
14497
|
});
|
|
14005
14498
|
this.refractSnapshotView = this.refractSnapshot.createView();
|
|
14499
|
+
this.passColour = null;
|
|
14500
|
+
this.passColourView = null;
|
|
14501
|
+
if ((this.passReadsUnion & COLOR_SNAPSHOT) !== 0)
|
|
14502
|
+
this.allocatePassColour();
|
|
14006
14503
|
this.refractMips = new SnapshotMips(device, this.pipelines, this.refractSnapshot, reconstructed ? RECON_HISTORY_FORMAT : format);
|
|
14007
14504
|
/*
|
|
14008
14505
|
* **Every cached flat bind group is stale the moment this view changes**, and the failure is
|
|
@@ -14218,6 +14715,10 @@ export class WebGPURenderer {
|
|
|
14218
14715
|
usage: USAGE,
|
|
14219
14716
|
});
|
|
14220
14717
|
this.ssrReflectionView = this.ssrReflection.createView();
|
|
14718
|
+
/* The frame's materials, at the scene's size; a stand-in where nothing traces them. */
|
|
14719
|
+
this.reflectionSurface?.size(width, height);
|
|
14720
|
+
const probeMap = this.reflectionSurface?.probeView ?? this.blankView;
|
|
14721
|
+
const tintMap = this.reflectionSurface?.tintView ?? this.blankView;
|
|
14221
14722
|
this.ssrBindGroup =
|
|
14222
14723
|
this.resolvedDepthView === null || this.sceneColorView === null
|
|
14223
14724
|
? null
|
|
@@ -14231,8 +14732,23 @@ export class WebGPURenderer {
|
|
|
14231
14732
|
},
|
|
14232
14733
|
...entry(SSR_TEXTURES.uSsrDepth, this.resolvedDepthView, true),
|
|
14233
14734
|
...entry(SSR_TEXTURES.uSsrScene, this.sceneColorView),
|
|
14735
|
+
...entry(SSR_TEXTURES.uSsrProbeMap, probeMap),
|
|
14736
|
+
...entry(SSR_TEXTURES.uSsrTintMap, tintMap),
|
|
14234
14737
|
],
|
|
14235
14738
|
});
|
|
14739
|
+
this.ssrMaterialGroup = device.createBindGroup({
|
|
14740
|
+
label: 'post.ssrMaterialGroup',
|
|
14741
|
+
layout: this.ssrMaterialLayout,
|
|
14742
|
+
entries: [
|
|
14743
|
+
{
|
|
14744
|
+
binding: SSR_MATERIAL_UNIFORMS,
|
|
14745
|
+
resource: { buffer: this.ssrMaterialUniforms, size: SSR_MATERIAL_SIZE },
|
|
14746
|
+
},
|
|
14747
|
+
...entry(SSR_MATERIAL_TEXTURES.uSsrReflection, this.ssrReflectionView),
|
|
14748
|
+
...entry(SSR_MATERIAL_TEXTURES.uSsrProbeMap, probeMap),
|
|
14749
|
+
...entry(SSR_MATERIAL_TEXTURES.uSsrTintMap, tintMap),
|
|
14750
|
+
],
|
|
14751
|
+
});
|
|
14236
14752
|
this.ssrResolveGroup = device.createBindGroup({
|
|
14237
14753
|
label: 'post.ssrResolveGroup',
|
|
14238
14754
|
layout: this.ssrResolveLayout,
|