@driftengine/core 4.8.6 → 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 +132 -0
- package/README.md +1 -1
- package/dist/index.d.ts +7 -2
- package/dist/index.js +8 -1
- package/dist/render/anisotropicLobe.d.ts +10 -0
- package/dist/render/anisotropicLobe.js +19 -0
- package/dist/render/areaLights.d.ts +35 -0
- package/dist/render/areaLights.js +35 -0
- package/dist/render/atmosphere.d.ts +13 -0
- package/dist/render/atmosphere.js +9 -1
- package/dist/render/backend/api.d.ts +41 -3
- 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 +140 -16
- package/dist/render/backend/webgl2/renderer.js +436 -44
- package/dist/render/backend/webgl2/sceneCaptureTarget.d.ts +29 -0
- package/dist/render/backend/webgl2/sceneCaptureTarget.js +96 -0
- 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/particlePass.js +2 -1
- 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 +166 -3
- package/dist/render/backend/webgpu/renderer.js +738 -83
- package/dist/render/backend/webgpu/sceneCaptureTarget.d.ts +24 -0
- package/dist/render/backend/webgpu/sceneCaptureTarget.js +59 -0
- package/dist/render/backend/webgpu/shaders/clusterBin.wgsl.d.ts +1 -1
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +4 -1
- package/dist/render/backend/webgpu/surfaceTexturePass.js +34 -8
- package/dist/render/backend/webgpu/waterPass.d.ts +1 -1
- package/dist/render/clusteredLights.d.ts +23 -5
- package/dist/render/clusteredLights.js +35 -8
- package/dist/render/displayRange.d.ts +31 -0
- package/dist/render/displayRange.js +47 -0
- package/dist/render/driftLight/bake.d.ts +2 -0
- package/dist/render/driftLight/bake.js +1 -1
- package/dist/render/driftLight/falloff.d.ts +4 -2
- package/dist/render/driftLight/falloff.js +8 -2
- 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/gi/bounceLights.d.ts +3 -2
- package/dist/render/gi/bounceLights.js +8 -3
- package/dist/render/instanceCull.js +5 -0
- package/dist/render/instances.d.ts +19 -5
- package/dist/render/instances.js +9 -3
- package/dist/render/lightBudget.d.ts +12 -1
- package/dist/render/lightBudget.js +23 -3
- package/dist/render/lightmap.d.ts +23 -9
- package/dist/render/lightmap.js +40 -18
- package/dist/render/particlePool.d.ts +21 -0
- package/dist/render/particlePool.js +7 -0
- package/dist/render/pass.d.ts +49 -0
- package/dist/render/pointLightSelection.d.ts +13 -0
- package/dist/render/pointLightSelection.js +3 -1
- package/dist/render/reflectionSurfaceTargets.d.ts +28 -0
- package/dist/render/reflectionSurfaceTargets.js +99 -0
- package/dist/render/renderQuality.d.ts +47 -15
- package/dist/render/renderQuality.js +4 -0
- package/dist/render/rendererHarness.js +3 -0
- package/dist/render/rgb9e5.d.ts +22 -0
- package/dist/render/rgb9e5.js +47 -0
- package/dist/render/sceneCapture.d.ts +29 -0
- package/dist/render/sceneCapture.js +36 -0
- package/dist/render/sceneTarget.d.ts +5 -0
- package/dist/render/sceneTarget.js +24 -1
- package/dist/render/screenSpaceReflection.d.ts +32 -1
- package/dist/render/screenSpaceReflection.js +14 -0
- package/dist/render/shaders/arcane.d.ts +1 -1
- package/dist/render/shaders/bolt.d.ts +1 -1
- package/dist/render/shaders/bolt.js +7 -1
- package/dist/render/shaders/caustics.d.ts +1 -1
- package/dist/render/shaders/depth.js +11 -0
- 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/anisotropic.d.ts +1 -1
- package/dist/render/shaders/flat/anisotropic.js +37 -3
- package/dist/render/shaders/flat/areaLight.d.ts +1 -1
- package/dist/render/shaders/flat/areaLight.js +46 -0
- package/dist/render/shaders/flat/glassTint.d.ts +3 -2
- package/dist/render/shaders/flat/glassTint.js +5 -1
- package/dist/render/shaders/flat/index.d.ts +15 -0
- package/dist/render/shaders/flat/index.js +22 -2
- package/dist/render/shaders/flat/lobes.d.ts +6 -0
- package/dist/render/shaders/flat/lobes.js +49 -43
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +85 -22
- package/dist/render/shaders/flat/models.d.ts +5 -2
- package/dist/render/shaders/flat/models.js +100 -16
- 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/preamble.js +10 -3
- 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/depth.wgsl.d.ts +4 -4
- package/dist/render/shaders/generated/depth.wgsl.js +4 -4
- 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 +192 -16
- package/dist/render/shaders/generated/flat.wgsl.js +230 -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 +32 -7
- package/dist/render/shaders/generated/ssrTrace.wgsl.js +32 -7
- package/dist/render/shaders/generated/water.wgsl.d.ts +23 -16
- package/dist/render/shaders/generated/water.wgsl.js +23 -16
- package/dist/render/shaders/gi/probeBake.wgsl.js +9 -5
- 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/recon/motion.wgsl.d.ts +1 -1
- package/dist/render/shaders/recon/motion.wgsl.js +13 -2
- 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 +46 -1
- package/dist/render/shaders/water.d.ts +1 -1
- package/dist/render/shaders/water.js +12 -3
- package/dist/render/skinScatter.d.ts +3 -1
- package/dist/render/skinScatter.js +3 -1
- package/dist/render/ssrPass.d.ts +16 -0
- package/dist/render/ssrPass.js +107 -1
- package/dist/render/surfaceModel.d.ts +29 -8
- package/dist/render/surfaceModel.js +33 -9
- package/dist/render/surfaceOverlay.d.ts +132 -0
- package/dist/render/surfaceOverlay.js +85 -0
- package/dist/render/surfaceTexture.d.ts +44 -1
- package/dist/render/surfaceTexture.js +96 -5
- 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 +22 -2
- package/src/render/anisotropicLobe.ts +29 -0
- package/src/render/areaLights.ts +58 -0
- package/src/render/atmosphere.ts +22 -1
- package/src/render/backend/api.ts +58 -2
- package/src/render/backend/createRenderer.ts +18 -1
- package/src/render/backend/webgl2/renderer.ts +548 -50
- package/src/render/backend/webgl2/sceneCaptureTarget.ts +130 -0
- package/src/render/backend/webgpu/device.ts +42 -3
- package/src/render/backend/webgpu/flatPass.ts +63 -26
- package/src/render/backend/webgpu/particlePass.ts +2 -1
- 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 +927 -100
- package/src/render/backend/webgpu/sceneCaptureTarget.ts +63 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +44 -14
- package/src/render/clusteredLights.ts +41 -8
- package/src/render/displayRange.ts +61 -0
- package/src/render/driftLight/bake.ts +3 -1
- package/src/render/driftLight/falloff.ts +13 -2
- 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/gi/bounceLights.ts +12 -3
- package/src/render/instanceCull.ts +4 -0
- package/src/render/instances.ts +26 -6
- package/src/render/lightBudget.ts +40 -2
- package/src/render/lightmap.ts +64 -23
- package/src/render/particlePool.ts +23 -0
- package/src/render/pass.ts +53 -1
- package/src/render/pointLightSelection.ts +20 -1
- package/src/render/reflectionSurfaceTargets.ts +105 -0
- package/src/render/renderQuality.ts +52 -15
- package/src/render/rendererHarness.ts +3 -0
- package/src/render/rgb9e5.ts +52 -0
- package/src/render/sceneCapture.ts +56 -0
- package/src/render/sceneTarget.ts +30 -2
- package/src/render/screenSpaceReflection.ts +38 -1
- package/src/render/shaders/bolt.ts +7 -1
- package/src/render/shaders/depth.ts +11 -0
- package/src/render/shaders/filmLook.ts +2 -1
- package/src/render/shaders/flat/anisotropic.ts +37 -3
- package/src/render/shaders/flat/areaLight.ts +46 -0
- package/src/render/shaders/flat/glassTint.ts +13 -2
- package/src/render/shaders/flat/index.ts +37 -2
- package/src/render/shaders/flat/lobes.ts +50 -43
- package/src/render/shaders/flat/main.ts +85 -22
- package/src/render/shaders/flat/models.ts +100 -16
- package/src/render/shaders/flat/overlay.ts +117 -0
- package/src/render/shaders/flat/pointShadow.ts +1 -1
- package/src/render/shaders/flat/preamble.ts +10 -3
- 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/depth.wgsl.ts +4 -4
- 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 +230 -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 +32 -7
- package/src/render/shaders/generated/water.wgsl.ts +23 -16
- package/src/render/shaders/gi/probeBake.wgsl.ts +9 -5
- package/src/render/shaders/gpudriven/fog.wgsl.ts +11 -3
- package/src/render/shaders/recon/motion.wgsl.ts +13 -2
- package/src/render/shaders/rush.ts +54 -11
- package/src/render/shaders/ssrResolve.ts +52 -0
- package/src/render/shaders/ssrTrace.ts +46 -1
- package/src/render/shaders/water.ts +12 -3
- package/src/render/skinScatter.ts +3 -1
- package/src/render/ssrPass.ts +139 -1
- package/src/render/surfaceModel.ts +47 -10
- package/src/render/surfaceOverlay.ts +195 -0
- package/src/render/surfaceTexture.ts +136 -9
- package/src/render/temporalAa.ts +20 -0
- package/src/render/vertexDefaults.ts +28 -0
|
@@ -129,7 +129,7 @@ import {
|
|
|
129
129
|
FACE_COUNT,
|
|
130
130
|
createResolvedPointShadows,
|
|
131
131
|
} from '../../pointShadowImage.ts';
|
|
132
|
-
import { MAX_AREA_LIGHTS, type AreaLightSource } from '../../areaLights.ts';
|
|
132
|
+
import { MAX_AREA_LIGHTS, packAreaSizesAndDoors, type AreaLightSource } from '../../areaLights.ts';
|
|
133
133
|
import {
|
|
134
134
|
areaShadowLayerCount,
|
|
135
135
|
castingRange,
|
|
@@ -175,12 +175,31 @@ import {
|
|
|
175
175
|
import type { TextStyle } from '../../textLayout.ts';
|
|
176
176
|
import { deviceSnappedCellSize, deviceSnappedOrigin } from '../../textLayout.ts';
|
|
177
177
|
import { GpuSurfaceTexture } from './surfaceTexturePass.ts';
|
|
178
|
+
import { GpuSceneCaptureTarget } from './sceneCaptureTarget.ts';
|
|
179
|
+
import { sceneCaptureTexels } from '../../sceneCapture.ts';
|
|
178
180
|
import { gpuCompressedFormats } from './compressedUpload.ts';
|
|
179
181
|
import { GpuClothBinding, GpuClothParticles, clothStandIn } from './clothTextures.ts';
|
|
180
182
|
import type { ClothViews } from './clothTextures.ts';
|
|
181
183
|
import type { ComputeHost } from './computeHost.ts';
|
|
182
|
-
import {
|
|
183
|
-
|
|
184
|
+
import {
|
|
185
|
+
MODEL_PARAM_FLOATS,
|
|
186
|
+
eyeAxisInWorld,
|
|
187
|
+
packModel,
|
|
188
|
+
skinProfileDistance,
|
|
189
|
+
} 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';
|
|
184
203
|
import { validateClothBinding } from '../../clothBindingData.ts';
|
|
185
204
|
import type { ClothBindingData } from '../../clothBindingData.ts';
|
|
186
205
|
import type { CompressedTextureFormat, SurfaceSource } from '../../compressedSource.ts';
|
|
@@ -209,7 +228,24 @@ import type { SurfaceMaterial, SurfaceTextureOptions } from '../../surfaceTextur
|
|
|
209
228
|
import type { RenderBackend } from '../api.ts';
|
|
210
229
|
import { reflectionTargetSize, type ReflectionSize } from '../../planarReflectionDraw.ts';
|
|
211
230
|
import { drawingBufferSize } from '../../drawingBuffer.ts';
|
|
212
|
-
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';
|
|
213
249
|
import { ExposurePass } from './exposurePass.ts';
|
|
214
250
|
import { AO_STORE } from '../../shaders/ambientOcclusion.ts';
|
|
215
251
|
import { clampAutoExposure } from '../../shaders/exposure.ts';
|
|
@@ -274,9 +310,15 @@ import {
|
|
|
274
310
|
DECAL_TEXTURES,
|
|
275
311
|
DECAL_UNIFORMS,
|
|
276
312
|
PREMULTIPLIED_OVER_BLEND,
|
|
313
|
+
FRAME_REFLECTION_BLEND,
|
|
277
314
|
REFLECTION_OVER_BLEND,
|
|
278
315
|
SSR_FRAG_FIELDS,
|
|
279
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,
|
|
280
322
|
SSR_RESOLVE_FRAG_WGSL,
|
|
281
323
|
SSR_RESOLVE_TEXTURES,
|
|
282
324
|
SSR_TEXTURES,
|
|
@@ -337,7 +379,7 @@ import type { LineSegments } from '../../linePoints.ts';
|
|
|
337
379
|
import { buildPlumeGeometry } from '../../plumeGeometry.ts';
|
|
338
380
|
import type { ParticleMaterial } from '../../particleMaterial.ts';
|
|
339
381
|
import { facingCode, type ParticleBatchOptions } from '../../particleBatch.ts';
|
|
340
|
-
import type { ParticleInstances } from '../../particlePool.ts';
|
|
382
|
+
import type { DeviceParticles, ParticleInstances } from '../../particlePool.ts';
|
|
341
383
|
import { PARTICLE_INDICES } from '../../shaders/particle.ts';
|
|
342
384
|
import type { PlumeOptions, PlumePlacement } from '../../plumeRenderer.ts';
|
|
343
385
|
import type { InstanceData } from '../../instancedMesh.ts';
|
|
@@ -385,6 +427,7 @@ import {
|
|
|
385
427
|
type FlatVariant,
|
|
386
428
|
flatPipeline,
|
|
387
429
|
flatPipelineAsync,
|
|
430
|
+
probeStageCeilings,
|
|
388
431
|
} from './flatPass.ts';
|
|
389
432
|
import type { OitTarget } from './flatPass.ts';
|
|
390
433
|
import {
|
|
@@ -687,34 +730,20 @@ const MAX_SKY_DRAWS = 64;
|
|
|
687
730
|
/**
|
|
688
731
|
* Whether the environment probe's permutation fits this device's per-stage binding limits.
|
|
689
732
|
*
|
|
690
|
-
*
|
|
691
|
-
*
|
|
692
|
-
*
|
|
693
|
-
*
|
|
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.
|
|
694
737
|
*/
|
|
695
738
|
function probeFits(device: GPUDevice): boolean {
|
|
696
739
|
/*
|
|
697
|
-
* **
|
|
698
|
-
*
|
|
699
|
-
*
|
|
700
|
-
*
|
|
701
|
-
* it
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
*
|
|
705
|
-
* It used to declare seventeen *samplers* too, and that was the binding constraint: this
|
|
706
|
-
* adapter offers exactly sixteen and no request can raise it, so the probe stood down and
|
|
707
|
-
* reflective surfaces kept a gradient. The shadow bindings share one declaration — see
|
|
708
|
-
* `// wgsl:share shadow` in `flat/preamble.ts`, which costs nothing because they were already
|
|
709
|
-
* one object — so the count is four, and the fourth is the normal map's: it is a colour image
|
|
710
|
-
* and wants filtering, which a non-filtering shadow sampler cannot give it.
|
|
711
|
-
*
|
|
712
|
-
* **Counted from the generated bindings rather than typed**: `FLAT_BINDINGS.flatFrag`'s
|
|
713
|
-
* widest variant is `directionalShadows+environmentProbe+pointShadows`, seven textures and
|
|
714
|
-
* four samplers. A stale number here reads as a device declining a feature it can run.
|
|
715
|
-
*/
|
|
716
|
-
const TEXTURES = 7;
|
|
717
|
-
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();
|
|
718
747
|
const fits =
|
|
719
748
|
device.limits.maxSampledTexturesPerShaderStage >= TEXTURES &&
|
|
720
749
|
device.limits.maxSamplersPerShaderStage >= SAMPLERS;
|
|
@@ -782,6 +811,7 @@ const VERB_PASS = 3;
|
|
|
782
811
|
/** What the depth snapshot copies from, and what it copies into. */
|
|
783
812
|
const DEPTH_SOURCE = maskOf('sceneDepth');
|
|
784
813
|
const DEPTH_SNAPSHOT = maskOf('depthSnapshot');
|
|
814
|
+
const COLOR_SNAPSHOT = maskOf('colorSnapshot');
|
|
785
815
|
|
|
786
816
|
/** The mirror's own colour and depth. */
|
|
787
817
|
const MIRROR_TARGET = maskOf('mirrorColor', 'mirrorDepth');
|
|
@@ -1912,6 +1942,17 @@ export class WebGPURenderer implements RendererApi {
|
|
|
1912
1942
|
*/
|
|
1913
1943
|
private refractSnapshot: GPUTexture | null = null;
|
|
1914
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);
|
|
1915
1956
|
/** Whether this frame has taken it. One snapshot serves every refracting draw. */
|
|
1916
1957
|
private refractSnapshotTaken = false;
|
|
1917
1958
|
/**
|
|
@@ -2422,8 +2463,18 @@ export class WebGPURenderer implements RendererApi {
|
|
|
2422
2463
|
private readonly reflectionQueue = new ReflectionQueue();
|
|
2423
2464
|
private ssrLayout!: GPUBindGroupLayout;
|
|
2424
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;
|
|
2425
2475
|
private ssrUniforms!: GPUBuffer;
|
|
2426
|
-
|
|
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));
|
|
2427
2478
|
private readonly ssrFloats = new Float32Array(this.ssrStaging);
|
|
2428
2479
|
private ssrReflection: GPUTexture | null = null;
|
|
2429
2480
|
private ssrReflectionView: GPUTextureView | null = null;
|
|
@@ -2948,6 +2999,117 @@ export class WebGPURenderer implements RendererApi {
|
|
|
2948
2999
|
});
|
|
2949
3000
|
}
|
|
2950
3001
|
|
|
3002
|
+
/**
|
|
3003
|
+
* A texture the scene can be drawn into, for a material to show. See `RendererApi` and
|
|
3004
|
+
* `sceneCapture.ts`. At the world's own format, which under `hdrScene` is half floats.
|
|
3005
|
+
*/
|
|
3006
|
+
createSceneCapture(width: number, height: number): GpuSurfaceTexture {
|
|
3007
|
+
const texture = new GpuSurfaceTexture(
|
|
3008
|
+
this.surface.device,
|
|
3009
|
+
this.pipelines,
|
|
3010
|
+
sceneCaptureTexels(width, height, this.quality.hdrScene),
|
|
3011
|
+
{ wrap: 'clamp', mipmap: false },
|
|
3012
|
+
);
|
|
3013
|
+
this.captureTargets.set(texture, new GpuSceneCaptureTarget(this.surface.device, width, height));
|
|
3014
|
+
return texture;
|
|
3015
|
+
}
|
|
3016
|
+
|
|
3017
|
+
/** Each capture's attachments, beside the texture a material binds. */
|
|
3018
|
+
private readonly captureTargets = new WeakMap<GpuSurfaceTexture, GpuSceneCaptureTarget>();
|
|
3019
|
+
/**
|
|
3020
|
+
* Whether a scene capture is being drawn: an off-screen pass as a probe bake is, so the frame's
|
|
3021
|
+
* camera, jitter, motion, refraction copy, skin's spread and late routing are left alone, but
|
|
3022
|
+
* lit by the probes and DriftLight as the frame is. See `captureScene`.
|
|
3023
|
+
*/
|
|
3024
|
+
private capturePassActive = false;
|
|
3025
|
+
|
|
3026
|
+
/**
|
|
3027
|
+
* Draw the scene into a capture from `camera`. See `RendererApi.captureScene`.
|
|
3028
|
+
*
|
|
3029
|
+
* **Its own encoder, as a probe bake's**: the frame's pass is set aside and restored, the rings
|
|
3030
|
+
* are marked and rewound around the submit, and the frame graph is flushed at both boundaries so
|
|
3031
|
+
* no recorded draw crosses one. **Radiance**: the output transform and exposure are held off.
|
|
3032
|
+
*/
|
|
3033
|
+
captureScene(
|
|
3034
|
+
capture: GpuSurfaceTexture,
|
|
3035
|
+
camera: Camera,
|
|
3036
|
+
clearColor: Vec3,
|
|
3037
|
+
draw: (camera: Camera) => void,
|
|
3038
|
+
): boolean {
|
|
3039
|
+
if (this.surface.lost) return false;
|
|
3040
|
+
const target = this.captureTargets.get(capture);
|
|
3041
|
+
const resolve = capture.layerView();
|
|
3042
|
+
if (target === undefined || resolve === null) return false;
|
|
3043
|
+
camera.updateMatrices(target.width / target.height);
|
|
3044
|
+
const { device } = this.surface;
|
|
3045
|
+
const { depth, colorMsaa } = target.attachments(this.samples, this.pipelines.format);
|
|
3046
|
+
|
|
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
|
+
*/
|
|
3054
|
+
if (this.quality.frameGraph) this.flushGraph();
|
|
3055
|
+
const savedPass = this.pass;
|
|
3056
|
+
const savedEncoder = this.encoder;
|
|
3057
|
+
this.markRings();
|
|
3058
|
+
const skinMark = this.skinPalettes.mark();
|
|
3059
|
+
const frameEpoch = this.encoderEpoch;
|
|
3060
|
+
this.encoderEpoch = ++this.encoderEpochs;
|
|
3061
|
+
const encoder = device.createCommandEncoder({ label: 'capture' });
|
|
3062
|
+
this.encoder = encoder;
|
|
3063
|
+
this.capturePassActive = true;
|
|
3064
|
+
const heldGrade = this.perFrameInts[this.materialField('uOutputTransform')] ?? 0;
|
|
3065
|
+
const heldExposure = this.perFrameFloats[this.materialField('uOutputExposure')] ?? 1;
|
|
3066
|
+
this.perFrameInts[this.materialField('uOutputTransform')] = 0;
|
|
3067
|
+
this.perFrameFloats[this.materialField('uOutputExposure')] = 1;
|
|
3068
|
+
this.materials.dirty();
|
|
3069
|
+
const pass = encoder.beginRenderPass({
|
|
3070
|
+
label: 'capture',
|
|
3071
|
+
colorAttachments: [
|
|
3072
|
+
{
|
|
3073
|
+
view: colorMsaa ?? resolve,
|
|
3074
|
+
resolveTarget: colorMsaa === null ? undefined : resolve,
|
|
3075
|
+
loadOp: 'clear',
|
|
3076
|
+
storeOp: resolvedStoreOp(
|
|
3077
|
+
colorMsaa !== null,
|
|
3078
|
+
true,
|
|
3079
|
+
this.quality.discardResolvedAttachments,
|
|
3080
|
+
),
|
|
3081
|
+
clearValue: { r: clearColor[0], g: clearColor[1], b: clearColor[2], a: 1 },
|
|
3082
|
+
},
|
|
3083
|
+
],
|
|
3084
|
+
depthStencilAttachment: {
|
|
3085
|
+
view: depth,
|
|
3086
|
+
depthClearValue: DEPTH_CLEAR,
|
|
3087
|
+
depthLoadOp: 'clear',
|
|
3088
|
+
depthStoreOp: 'discard',
|
|
3089
|
+
},
|
|
3090
|
+
});
|
|
3091
|
+
this.pass = pass;
|
|
3092
|
+
try {
|
|
3093
|
+
draw(camera);
|
|
3094
|
+
/* The callback's draws replayed into this pass before it ends: see `bakeProbe`. */
|
|
3095
|
+
if (this.quality.frameGraph) this.flushGraph();
|
|
3096
|
+
} finally {
|
|
3097
|
+
pass.end();
|
|
3098
|
+
this.pass = null;
|
|
3099
|
+
this.flushRings();
|
|
3100
|
+
device.queue.submit([encoder.finish()]);
|
|
3101
|
+
this.rewindRings(skinMark);
|
|
3102
|
+
this.encoderEpoch = frameEpoch;
|
|
3103
|
+
this.encoder = savedEncoder;
|
|
3104
|
+
this.pass = savedPass;
|
|
3105
|
+
this.perFrameInts[this.materialField('uOutputTransform')] = heldGrade;
|
|
3106
|
+
this.perFrameFloats[this.materialField('uOutputExposure')] = heldExposure;
|
|
3107
|
+
this.materials.dirty();
|
|
3108
|
+
this.capturePassActive = false;
|
|
3109
|
+
}
|
|
3110
|
+
return true;
|
|
3111
|
+
}
|
|
3112
|
+
|
|
2951
3113
|
/**
|
|
2952
3114
|
* GPU textures an `updateSurfaceTexture` replaced, destroyed when the next frame begins.
|
|
2953
3115
|
*
|
|
@@ -2994,8 +3156,11 @@ export class WebGPURenderer implements RendererApi {
|
|
|
2994
3156
|
if (this.ormMap === texture) this.ormMap = null;
|
|
2995
3157
|
if (this.emissiveMap === texture) this.emissiveMap = null;
|
|
2996
3158
|
if (this.modelMap === texture) this.modelMap = null;
|
|
3159
|
+
if (this.overlayMaps === texture) this.overlayMaps = null;
|
|
2997
3160
|
this.forgetBindingsOf(texture);
|
|
2998
3161
|
texture.dispose();
|
|
3162
|
+
this.captureTargets.get(texture)?.dispose();
|
|
3163
|
+
this.captureTargets.delete(texture);
|
|
2999
3164
|
}
|
|
3000
3165
|
|
|
3001
3166
|
/**
|
|
@@ -3010,6 +3175,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
3010
3175
|
/* Same reasoning, for whichever SDF labels were bound against this atlas. */
|
|
3011
3176
|
this.sdfTextBindGroups.delete(texture);
|
|
3012
3177
|
this.flatBindGroups.clear();
|
|
3178
|
+
this.overlayGroups.clear();
|
|
3013
3179
|
/*
|
|
3014
3180
|
* The blank one is built with the albedo held aside, because `buildFlatBindGroup` reads
|
|
3015
3181
|
* `this.albedo` for that slot — leaving it in place would put a real colour map into the
|
|
@@ -3207,9 +3373,9 @@ export class WebGPURenderer implements RendererApi {
|
|
|
3207
3373
|
if (model?.kind === 'skin' && this.skinScatter !== null) {
|
|
3208
3374
|
this.skinScatter.setProfile(
|
|
3209
3375
|
model.profile,
|
|
3210
|
-
model
|
|
3211
|
-
model
|
|
3212
|
-
model
|
|
3376
|
+
skinProfileDistance(model, 0),
|
|
3377
|
+
skinProfileDistance(model, 1),
|
|
3378
|
+
skinProfileDistance(model, 2),
|
|
3213
3379
|
);
|
|
3214
3380
|
}
|
|
3215
3381
|
this.material((f, i) => {
|
|
@@ -3259,7 +3425,14 @@ export class WebGPURenderer implements RendererApi {
|
|
|
3259
3425
|
f[emissive + 1] = asked?.[1] ?? 1;
|
|
3260
3426
|
f[emissive + 2] = asked?.[2] ?? 1;
|
|
3261
3427
|
/* The model's numbers and whether its map is bound. See `packModel`. */
|
|
3262
|
-
packModel(
|
|
3428
|
+
packModel(
|
|
3429
|
+
material?.model ?? null,
|
|
3430
|
+
modelMap !== null,
|
|
3431
|
+
this.modelParams,
|
|
3432
|
+
material?.physicalSpecular === true,
|
|
3433
|
+
);
|
|
3434
|
+
/* The first physical highlight asked for is when the lit pipelines start carrying one. */
|
|
3435
|
+
if (material?.physicalSpecular === true) this.enableLit('PHYSICAL_SPECULAR');
|
|
3263
3436
|
f.set(this.modelParams, this.materialField('uModelParams'));
|
|
3264
3437
|
});
|
|
3265
3438
|
/*
|
|
@@ -3396,6 +3569,20 @@ export class WebGPURenderer implements RendererApi {
|
|
|
3396
3569
|
this.material((f) => f.set(this.ambientSH, this.materialField('uAmbientSH')));
|
|
3397
3570
|
}
|
|
3398
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
|
+
|
|
3399
3586
|
/** How reflective the surfaces drawn next are. Clamped, as `renderer.ts` clamps it. */
|
|
3400
3587
|
setSurfaceReflectivity(amount: number): void {
|
|
3401
3588
|
this.material((f) => {
|
|
@@ -4140,7 +4327,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
4140
4327
|
*/
|
|
4141
4328
|
private gradeCode(): number {
|
|
4142
4329
|
return !this.hasComposite || !this.quality.hdrScene
|
|
4143
|
-
? (
|
|
4330
|
+
? forwardTransformCode(this.quality.outputTransform)
|
|
4144
4331
|
: 0;
|
|
4145
4332
|
}
|
|
4146
4333
|
|
|
@@ -4175,7 +4362,43 @@ export class WebGPURenderer implements RendererApi {
|
|
|
4175
4362
|
|
|
4176
4363
|
const device = this.surface.device;
|
|
4177
4364
|
device.queue.writeBuffer(batch.instances, 0, batch.staging, 0, live * PARTICLE_INSTANCE_FLOATS);
|
|
4365
|
+
this.drawParticleInstances(batch, batch.instances, live, camera, env, timeSeconds);
|
|
4366
|
+
}
|
|
4178
4367
|
|
|
4368
|
+
/**
|
|
4369
|
+
* Draw particles a caller's own compute wrote, where they lie. See `DeviceParticles`.
|
|
4370
|
+
*
|
|
4371
|
+
* **The pool's draw with its instance stream swapped**: the same material, blend, flipbook, soft
|
|
4372
|
+
* edge and fog, reading the caller's buffer in place of the one the pool uploads into — so nothing
|
|
4373
|
+
* crosses back to the CPU. Not sorted, and a count past what the buffer holds is cut to it rather
|
|
4374
|
+
* than handed to a draw the device would refuse with the frame.
|
|
4375
|
+
*/
|
|
4376
|
+
drawDeviceParticles(
|
|
4377
|
+
batch: GpuParticles,
|
|
4378
|
+
particles: DeviceParticles,
|
|
4379
|
+
camera: Camera,
|
|
4380
|
+
env: Environment,
|
|
4381
|
+
timeSeconds: number,
|
|
4382
|
+
): void {
|
|
4383
|
+
const fits = Math.floor(particles.buffer.size / (PARTICLE_INSTANCE_FLOATS * 4));
|
|
4384
|
+
const live = Math.min(Math.max(0, Math.floor(particles.count)), fits);
|
|
4385
|
+
/* Not above zero covers a NaN as well: a count or a size that is no number draws nothing. */
|
|
4386
|
+
if (!this.canDraw() || !(live > 0)) return;
|
|
4387
|
+
if (batch.drawnInEncoder === this.encoderEpoch) return;
|
|
4388
|
+
batch.drawnInEncoder = this.encoderEpoch;
|
|
4389
|
+
this.drawParticleInstances(batch, particles.buffer, live, camera, env, timeSeconds);
|
|
4390
|
+
}
|
|
4391
|
+
|
|
4392
|
+
/** A pool's uniforms for this view, and its draw from `instances`: both entry points' tail. */
|
|
4393
|
+
private drawParticleInstances(
|
|
4394
|
+
batch: GpuParticles,
|
|
4395
|
+
instances: GPUBuffer,
|
|
4396
|
+
live: number,
|
|
4397
|
+
camera: Camera,
|
|
4398
|
+
env: Environment,
|
|
4399
|
+
timeSeconds: number,
|
|
4400
|
+
): void {
|
|
4401
|
+
const device = this.surface.device;
|
|
4179
4402
|
const v = batch.vertFloats;
|
|
4180
4403
|
const atV = (name: string): number => (PARTICLE_VERT_FIELDS[name]?.offset ?? -4) / 4;
|
|
4181
4404
|
mat4.multiply(this.correctedViewProj, CLIP_CORRECTION, camera.viewProjection);
|
|
@@ -4255,7 +4478,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
4255
4478
|
particleCommand.pipeline = particlePipe;
|
|
4256
4479
|
particleCommand.bindGroup = group;
|
|
4257
4480
|
particleCommand.vertexBuffers[0] = batch.corners;
|
|
4258
|
-
particleCommand.vertexBuffers[1] =
|
|
4481
|
+
particleCommand.vertexBuffers[1] = instances;
|
|
4259
4482
|
particleCommand.vertexCount = 2;
|
|
4260
4483
|
particleCommand.indexBuffer = batch.indexBuffer;
|
|
4261
4484
|
particleCommand.indexed = true;
|
|
@@ -4267,7 +4490,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
4267
4490
|
pass.setPipeline(particlePipe);
|
|
4268
4491
|
pass.setBindGroup(0, group);
|
|
4269
4492
|
pass.setVertexBuffer(0, batch.corners);
|
|
4270
|
-
pass.setVertexBuffer(1,
|
|
4493
|
+
pass.setVertexBuffer(1, instances);
|
|
4271
4494
|
pass.setIndexBuffer(batch.indexBuffer, 'uint32');
|
|
4272
4495
|
pass.drawIndexed(PARTICLE_INDICES, live);
|
|
4273
4496
|
}
|
|
@@ -4286,7 +4509,12 @@ export class WebGPURenderer implements RendererApi {
|
|
|
4286
4509
|
const fields = batch.fields;
|
|
4287
4510
|
const atF = (name: string): number => (fields[name]?.offset ?? -4) / 4;
|
|
4288
4511
|
let depth: GPUTextureView | null = null;
|
|
4289
|
-
if (
|
|
4512
|
+
if (
|
|
4513
|
+
batch.softDepth > 0 &&
|
|
4514
|
+
!this.reflectionPassActive &&
|
|
4515
|
+
!this.probePassActive &&
|
|
4516
|
+
!this.capturePassActive
|
|
4517
|
+
) {
|
|
4290
4518
|
if (this.openPass() !== null && this.takeVolumeDepth()) depth = this.resolvedDepthView;
|
|
4291
4519
|
if (depth === null && !this.warnedSpriteDepth) {
|
|
4292
4520
|
this.warnedSpriteDepth = true;
|
|
@@ -4660,6 +4888,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
4660
4888
|
scatterInto(f, WATER_FRAG_FIELDS['uLightRadius'], lights.radii, 1);
|
|
4661
4889
|
scatterInto(f, WATER_FRAG_FIELDS['uLightSourceRadius'], lights.sourceRadii, 1);
|
|
4662
4890
|
scatterInto(f, WATER_FRAG_FIELDS['uLightWeight'], lights.weights, 1);
|
|
4891
|
+
scatterInto(f, WATER_FRAG_FIELDS['uLightCone'], lights.conesAndFalloffs, 4);
|
|
4663
4892
|
|
|
4664
4893
|
const medium = resolveAtmosphere(
|
|
4665
4894
|
env,
|
|
@@ -5617,8 +5846,11 @@ export class WebGPURenderer implements RendererApi {
|
|
|
5617
5846
|
cookies: new Float32Array(0),
|
|
5618
5847
|
heldSourceRadii: new Float32Array(MAX_POINT_LIGHTS),
|
|
5619
5848
|
axesAndChannels: new Float32Array(MAX_POINT_LIGHTS * 4),
|
|
5849
|
+
conesAndFalloffs: new Float32Array(MAX_POINT_LIGHTS * 4),
|
|
5620
5850
|
heldWeights: new Float32Array(MAX_POINT_LIGHTS),
|
|
5621
5851
|
};
|
|
5852
|
+
/** The fixed arm's rectangles as `uAreaLightSize` holds them. See `packAreaSizesAndDoors`. */
|
|
5853
|
+
private readonly areaSizesAndDoors = new Float32Array(MAX_AREA_LIGHTS * 4);
|
|
5622
5854
|
/** Refilled per frame rather than allocated; see `resolveAtmosphere`. */
|
|
5623
5855
|
private readonly medium: ResolvedAtmosphere = {
|
|
5624
5856
|
fogColor: new Float32Array(3),
|
|
@@ -6414,6 +6646,10 @@ export class WebGPURenderer implements RendererApi {
|
|
|
6414
6646
|
this.skinPalettes = new SkinPaletteRing(quality.drawsPerFrame);
|
|
6415
6647
|
this.maxDrawingBufferPixels = quality.maxDrawingBufferPixels;
|
|
6416
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
|
+
}
|
|
6417
6653
|
this.flushSchedule =
|
|
6418
6654
|
quality.frameGraph && quality.identifierGraph
|
|
6419
6655
|
? createFlushSchedule(MAX_DRAWS_PER_FRAME)
|
|
@@ -6491,6 +6727,26 @@ export class WebGPURenderer implements RendererApi {
|
|
|
6491
6727
|
);
|
|
6492
6728
|
}
|
|
6493
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;
|
|
6494
6750
|
this.skinPipelines = skinScreen
|
|
6495
6751
|
? new PipelineCache(
|
|
6496
6752
|
device,
|
|
@@ -6881,6 +7137,11 @@ export class WebGPURenderer implements RendererApi {
|
|
|
6881
7137
|
addressModeU: 'clamp-to-edge',
|
|
6882
7138
|
addressModeV: 'clamp-to-edge',
|
|
6883
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
|
+
}
|
|
6884
7145
|
return { view: this.refractSnapshotView ?? blankView, sampler: this.refractSampler };
|
|
6885
7146
|
}
|
|
6886
7147
|
/*
|
|
@@ -7057,6 +7318,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
7057
7318
|
{ binding: RUSH_TEXTURES.uExposureHeld },
|
|
7058
7319
|
/* Local exposure's grid, read filtered between tiles. See `localExposure.ts`. */
|
|
7059
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 },
|
|
7060
7323
|
]);
|
|
7061
7324
|
this.rushUniforms = device.createBuffer({
|
|
7062
7325
|
label: 'post.rushUniforms',
|
|
@@ -7074,11 +7337,29 @@ export class WebGPURenderer implements RendererApi {
|
|
|
7074
7337
|
this.ssrLayout = createPostStageLayout(device, SSR_UNIFORMS, SSR_FRAG_SIZE, [
|
|
7075
7338
|
{ binding: SSR_TEXTURES.uSsrDepth, filterable: false },
|
|
7076
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 },
|
|
7077
7343
|
]);
|
|
7078
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
|
+
});
|
|
7079
7360
|
this.ssrUniforms = device.createBuffer({
|
|
7080
7361
|
label: 'post.ssrUniforms',
|
|
7081
|
-
size: SSR_SLOT * MAX_REFLECTIVE_SURFACES,
|
|
7362
|
+
size: SSR_SLOT * (MAX_REFLECTIVE_SURFACES + 1),
|
|
7082
7363
|
usage: USAGE_UNIFORM_DST,
|
|
7083
7364
|
});
|
|
7084
7365
|
this.decalUniforms = device.createBuffer({
|
|
@@ -7782,7 +8063,11 @@ export class WebGPURenderer implements RendererApi {
|
|
|
7782
8063
|
/* Last frame's snapshot is last frame's scene. */
|
|
7783
8064
|
this.refractSnapshotTaken = false;
|
|
7784
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;
|
|
7785
8069
|
this.skinScatter?.reset();
|
|
8070
|
+
this.reflectionSurface?.reset();
|
|
7786
8071
|
this.plumeVerts.reset();
|
|
7787
8072
|
this.plumeFrags.reset();
|
|
7788
8073
|
this.waterVerts.reset();
|
|
@@ -8217,7 +8502,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
8217
8502
|
* the viewer sees, and bounding it through a mirrored camera puts it in the wrong half of the
|
|
8218
8503
|
* screen.
|
|
8219
8504
|
*/
|
|
8220
|
-
if (!this.reflectionPassActive) {
|
|
8505
|
+
if (!this.reflectionPassActive && !this.capturePassActive) {
|
|
8221
8506
|
mat4.copy(this.frameRawViewProj, camera.viewProjection);
|
|
8222
8507
|
/* The eye in world space, which the resolve's normals are turned to face. */
|
|
8223
8508
|
this.reconEye.set(camera.position);
|
|
@@ -8251,7 +8536,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
8251
8536
|
* second temporal frame stepped the sequence twice a frame, so the world saw every other
|
|
8252
8537
|
* position and the anti-flicker's period never started.
|
|
8253
8538
|
*/
|
|
8254
|
-
|
|
8539
|
+
/* A capture has no resolve to undo a jitter and is no frame of the sequence. */
|
|
8540
|
+
if (this.framePresented || this.capturePassActive) this.viewProj = this.correctedViewProj;
|
|
8255
8541
|
else this.jitterFrame();
|
|
8256
8542
|
/*
|
|
8257
8543
|
* The frustum, once a frame, and from the **uncorrected** matrix.
|
|
@@ -8391,6 +8677,9 @@ export class WebGPURenderer implements RendererApi {
|
|
|
8391
8677
|
f[at('uWriteMode') + 1] = 0;
|
|
8392
8678
|
/* 1, not 0: a multiplier's identity is one. See `setEnvironmentGain`. */
|
|
8393
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);
|
|
8394
8683
|
/* The frame's own ambient until a draw asks for its own. See `setAmbientSH`. */
|
|
8395
8684
|
f.fill(0, at('uAmbientSH'), at('uAmbientSH') + AMBIENT_SH_FLOATS);
|
|
8396
8685
|
/*
|
|
@@ -8593,7 +8882,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
8593
8882
|
* uniform added on one backend is bound on both in the same change.
|
|
8594
8883
|
*/
|
|
8595
8884
|
scatterInto(f, this.fragment.fields['uLightDir'], lights.directions, 3);
|
|
8596
|
-
|
|
8885
|
+
/* The cone and, in z, the light's own falloff exponent. See `conesAndFalloffs`. */
|
|
8886
|
+
scatterInto(f, this.fragment.fields['uLightCone'], lights.conesAndFalloffs, 4);
|
|
8597
8887
|
scatterInto(f, this.fragment.fields['uLightIesProfile'], lights.iesProfiles, 1);
|
|
8598
8888
|
/* The azimuth and, in w, the light's channels. See `axesAndChannels`. */
|
|
8599
8889
|
scatterInto(f, this.fragment.fields['uLightIesAxis'], lights.axesAndChannels, 4);
|
|
@@ -8615,7 +8905,9 @@ export class WebGPURenderer implements RendererApi {
|
|
|
8615
8905
|
scatterInto(f, this.fragment.fields['uAreaLightColor'], area.colors, 3);
|
|
8616
8906
|
scatterInto(f, this.fragment.fields['uAreaLightRight'], area.right, 3);
|
|
8617
8907
|
scatterInto(f, this.fragment.fields['uAreaLightUp'], area.up, 3);
|
|
8618
|
-
|
|
8908
|
+
/* The half extents and, in zw, the barn doors. See `packAreaSizesAndDoors`. */
|
|
8909
|
+
packAreaSizesAndDoors(area, this.areaSizesAndDoors);
|
|
8910
|
+
scatterInto(f, this.fragment.fields['uAreaLightSize'], this.areaSizesAndDoors, 4);
|
|
8619
8911
|
scatterInto(f, this.fragment.fields['uAreaLightTwoSided'], area.twoSided, 1);
|
|
8620
8912
|
}
|
|
8621
8913
|
|
|
@@ -8780,6 +9072,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
8780
9072
|
this.emissiveMap = null;
|
|
8781
9073
|
this.modelMap = null;
|
|
8782
9074
|
this.bindGroup = this.blankAlbedoBindGroup;
|
|
9075
|
+
/* Nor an overlay; its numbers are zeroed with the block's other defaults. */
|
|
9076
|
+
this.overlayMaps = null;
|
|
8783
9077
|
this.uvScale[0] = 1;
|
|
8784
9078
|
this.uvScale[1] = 1;
|
|
8785
9079
|
this.uvOffset[0] = 0;
|
|
@@ -8867,6 +9161,9 @@ export class WebGPURenderer implements RendererApi {
|
|
|
8867
9161
|
* where it happened to pass, write motion for a surface no pixel of the frame belongs to.
|
|
8868
9162
|
*/
|
|
8869
9163
|
if (this.tracksMotionNow()) this.recordDrawMotion(mesh, model, previousModel);
|
|
9164
|
+
else if (previousModel !== null && this.quality.cameraMotionBlur * this.motionBlurScale > 0) {
|
|
9165
|
+
this.warnObjectBlur();
|
|
9166
|
+
}
|
|
8870
9167
|
noteMapsWithoutUvs(mesh, mesh.hasUvs, this.materialHasMaps);
|
|
8871
9168
|
/* An opaque draw meets the medium as the dial says: `submitMesh` reads an absent `fog` as
|
|
8872
9169
|
true, which is a translucent draw's default and would put every opaque draw back in the fog
|
|
@@ -9286,6 +9583,26 @@ export class WebGPURenderer implements RendererApi {
|
|
|
9286
9583
|
material,
|
|
9287
9584
|
);
|
|
9288
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
|
+
}
|
|
9289
9606
|
}
|
|
9290
9607
|
/* Back to the defaults for whatever is drawn next, exactly as `renderer.ts` restores them —
|
|
9291
9608
|
unconditionally, because the scratch state above is shared by every draw after this one
|
|
@@ -9437,6 +9754,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
9437
9754
|
const refraction = options.refraction ?? 0;
|
|
9438
9755
|
const glassy = resolveGlass(options.glass, this.glassScratch);
|
|
9439
9756
|
if ((refraction <= 0 && !glassy) || !this.takeRefractSnapshot()) return false;
|
|
9757
|
+
this.seeThroughBound = true;
|
|
9440
9758
|
const glass = this.glassScratch;
|
|
9441
9759
|
/* Strength, path thickness, transmission, frost: one vector, for the budget the shader states. */
|
|
9442
9760
|
const seeAt = this.materialField('uSeeThrough');
|
|
@@ -9458,6 +9776,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
9458
9776
|
|
|
9459
9777
|
/** Put the scratch back after `bindSeeThrough`: refraction and glass off. */
|
|
9460
9778
|
private releaseSeeThrough(): void {
|
|
9779
|
+
this.seeThroughBound = false;
|
|
9461
9780
|
const seeAt = this.materialField('uSeeThrough');
|
|
9462
9781
|
this.perFrameFloats[seeAt] = 0;
|
|
9463
9782
|
this.perFrameFloats[seeAt + 2] = 0;
|
|
@@ -9658,6 +9977,26 @@ export class WebGPURenderer implements RendererApi {
|
|
|
9658
9977
|
if (cut === null) pass.drawIndexed(mesh.indexCount, count);
|
|
9659
9978
|
else pass.drawIndexedIndirect(cut.args, 0);
|
|
9660
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
|
+
}
|
|
9661
10000
|
}
|
|
9662
10001
|
/* Back to the defaults, unconditionally and for the reason `drawMesh` gives at length: this
|
|
9663
10002
|
scratch block is cumulative state shared by every draw after this one, and an early return
|
|
@@ -10365,6 +10704,20 @@ export class WebGPURenderer implements RendererApi {
|
|
|
10365
10704
|
private warnedNoPointShadowArray = false;
|
|
10366
10705
|
/** Said once, for the retired ambient dial. */
|
|
10367
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;
|
|
10368
10721
|
private readonly castingWorldIndices = new Int32Array(POINT_SHADOW_POOL).fill(-1);
|
|
10369
10722
|
private readonly resolvedPointShadows = createResolvedPointShadows(MAX_POINT_LIGHTS);
|
|
10370
10723
|
/** One face's view-projection, rebuilt per face rather than allocated. */
|
|
@@ -10813,6 +11166,43 @@ export class WebGPURenderer implements RendererApi {
|
|
|
10813
11166
|
);
|
|
10814
11167
|
}
|
|
10815
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
|
+
|
|
10816
11206
|
/**
|
|
10817
11207
|
* The bind group a draw uses, skinned or not.
|
|
10818
11208
|
*
|
|
@@ -10829,7 +11219,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
10829
11219
|
morphed: boolean,
|
|
10830
11220
|
deltas: GPUTextureView | null,
|
|
10831
11221
|
): GPUBindGroup {
|
|
10832
|
-
|
|
11222
|
+
const base = this.overlayGroup();
|
|
11223
|
+
if (!skinned && !morphed) return base;
|
|
10833
11224
|
const palette = skinned ? this.skinPalettes.view(this.skinPaletteSlot) : null;
|
|
10834
11225
|
/* Slot `k` caches at `k + 1`; index 0 is the morphed draw that is not skinned and has no
|
|
10835
11226
|
slot of its own. See `skinnedGroups`. */
|
|
@@ -10838,12 +11229,14 @@ export class WebGPURenderer implements RendererApi {
|
|
|
10838
11229
|
const cloth = skinned ? this.clothViews() : null;
|
|
10839
11230
|
if (
|
|
10840
11231
|
(this.skinnedGroups[at] ?? null) === null ||
|
|
10841
|
-
this.skinnedGroupsBeside[at] !==
|
|
11232
|
+
this.skinnedGroupsBeside[at] !== base ||
|
|
10842
11233
|
this.skinnedGroupsPalette[at] !== palette ||
|
|
10843
11234
|
this.skinnedGroupsDeltas[at] !== deltas ||
|
|
10844
11235
|
this.skinnedGroupsCloth[at] !== (cloth?.particles ?? null) ||
|
|
10845
11236
|
this.skinnedGroupsClothBinding[at] !== (cloth?.binding ?? null)
|
|
10846
11237
|
) {
|
|
11238
|
+
/* Built from the live material, with the overlay's atlas where `base` is its twin. */
|
|
11239
|
+
this.overlayBinding = base !== this.bindGroup;
|
|
10847
11240
|
this.skinnedGroups[at] = createFlatBindGroup(
|
|
10848
11241
|
this.surface.device,
|
|
10849
11242
|
this.flatLayoutFor(skinned, morphed),
|
|
@@ -10855,7 +11248,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
10855
11248
|
deltas,
|
|
10856
11249
|
cloth,
|
|
10857
11250
|
);
|
|
10858
|
-
this.
|
|
11251
|
+
this.overlayBinding = false;
|
|
11252
|
+
this.skinnedGroupsBeside[at] = base;
|
|
10859
11253
|
this.skinnedGroupsPalette[at] = palette;
|
|
10860
11254
|
this.skinnedGroupsDeltas[at] = deltas;
|
|
10861
11255
|
this.skinnedGroupsCloth[at] = cloth?.particles ?? null;
|
|
@@ -10921,6 +11315,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
10921
11315
|
*/
|
|
10922
11316
|
private rebuildFlatGroupsForNewRings(): void {
|
|
10923
11317
|
this.flatBindGroups.clear();
|
|
11318
|
+
this.overlayGroups.clear();
|
|
10924
11319
|
this.flatEnvironment = this.flatTextures('uEnvironment').view;
|
|
10925
11320
|
this.skinnedGroups.length = 0;
|
|
10926
11321
|
|
|
@@ -10984,6 +11379,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
10984
11379
|
*/
|
|
10985
11380
|
private rebuildFlatBindGroup(): void {
|
|
10986
11381
|
this.flatBindGroups.clear();
|
|
11382
|
+
this.overlayGroups.clear();
|
|
10987
11383
|
this.flatEnvironment = this.flatTextures('uEnvironment').view;
|
|
10988
11384
|
if (
|
|
10989
11385
|
this.albedo === null &&
|
|
@@ -11225,9 +11621,14 @@ export class WebGPURenderer implements RendererApi {
|
|
|
11225
11621
|
? (OUTPUT_TRANSFORM_CODE[this.quality.outputTransform] ?? 0)
|
|
11226
11622
|
: 0;
|
|
11227
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;
|
|
11228
11627
|
f.set(this.reprojection, at('uReprojection'));
|
|
11229
11628
|
f[at('uMotionStrength')] = motionStrength;
|
|
11230
|
-
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;
|
|
11231
11632
|
f[at('uAoStrength')] = this.frameProjection === null ? 0 : aoStrength;
|
|
11232
11633
|
/*
|
|
11233
11634
|
* The occlusion read where this frame's jitter put it (`uAoOffset`): measured from the jittered
|
|
@@ -11271,6 +11672,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
11271
11672
|
f[at('uGradeSize')] = this.gradeSize;
|
|
11272
11673
|
/* The lens and the print. See `setVignette` and `setFilmGrain`. */
|
|
11273
11674
|
f[at('uVignette')] = this.vignette;
|
|
11675
|
+
f.set(this.fringe, at('uFringe'));
|
|
11274
11676
|
f[at('uAutoExposure')] = adapting ? this.autoExposure : 0;
|
|
11275
11677
|
f[at('uLocalExposure')] = measuring && localising ? this.localExposure : 0;
|
|
11276
11678
|
f[at('uGrain')] = this.grain;
|
|
@@ -11316,7 +11718,11 @@ export class WebGPURenderer implements RendererApi {
|
|
|
11316
11718
|
private rebuildRushBindGroup(): void {
|
|
11317
11719
|
if (this.sceneColorView === null || this.resolvedDepthView === null) return;
|
|
11318
11720
|
if (this.aoTargetView === null) return;
|
|
11319
|
-
this.rushBindGroup = this.buildRushBindGroup(
|
|
11721
|
+
this.rushBindGroup = this.buildRushBindGroup(
|
|
11722
|
+
'post.rushBindGroup',
|
|
11723
|
+
this.sceneColorView,
|
|
11724
|
+
this.sceneColorView,
|
|
11725
|
+
);
|
|
11320
11726
|
/*
|
|
11321
11727
|
* **And the reconstruction's twin, from the same three call sites.** A colour grade replaces
|
|
11322
11728
|
* the lookup table and a resize replaces the depth and the occlusion target, and both groups
|
|
@@ -11324,12 +11730,21 @@ export class WebGPURenderer implements RendererApi {
|
|
|
11324
11730
|
* come to read a stale view on exactly the frames a consumer changed something.
|
|
11325
11731
|
*/
|
|
11326
11732
|
if (this.reconShownView !== null) {
|
|
11327
|
-
this.reconRushGroup = this.buildRushBindGroup(
|
|
11733
|
+
this.reconRushGroup = this.buildRushBindGroup(
|
|
11734
|
+
'recon.rushBindGroup',
|
|
11735
|
+
this.reconShownView,
|
|
11736
|
+
this.reconMotionView ?? this.reconShownView,
|
|
11737
|
+
);
|
|
11328
11738
|
}
|
|
11329
11739
|
}
|
|
11330
11740
|
|
|
11331
11741
|
/** The composite's group over one scene source. See `rebuildRushBindGroup`. */
|
|
11332
|
-
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 {
|
|
11333
11748
|
const { device } = this.surface;
|
|
11334
11749
|
if (this.resolvedDepthView === null || this.aoTargetView === null) return null;
|
|
11335
11750
|
const entry = (
|
|
@@ -11361,6 +11776,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
11361
11776
|
and a declared binding still needs a complete texture, as the bloom fallback says. */
|
|
11362
11777
|
...entry(RUSH_TEXTURES.uExposureHeld, this.exposurePass?.heldView ?? sceneView),
|
|
11363
11778
|
...entry(RUSH_TEXTURES.uExposureLocal, this.exposurePass?.localView ?? sceneView),
|
|
11779
|
+
...entry(RUSH_TEXTURES.uMotion, motionView),
|
|
11364
11780
|
],
|
|
11365
11781
|
layout: this.rushLayout,
|
|
11366
11782
|
});
|
|
@@ -11644,7 +12060,11 @@ export class WebGPURenderer implements RendererApi {
|
|
|
11644
12060
|
);
|
|
11645
12061
|
}
|
|
11646
12062
|
/* The composite's own group, over the resolved picture instead of the scene target. */
|
|
11647
|
-
this.reconRushGroup = this.buildRushBindGroup(
|
|
12063
|
+
this.reconRushGroup = this.buildRushBindGroup(
|
|
12064
|
+
'recon.rushBindGroup',
|
|
12065
|
+
this.reconShownView,
|
|
12066
|
+
this.reconMotionView ?? this.reconShownView,
|
|
12067
|
+
);
|
|
11648
12068
|
/* And the scene's, because `ensureComposite` built it before this one existed. */
|
|
11649
12069
|
this.rebuildRushBindGroup();
|
|
11650
12070
|
/* And the bloom's, which blooms the resolved picture: see `rebuildBloomGroups`. */
|
|
@@ -11723,7 +12143,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
11723
12143
|
routesLate(
|
|
11724
12144
|
this.reconstructing,
|
|
11725
12145
|
this.reflectionPassActive,
|
|
11726
|
-
this.probePassActive,
|
|
12146
|
+
this.probePassActive || this.capturePassActive,
|
|
11727
12147
|
this.framePresented,
|
|
11728
12148
|
)
|
|
11729
12149
|
);
|
|
@@ -11758,7 +12178,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
11758
12178
|
const depth = this.depthView;
|
|
11759
12179
|
if (!this.reconstructing || late === null || shown === null || depth === null) return;
|
|
11760
12180
|
const oitSet = this.oitActive && this.translucentQueue.length > 0;
|
|
11761
|
-
if (late.pending === 0 && !oitSet) return;
|
|
12181
|
+
if (late.pending === 0 && !oitSet && this.latePasses.length === 0) return;
|
|
11762
12182
|
const width = Math.max(1, this.surface.canvas.width);
|
|
11763
12183
|
const height = Math.max(1, this.surface.canvas.height);
|
|
11764
12184
|
/* The order-independent set tests against this depth too, so it is made for either. */
|
|
@@ -11782,20 +12202,64 @@ export class WebGPURenderer implements RendererApi {
|
|
|
11782
12202
|
this.refractSnapshotTaken = true;
|
|
11783
12203
|
}
|
|
11784
12204
|
this.lateRefractWanted = false;
|
|
11785
|
-
if (late.pending
|
|
11786
|
-
|
|
11787
|
-
|
|
11788
|
-
|
|
11789
|
-
|
|
11790
|
-
|
|
11791
|
-
|
|
11792
|
-
|
|
11793
|
-
|
|
11794
|
-
|
|
11795
|
-
|
|
11796
|
-
|
|
11797
|
-
|
|
11798
|
-
|
|
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
|
+
}
|
|
11799
12263
|
}
|
|
11800
12264
|
|
|
11801
12265
|
/** Whether this draw lands in the reconstructed picture. See `lateRouting.ts`. */
|
|
@@ -11803,7 +12267,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
11803
12267
|
return tracksMotion(
|
|
11804
12268
|
this.reconstructing,
|
|
11805
12269
|
this.reflectionPassActive,
|
|
11806
|
-
this.probePassActive,
|
|
12270
|
+
this.probePassActive || this.capturePassActive,
|
|
11807
12271
|
this.framePresented,
|
|
11808
12272
|
);
|
|
11809
12273
|
}
|
|
@@ -12222,7 +12686,10 @@ export class WebGPURenderer implements RendererApi {
|
|
|
12222
12686
|
* other is the disagreement the parity rule exists to prevent. Said once rather than dropped.
|
|
12223
12687
|
*/
|
|
12224
12688
|
private runReflections(encoder: GPUCommandEncoder): void {
|
|
12225
|
-
|
|
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;
|
|
12226
12693
|
const scene = this.sceneColorView;
|
|
12227
12694
|
const reflection = this.ssrReflectionView;
|
|
12228
12695
|
const group = this.ssrBindGroup;
|
|
@@ -12260,14 +12727,88 @@ export class WebGPURenderer implements RendererApi {
|
|
|
12260
12727
|
f.set(surface.axis, base + at('uSsrAxis'));
|
|
12261
12728
|
f.set(surface.tint, base + at('uSsrTint'));
|
|
12262
12729
|
f[base + at('uSsrStrength')] = surface.strength;
|
|
12730
|
+
f[base + at('uSsrFresnel')] = surface.fresnel ? 1 : 0;
|
|
12731
|
+
f[base + at('uSsrFresnel') + 1] = surface.roughness;
|
|
12263
12732
|
f[base + at('uSsrFacingCos')] = surface.facingCos;
|
|
12264
12733
|
f[base + at('uSsrReach')] = surface.reachM;
|
|
12265
12734
|
f[base + at('uSsrThickness')] = surface.thicknessM;
|
|
12266
12735
|
f[base + at('uSsrSteps')] = surface.steps;
|
|
12267
12736
|
f[base + at('uSsrEdgeFade')] = REFLECTION_EDGE_FADE;
|
|
12737
|
+
f[base + at('uSsrMaterial')] = 0;
|
|
12268
12738
|
slot++;
|
|
12269
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
|
+
}
|
|
12270
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;
|
|
12271
12812
|
|
|
12272
12813
|
/* Cleared whole and then scissored per surface: the resolve reads every pixel, and one left
|
|
12273
12814
|
from the last frame would composite that reflection over this frame's scene. */
|
|
@@ -12277,17 +12818,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
12277
12818
|
{ view: reflection, loadOp: 'clear', storeOp: 'store', clearValue: [0, 0, 0, 0] },
|
|
12278
12819
|
],
|
|
12279
12820
|
});
|
|
12280
|
-
trace.setPipeline(
|
|
12281
|
-
postPipeline(
|
|
12282
|
-
this.pipelines,
|
|
12283
|
-
device,
|
|
12284
|
-
this.ssrLayout,
|
|
12285
|
-
'post.ssrTrace',
|
|
12286
|
-
SSR_TRACE_FRAG_WGSL,
|
|
12287
|
-
this.pipelines.format,
|
|
12288
|
-
PREMULTIPLIED_OVER_BLEND,
|
|
12289
|
-
),
|
|
12290
|
-
);
|
|
12821
|
+
trace.setPipeline(tracePipeline);
|
|
12291
12822
|
slot = 0;
|
|
12292
12823
|
this.reflectionQueue.replay((surface) => {
|
|
12293
12824
|
const index = slot++;
|
|
@@ -12963,7 +13494,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
12963
13494
|
* glass, and a frame that refracts nothing breaks no pass at all.
|
|
12964
13495
|
*/
|
|
12965
13496
|
private takeRefractSnapshot(): boolean {
|
|
12966
|
-
if (this.reflectionPassActive || this.probePassActive) return false;
|
|
13497
|
+
if (this.reflectionPassActive || this.probePassActive || this.capturePassActive) return false;
|
|
12967
13498
|
if (this.refractSnapshotTaken) return true;
|
|
12968
13499
|
/* A late draw refracts the reconstructed picture, which does not exist until the resolve has
|
|
12969
13500
|
run; `runLate` takes the copy then, once, before any late draw is replayed. */
|
|
@@ -13044,12 +13575,97 @@ export class WebGPURenderer implements RendererApi {
|
|
|
13044
13575
|
!this.skinScattered &&
|
|
13045
13576
|
!this.reflectionPassActive &&
|
|
13046
13577
|
!this.probePassActive &&
|
|
13578
|
+
!this.capturePassActive &&
|
|
13579
|
+
!this.framePresented &&
|
|
13580
|
+
!this.overlayActive &&
|
|
13581
|
+
this.oitMode === 'none'
|
|
13582
|
+
);
|
|
13583
|
+
}
|
|
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 &&
|
|
13047
13592
|
!this.framePresented &&
|
|
13048
13593
|
!this.overlayActive &&
|
|
13049
13594
|
this.oitMode === 'none'
|
|
13050
13595
|
);
|
|
13051
13596
|
}
|
|
13052
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
|
+
|
|
13053
13669
|
/**
|
|
13054
13670
|
* A skin draw's diffuse and colour halves, kept for the spread: the frame's draw again, twice,
|
|
13055
13671
|
* with the same slots, buffers and bind group, through the pipelines that write the diffuse's
|
|
@@ -13129,10 +13745,17 @@ export class WebGPURenderer implements RendererApi {
|
|
|
13129
13745
|
private runSkinScatter(): void {
|
|
13130
13746
|
const skin = this.skinScatter;
|
|
13131
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;
|
|
13132
13755
|
/* Latched whether or not skin was kept: a skin drawn after this point is the whole surface. */
|
|
13133
13756
|
this.skinScattered = true;
|
|
13134
13757
|
if (!skin.pending) return;
|
|
13135
|
-
if (this.
|
|
13758
|
+
if (this.oitReplaying) return;
|
|
13136
13759
|
const encoder = this.encoder;
|
|
13137
13760
|
const depth = this.depthView;
|
|
13138
13761
|
const resolved = this.resolvedDepthView;
|
|
@@ -13201,7 +13824,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
13201
13824
|
* own terms: `resolvedDepthView` holds the *frame's* depth, and clamping a mirrored beam
|
|
13202
13825
|
* against it would stop it at geometry that is not in front of it.
|
|
13203
13826
|
*/
|
|
13204
|
-
if (this.reflectionPassActive) return false;
|
|
13827
|
+
if (this.reflectionPassActive || this.capturePassActive) return false;
|
|
13205
13828
|
if (this.resolvedDepthView === null) return false;
|
|
13206
13829
|
if (this.volumeDepthTaken) return true;
|
|
13207
13830
|
const encoder = this.encoder;
|
|
@@ -13257,6 +13880,65 @@ export class WebGPURenderer implements RendererApi {
|
|
|
13257
13880
|
return true;
|
|
13258
13881
|
}
|
|
13259
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
|
+
|
|
13260
13942
|
/**
|
|
13261
13943
|
* The pass to record into, reopening one on the canvas if the frame has already gone.
|
|
13262
13944
|
*
|
|
@@ -13448,6 +14130,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
13448
14130
|
/* And a march reads the same copy, for the same reason and with the same failure if it is
|
|
13449
14131
|
discarded: a depth of zero is the far plane reversed, so every ray finds nothing. */
|
|
13450
14132
|
this.reflectionQueue.length > 0 ||
|
|
14133
|
+
/* And the frame's materials' reflections, whose surface half tests against the depth too. */
|
|
14134
|
+
this.reflectionSurface !== null ||
|
|
13451
14135
|
/* And the medium's own march, which stops where the frame's depth says a surface is. Left
|
|
13452
14136
|
out of this, every ray runs to `maxDistance` through the walls and the fog is drawn on
|
|
13453
14137
|
top of the room rather than in it. */
|
|
@@ -13761,7 +14445,9 @@ export class WebGPURenderer implements RendererApi {
|
|
|
13761
14445
|
* any future branch added to one belongs in the other.
|
|
13762
14446
|
*/
|
|
13763
14447
|
private targetPipelines(): PipelineCache {
|
|
13764
|
-
if (this.probePassActive || this.reflectionPassActive
|
|
14448
|
+
if (this.probePassActive || this.reflectionPassActive || this.capturePassActive) {
|
|
14449
|
+
return this.pipelines;
|
|
14450
|
+
}
|
|
13765
14451
|
return this.overlayActive || this.framePresented ? this.overlayPipelines : this.pipelines;
|
|
13766
14452
|
}
|
|
13767
14453
|
|
|
@@ -14310,6 +14996,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
14310
14996
|
const reads = maskOf(...(definition.reads ?? []));
|
|
14311
14997
|
this.passReads.set(handle, reads);
|
|
14312
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();
|
|
14313
15001
|
if (definition.prepare !== undefined) this.preparingPasses++;
|
|
14314
15002
|
definition.init?.(this.passDevice());
|
|
14315
15003
|
return handle;
|
|
@@ -14329,12 +15017,16 @@ export class WebGPURenderer implements RendererApi {
|
|
|
14329
15017
|
environment: PassEnvironment | null;
|
|
14330
15018
|
distanceField: ComposedField | null;
|
|
14331
15019
|
jitter: Float32Array;
|
|
15020
|
+
sceneDepth: GPUTextureView | null;
|
|
15021
|
+
sceneColor: GPUTextureView | null;
|
|
14332
15022
|
} = {
|
|
14333
15023
|
backend: 'webgpu',
|
|
14334
15024
|
encoder: null,
|
|
14335
15025
|
environment: null,
|
|
14336
15026
|
distanceField: null,
|
|
14337
15027
|
jitter: this.passJitter,
|
|
15028
|
+
sceneDepth: null,
|
|
15029
|
+
sceneColor: null,
|
|
14338
15030
|
};
|
|
14339
15031
|
/**
|
|
14340
15032
|
* The object handed to a pass as `PrepareContext.environment`, rebuilt only when it changes.
|
|
@@ -14432,6 +15124,15 @@ export class WebGPURenderer implements RendererApi {
|
|
|
14432
15124
|
* than merely what happens.
|
|
14433
15125
|
*/
|
|
14434
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;
|
|
14435
15136
|
for (const definition of this.passes.definitions) {
|
|
14436
15137
|
definition?.prepare?.(this.prepareContext as PrepareContext);
|
|
14437
15138
|
}
|
|
@@ -14448,20 +15149,42 @@ export class WebGPURenderer implements RendererApi {
|
|
|
14448
15149
|
if (!this.canDraw()) return;
|
|
14449
15150
|
const definition = passAt(this.passes, handle);
|
|
14450
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
|
+
}
|
|
14451
15181
|
if (!this.quality.frameGraph) {
|
|
14452
15182
|
const pass = this.openPass();
|
|
14453
15183
|
if (pass === null) return;
|
|
14454
15184
|
definition.draw(this.fillPassContext(pass));
|
|
14455
15185
|
return;
|
|
14456
15186
|
}
|
|
14457
|
-
recordNode(
|
|
14458
|
-
this.arena,
|
|
14459
|
-
VERB_PASS,
|
|
14460
|
-
this.passReads.get(handle) ?? 0,
|
|
14461
|
-
this.currentTarget(),
|
|
14462
|
-
handle,
|
|
14463
|
-
0,
|
|
14464
|
-
);
|
|
15187
|
+
recordNode(this.arena, VERB_PASS, reads, this.currentTarget(), handle, 0);
|
|
14465
15188
|
if (this.framePresented) this.queueOverlayFlush();
|
|
14466
15189
|
}
|
|
14467
15190
|
|
|
@@ -14582,8 +15305,9 @@ export class WebGPURenderer implements RendererApi {
|
|
|
14582
15305
|
): void {
|
|
14583
15306
|
const binner = this.clusterBinner;
|
|
14584
15307
|
i[at('uClustered')] = binner === null ? 0 : 1;
|
|
14585
|
-
if (binner === null) return;
|
|
14586
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\`). */
|
|
14587
15311
|
const p = camera.projection;
|
|
14588
15312
|
const tanHalfFovY = 1 / (p[5] ?? 1);
|
|
14589
15313
|
const aspect = (p[5] ?? 1) / (p[0] ?? 1);
|
|
@@ -14599,6 +15323,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
14599
15323
|
f[frustumAt + 2] = tanHalfFovY;
|
|
14600
15324
|
f[frustumAt + 3] = aspect;
|
|
14601
15325
|
f.set(camera.view as Float32Array, at('uView'));
|
|
15326
|
+
if (binner === null) return;
|
|
14602
15327
|
|
|
14603
15328
|
/* The rectangles past the fixed arm's go into the table. See `ClusterLightSet.areas`. */
|
|
14604
15329
|
const set = fillClusterLightSet(env, this.clusterLights, this.shadedAreaLights);
|
|
@@ -14923,6 +15648,49 @@ export class WebGPURenderer implements RendererApi {
|
|
|
14923
15648
|
/** This frame's exposure into the tone curve. Starts at the profile's own. */
|
|
14924
15649
|
private exposure: number;
|
|
14925
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
|
+
|
|
14926
15694
|
/*
|
|
14927
15695
|
* -----------------------------------------------------------------------------------------
|
|
14928
15696
|
* The three screen effects, which this backend cannot yet honour — and says so.
|
|
@@ -14967,6 +15735,8 @@ export class WebGPURenderer implements RendererApi {
|
|
|
14967
15735
|
private readonly veilColor: Vec3 = [0, 0, 0];
|
|
14968
15736
|
private veilAlpha = 0;
|
|
14969
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);
|
|
14970
15740
|
private grain = 0;
|
|
14971
15741
|
private grainSeed = 0;
|
|
14972
15742
|
private warnedLookWithoutComposite = false;
|
|
@@ -14990,6 +15760,21 @@ export class WebGPURenderer implements RendererApi {
|
|
|
14990
15760
|
this.vignette = clampVignette(strength);
|
|
14991
15761
|
}
|
|
14992
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
|
+
|
|
14993
15778
|
/**
|
|
14994
15779
|
* Film grain, held until changed: an amplitude in display values (0 is none, 0.03 is a fine
|
|
14995
15780
|
* grain) and this frame's seed. **The seed is the caller's**, because the engine takes time from
|
|
@@ -15141,9 +15926,27 @@ export class WebGPURenderer implements RendererApi {
|
|
|
15141
15926
|
this.rushStrength = Math.min(Math.max(strength, 0), 1);
|
|
15142
15927
|
}
|
|
15143
15928
|
|
|
15144
|
-
/**
|
|
15145
|
-
|
|
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 {
|
|
15146
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);
|
|
15147
15950
|
}
|
|
15148
15951
|
/**
|
|
15149
15952
|
* The camera has cut: this frame is a new shot, not the next moment of the last one.
|
|
@@ -15752,17 +16555,18 @@ export class WebGPURenderer implements RendererApi {
|
|
|
15752
16555
|
* `bindMeshPass` and `drawMesh`, which record into whatever `this.pass` is. That is the same
|
|
15753
16556
|
* arrangement `bakeFace` uses for a point light's cube, and it is why a bake must not be
|
|
15754
16557
|
* opened inside a frame's pass by a caller who then expects the frame to continue.
|
|
15755
|
-
|
|
15756
|
-
const savedPass = this.pass;
|
|
15757
|
-
/*
|
|
16558
|
+
*
|
|
15758
16559
|
* **A bake is a boundary, and boundaries flush.** The mirror says why in its own words: what
|
|
15759
16560
|
* has been recorded and not yet replayed belongs to the pass that is about to end, and leaving
|
|
15760
16561
|
* it replays it into whichever pass opens next — a draw in the wrong target rather than a
|
|
15761
16562
|
* missing one. This is the fourth such boundary in the backend and the only one that was never
|
|
15762
16563
|
* given the treatment, which is what let a bake carry the frame's pending draws into a cube
|
|
15763
|
-
* 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`.
|
|
15764
16567
|
*/
|
|
15765
16568
|
if (this.quality.frameGraph) this.flushGraph();
|
|
16569
|
+
const savedPass = this.pass;
|
|
15766
16570
|
const savedEncoder = this.encoder;
|
|
15767
16571
|
/*
|
|
15768
16572
|
* **The rings are marked here and given back after the submit below**, because this bake is its
|
|
@@ -16454,6 +17258,7 @@ export class WebGPURenderer implements RendererApi {
|
|
|
16454
17258
|
this.sceneColor,
|
|
16455
17259
|
this.sceneColorMsaa,
|
|
16456
17260
|
this.refractSnapshot,
|
|
17261
|
+
this.passColour,
|
|
16457
17262
|
this.resolvedDepth,
|
|
16458
17263
|
this.aoTarget,
|
|
16459
17264
|
this.aoScratch,
|
|
@@ -16523,6 +17328,9 @@ export class WebGPURenderer implements RendererApi {
|
|
|
16523
17328
|
usage: 0x4 | 0x2 | 0x10, // TEXTURE_BINDING | COPY_DST | RENDER_ATTACHMENT
|
|
16524
17329
|
});
|
|
16525
17330
|
this.refractSnapshotView = this.refractSnapshot.createView();
|
|
17331
|
+
this.passColour = null;
|
|
17332
|
+
this.passColourView = null;
|
|
17333
|
+
if ((this.passReadsUnion & COLOR_SNAPSHOT) !== 0) this.allocatePassColour();
|
|
16526
17334
|
this.refractMips = new SnapshotMips(
|
|
16527
17335
|
device,
|
|
16528
17336
|
this.pipelines,
|
|
@@ -16751,6 +17559,10 @@ export class WebGPURenderer implements RendererApi {
|
|
|
16751
17559
|
usage: USAGE,
|
|
16752
17560
|
});
|
|
16753
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;
|
|
16754
17566
|
this.ssrBindGroup =
|
|
16755
17567
|
this.resolvedDepthView === null || this.sceneColorView === null
|
|
16756
17568
|
? null
|
|
@@ -16764,8 +17576,23 @@ export class WebGPURenderer implements RendererApi {
|
|
|
16764
17576
|
},
|
|
16765
17577
|
...entry(SSR_TEXTURES.uSsrDepth, this.resolvedDepthView, true),
|
|
16766
17578
|
...entry(SSR_TEXTURES.uSsrScene, this.sceneColorView),
|
|
17579
|
+
...entry(SSR_TEXTURES.uSsrProbeMap, probeMap),
|
|
17580
|
+
...entry(SSR_TEXTURES.uSsrTintMap, tintMap),
|
|
16767
17581
|
],
|
|
16768
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
|
+
});
|
|
16769
17596
|
this.ssrResolveGroup = device.createBindGroup({
|
|
16770
17597
|
label: 'post.ssrResolveGroup',
|
|
16771
17598
|
layout: this.ssrResolveLayout,
|