@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
|
@@ -44,7 +44,7 @@ import { COOKIE_TILE } from '../../lightBudget.js';
|
|
|
44
44
|
import { FULL_LIGHT_BUDGET } from '../../uniformVectorBudget.js';
|
|
45
45
|
import { toHalfFloats } from '../../halfFloat.js';
|
|
46
46
|
import { DEFAULT_SOURCE_RADIUS, FACE_COUNT, createResolvedPointShadows, } from '../../pointShadowImage.js';
|
|
47
|
-
import { MAX_AREA_LIGHTS } from '../../areaLights.js';
|
|
47
|
+
import { MAX_AREA_LIGHTS, packAreaSizesAndDoors } from '../../areaLights.js';
|
|
48
48
|
import { areaShadowLayerCount, castingRange, createResolvedAreaShadows, firstAreaShadowLayer, AreaShadowSet, } from '../../areaShadowSet.js';
|
|
49
49
|
import { createBakeScratch, runPointShadowBakes, selectCastingLights, } from '../../pointShadowBudget.js';
|
|
50
50
|
import { POINT_SHADOW_SAMPLER, PointShadowSystem, } from '../../pointShadowSystem.js';
|
|
@@ -54,20 +54,28 @@ import { createPanelBindGroup, createPanelBindGroupLayout, createPanelCorners, p
|
|
|
54
54
|
import { createGpuTextCube, createTextBindGroup, createTextBindGroupLayout, GpuText, textPipeline, TEXT_FRAG_FIELDS, TEXT_FRAG_SIZE, TEXT_VERT_FIELDS, TEXT_VERT_SIZE, } from './textPass.js';
|
|
55
55
|
import { deviceSnappedCellSize, deviceSnappedOrigin } from '../../textLayout.js';
|
|
56
56
|
import { GpuSurfaceTexture } from './surfaceTexturePass.js';
|
|
57
|
+
import { GpuSceneCaptureTarget } from './sceneCaptureTarget.js';
|
|
58
|
+
import { sceneCaptureTexels } from '../../sceneCapture.js';
|
|
57
59
|
import { gpuCompressedFormats } from './compressedUpload.js';
|
|
58
60
|
import { GpuClothBinding, GpuClothParticles, clothStandIn } from './clothTextures.js';
|
|
59
|
-
import { MODEL_PARAM_FLOATS, eyeAxisInWorld, packModel } from '../../surfaceModel.js';
|
|
61
|
+
import { MODEL_PARAM_FLOATS, eyeAxisInWorld, packModel, skinProfileDistance, } from '../../surfaceModel.js';
|
|
62
|
+
import { OVERLAY_FLOATS, overlayLays, packSurfaceOverlay, } from '../../surfaceOverlay.js';
|
|
63
|
+
import { frameReflectionsRefused, resolveFrameReflections, } from '../../frameReflections.js';
|
|
64
|
+
import { ReflectionSurfacePass } from './reflectionSurfacePass.js';
|
|
60
65
|
import { validateClothBinding } from '../../clothBindingData.js';
|
|
61
66
|
import { createSdfTextBindGroup, createSdfTextBindGroupLayout, GpuSdfText, sdfTextPipeline, SDF_TEXT_FRAG_FIELDS, SDF_TEXT_FRAG_SIZE, SDF_TEXT_VERT_FIELDS, SDF_TEXT_VERT_SIZE, } from './sdfTextPass.js';
|
|
62
67
|
import { createFilmBindGroup, createFilmBindGroupLayout, filmPipeline, FILM_FRAG_FIELDS, FILM_FRAG_SIZE, FILM_VERT_FIELDS, FILM_VERT_SIZE, } from './filmPass.js';
|
|
63
68
|
import { reflectionTargetSize } from '../../planarReflectionDraw.js';
|
|
64
69
|
import { drawingBufferSize } from '../../drawingBuffer.js';
|
|
65
|
-
import { MOTION_BLUR_MAX_UV, OUTPUT_TRANSFORM_CODE, RUSH_REACH_UV } from '../../vertexDefaults.js';
|
|
70
|
+
import { MOTION_BLUR_MAX_UV, OBJECT_BLUR_UNKEPT, OUTPUT_TRANSFORM_CODE, RUSH_REACH_UV, clampMotionShare, forwardTransformCode, } from '../../vertexDefaults.js';
|
|
71
|
+
import { FRINGE_FLOATS, resolveFringe } from '../../shaders/fringe.js';
|
|
72
|
+
import { displayHeadroom } from '../../displayRange.js';
|
|
73
|
+
import { DEFAULT_FILMIC_CURVE, FILMIC_CLAMPED, FILMIC_CONSTANTS, FILMIC_WITHOUT_COMPOSITE, resolveFilmicCurve, } from '../../filmicCurve.js';
|
|
66
74
|
import { ExposurePass } from './exposurePass.js';
|
|
67
75
|
import { AO_STORE } from '../../shaders/ambientOcclusion.js';
|
|
68
76
|
import { clampAutoExposure } from '../../shaders/exposure.js';
|
|
69
77
|
import { clampLocalExposure } from '../../shaders/localExposure.js';
|
|
70
|
-
import { createDepthResolveLayout, createPostStageLayout, depthResolvePipeline, postPipeline, postPipelineTargets, sceneColorFormat, AO_BLUR_FIELDS, AO_BLUR_FRAG_WGSL, AO_BLUR_SIZE, AO_BLUR_TEXTURES, AO_BLUR_UNIFORMS, AO_FRAG_FIELDS, AO_FRAG_SIZE, AO_FRAG_WGSL, AO_TEXTURES, AO_UNIFORMS, BLOOM_DOWNSAMPLE_FIELDS, BLOOM_DOWNSAMPLE_FRAG_WGSL, BLOOM_PREFILTER_FIELDS, BLOOM_PREFILTER_FRAG_WGSL, BLOOM_STAGE_SIZE, BLOOM_TEXTURES, BLOOM_UNIFORMS, BLOOM_UPSAMPLE_FIELDS, TINTED_ADD_BLEND, BLOOM_UPSAMPLE_FRAG_WGSL, RESOLVED_DEPTH_FORMAT, RUSH_FRAG_FIELDS, RUSH_FRAG_SIZE, RUSH_FRAG_WGSL, RUSH_TEXTURES, RUSH_UNIFORMS, TAA_FRAG_FIELDS, TAA_FRAG_SIZE, TAA_TEXTURES, TAA_UNIFORMS, TEMPORAL_RESOLVE_FRAG_WGSL, MEDIUM_FRAG_FIELDS, MEDIUM_FRAG_SIZE, MEDIUM_FRAG_WGSL, MEDIUM_TEXTURES, MEDIUM_TRANSMIT_BLEND, MEDIUM_UNIFORMS, MEDIUM_UPSAMPLE_FIELDS, MEDIUM_UPSAMPLE_FRAG_WGSL, MEDIUM_UPSAMPLE_SIZE, MEDIUM_UPSAMPLE_TEXTURES, MEDIUM_UPSAMPLE_UNIFORMS, OIT_COMPOSITE_BLEND, OIT_RESOLVE_FRAG_WGSL, OIT_TEXTURES, DECAL_FRAG_FIELDS, DECAL_FRAG_SIZE, DECAL_MULTIPLY_BLEND, DECAL_PROJECT_FRAG_WGSL, DECAL_TEXTURES, DECAL_UNIFORMS, PREMULTIPLIED_OVER_BLEND, REFLECTION_OVER_BLEND, SSR_FRAG_FIELDS, SSR_FRAG_SIZE, SSR_RESOLVE_FRAG_WGSL, SSR_RESOLVE_TEXTURES, SSR_TEXTURES, SSR_TRACE_FRAG_WGSL, SSR_UNIFORMS, createOitResolveLayout, createSsrResolveLayout, } from './postPass.js';
|
|
78
|
+
import { createDepthResolveLayout, createPostStageLayout, depthResolvePipeline, postPipeline, postPipelineTargets, sceneColorFormat, AO_BLUR_FIELDS, AO_BLUR_FRAG_WGSL, AO_BLUR_SIZE, AO_BLUR_TEXTURES, AO_BLUR_UNIFORMS, AO_FRAG_FIELDS, AO_FRAG_SIZE, AO_FRAG_WGSL, AO_TEXTURES, AO_UNIFORMS, BLOOM_DOWNSAMPLE_FIELDS, BLOOM_DOWNSAMPLE_FRAG_WGSL, BLOOM_PREFILTER_FIELDS, BLOOM_PREFILTER_FRAG_WGSL, BLOOM_STAGE_SIZE, BLOOM_TEXTURES, BLOOM_UNIFORMS, BLOOM_UPSAMPLE_FIELDS, TINTED_ADD_BLEND, BLOOM_UPSAMPLE_FRAG_WGSL, RESOLVED_DEPTH_FORMAT, RUSH_FRAG_FIELDS, RUSH_FRAG_SIZE, RUSH_FRAG_WGSL, RUSH_TEXTURES, RUSH_UNIFORMS, TAA_FRAG_FIELDS, TAA_FRAG_SIZE, TAA_TEXTURES, TAA_UNIFORMS, TEMPORAL_RESOLVE_FRAG_WGSL, MEDIUM_FRAG_FIELDS, MEDIUM_FRAG_SIZE, MEDIUM_FRAG_WGSL, MEDIUM_TEXTURES, MEDIUM_TRANSMIT_BLEND, MEDIUM_UNIFORMS, MEDIUM_UPSAMPLE_FIELDS, MEDIUM_UPSAMPLE_FRAG_WGSL, MEDIUM_UPSAMPLE_SIZE, MEDIUM_UPSAMPLE_TEXTURES, MEDIUM_UPSAMPLE_UNIFORMS, OIT_COMPOSITE_BLEND, OIT_RESOLVE_FRAG_WGSL, OIT_TEXTURES, DECAL_FRAG_FIELDS, DECAL_FRAG_SIZE, DECAL_MULTIPLY_BLEND, DECAL_PROJECT_FRAG_WGSL, DECAL_TEXTURES, DECAL_UNIFORMS, PREMULTIPLIED_OVER_BLEND, FRAME_REFLECTION_BLEND, REFLECTION_OVER_BLEND, SSR_FRAG_FIELDS, SSR_FRAG_SIZE, SSR_MATERIAL_FIELDS, SSR_MATERIAL_RESOLVE_FRAG_WGSL, SSR_MATERIAL_SIZE, SSR_MATERIAL_TEXTURES, SSR_MATERIAL_UNIFORMS, SSR_RESOLVE_FRAG_WGSL, SSR_RESOLVE_TEXTURES, SSR_TEXTURES, SSR_TRACE_FRAG_WGSL, SSR_UNIFORMS, createOitResolveLayout, createSsrResolveLayout, } from './postPass.js';
|
|
71
79
|
import { GRADE_PLACEHOLDER_SIZE, identityGradeLut, validateGradeLut, } from '../../colourGrade.js';
|
|
72
80
|
import { FILM_LOOK_WITHOUT_COMPOSITE, clampGrain, clampVignette, grainSeed, } from '../../shaders/filmLook.js';
|
|
73
81
|
import { createProbeMipBindGroupLayout, createProbePrefilterBindGroupLayout, PREFILTER_UNIFORM_OFFSETS, probeLevels, probeMipPipeline, probePrefilterPipeline, PROBE_FACES, PROBE_FACE_BASIS, PROBE_FAR_M, PROBE_FOV_DEG, PROBE_MIP_UNIFORM_SIZE, PROBE_NEAR_M, } from './probePass.js';
|
|
@@ -91,7 +99,7 @@ import { GpuGlassTint } from './glassTintPass.js';
|
|
|
91
99
|
import { SUN_DYNAMIC_LAYER, SUN_GLASS_MOVING_LAYER, SUN_GLASS_STATIC_LAYER, SUN_PEELED_LAYER, SUN_STATIC_LAYER, } from '../../shadowMap.js';
|
|
92
100
|
import { VERTEX_LAYOUT, createGpuMesh, createGpuMeshIncremental } from './buffers.js';
|
|
93
101
|
import { SkinPaletteRing } from './skinPaletteRing.js';
|
|
94
|
-
import { DEPTH_FORMAT, FLAT_VERT_FIELDS, FLAT_VERT_SIZE, createFlatBindGroup, createFlatBindGroupLayout, flatFragmentBindings, flatVariant, flatVertexBindings, flatPipeline, flatPipelineAsync, } from './flatPass.js';
|
|
102
|
+
import { DEPTH_FORMAT, FLAT_VERT_FIELDS, FLAT_VERT_SIZE, createFlatBindGroup, createFlatBindGroupLayout, flatFragmentBindings, flatVariant, flatVertexBindings, flatPipeline, flatPipelineAsync, probeStageCeilings, } from './flatPass.js';
|
|
95
103
|
import { DEPTH_FRAG_BINDING, DEPTH_FRAG_SIZE, DEPTH_FRAG_WGSL, DEPTH_PREVIOUS_BINDING, DEPTH_CUTOUT_VERT_FIELDS, DEPTH_CUTOUT_VERT_SIZE, DEPTH_INSTANCED_CUTOUT_VERT_FIELDS, DEPTH_VERT_FIELDS, DEPTH_VERT_SIZE, SHADOW_FORMAT, createDepthBindGroup, createDepthBindGroupLayout, depthPipeline, } from './depthPass.js';
|
|
96
104
|
import { PipelineCache } from './pipelineCache.js';
|
|
97
105
|
import { SKIN_TARGET_FORMAT, SkinScatterPass } from './skinScatterPass.js';
|
|
@@ -213,34 +221,20 @@ const MAX_SKY_DRAWS = 64;
|
|
|
213
221
|
/**
|
|
214
222
|
* Whether the environment probe's permutation fits this device's per-stage binding limits.
|
|
215
223
|
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
224
|
+
* **Two ceilings, counted separately**: sampled textures and samplers, since textures that share a
|
|
225
|
+
* sampler (`// wgsl:share` in `flat/preamble.ts`) count once against the second. The widest variant
|
|
226
|
+
* binds sixteen textures and twelve samplers, which is exactly WebGPU's default texture ceiling, and
|
|
227
|
+
* `select.ts` asks for the adapter's own — 48 here — so the probe does not depend on that request.
|
|
220
228
|
*/
|
|
221
229
|
function probeFits(device) {
|
|
222
230
|
/*
|
|
223
|
-
* **
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
* it
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
*
|
|
231
|
-
* It used to declare seventeen *samplers* too, and that was the binding constraint: this
|
|
232
|
-
* adapter offers exactly sixteen and no request can raise it, so the probe stood down and
|
|
233
|
-
* reflective surfaces kept a gradient. The shadow bindings share one declaration — see
|
|
234
|
-
* `// wgsl:share shadow` in `flat/preamble.ts`, which costs nothing because they were already
|
|
235
|
-
* one object — so the count is four, and the fourth is the normal map's: it is a colour image
|
|
236
|
-
* and wants filtering, which a non-filtering shadow sampler cannot give it.
|
|
237
|
-
*
|
|
238
|
-
* **Counted from the generated bindings rather than typed**: `FLAT_BINDINGS.flatFrag`'s
|
|
239
|
-
* widest variant is `directionalShadows+environmentProbe+pointShadows`, seven textures and
|
|
240
|
-
* four samplers. A stale number here reads as a device declining a feature it can run.
|
|
241
|
-
*/
|
|
242
|
-
const TEXTURES = 7;
|
|
243
|
-
const SAMPLERS = 4;
|
|
231
|
+
* **Counted from the generated bindings rather than typed** (`probeStageCeilings`). It was typed
|
|
232
|
+
* here as seven textures and four samplers while the widest variant grew to sixteen and twelve
|
|
233
|
+
* with the material maps, glass, the fixture atlas and DriftLight: a stale number reads as a
|
|
234
|
+
* device declining a feature it can run, or, the way this one had drifted, as one admitted to a
|
|
235
|
+
* permutation it cannot bind.
|
|
236
|
+
*/
|
|
237
|
+
const { textures: TEXTURES, samplers: SAMPLERS } = probeStageCeilings();
|
|
244
238
|
const fits = device.limits.maxSampledTexturesPerShaderStage >= TEXTURES &&
|
|
245
239
|
device.limits.maxSamplersPerShaderStage >= SAMPLERS;
|
|
246
240
|
if (!fits) {
|
|
@@ -277,6 +271,7 @@ const VERB_PASS = 3;
|
|
|
277
271
|
/** What the depth snapshot copies from, and what it copies into. */
|
|
278
272
|
const DEPTH_SOURCE = maskOf('sceneDepth');
|
|
279
273
|
const DEPTH_SNAPSHOT = maskOf('depthSnapshot');
|
|
274
|
+
const COLOR_SNAPSHOT = maskOf('colorSnapshot');
|
|
280
275
|
/** The mirror's own colour and depth. */
|
|
281
276
|
const MIRROR_TARGET = maskOf('mirrorColor', 'mirrorDepth');
|
|
282
277
|
/**
|
|
@@ -1278,6 +1273,17 @@ export class WebGPURenderer {
|
|
|
1278
1273
|
*/
|
|
1279
1274
|
refractSnapshot = null;
|
|
1280
1275
|
refractSnapshotView = null;
|
|
1276
|
+
/**
|
|
1277
|
+
* The frame's colour as a registered pass reads it (`reads: ['colorSnapshot']`): the scene's own
|
|
1278
|
+
* size and format, refilled at each such pass's draw. Allocated only once a pass declares the
|
|
1279
|
+
* read, beside the frame's targets, and never in the frame loop. See `takePassColour`.
|
|
1280
|
+
*/
|
|
1281
|
+
passColour = null;
|
|
1282
|
+
passColourView = null;
|
|
1283
|
+
/** Registered passes that read the colour, queued this frame for after the upscale. */
|
|
1284
|
+
latePasses = [];
|
|
1285
|
+
/** A late pass draws unjittered, as every late draw does. */
|
|
1286
|
+
latePassJitter = new Float32Array(2);
|
|
1281
1287
|
/** Whether this frame has taken it. One snapshot serves every refracting draw. */
|
|
1282
1288
|
refractSnapshotTaken = false;
|
|
1283
1289
|
/**
|
|
@@ -1780,8 +1786,18 @@ export class WebGPURenderer {
|
|
|
1780
1786
|
reflectionQueue = new ReflectionQueue();
|
|
1781
1787
|
ssrLayout;
|
|
1782
1788
|
ssrResolveLayout;
|
|
1789
|
+
/** The frame's materials' reflections, resolved; null where off. See `frameReflections.ts`. */
|
|
1790
|
+
frameReflections;
|
|
1791
|
+
/** Their surface half, null where they are off or cannot be drawn: `reflectionSurfacePass.ts`. */
|
|
1792
|
+
reflectionSurface;
|
|
1793
|
+
/** The material resolve: its layout, its one block (the blur) and its group over the three maps. */
|
|
1794
|
+
ssrMaterialLayout;
|
|
1795
|
+
ssrMaterialUniforms;
|
|
1796
|
+
ssrMaterialFloats = new Float32Array(SSR_MATERIAL_SIZE / 4);
|
|
1797
|
+
ssrMaterialGroup = null;
|
|
1783
1798
|
ssrUniforms;
|
|
1784
|
-
|
|
1799
|
+
/* One slot a surface, and one more for the frame's materials' trace. */
|
|
1800
|
+
ssrStaging = new ArrayBuffer(SSR_SLOT * (MAX_REFLECTIVE_SURFACES + 1));
|
|
1785
1801
|
ssrFloats = new Float32Array(this.ssrStaging);
|
|
1786
1802
|
ssrReflection = null;
|
|
1787
1803
|
ssrReflectionView = null;
|
|
@@ -2227,6 +2243,104 @@ export class WebGPURenderer {
|
|
|
2227
2243
|
mipmap: false,
|
|
2228
2244
|
});
|
|
2229
2245
|
}
|
|
2246
|
+
/**
|
|
2247
|
+
* A texture the scene can be drawn into, for a material to show. See `RendererApi` and
|
|
2248
|
+
* `sceneCapture.ts`. At the world's own format, which under `hdrScene` is half floats.
|
|
2249
|
+
*/
|
|
2250
|
+
createSceneCapture(width, height) {
|
|
2251
|
+
const texture = new GpuSurfaceTexture(this.surface.device, this.pipelines, sceneCaptureTexels(width, height, this.quality.hdrScene), { wrap: 'clamp', mipmap: false });
|
|
2252
|
+
this.captureTargets.set(texture, new GpuSceneCaptureTarget(this.surface.device, width, height));
|
|
2253
|
+
return texture;
|
|
2254
|
+
}
|
|
2255
|
+
/** Each capture's attachments, beside the texture a material binds. */
|
|
2256
|
+
captureTargets = new WeakMap();
|
|
2257
|
+
/**
|
|
2258
|
+
* Whether a scene capture is being drawn: an off-screen pass as a probe bake is, so the frame's
|
|
2259
|
+
* camera, jitter, motion, refraction copy, skin's spread and late routing are left alone, but
|
|
2260
|
+
* lit by the probes and DriftLight as the frame is. See `captureScene`.
|
|
2261
|
+
*/
|
|
2262
|
+
capturePassActive = false;
|
|
2263
|
+
/**
|
|
2264
|
+
* Draw the scene into a capture from `camera`. See `RendererApi.captureScene`.
|
|
2265
|
+
*
|
|
2266
|
+
* **Its own encoder, as a probe bake's**: the frame's pass is set aside and restored, the rings
|
|
2267
|
+
* are marked and rewound around the submit, and the frame graph is flushed at both boundaries so
|
|
2268
|
+
* no recorded draw crosses one. **Radiance**: the output transform and exposure are held off.
|
|
2269
|
+
*/
|
|
2270
|
+
captureScene(capture, camera, clearColor, draw) {
|
|
2271
|
+
if (this.surface.lost)
|
|
2272
|
+
return false;
|
|
2273
|
+
const target = this.captureTargets.get(capture);
|
|
2274
|
+
const resolve = capture.layerView();
|
|
2275
|
+
if (target === undefined || resolve === null)
|
|
2276
|
+
return false;
|
|
2277
|
+
camera.updateMatrices(target.width / target.height);
|
|
2278
|
+
const { device } = this.surface;
|
|
2279
|
+
const { depth, colorMsaa } = target.attachments(this.samples, this.pipelines.format);
|
|
2280
|
+
/*
|
|
2281
|
+
* **Flushed, and only then set aside.** The flush replays what the frame has recorded, and
|
|
2282
|
+
* replaying opens the frame's pass where none is open yet; saved before it, the pass given back
|
|
2283
|
+
* afterwards was the null that stood there, while the one the flush opened was left open on the
|
|
2284
|
+
* frame's encoder and every later pass on it was refused — a black frame for any capture taken
|
|
2285
|
+
* after the frame had drawn something. `scripts/capture-check.mjs` takes one at three points.
|
|
2286
|
+
*/
|
|
2287
|
+
if (this.quality.frameGraph)
|
|
2288
|
+
this.flushGraph();
|
|
2289
|
+
const savedPass = this.pass;
|
|
2290
|
+
const savedEncoder = this.encoder;
|
|
2291
|
+
this.markRings();
|
|
2292
|
+
const skinMark = this.skinPalettes.mark();
|
|
2293
|
+
const frameEpoch = this.encoderEpoch;
|
|
2294
|
+
this.encoderEpoch = ++this.encoderEpochs;
|
|
2295
|
+
const encoder = device.createCommandEncoder({ label: 'capture' });
|
|
2296
|
+
this.encoder = encoder;
|
|
2297
|
+
this.capturePassActive = true;
|
|
2298
|
+
const heldGrade = this.perFrameInts[this.materialField('uOutputTransform')] ?? 0;
|
|
2299
|
+
const heldExposure = this.perFrameFloats[this.materialField('uOutputExposure')] ?? 1;
|
|
2300
|
+
this.perFrameInts[this.materialField('uOutputTransform')] = 0;
|
|
2301
|
+
this.perFrameFloats[this.materialField('uOutputExposure')] = 1;
|
|
2302
|
+
this.materials.dirty();
|
|
2303
|
+
const pass = encoder.beginRenderPass({
|
|
2304
|
+
label: 'capture',
|
|
2305
|
+
colorAttachments: [
|
|
2306
|
+
{
|
|
2307
|
+
view: colorMsaa ?? resolve,
|
|
2308
|
+
resolveTarget: colorMsaa === null ? undefined : resolve,
|
|
2309
|
+
loadOp: 'clear',
|
|
2310
|
+
storeOp: resolvedStoreOp(colorMsaa !== null, true, this.quality.discardResolvedAttachments),
|
|
2311
|
+
clearValue: { r: clearColor[0], g: clearColor[1], b: clearColor[2], a: 1 },
|
|
2312
|
+
},
|
|
2313
|
+
],
|
|
2314
|
+
depthStencilAttachment: {
|
|
2315
|
+
view: depth,
|
|
2316
|
+
depthClearValue: DEPTH_CLEAR,
|
|
2317
|
+
depthLoadOp: 'clear',
|
|
2318
|
+
depthStoreOp: 'discard',
|
|
2319
|
+
},
|
|
2320
|
+
});
|
|
2321
|
+
this.pass = pass;
|
|
2322
|
+
try {
|
|
2323
|
+
draw(camera);
|
|
2324
|
+
/* The callback's draws replayed into this pass before it ends: see `bakeProbe`. */
|
|
2325
|
+
if (this.quality.frameGraph)
|
|
2326
|
+
this.flushGraph();
|
|
2327
|
+
}
|
|
2328
|
+
finally {
|
|
2329
|
+
pass.end();
|
|
2330
|
+
this.pass = null;
|
|
2331
|
+
this.flushRings();
|
|
2332
|
+
device.queue.submit([encoder.finish()]);
|
|
2333
|
+
this.rewindRings(skinMark);
|
|
2334
|
+
this.encoderEpoch = frameEpoch;
|
|
2335
|
+
this.encoder = savedEncoder;
|
|
2336
|
+
this.pass = savedPass;
|
|
2337
|
+
this.perFrameInts[this.materialField('uOutputTransform')] = heldGrade;
|
|
2338
|
+
this.perFrameFloats[this.materialField('uOutputExposure')] = heldExposure;
|
|
2339
|
+
this.materials.dirty();
|
|
2340
|
+
this.capturePassActive = false;
|
|
2341
|
+
}
|
|
2342
|
+
return true;
|
|
2343
|
+
}
|
|
2230
2344
|
/**
|
|
2231
2345
|
* GPU textures an `updateSurfaceTexture` replaced, destroyed when the next frame begins.
|
|
2232
2346
|
*
|
|
@@ -2278,8 +2392,12 @@ export class WebGPURenderer {
|
|
|
2278
2392
|
this.emissiveMap = null;
|
|
2279
2393
|
if (this.modelMap === texture)
|
|
2280
2394
|
this.modelMap = null;
|
|
2395
|
+
if (this.overlayMaps === texture)
|
|
2396
|
+
this.overlayMaps = null;
|
|
2281
2397
|
this.forgetBindingsOf(texture);
|
|
2282
2398
|
texture.dispose();
|
|
2399
|
+
this.captureTargets.get(texture)?.dispose();
|
|
2400
|
+
this.captureTargets.delete(texture);
|
|
2283
2401
|
}
|
|
2284
2402
|
/**
|
|
2285
2403
|
* Drop every bind group that may hold `texture`'s view, and rebuild the ones bound now.
|
|
@@ -2293,6 +2411,7 @@ export class WebGPURenderer {
|
|
|
2293
2411
|
/* Same reasoning, for whichever SDF labels were bound against this atlas. */
|
|
2294
2412
|
this.sdfTextBindGroups.delete(texture);
|
|
2295
2413
|
this.flatBindGroups.clear();
|
|
2414
|
+
this.overlayGroups.clear();
|
|
2296
2415
|
/*
|
|
2297
2416
|
* The blank one is built with the albedo held aside, because `buildFlatBindGroup` reads
|
|
2298
2417
|
* `this.albedo` for that slot — leaving it in place would put a real colour map into the
|
|
@@ -2458,7 +2577,7 @@ export class WebGPURenderer {
|
|
|
2458
2577
|
const model = material?.model;
|
|
2459
2578
|
warnUnpagedLightmap(model, modelMap);
|
|
2460
2579
|
if (model?.kind === 'skin' && this.skinScatter !== null) {
|
|
2461
|
-
this.skinScatter.setProfile(model.profile, model
|
|
2580
|
+
this.skinScatter.setProfile(model.profile, skinProfileDistance(model, 0), skinProfileDistance(model, 1), skinProfileDistance(model, 2));
|
|
2462
2581
|
}
|
|
2463
2582
|
this.material((f, i) => {
|
|
2464
2583
|
/* The material's four switches, one row: albedo, ORM map, emissive map, two-sided. */
|
|
@@ -2503,7 +2622,10 @@ export class WebGPURenderer {
|
|
|
2503
2622
|
f[emissive + 1] = asked?.[1] ?? 1;
|
|
2504
2623
|
f[emissive + 2] = asked?.[2] ?? 1;
|
|
2505
2624
|
/* The model's numbers and whether its map is bound. See `packModel`. */
|
|
2506
|
-
packModel(material?.model ?? null, modelMap !== null, this.modelParams);
|
|
2625
|
+
packModel(material?.model ?? null, modelMap !== null, this.modelParams, material?.physicalSpecular === true);
|
|
2626
|
+
/* The first physical highlight asked for is when the lit pipelines start carrying one. */
|
|
2627
|
+
if (material?.physicalSpecular === true)
|
|
2628
|
+
this.enableLit('PHYSICAL_SPECULAR');
|
|
2507
2629
|
f.set(this.modelParams, this.materialField('uModelParams'));
|
|
2508
2630
|
});
|
|
2509
2631
|
/*
|
|
@@ -2625,6 +2747,21 @@ export class WebGPURenderer {
|
|
|
2625
2747
|
packAmbientSH(coefficients, this.ambientSH);
|
|
2626
2748
|
this.material((f) => f.set(this.ambientSH, this.materialField('uAmbientSH')));
|
|
2627
2749
|
}
|
|
2750
|
+
/**
|
|
2751
|
+
* The overlay the following draws wear, or null for none: pass state, put back to none by
|
|
2752
|
+
* `bindMeshPass`. See `renderer.ts` and `surfaceOverlay.ts`. The numbers go in the material block,
|
|
2753
|
+
* which every variant declares; the atlas goes in a twin of each flat group (`overlayGroup`).
|
|
2754
|
+
*/
|
|
2755
|
+
setSurfaceOverlay(overlay) {
|
|
2756
|
+
if (this.surface.lost)
|
|
2757
|
+
return;
|
|
2758
|
+
packSurfaceOverlay(overlay, this.overlayFloats);
|
|
2759
|
+
/* The first overlay laid is when the lit pipelines start carrying one. */
|
|
2760
|
+
if (overlayLays(overlay))
|
|
2761
|
+
this.enableLit('SURFACE_OVERLAY');
|
|
2762
|
+
this.overlayMaps = (overlay?.maps ?? null);
|
|
2763
|
+
this.material((f) => f.set(this.overlayFloats, this.materialField('uOverlay')));
|
|
2764
|
+
}
|
|
2628
2765
|
/** How reflective the surfaces drawn next are. Clamped, as `renderer.ts` clamps it. */
|
|
2629
2766
|
setSurfaceReflectivity(amount) {
|
|
2630
2767
|
this.material((f) => {
|
|
@@ -3295,7 +3432,7 @@ export class WebGPURenderer {
|
|
|
3295
3432
|
*/
|
|
3296
3433
|
gradeCode() {
|
|
3297
3434
|
return !this.hasComposite || !this.quality.hdrScene
|
|
3298
|
-
? (
|
|
3435
|
+
? forwardTransformCode(this.quality.outputTransform)
|
|
3299
3436
|
: 0;
|
|
3300
3437
|
}
|
|
3301
3438
|
gradeExposure() {
|
|
@@ -3321,6 +3458,30 @@ export class WebGPURenderer {
|
|
|
3321
3458
|
return;
|
|
3322
3459
|
const device = this.surface.device;
|
|
3323
3460
|
device.queue.writeBuffer(batch.instances, 0, batch.staging, 0, live * PARTICLE_INSTANCE_FLOATS);
|
|
3461
|
+
this.drawParticleInstances(batch, batch.instances, live, camera, env, timeSeconds);
|
|
3462
|
+
}
|
|
3463
|
+
/**
|
|
3464
|
+
* Draw particles a caller's own compute wrote, where they lie. See `DeviceParticles`.
|
|
3465
|
+
*
|
|
3466
|
+
* **The pool's draw with its instance stream swapped**: the same material, blend, flipbook, soft
|
|
3467
|
+
* edge and fog, reading the caller's buffer in place of the one the pool uploads into — so nothing
|
|
3468
|
+
* crosses back to the CPU. Not sorted, and a count past what the buffer holds is cut to it rather
|
|
3469
|
+
* than handed to a draw the device would refuse with the frame.
|
|
3470
|
+
*/
|
|
3471
|
+
drawDeviceParticles(batch, particles, camera, env, timeSeconds) {
|
|
3472
|
+
const fits = Math.floor(particles.buffer.size / (PARTICLE_INSTANCE_FLOATS * 4));
|
|
3473
|
+
const live = Math.min(Math.max(0, Math.floor(particles.count)), fits);
|
|
3474
|
+
/* Not above zero covers a NaN as well: a count or a size that is no number draws nothing. */
|
|
3475
|
+
if (!this.canDraw() || !(live > 0))
|
|
3476
|
+
return;
|
|
3477
|
+
if (batch.drawnInEncoder === this.encoderEpoch)
|
|
3478
|
+
return;
|
|
3479
|
+
batch.drawnInEncoder = this.encoderEpoch;
|
|
3480
|
+
this.drawParticleInstances(batch, particles.buffer, live, camera, env, timeSeconds);
|
|
3481
|
+
}
|
|
3482
|
+
/** A pool's uniforms for this view, and its draw from `instances`: both entry points' tail. */
|
|
3483
|
+
drawParticleInstances(batch, instances, live, camera, env, timeSeconds) {
|
|
3484
|
+
const device = this.surface.device;
|
|
3324
3485
|
const v = batch.vertFloats;
|
|
3325
3486
|
const atV = (name) => (PARTICLE_VERT_FIELDS[name]?.offset ?? -4) / 4;
|
|
3326
3487
|
mat4.multiply(this.correctedViewProj, CLIP_CORRECTION, camera.viewProjection);
|
|
@@ -3392,7 +3553,7 @@ export class WebGPURenderer {
|
|
|
3392
3553
|
particleCommand.pipeline = particlePipe;
|
|
3393
3554
|
particleCommand.bindGroup = group;
|
|
3394
3555
|
particleCommand.vertexBuffers[0] = batch.corners;
|
|
3395
|
-
particleCommand.vertexBuffers[1] =
|
|
3556
|
+
particleCommand.vertexBuffers[1] = instances;
|
|
3396
3557
|
particleCommand.vertexCount = 2;
|
|
3397
3558
|
particleCommand.indexBuffer = batch.indexBuffer;
|
|
3398
3559
|
particleCommand.indexed = true;
|
|
@@ -3406,7 +3567,7 @@ export class WebGPURenderer {
|
|
|
3406
3567
|
pass.setPipeline(particlePipe);
|
|
3407
3568
|
pass.setBindGroup(0, group);
|
|
3408
3569
|
pass.setVertexBuffer(0, batch.corners);
|
|
3409
|
-
pass.setVertexBuffer(1,
|
|
3570
|
+
pass.setVertexBuffer(1, instances);
|
|
3410
3571
|
pass.setIndexBuffer(batch.indexBuffer, 'uint32');
|
|
3411
3572
|
pass.drawIndexed(PARTICLE_INDICES, live);
|
|
3412
3573
|
}
|
|
@@ -3424,7 +3585,10 @@ export class WebGPURenderer {
|
|
|
3424
3585
|
const fields = batch.fields;
|
|
3425
3586
|
const atF = (name) => (fields[name]?.offset ?? -4) / 4;
|
|
3426
3587
|
let depth = null;
|
|
3427
|
-
if (batch.softDepth > 0 &&
|
|
3588
|
+
if (batch.softDepth > 0 &&
|
|
3589
|
+
!this.reflectionPassActive &&
|
|
3590
|
+
!this.probePassActive &&
|
|
3591
|
+
!this.capturePassActive) {
|
|
3428
3592
|
if (this.openPass() !== null && this.takeVolumeDepth())
|
|
3429
3593
|
depth = this.resolvedDepthView;
|
|
3430
3594
|
if (depth === null && !this.warnedSpriteDepth) {
|
|
@@ -3717,6 +3881,7 @@ export class WebGPURenderer {
|
|
|
3717
3881
|
scatterInto(f, WATER_FRAG_FIELDS['uLightRadius'], lights.radii, 1);
|
|
3718
3882
|
scatterInto(f, WATER_FRAG_FIELDS['uLightSourceRadius'], lights.sourceRadii, 1);
|
|
3719
3883
|
scatterInto(f, WATER_FRAG_FIELDS['uLightWeight'], lights.weights, 1);
|
|
3884
|
+
scatterInto(f, WATER_FRAG_FIELDS['uLightCone'], lights.conesAndFalloffs, 4);
|
|
3720
3885
|
const medium = resolveAtmosphere(env, camera.position[1] ?? 0, this.quality.underwaterAtmosphere, this.medium);
|
|
3721
3886
|
f.set(medium.fogColor, atF('uFogColor'));
|
|
3722
3887
|
f[atF('uFogDensity')] = medium.fogDensity;
|
|
@@ -4509,8 +4674,11 @@ export class WebGPURenderer {
|
|
|
4509
4674
|
cookies: new Float32Array(0),
|
|
4510
4675
|
heldSourceRadii: new Float32Array(MAX_POINT_LIGHTS),
|
|
4511
4676
|
axesAndChannels: new Float32Array(MAX_POINT_LIGHTS * 4),
|
|
4677
|
+
conesAndFalloffs: new Float32Array(MAX_POINT_LIGHTS * 4),
|
|
4512
4678
|
heldWeights: new Float32Array(MAX_POINT_LIGHTS),
|
|
4513
4679
|
};
|
|
4680
|
+
/** The fixed arm's rectangles as `uAreaLightSize` holds them. See `packAreaSizesAndDoors`. */
|
|
4681
|
+
areaSizesAndDoors = new Float32Array(MAX_AREA_LIGHTS * 4);
|
|
4514
4682
|
/** Refilled per frame rather than allocated; see `resolveAtmosphere`. */
|
|
4515
4683
|
medium = {
|
|
4516
4684
|
fogColor: new Float32Array(3),
|
|
@@ -5184,6 +5352,10 @@ export class WebGPURenderer {
|
|
|
5184
5352
|
this.skinPalettes = new SkinPaletteRing(quality.drawsPerFrame);
|
|
5185
5353
|
this.maxDrawingBufferPixels = quality.maxDrawingBufferPixels;
|
|
5186
5354
|
this.exposure = quality.outputExposure;
|
|
5355
|
+
resolveFilmicCurve(DEFAULT_FILMIC_CURVE, this.filmConstants);
|
|
5356
|
+
if (quality.outputTransform === 'filmic' && !(quality.screenEffects && quality.hdrScene)) {
|
|
5357
|
+
console.warn(FILMIC_WITHOUT_COMPOSITE);
|
|
5358
|
+
}
|
|
5187
5359
|
this.flushSchedule =
|
|
5188
5360
|
quality.frameGraph && quality.identifierGraph
|
|
5189
5361
|
? createFlushSchedule(MAX_DRAWS_PER_FRAME)
|
|
@@ -5253,6 +5425,25 @@ export class WebGPURenderer {
|
|
|
5253
5425
|
console.warn("WebGPU: skinScattering 'screen-space' needs screenEffects to add into; skin is pre-integrated");
|
|
5254
5426
|
}
|
|
5255
5427
|
this.skinScatter = skinScreen ? new SkinScatterPass(device) : null;
|
|
5428
|
+
/*
|
|
5429
|
+
* The frame's materials' reflections, where the frame can carry them: scene light to swap the
|
|
5430
|
+
* probe's share in, and one sample, which the trace's depth copy and both maps are. Said once
|
|
5431
|
+
* where it cannot. See `frameReflections.ts`.
|
|
5432
|
+
*/
|
|
5433
|
+
this.frameReflections = resolveFrameReflections(quality.screenSpaceReflections);
|
|
5434
|
+
const reflectionsRefusal = this.frameReflections === null
|
|
5435
|
+
? null
|
|
5436
|
+
: !quality.screenEffects || !quality.hdrScene
|
|
5437
|
+
? 'the frame keeps no scene light to swap the probe in (`screenEffects` and `hdrScene`)'
|
|
5438
|
+
: this.samples > 1
|
|
5439
|
+
? 'a multisampled frame has no single depth or colour to march through'
|
|
5440
|
+
: null;
|
|
5441
|
+
if (reflectionsRefusal !== null)
|
|
5442
|
+
console.warn(`WebGPU: ${frameReflectionsRefused(reflectionsRefusal)}`);
|
|
5443
|
+
this.reflectionSurface =
|
|
5444
|
+
this.frameReflections !== null && reflectionsRefusal === null
|
|
5445
|
+
? new ReflectionSurfacePass(device)
|
|
5446
|
+
: null;
|
|
5256
5447
|
this.skinPipelines = skinScreen
|
|
5257
5448
|
? new PipelineCache(device, SKIN_TARGET_FORMAT, this.samples, glassShadows, quality.clusteredLights)
|
|
5258
5449
|
: null;
|
|
@@ -5608,6 +5799,11 @@ export class WebGPURenderer {
|
|
|
5608
5799
|
addressModeU: 'clamp-to-edge',
|
|
5609
5800
|
addressModeV: 'clamp-to-edge',
|
|
5610
5801
|
});
|
|
5802
|
+
/* Or a surface overlay's atlas, in the twin a draw wearing one binds: `overlayGroup`. */
|
|
5803
|
+
const maps = this.overlayBinding ? this.overlayMaps : null;
|
|
5804
|
+
if (maps !== null) {
|
|
5805
|
+
return { view: maps.layerView() ?? blankView, sampler: this.refractSampler };
|
|
5806
|
+
}
|
|
5611
5807
|
return { view: this.refractSnapshotView ?? blankView, sampler: this.refractSampler };
|
|
5612
5808
|
}
|
|
5613
5809
|
/*
|
|
@@ -5738,6 +5934,8 @@ export class WebGPURenderer {
|
|
|
5738
5934
|
{ binding: RUSH_TEXTURES.uExposureHeld },
|
|
5739
5935
|
/* Local exposure's grid, read filtered between tiles. See `localExposure.ts`. */
|
|
5740
5936
|
{ binding: RUSH_TEXTURES.uExposureLocal },
|
|
5937
|
+
/* Each mover's own motion: the reconstruction's target, or the scene where there is none. */
|
|
5938
|
+
{ binding: RUSH_TEXTURES.uMotion },
|
|
5741
5939
|
]);
|
|
5742
5940
|
this.rushUniforms = device.createBuffer({
|
|
5743
5941
|
label: 'post.rushUniforms',
|
|
@@ -5755,11 +5953,24 @@ export class WebGPURenderer {
|
|
|
5755
5953
|
this.ssrLayout = createPostStageLayout(device, SSR_UNIFORMS, SSR_FRAG_SIZE, [
|
|
5756
5954
|
{ binding: SSR_TEXTURES.uSsrDepth, filterable: false },
|
|
5757
5955
|
{ binding: SSR_TEXTURES.uSsrScene },
|
|
5956
|
+
/* The frame's materials, for the trace that reads them: `frameReflections.ts`. */
|
|
5957
|
+
{ binding: SSR_TEXTURES.uSsrProbeMap },
|
|
5958
|
+
{ binding: SSR_TEXTURES.uSsrTintMap },
|
|
5758
5959
|
]);
|
|
5759
5960
|
this.ssrResolveLayout = createSsrResolveLayout(device);
|
|
5961
|
+
this.ssrMaterialLayout = createPostStageLayout(device, SSR_MATERIAL_UNIFORMS, SSR_MATERIAL_SIZE, [
|
|
5962
|
+
{ binding: SSR_MATERIAL_TEXTURES.uSsrReflection },
|
|
5963
|
+
{ binding: SSR_MATERIAL_TEXTURES.uSsrProbeMap },
|
|
5964
|
+
{ binding: SSR_MATERIAL_TEXTURES.uSsrTintMap },
|
|
5965
|
+
]);
|
|
5966
|
+
this.ssrMaterialUniforms = device.createBuffer({
|
|
5967
|
+
label: 'post.ssrMaterialUniforms',
|
|
5968
|
+
size: SSR_MATERIAL_SIZE,
|
|
5969
|
+
usage: USAGE_UNIFORM_DST,
|
|
5970
|
+
});
|
|
5760
5971
|
this.ssrUniforms = device.createBuffer({
|
|
5761
5972
|
label: 'post.ssrUniforms',
|
|
5762
|
-
size: SSR_SLOT * MAX_REFLECTIVE_SURFACES,
|
|
5973
|
+
size: SSR_SLOT * (MAX_REFLECTIVE_SURFACES + 1),
|
|
5763
5974
|
usage: USAGE_UNIFORM_DST,
|
|
5764
5975
|
});
|
|
5765
5976
|
this.decalUniforms = device.createBuffer({
|
|
@@ -6175,7 +6386,11 @@ export class WebGPURenderer {
|
|
|
6175
6386
|
/* Last frame's snapshot is last frame's scene. */
|
|
6176
6387
|
this.refractSnapshotTaken = false;
|
|
6177
6388
|
this.skinScattered = false;
|
|
6389
|
+
/* One frame of the jitter's sequence however many times the frame binds its camera. */
|
|
6390
|
+
this.temporalHistory.nextFrame();
|
|
6391
|
+
this.latePasses.length = 0;
|
|
6178
6392
|
this.skinScatter?.reset();
|
|
6393
|
+
this.reflectionSurface?.reset();
|
|
6179
6394
|
this.plumeVerts.reset();
|
|
6180
6395
|
this.plumeFrags.reset();
|
|
6181
6396
|
this.waterVerts.reset();
|
|
@@ -6566,7 +6781,7 @@ export class WebGPURenderer {
|
|
|
6566
6781
|
* the viewer sees, and bounding it through a mirrored camera puts it in the wrong half of the
|
|
6567
6782
|
* screen.
|
|
6568
6783
|
*/
|
|
6569
|
-
if (!this.reflectionPassActive) {
|
|
6784
|
+
if (!this.reflectionPassActive && !this.capturePassActive) {
|
|
6570
6785
|
mat4.copy(this.frameRawViewProj, camera.viewProjection);
|
|
6571
6786
|
/* The eye in world space, which the resolve's normals are turned to face. */
|
|
6572
6787
|
this.reconEye.set(camera.position);
|
|
@@ -6600,7 +6815,8 @@ export class WebGPURenderer {
|
|
|
6600
6815
|
* second temporal frame stepped the sequence twice a frame, so the world saw every other
|
|
6601
6816
|
* position and the anti-flicker's period never started.
|
|
6602
6817
|
*/
|
|
6603
|
-
|
|
6818
|
+
/* A capture has no resolve to undo a jitter and is no frame of the sequence. */
|
|
6819
|
+
if (this.framePresented || this.capturePassActive)
|
|
6604
6820
|
this.viewProj = this.correctedViewProj;
|
|
6605
6821
|
else
|
|
6606
6822
|
this.jitterFrame();
|
|
@@ -6731,6 +6947,9 @@ export class WebGPURenderer {
|
|
|
6731
6947
|
f[at('uWriteMode') + 1] = 0;
|
|
6732
6948
|
/* 1, not 0: a multiplier's identity is one. See `setEnvironmentGain`. */
|
|
6733
6949
|
f[at('uEnvironmentDials') + 2] = 1;
|
|
6950
|
+
/* No overlay until a draw sets one. See `setSurfaceOverlay`. */
|
|
6951
|
+
this.overlayFloats.fill(0);
|
|
6952
|
+
f.fill(0, at('uOverlay'), at('uOverlay') + OVERLAY_FLOATS);
|
|
6734
6953
|
/* The frame's own ambient until a draw asks for its own. See `setAmbientSH`. */
|
|
6735
6954
|
f.fill(0, at('uAmbientSH'), at('uAmbientSH') + AMBIENT_SH_FLOATS);
|
|
6736
6955
|
/*
|
|
@@ -6928,7 +7147,8 @@ export class WebGPURenderer {
|
|
|
6928
7147
|
* uniform added on one backend is bound on both in the same change.
|
|
6929
7148
|
*/
|
|
6930
7149
|
scatterInto(f, this.fragment.fields['uLightDir'], lights.directions, 3);
|
|
6931
|
-
|
|
7150
|
+
/* The cone and, in z, the light's own falloff exponent. See `conesAndFalloffs`. */
|
|
7151
|
+
scatterInto(f, this.fragment.fields['uLightCone'], lights.conesAndFalloffs, 4);
|
|
6932
7152
|
scatterInto(f, this.fragment.fields['uLightIesProfile'], lights.iesProfiles, 1);
|
|
6933
7153
|
/* The azimuth and, in w, the light's channels. See `axesAndChannels`. */
|
|
6934
7154
|
scatterInto(f, this.fragment.fields['uLightIesAxis'], lights.axesAndChannels, 4);
|
|
@@ -6949,7 +7169,9 @@ export class WebGPURenderer {
|
|
|
6949
7169
|
scatterInto(f, this.fragment.fields['uAreaLightColor'], area.colors, 3);
|
|
6950
7170
|
scatterInto(f, this.fragment.fields['uAreaLightRight'], area.right, 3);
|
|
6951
7171
|
scatterInto(f, this.fragment.fields['uAreaLightUp'], area.up, 3);
|
|
6952
|
-
|
|
7172
|
+
/* The half extents and, in zw, the barn doors. See `packAreaSizesAndDoors`. */
|
|
7173
|
+
packAreaSizesAndDoors(area, this.areaSizesAndDoors);
|
|
7174
|
+
scatterInto(f, this.fragment.fields['uAreaLightSize'], this.areaSizesAndDoors, 4);
|
|
6953
7175
|
scatterInto(f, this.fragment.fields['uAreaLightTwoSided'], area.twoSided, 1);
|
|
6954
7176
|
}
|
|
6955
7177
|
/*
|
|
@@ -7096,6 +7318,8 @@ export class WebGPURenderer {
|
|
|
7096
7318
|
this.emissiveMap = null;
|
|
7097
7319
|
this.modelMap = null;
|
|
7098
7320
|
this.bindGroup = this.blankAlbedoBindGroup;
|
|
7321
|
+
/* Nor an overlay; its numbers are zeroed with the block's other defaults. */
|
|
7322
|
+
this.overlayMaps = null;
|
|
7099
7323
|
this.uvScale[0] = 1;
|
|
7100
7324
|
this.uvScale[1] = 1;
|
|
7101
7325
|
this.uvOffset[0] = 0;
|
|
@@ -7177,6 +7401,9 @@ export class WebGPURenderer {
|
|
|
7177
7401
|
*/
|
|
7178
7402
|
if (this.tracksMotionNow())
|
|
7179
7403
|
this.recordDrawMotion(mesh, model, previousModel);
|
|
7404
|
+
else if (previousModel !== null && this.quality.cameraMotionBlur * this.motionBlurScale > 0) {
|
|
7405
|
+
this.warnObjectBlur();
|
|
7406
|
+
}
|
|
7180
7407
|
noteMapsWithoutUvs(mesh, mesh.hasUvs, this.materialHasMaps);
|
|
7181
7408
|
/* An opaque draw meets the medium as the dial says: `submitMesh` reads an absent `fog` as
|
|
7182
7409
|
true, which is a translucent draw's default and would put every opaque draw back in the fog
|
|
@@ -7532,6 +7759,10 @@ export class WebGPURenderer {
|
|
|
7532
7759
|
if (splits) {
|
|
7533
7760
|
this.keepSkinDiffuse(mesh, key, base, skinned, morphed, deltas, layer, eight, clothed, slot, material);
|
|
7534
7761
|
}
|
|
7762
|
+
/* And its surface half, for the frame's materials' reflections. See `keepReflectionSurface`. */
|
|
7763
|
+
if (!blend && lit && depthWrite && this.reflectionSurfaceKeeps()) {
|
|
7764
|
+
this.keepReflectionSurface(mesh, key, base, skinned, morphed, deltas, layer, eight, clothed, modelKind, slot, material, null, 1, null);
|
|
7765
|
+
}
|
|
7535
7766
|
}
|
|
7536
7767
|
/* Back to the defaults for whatever is drawn next, exactly as `renderer.ts` restores them —
|
|
7537
7768
|
unconditionally, because the scratch state above is shared by every draw after this one
|
|
@@ -7661,6 +7892,7 @@ export class WebGPURenderer {
|
|
|
7661
7892
|
const glassy = resolveGlass(options.glass, this.glassScratch);
|
|
7662
7893
|
if ((refraction <= 0 && !glassy) || !this.takeRefractSnapshot())
|
|
7663
7894
|
return false;
|
|
7895
|
+
this.seeThroughBound = true;
|
|
7664
7896
|
const glass = this.glassScratch;
|
|
7665
7897
|
/* Strength, path thickness, transmission, frost: one vector, for the budget the shader states. */
|
|
7666
7898
|
const seeAt = this.materialField('uSeeThrough');
|
|
@@ -7681,6 +7913,7 @@ export class WebGPURenderer {
|
|
|
7681
7913
|
}
|
|
7682
7914
|
/** Put the scratch back after `bindSeeThrough`: refraction and glass off. */
|
|
7683
7915
|
releaseSeeThrough() {
|
|
7916
|
+
this.seeThroughBound = false;
|
|
7684
7917
|
const seeAt = this.materialField('uSeeThrough');
|
|
7685
7918
|
this.perFrameFloats[seeAt] = 0;
|
|
7686
7919
|
this.perFrameFloats[seeAt + 2] = 0;
|
|
@@ -7845,6 +8078,10 @@ export class WebGPURenderer {
|
|
|
7845
8078
|
else
|
|
7846
8079
|
pass.drawIndexedIndirect(cut.args, 0);
|
|
7847
8080
|
}
|
|
8081
|
+
/* And its surface half, for the frame's materials' reflections, with the same survivors. */
|
|
8082
|
+
if (!blend && lit && depthWrite && this.reflectionSurfaceKeeps()) {
|
|
8083
|
+
this.keepReflectionSurface(mesh, key, base, false, false, null, layer, false, false, modelKind, slot, material, instanceBuffer, count, cut === null ? null : cut.args);
|
|
8084
|
+
}
|
|
7848
8085
|
}
|
|
7849
8086
|
/* Back to the defaults, unconditionally and for the reason `drawMesh` gives at length: this
|
|
7850
8087
|
scratch block is cumulative state shared by every draw after this one, and an early return
|
|
@@ -8473,6 +8710,20 @@ export class WebGPURenderer {
|
|
|
8473
8710
|
warnedNoPointShadowArray = false;
|
|
8474
8711
|
/** Said once, for the retired ambient dial. */
|
|
8475
8712
|
warnedEnvironmentAmbient = false;
|
|
8713
|
+
/** The overlay the draws wear, `uOverlay`: see `setSurfaceOverlay`. */
|
|
8714
|
+
overlayFloats = new Float32Array(OVERLAY_FLOATS);
|
|
8715
|
+
/** Its atlas, bound where the refraction copy goes in a twin of each flat group. */
|
|
8716
|
+
overlayMaps = null;
|
|
8717
|
+
/**
|
|
8718
|
+
* Each flat group's twin with the atlas in the refraction slot, by the group and the atlas, built
|
|
8719
|
+
* on the first draw that wears it and dropped wherever `flatBindGroups` is.
|
|
8720
|
+
*/
|
|
8721
|
+
overlayGroups = new Map();
|
|
8722
|
+
/** True while a twin is built: what `flatTextures` hands the refraction slot then. */
|
|
8723
|
+
overlayBinding = false;
|
|
8724
|
+
/** True between `bindSeeThrough` and `releaseSeeThrough`: that draw reads the copy, not the atlas. */
|
|
8725
|
+
seeThroughBound = false;
|
|
8726
|
+
warnedOverlayRefraction = false;
|
|
8476
8727
|
castingWorldIndices = new Int32Array(POINT_SHADOW_POOL).fill(-1);
|
|
8477
8728
|
resolvedPointShadows = createResolvedPointShadows(MAX_POINT_LIGHTS);
|
|
8478
8729
|
/** One face's view-projection, rebuilt per face rather than allocated. */
|
|
@@ -8823,6 +9074,41 @@ export class WebGPURenderer {
|
|
|
8823
9074
|
buildFlatBindGroup() {
|
|
8824
9075
|
return createFlatBindGroup(this.surface.device, this.bindGroupLayout, this.variant, this.perDraw.buffer, this.perFrame.buffer, this.flatTextures);
|
|
8825
9076
|
}
|
|
9077
|
+
/**
|
|
9078
|
+
* The open material's flat group, or its twin with the surface overlay's atlas in the refraction
|
|
9079
|
+
* slot where the draw wears one and does not refract. A twin is built from the live material,
|
|
9080
|
+
* which `bindGroup` always is, and kept by the group and the atlas.
|
|
9081
|
+
*
|
|
9082
|
+
* **A draw that refracts, or is glass, keeps the frame's copy** and its overlay reads no image —
|
|
9083
|
+
* the shader asks the same of `uSeeThrough` (`overlayImagesBound`) — said once.
|
|
9084
|
+
*/
|
|
9085
|
+
overlayGroup() {
|
|
9086
|
+
const maps = this.overlayMaps;
|
|
9087
|
+
if (maps === null)
|
|
9088
|
+
return this.bindGroup;
|
|
9089
|
+
if (this.seeThroughBound) {
|
|
9090
|
+
if (!this.warnedOverlayRefraction) {
|
|
9091
|
+
this.warnedOverlayRefraction = true;
|
|
9092
|
+
console.warn('WebGPU: a draw that refracts or is glass wears a surface overlay with maps. The frame ' +
|
|
9093
|
+
"copy it reads takes the overlay's slot, so its rim, dissolve and wrinkles read no image.");
|
|
9094
|
+
}
|
|
9095
|
+
return this.bindGroup;
|
|
9096
|
+
}
|
|
9097
|
+
let byMaps = this.overlayGroups.get(this.bindGroup);
|
|
9098
|
+
if (byMaps === undefined) {
|
|
9099
|
+
byMaps = new Map();
|
|
9100
|
+
this.overlayGroups.set(this.bindGroup, byMaps);
|
|
9101
|
+
}
|
|
9102
|
+
let twin = byMaps.get(maps);
|
|
9103
|
+
if (twin === undefined) {
|
|
9104
|
+
this.overlayBinding = true;
|
|
9105
|
+
twin = this.buildFlatBindGroup();
|
|
9106
|
+
this.overlayBinding = false;
|
|
9107
|
+
this.bindGroupBudget.ask();
|
|
9108
|
+
byMaps.set(maps, twin);
|
|
9109
|
+
}
|
|
9110
|
+
return twin;
|
|
9111
|
+
}
|
|
8826
9112
|
/**
|
|
8827
9113
|
* The bind group a draw uses, skinned or not.
|
|
8828
9114
|
*
|
|
@@ -8835,8 +9121,9 @@ export class WebGPURenderer {
|
|
|
8835
9121
|
* rebuild this every draw.
|
|
8836
9122
|
*/
|
|
8837
9123
|
flatBindGroupFor(skinned, morphed, deltas) {
|
|
9124
|
+
const base = this.overlayGroup();
|
|
8838
9125
|
if (!skinned && !morphed)
|
|
8839
|
-
return
|
|
9126
|
+
return base;
|
|
8840
9127
|
const palette = skinned ? this.skinPalettes.view(this.skinPaletteSlot) : null;
|
|
8841
9128
|
/* Slot `k` caches at `k + 1`; index 0 is the morphed draw that is not skinned and has no
|
|
8842
9129
|
slot of its own. See `skinnedGroups`. */
|
|
@@ -8844,13 +9131,16 @@ export class WebGPURenderer {
|
|
|
8844
9131
|
/* A skinned group carries the cloth's views, or the stand-in's: see `clothViews`. */
|
|
8845
9132
|
const cloth = skinned ? this.clothViews() : null;
|
|
8846
9133
|
if ((this.skinnedGroups[at] ?? null) === null ||
|
|
8847
|
-
this.skinnedGroupsBeside[at] !==
|
|
9134
|
+
this.skinnedGroupsBeside[at] !== base ||
|
|
8848
9135
|
this.skinnedGroupsPalette[at] !== palette ||
|
|
8849
9136
|
this.skinnedGroupsDeltas[at] !== deltas ||
|
|
8850
9137
|
this.skinnedGroupsCloth[at] !== (cloth?.particles ?? null) ||
|
|
8851
9138
|
this.skinnedGroupsClothBinding[at] !== (cloth?.binding ?? null)) {
|
|
9139
|
+
/* Built from the live material, with the overlay's atlas where `base` is its twin. */
|
|
9140
|
+
this.overlayBinding = base !== this.bindGroup;
|
|
8852
9141
|
this.skinnedGroups[at] = createFlatBindGroup(this.surface.device, this.flatLayoutFor(skinned, morphed), this.variant, this.perDraw.buffer, this.perFrame.buffer, this.flatTextures, palette, deltas, cloth);
|
|
8853
|
-
this.
|
|
9142
|
+
this.overlayBinding = false;
|
|
9143
|
+
this.skinnedGroupsBeside[at] = base;
|
|
8854
9144
|
this.skinnedGroupsPalette[at] = palette;
|
|
8855
9145
|
this.skinnedGroupsDeltas[at] = deltas;
|
|
8856
9146
|
this.skinnedGroupsCloth[at] = cloth?.particles ?? null;
|
|
@@ -8917,6 +9207,7 @@ export class WebGPURenderer {
|
|
|
8917
9207
|
*/
|
|
8918
9208
|
rebuildFlatGroupsForNewRings() {
|
|
8919
9209
|
this.flatBindGroups.clear();
|
|
9210
|
+
this.overlayGroups.clear();
|
|
8920
9211
|
this.flatEnvironment = this.flatTextures('uEnvironment').view;
|
|
8921
9212
|
this.skinnedGroups.length = 0;
|
|
8922
9213
|
const albedo = this.albedo;
|
|
@@ -8969,6 +9260,7 @@ export class WebGPURenderer {
|
|
|
8969
9260
|
*/
|
|
8970
9261
|
rebuildFlatBindGroup() {
|
|
8971
9262
|
this.flatBindGroups.clear();
|
|
9263
|
+
this.overlayGroups.clear();
|
|
8972
9264
|
this.flatEnvironment = this.flatTextures('uEnvironment').view;
|
|
8973
9265
|
if (this.albedo === null &&
|
|
8974
9266
|
this.normalMap === null &&
|
|
@@ -9194,9 +9486,14 @@ export class WebGPURenderer {
|
|
|
9194
9486
|
? (OUTPUT_TRANSFORM_CODE[this.quality.outputTransform] ?? 0)
|
|
9195
9487
|
: 0;
|
|
9196
9488
|
f[at('uOutputExposure')] = graded ? this.exposure : 1;
|
|
9489
|
+
f.set(this.filmA, at('uFilmA'));
|
|
9490
|
+
f.set(this.filmB, at('uFilmB'));
|
|
9491
|
+
f[at('uDisplayHeadroom')] = this.headroom;
|
|
9197
9492
|
f.set(this.reprojection, at('uReprojection'));
|
|
9198
9493
|
f[at('uMotionStrength')] = motionStrength;
|
|
9199
|
-
f[at('uMotionMax')] =
|
|
9494
|
+
f[at('uMotionMax')] = this.motionBlurMax;
|
|
9495
|
+
/* Each mover's own motion, where the reconstruction drew its target this frame. */
|
|
9496
|
+
f[at('uObjectMotion')] = this.reconstructing && this.reconMotionView !== null ? 1 : 0;
|
|
9200
9497
|
f[at('uAoStrength')] = this.frameProjection === null ? 0 : aoStrength;
|
|
9201
9498
|
/*
|
|
9202
9499
|
* The occlusion read where this frame's jitter put it (`uAoOffset`): measured from the jittered
|
|
@@ -9240,6 +9537,7 @@ export class WebGPURenderer {
|
|
|
9240
9537
|
f[at('uGradeSize')] = this.gradeSize;
|
|
9241
9538
|
/* The lens and the print. See `setVignette` and `setFilmGrain`. */
|
|
9242
9539
|
f[at('uVignette')] = this.vignette;
|
|
9540
|
+
f.set(this.fringe, at('uFringe'));
|
|
9243
9541
|
f[at('uAutoExposure')] = adapting ? this.autoExposure : 0;
|
|
9244
9542
|
f[at('uLocalExposure')] = measuring && localising ? this.localExposure : 0;
|
|
9245
9543
|
f[at('uGrain')] = this.grain;
|
|
@@ -9277,7 +9575,7 @@ export class WebGPURenderer {
|
|
|
9277
9575
|
return;
|
|
9278
9576
|
if (this.aoTargetView === null)
|
|
9279
9577
|
return;
|
|
9280
|
-
this.rushBindGroup = this.buildRushBindGroup('post.rushBindGroup', this.sceneColorView);
|
|
9578
|
+
this.rushBindGroup = this.buildRushBindGroup('post.rushBindGroup', this.sceneColorView, this.sceneColorView);
|
|
9281
9579
|
/*
|
|
9282
9580
|
* **And the reconstruction's twin, from the same three call sites.** A colour grade replaces
|
|
9283
9581
|
* the lookup table and a resize replaces the depth and the occlusion target, and both groups
|
|
@@ -9285,11 +9583,13 @@ export class WebGPURenderer {
|
|
|
9285
9583
|
* come to read a stale view on exactly the frames a consumer changed something.
|
|
9286
9584
|
*/
|
|
9287
9585
|
if (this.reconShownView !== null) {
|
|
9288
|
-
this.reconRushGroup = this.buildRushBindGroup('recon.rushBindGroup', this.reconShownView);
|
|
9586
|
+
this.reconRushGroup = this.buildRushBindGroup('recon.rushBindGroup', this.reconShownView, this.reconMotionView ?? this.reconShownView);
|
|
9289
9587
|
}
|
|
9290
9588
|
}
|
|
9291
9589
|
/** The composite's group over one scene source. See `rebuildRushBindGroup`. */
|
|
9292
|
-
buildRushBindGroup(label, sceneView
|
|
9590
|
+
buildRushBindGroup(label, sceneView,
|
|
9591
|
+
/** Each mover's own motion, or a stand-in the shader is told not to read. */
|
|
9592
|
+
motionView) {
|
|
9293
9593
|
const { device } = this.surface;
|
|
9294
9594
|
if (this.resolvedDepthView === null || this.aoTargetView === null)
|
|
9295
9595
|
return null;
|
|
@@ -9318,6 +9618,7 @@ export class WebGPURenderer {
|
|
|
9318
9618
|
and a declared binding still needs a complete texture, as the bloom fallback says. */
|
|
9319
9619
|
...entry(RUSH_TEXTURES.uExposureHeld, this.exposurePass?.heldView ?? sceneView),
|
|
9320
9620
|
...entry(RUSH_TEXTURES.uExposureLocal, this.exposurePass?.localView ?? sceneView),
|
|
9621
|
+
...entry(RUSH_TEXTURES.uMotion, motionView),
|
|
9321
9622
|
],
|
|
9322
9623
|
layout: this.rushLayout,
|
|
9323
9624
|
});
|
|
@@ -9574,7 +9875,7 @@ export class WebGPURenderer {
|
|
|
9574
9875
|
this.reconSharpenGroups[write] = build(`recon.sharpen${String(write)}`, target, this.reconShownView, records, nextRecords, swings, nextSwings);
|
|
9575
9876
|
}
|
|
9576
9877
|
/* The composite's own group, over the resolved picture instead of the scene target. */
|
|
9577
|
-
this.reconRushGroup = this.buildRushBindGroup('recon.rushBindGroup', this.reconShownView);
|
|
9878
|
+
this.reconRushGroup = this.buildRushBindGroup('recon.rushBindGroup', this.reconShownView, this.reconMotionView ?? this.reconShownView);
|
|
9578
9879
|
/* And the scene's, because `ensureComposite` built it before this one existed. */
|
|
9579
9880
|
this.rebuildRushBindGroup();
|
|
9580
9881
|
/* And the bloom's, which blooms the resolved picture: see `rebuildBloomGroups`. */
|
|
@@ -9631,7 +9932,7 @@ export class WebGPURenderer {
|
|
|
9631
9932
|
drawsLate() {
|
|
9632
9933
|
return (this.latePass !== null &&
|
|
9633
9934
|
this.oitEncoder === null &&
|
|
9634
|
-
routesLate(this.reconstructing, this.reflectionPassActive, this.probePassActive, this.framePresented));
|
|
9935
|
+
routesLate(this.reconstructing, this.reflectionPassActive, this.probePassActive || this.capturePassActive, this.framePresented));
|
|
9635
9936
|
}
|
|
9636
9937
|
/**
|
|
9637
9938
|
* The command a blended draw records into: the late pool while reconstructing, the frame graph
|
|
@@ -9663,7 +9964,7 @@ export class WebGPURenderer {
|
|
|
9663
9964
|
if (!this.reconstructing || late === null || shown === null || depth === null)
|
|
9664
9965
|
return;
|
|
9665
9966
|
const oitSet = this.oitActive && this.translucentQueue.length > 0;
|
|
9666
|
-
if (late.pending === 0 && !oitSet)
|
|
9967
|
+
if (late.pending === 0 && !oitSet && this.latePasses.length === 0)
|
|
9667
9968
|
return;
|
|
9668
9969
|
const width = Math.max(1, this.surface.canvas.width);
|
|
9669
9970
|
const height = Math.max(1, this.surface.canvas.height);
|
|
@@ -9675,25 +9976,64 @@ export class WebGPURenderer {
|
|
|
9675
9976
|
this.refractSnapshotTaken = true;
|
|
9676
9977
|
}
|
|
9677
9978
|
this.lateRefractWanted = false;
|
|
9678
|
-
if (late.pending
|
|
9679
|
-
|
|
9680
|
-
|
|
9681
|
-
|
|
9682
|
-
|
|
9683
|
-
|
|
9684
|
-
|
|
9685
|
-
|
|
9686
|
-
|
|
9687
|
-
|
|
9688
|
-
|
|
9689
|
-
}
|
|
9690
|
-
|
|
9691
|
-
|
|
9692
|
-
|
|
9979
|
+
if (late.pending > 0) {
|
|
9980
|
+
const pass = encoder.beginRenderPass({
|
|
9981
|
+
label: 'recon.late',
|
|
9982
|
+
timestampWrites: this.gpuTimer.writesFor(),
|
|
9983
|
+
colorAttachments: [{ view: shown, loadOp: 'load', storeOp: 'store' }],
|
|
9984
|
+
depthStencilAttachment: {
|
|
9985
|
+
view: late.depthView(width, height),
|
|
9986
|
+
depthLoadOp: 'load',
|
|
9987
|
+
/* Kept: the order-independent set replays against it after this pass. */
|
|
9988
|
+
depthStoreOp: 'store',
|
|
9989
|
+
},
|
|
9990
|
+
});
|
|
9991
|
+
late.replay(pass, this.issueBound);
|
|
9992
|
+
pass.end();
|
|
9993
|
+
}
|
|
9994
|
+
this.runLatePasses(encoder, shown, late.depthView(width, height));
|
|
9995
|
+
}
|
|
9996
|
+
/**
|
|
9997
|
+
* The registered passes `drawPass` queued for after the upscale: each handed a fresh copy of the
|
|
9998
|
+
* reconstructed picture with every late draw in it, in the output-size texture the late
|
|
9999
|
+
* refraction keeps (its own reads are done by now: they were recorded before this copy), then
|
|
10000
|
+
* drawn over the picture in a pass of its own.
|
|
10001
|
+
*/
|
|
10002
|
+
runLatePasses(encoder, shown, depth) {
|
|
10003
|
+
const copy = this.refractSnapshot;
|
|
10004
|
+
const source = this.reconShown;
|
|
10005
|
+
for (const handle of this.latePasses) {
|
|
10006
|
+
const definition = passAt(this.passes, handle);
|
|
10007
|
+
if (definition === undefined)
|
|
10008
|
+
continue;
|
|
10009
|
+
if (copy !== null && source !== null) {
|
|
10010
|
+
encoder.copyTextureToTexture({ texture: source }, { texture: copy }, [
|
|
10011
|
+
copy.width,
|
|
10012
|
+
copy.height,
|
|
10013
|
+
]);
|
|
10014
|
+
}
|
|
10015
|
+
const pass = encoder.beginRenderPass({
|
|
10016
|
+
label: 'recon.latePass',
|
|
10017
|
+
timestampWrites: this.gpuTimer.writesFor(),
|
|
10018
|
+
colorAttachments: [{ view: shown, loadOp: 'load', storeOp: 'store' }],
|
|
10019
|
+
depthStencilAttachment: { view: depth, depthLoadOp: 'load', depthStoreOp: 'store' },
|
|
10020
|
+
});
|
|
10021
|
+
const context = this.passContext;
|
|
10022
|
+
context.pass = pass;
|
|
10023
|
+
context.outputTransform = this.gradeCode();
|
|
10024
|
+
context.outputExposure = this.gradeExposure();
|
|
10025
|
+
context.jitter = this.latePassJitter;
|
|
10026
|
+
context.format = RECON_HISTORY_FORMAT;
|
|
10027
|
+
context.depthFormat = DEPTH_FORMAT;
|
|
10028
|
+
context.samples = 1;
|
|
10029
|
+
definition.draw(context);
|
|
10030
|
+
context.jitter = this.passJitter;
|
|
10031
|
+
pass.end();
|
|
10032
|
+
}
|
|
9693
10033
|
}
|
|
9694
10034
|
/** Whether this draw lands in the reconstructed picture. See `lateRouting.ts`. */
|
|
9695
10035
|
tracksMotionNow() {
|
|
9696
|
-
return tracksMotion(this.reconstructing, this.reflectionPassActive, this.probePassActive, this.framePresented);
|
|
10036
|
+
return tracksMotion(this.reconstructing, this.reflectionPassActive, this.probePassActive || this.capturePassActive, this.framePresented);
|
|
9697
10037
|
}
|
|
9698
10038
|
/**
|
|
9699
10039
|
* What this draw's motion is, recorded for the motion pass.
|
|
@@ -10056,7 +10396,10 @@ export class WebGPURenderer {
|
|
|
10056
10396
|
* other is the disagreement the parity rule exists to prevent. Said once rather than dropped.
|
|
10057
10397
|
*/
|
|
10058
10398
|
runReflections(encoder) {
|
|
10059
|
-
|
|
10399
|
+
const frameReflections = this.frameReflections;
|
|
10400
|
+
const surfaceHalf = this.reflectionSurface;
|
|
10401
|
+
const materials = frameReflections !== null && surfaceHalf !== null;
|
|
10402
|
+
if (this.reflectionQueue.length === 0 && !materials)
|
|
10060
10403
|
return;
|
|
10061
10404
|
const scene = this.sceneColorView;
|
|
10062
10405
|
const reflection = this.ssrReflectionView;
|
|
@@ -10094,14 +10437,70 @@ export class WebGPURenderer {
|
|
|
10094
10437
|
f.set(surface.axis, base + at('uSsrAxis'));
|
|
10095
10438
|
f.set(surface.tint, base + at('uSsrTint'));
|
|
10096
10439
|
f[base + at('uSsrStrength')] = surface.strength;
|
|
10440
|
+
f[base + at('uSsrFresnel')] = surface.fresnel ? 1 : 0;
|
|
10441
|
+
f[base + at('uSsrFresnel') + 1] = surface.roughness;
|
|
10097
10442
|
f[base + at('uSsrFacingCos')] = surface.facingCos;
|
|
10098
10443
|
f[base + at('uSsrReach')] = surface.reachM;
|
|
10099
10444
|
f[base + at('uSsrThickness')] = surface.thicknessM;
|
|
10100
10445
|
f[base + at('uSsrSteps')] = surface.steps;
|
|
10101
10446
|
f[base + at('uSsrEdgeFade')] = REFLECTION_EDGE_FADE;
|
|
10447
|
+
f[base + at('uSsrMaterial')] = 0;
|
|
10102
10448
|
slot++;
|
|
10103
10449
|
});
|
|
10450
|
+
const regions = slot;
|
|
10451
|
+
/* The frame's materials' trace takes the slot after the surfaces': the box, its axis and its
|
|
10452
|
+
strength unread, the march and the ceiling its own. See `frameReflections.ts`. */
|
|
10453
|
+
if (materials) {
|
|
10454
|
+
const base = (slot * SSR_SLOT) / 4;
|
|
10455
|
+
f.set(this.ssrDepthToWorld, base + at('uSsrDepthToWorld'));
|
|
10456
|
+
f.set(this.ssrViewProj, base + at('uSsrViewProj'));
|
|
10457
|
+
f.set(this.frameEye, base + at('uSsrEye'));
|
|
10458
|
+
f.fill(0, base + at('uSsrAxis'), base + at('uSsrAxis') + 3);
|
|
10459
|
+
f.fill(1, base + at('uSsrTint'), base + at('uSsrTint') + 3);
|
|
10460
|
+
f[base + at('uSsrStrength')] = 1;
|
|
10461
|
+
f[base + at('uSsrFresnel')] = 0;
|
|
10462
|
+
f[base + at('uSsrFacingCos')] = -1;
|
|
10463
|
+
f[base + at('uSsrReach')] = frameReflections.reachM;
|
|
10464
|
+
f[base + at('uSsrThickness')] = frameReflections.thicknessM;
|
|
10465
|
+
f[base + at('uSsrSteps')] = frameReflections.steps;
|
|
10466
|
+
f[base + at('uSsrEdgeFade')] = REFLECTION_EDGE_FADE;
|
|
10467
|
+
f[base + at('uSsrMaterial')] = 1;
|
|
10468
|
+
f[base + at('uSsrMaxRoughness')] = frameReflections.maxRoughness;
|
|
10469
|
+
slot++;
|
|
10470
|
+
}
|
|
10104
10471
|
device.queue.writeBuffer(this.ssrUniforms, 0, this.ssrStaging, 0, slot * SSR_SLOT);
|
|
10472
|
+
const tracePipeline = postPipeline(this.pipelines, device, this.ssrLayout, 'post.ssrTrace', SSR_TRACE_FRAG_WGSL, this.pipelines.format, PREMULTIPLIED_OVER_BLEND);
|
|
10473
|
+
if (materials && this.depthView !== null && this.ssrMaterialGroup !== null) {
|
|
10474
|
+
/* The frame's opaque lit draws again, into what the trace and the resolve read. */
|
|
10475
|
+
surfaceHalf.draw(encoder, this.depthView, this.issueBound);
|
|
10476
|
+
const trace = encoder.beginRenderPass({
|
|
10477
|
+
label: 'post.ssr.materialTrace',
|
|
10478
|
+
colorAttachments: [
|
|
10479
|
+
{ view: reflection, loadOp: 'clear', storeOp: 'store', clearValue: [0, 0, 0, 0] },
|
|
10480
|
+
],
|
|
10481
|
+
});
|
|
10482
|
+
trace.setPipeline(tracePipeline);
|
|
10483
|
+
trace.setBindGroup(0, group, [regions * SSR_SLOT]);
|
|
10484
|
+
trace.draw(3);
|
|
10485
|
+
trace.end();
|
|
10486
|
+
/* The blur's texel and its radius at roughness 1, as a share of the frame's height. */
|
|
10487
|
+
const m = this.ssrMaterialFloats;
|
|
10488
|
+
const blurAt = this.postField(SSR_MATERIAL_FIELDS, 'uSsrBlur');
|
|
10489
|
+
m[blurAt] = 1 / Math.max(width, 1);
|
|
10490
|
+
m[blurAt + 1] = 1 / Math.max(height, 1);
|
|
10491
|
+
m[blurAt + 2] = frameReflections.blur * height;
|
|
10492
|
+
device.queue.writeBuffer(this.ssrMaterialUniforms, 0, m);
|
|
10493
|
+
const resolve = encoder.beginRenderPass({
|
|
10494
|
+
label: 'post.ssr.materialResolve',
|
|
10495
|
+
colorAttachments: [{ view: scene, loadOp: 'load', storeOp: 'store' }],
|
|
10496
|
+
});
|
|
10497
|
+
resolve.setPipeline(postPipeline(this.pipelines, device, this.ssrMaterialLayout, 'post.ssrMaterialResolve', SSR_MATERIAL_RESOLVE_FRAG_WGSL, this.pipelines.format, FRAME_REFLECTION_BLEND));
|
|
10498
|
+
resolve.setBindGroup(0, this.ssrMaterialGroup, [0]);
|
|
10499
|
+
resolve.draw(3);
|
|
10500
|
+
resolve.end();
|
|
10501
|
+
}
|
|
10502
|
+
if (regions === 0)
|
|
10503
|
+
return;
|
|
10105
10504
|
/* Cleared whole and then scissored per surface: the resolve reads every pixel, and one left
|
|
10106
10505
|
from the last frame would composite that reflection over this frame's scene. */
|
|
10107
10506
|
const trace = encoder.beginRenderPass({
|
|
@@ -10110,7 +10509,7 @@ export class WebGPURenderer {
|
|
|
10110
10509
|
{ view: reflection, loadOp: 'clear', storeOp: 'store', clearValue: [0, 0, 0, 0] },
|
|
10111
10510
|
],
|
|
10112
10511
|
});
|
|
10113
|
-
trace.setPipeline(
|
|
10512
|
+
trace.setPipeline(tracePipeline);
|
|
10114
10513
|
slot = 0;
|
|
10115
10514
|
this.reflectionQueue.replay((surface) => {
|
|
10116
10515
|
const index = slot++;
|
|
@@ -10654,7 +11053,7 @@ export class WebGPURenderer {
|
|
|
10654
11053
|
* glass, and a frame that refracts nothing breaks no pass at all.
|
|
10655
11054
|
*/
|
|
10656
11055
|
takeRefractSnapshot() {
|
|
10657
|
-
if (this.reflectionPassActive || this.probePassActive)
|
|
11056
|
+
if (this.reflectionPassActive || this.probePassActive || this.capturePassActive)
|
|
10658
11057
|
return false;
|
|
10659
11058
|
if (this.refractSnapshotTaken)
|
|
10660
11059
|
return true;
|
|
@@ -10737,10 +11136,55 @@ export class WebGPURenderer {
|
|
|
10737
11136
|
!this.skinScattered &&
|
|
10738
11137
|
!this.reflectionPassActive &&
|
|
10739
11138
|
!this.probePassActive &&
|
|
11139
|
+
!this.capturePassActive &&
|
|
11140
|
+
!this.framePresented &&
|
|
11141
|
+
!this.overlayActive &&
|
|
11142
|
+
this.oitMode === 'none');
|
|
11143
|
+
}
|
|
11144
|
+
/** Whether a draw now is the frame's own, and so has a surface half to keep. */
|
|
11145
|
+
reflectionSurfaceKeeps() {
|
|
11146
|
+
return (this.reflectionSurface !== null &&
|
|
11147
|
+
!this.reflectionPassActive &&
|
|
11148
|
+
!this.probePassActive &&
|
|
11149
|
+
!this.capturePassActive &&
|
|
10740
11150
|
!this.framePresented &&
|
|
10741
11151
|
!this.overlayActive &&
|
|
10742
11152
|
this.oitMode === 'none');
|
|
10743
11153
|
}
|
|
11154
|
+
/**
|
|
11155
|
+
* An opaque lit draw's surface half, kept for the frame's materials' reflections: the draw again,
|
|
11156
|
+
* with the same slots, buffers and bind group, through the pipeline with `REFLECTION_SURFACE` on —
|
|
11157
|
+
* which writes what the pixel reflects of its environment into the two targets
|
|
11158
|
+
* `reflectionSurfacePass.ts` holds and tests for the depth its frame draw wrote. Replayed when the
|
|
11159
|
+
* frame's pass has ended, before the trace reads them. See `shaders/flat/reflectionSurface.ts`.
|
|
11160
|
+
*/
|
|
11161
|
+
keepReflectionSurface(mesh, frameKey, base, skinned, morphed, deltas, layer, eight, clothed, modelKind, slot, material, instanceBuffer, instances, indirect) {
|
|
11162
|
+
const surface = this.reflectionSurface;
|
|
11163
|
+
const present = this.meshPresent.get(base);
|
|
11164
|
+
if (surface === null || present === undefined)
|
|
11165
|
+
return;
|
|
11166
|
+
const key = `${frameKey}|rs`;
|
|
11167
|
+
const pipeline = this.pipelines.peek(key) ??
|
|
11168
|
+
flatPipeline(this.pipelines, this.surface.device, this.flatLayoutFor(skinned, morphed), this.variant, key, present, false, skinned, morphed, true, layer, instanceBuffer !== null, 'none', this.materialDoubleSided, false, eight, clothed, modelKind, 'whole', true);
|
|
11169
|
+
const command = surface.take();
|
|
11170
|
+
command.pipeline = pipeline;
|
|
11171
|
+
command.bindGroup = this.flatBindGroupFor(skinned, morphed, deltas);
|
|
11172
|
+
command.offsetA = slot;
|
|
11173
|
+
command.offsetB = material;
|
|
11174
|
+
command.offsetCount = 2;
|
|
11175
|
+
let index = 0;
|
|
11176
|
+
for (; index < mesh.vertexBuffers.length; index++) {
|
|
11177
|
+
command.vertexBuffers[index] = mesh.vertexBuffers[index];
|
|
11178
|
+
}
|
|
11179
|
+
if (instanceBuffer !== null)
|
|
11180
|
+
command.vertexBuffers[index++] = instanceBuffer;
|
|
11181
|
+
command.vertexCount = index;
|
|
11182
|
+
command.indexBuffer = mesh.indexBuffer;
|
|
11183
|
+
command.indexed = true;
|
|
11184
|
+
command.count = mesh.indexCount;
|
|
11185
|
+
command.instances = instances;
|
|
11186
|
+
command.indirect = indirect;
|
|
11187
|
+
}
|
|
10744
11188
|
/**
|
|
10745
11189
|
* A skin draw's diffuse and colour halves, kept for the spread: the frame's draw again, twice,
|
|
10746
11190
|
* with the same slots, buffers and bind group, through the pipelines that write the diffuse's
|
|
@@ -10788,11 +11232,19 @@ export class WebGPURenderer {
|
|
|
10788
11232
|
const skin = this.skinScatter;
|
|
10789
11233
|
if (skin === null || this.skinScattered)
|
|
10790
11234
|
return;
|
|
11235
|
+
/*
|
|
11236
|
+
* **Not the frame's picture, so not the frame's moment**: a blended draw in a mirror, a probe
|
|
11237
|
+
* bake or a scene capture lands over none of the frame's skin. Latching there switched the
|
|
11238
|
+
* spread off for the frame, so a capture with a glowing sign in it left every face drawn after
|
|
11239
|
+
* it flat. `scripts/capture-check.mjs`.
|
|
11240
|
+
*/
|
|
11241
|
+
if (this.reflectionPassActive || this.probePassActive || this.capturePassActive)
|
|
11242
|
+
return;
|
|
10791
11243
|
/* Latched whether or not skin was kept: a skin drawn after this point is the whole surface. */
|
|
10792
11244
|
this.skinScattered = true;
|
|
10793
11245
|
if (!skin.pending)
|
|
10794
11246
|
return;
|
|
10795
|
-
if (this.
|
|
11247
|
+
if (this.oitReplaying)
|
|
10796
11248
|
return;
|
|
10797
11249
|
const encoder = this.encoder;
|
|
10798
11250
|
const depth = this.depthView;
|
|
@@ -10851,7 +11303,7 @@ export class WebGPURenderer {
|
|
|
10851
11303
|
* own terms: `resolvedDepthView` holds the *frame's* depth, and clamping a mirrored beam
|
|
10852
11304
|
* against it would stop it at geometry that is not in front of it.
|
|
10853
11305
|
*/
|
|
10854
|
-
if (this.reflectionPassActive)
|
|
11306
|
+
if (this.reflectionPassActive || this.capturePassActive)
|
|
10855
11307
|
return false;
|
|
10856
11308
|
if (this.resolvedDepthView === null)
|
|
10857
11309
|
return false;
|
|
@@ -10907,6 +11359,66 @@ export class WebGPURenderer {
|
|
|
10907
11359
|
this.volumeDepthTaken = true;
|
|
10908
11360
|
return true;
|
|
10909
11361
|
}
|
|
11362
|
+
/** The colour copy a registered pass reads, at the scene's size and format. See `passColour`. */
|
|
11363
|
+
allocatePassColour() {
|
|
11364
|
+
const source = this.sceneColor;
|
|
11365
|
+
if (source === null || this.passColour !== null)
|
|
11366
|
+
return;
|
|
11367
|
+
this.passColour = this.surface.device.createTexture({
|
|
11368
|
+
label: 'pass.sceneColor',
|
|
11369
|
+
size: [source.width, source.height],
|
|
11370
|
+
format: this.pipelines.format,
|
|
11371
|
+
usage: 0x4 | 0x2, // TEXTURE_BINDING | COPY_DST
|
|
11372
|
+
});
|
|
11373
|
+
this.passColourView = this.passColour.createView();
|
|
11374
|
+
}
|
|
11375
|
+
/**
|
|
11376
|
+
* The frame's colour so far, into `passColour`, for a registered pass that reads it: the
|
|
11377
|
+
* boundary `takeRefractSnapshot` describes — flush, end the pass, copy what its end resolved,
|
|
11378
|
+
* reopen with `load` — and refused where that one is, in a mirror, a probe bake or a capture,
|
|
11379
|
+
* whose pictures are not the frame's. **Not latched**, unlike refraction's: each reading pass
|
|
11380
|
+
* sees the passes drawn before it, at a full-frame copy each.
|
|
11381
|
+
*/
|
|
11382
|
+
takePassColour() {
|
|
11383
|
+
if (this.reflectionPassActive || this.probePassActive || this.capturePassActive)
|
|
11384
|
+
return;
|
|
11385
|
+
const copy = this.passColour;
|
|
11386
|
+
const source = this.sceneColor;
|
|
11387
|
+
const encoder = this.encoder;
|
|
11388
|
+
if (copy === null || source === null || encoder === null || this.pass === null)
|
|
11389
|
+
return;
|
|
11390
|
+
if (this.quality.frameGraph)
|
|
11391
|
+
this.flushGraph();
|
|
11392
|
+
this.pass.end();
|
|
11393
|
+
encoder.copyTextureToTexture({ texture: source }, { texture: copy }, [copy.width, copy.height]);
|
|
11394
|
+
this.pass = this.reopenFramePass('pass.colourTaken');
|
|
11395
|
+
}
|
|
11396
|
+
/**
|
|
11397
|
+
* The frame's pass again after a copy between passes, loading everything already drawn: the same
|
|
11398
|
+
* attachments the copy's caller ended, with nothing cleared.
|
|
11399
|
+
*/
|
|
11400
|
+
reopenFramePass(label) {
|
|
11401
|
+
const encoder = this.encoder;
|
|
11402
|
+
const target = this.compositeTarget();
|
|
11403
|
+
if (encoder === null || target === null)
|
|
11404
|
+
return null;
|
|
11405
|
+
const multisampled = this.compositeMsaa() !== null;
|
|
11406
|
+
return encoder.beginRenderPass({
|
|
11407
|
+
label,
|
|
11408
|
+
timestampWrites: this.gpuTimer.writesFor(),
|
|
11409
|
+
colorAttachments: [
|
|
11410
|
+
{
|
|
11411
|
+
view: this.compositeMsaa() ?? target,
|
|
11412
|
+
resolveTarget: multisampled ? target : undefined,
|
|
11413
|
+
loadOp: 'load',
|
|
11414
|
+
storeOp: resolvedStoreOp(multisampled, false, this.quality.discardResolvedAttachments),
|
|
11415
|
+
},
|
|
11416
|
+
],
|
|
11417
|
+
depthStencilAttachment: this.depthView === null
|
|
11418
|
+
? undefined
|
|
11419
|
+
: { view: this.depthView, depthLoadOp: 'load', depthStoreOp: 'store' },
|
|
11420
|
+
});
|
|
11421
|
+
}
|
|
10910
11422
|
/**
|
|
10911
11423
|
* The pass to record into, reopening one on the canvas if the frame has already gone.
|
|
10912
11424
|
*
|
|
@@ -11088,6 +11600,8 @@ export class WebGPURenderer {
|
|
|
11088
11600
|
/* And a march reads the same copy, for the same reason and with the same failure if it is
|
|
11089
11601
|
discarded: a depth of zero is the far plane reversed, so every ray finds nothing. */
|
|
11090
11602
|
this.reflectionQueue.length > 0 ||
|
|
11603
|
+
/* And the frame's materials' reflections, whose surface half tests against the depth too. */
|
|
11604
|
+
this.reflectionSurface !== null ||
|
|
11091
11605
|
/* And the medium's own march, which stops where the frame's depth says a surface is. Left
|
|
11092
11606
|
out of this, every ray runs to `maxDistance` through the walls and the fog is drawn on
|
|
11093
11607
|
top of the room rather than in it. */
|
|
@@ -11408,8 +11922,9 @@ export class WebGPURenderer {
|
|
|
11408
11922
|
* any future branch added to one belongs in the other.
|
|
11409
11923
|
*/
|
|
11410
11924
|
targetPipelines() {
|
|
11411
|
-
if (this.probePassActive || this.reflectionPassActive)
|
|
11925
|
+
if (this.probePassActive || this.reflectionPassActive || this.capturePassActive) {
|
|
11412
11926
|
return this.pipelines;
|
|
11927
|
+
}
|
|
11413
11928
|
return this.overlayActive || this.framePresented ? this.overlayPipelines : this.pipelines;
|
|
11414
11929
|
}
|
|
11415
11930
|
/** End and submit a reopened pass. Safe to call when there is none. */
|
|
@@ -11903,6 +12418,9 @@ export class WebGPURenderer {
|
|
|
11903
12418
|
const reads = maskOf(...(definition.reads ?? []));
|
|
11904
12419
|
this.passReads.set(handle, reads);
|
|
11905
12420
|
this.passReadsUnion |= reads;
|
|
12421
|
+
/* The colour copy a reader needs, now rather than at its first draw: see `passColour`. */
|
|
12422
|
+
if ((reads & COLOR_SNAPSHOT) !== 0 && this.passColour === null)
|
|
12423
|
+
this.allocatePassColour();
|
|
11906
12424
|
if (definition.prepare !== undefined)
|
|
11907
12425
|
this.preparingPasses++;
|
|
11908
12426
|
definition.init?.(this.passDevice());
|
|
@@ -11922,6 +12440,8 @@ export class WebGPURenderer {
|
|
|
11922
12440
|
environment: null,
|
|
11923
12441
|
distanceField: null,
|
|
11924
12442
|
jitter: this.passJitter,
|
|
12443
|
+
sceneDepth: null,
|
|
12444
|
+
sceneColor: null,
|
|
11925
12445
|
};
|
|
11926
12446
|
/**
|
|
11927
12447
|
* The object handed to a pass as `PrepareContext.environment`, rebuilt only when it changes.
|
|
@@ -12007,6 +12527,15 @@ export class WebGPURenderer {
|
|
|
12007
12527
|
* than merely what happens.
|
|
12008
12528
|
*/
|
|
12009
12529
|
this.prepareContext.distanceField = this.fieldComposer?.field ?? null;
|
|
12530
|
+
/* Settled for the frame: `ensureComposite` has run, so the view stays until the pass draws. */
|
|
12531
|
+
this.prepareContext.sceneDepth = this.resolvedDepthView;
|
|
12532
|
+
/*
|
|
12533
|
+
* Under a reconstruction a colour reader draws after the upscale (see `drawPass`), so it reads
|
|
12534
|
+
* the output-size copy the late refraction keeps rather than the render's.
|
|
12535
|
+
*/
|
|
12536
|
+
this.prepareContext.sceneColor = this.reconstructionWanted
|
|
12537
|
+
? this.refractSnapshotView
|
|
12538
|
+
: this.passColourView;
|
|
12010
12539
|
for (const definition of this.passes.definitions) {
|
|
12011
12540
|
definition?.prepare?.(this.prepareContext);
|
|
12012
12541
|
}
|
|
@@ -12024,6 +12553,35 @@ export class WebGPURenderer {
|
|
|
12024
12553
|
const definition = passAt(this.passes, handle);
|
|
12025
12554
|
if (definition === undefined)
|
|
12026
12555
|
return;
|
|
12556
|
+
const reads = this.passReads.get(handle) ?? 0;
|
|
12557
|
+
/*
|
|
12558
|
+
* **Under a reconstruction, a pass that reads the frame's colour draws after the blended draws
|
|
12559
|
+
* do.** Those land after the upscale, at the output's size, so a pass drawn among them at the
|
|
12560
|
+
* render's would read a frame without them and then be covered by them — a full-screen effect
|
|
12561
|
+
* with the hit sparks drawn sharp over it. Queued instead, and drawn by `runLate` over the
|
|
12562
|
+
* reconstructed picture, after every late draw, whatever order it was asked in.
|
|
12563
|
+
*/
|
|
12564
|
+
if ((reads & COLOR_SNAPSHOT) !== 0 && this.drawsLate()) {
|
|
12565
|
+
this.latePasses.push(handle);
|
|
12566
|
+
return;
|
|
12567
|
+
}
|
|
12568
|
+
/*
|
|
12569
|
+
* A pass that reads the depth copy gets it with everything drawn so far: the copy is taken
|
|
12570
|
+
* here, once a frame, as a soft-edged sprite takes it. Before `openPass` below, because taking
|
|
12571
|
+
* it ends the frame's pass and opens another.
|
|
12572
|
+
*/
|
|
12573
|
+
if ((reads & DEPTH_SNAPSHOT) !== 0 && !this.framePresented && this.openPass() !== null) {
|
|
12574
|
+
this.takeVolumeDepth();
|
|
12575
|
+
}
|
|
12576
|
+
/*
|
|
12577
|
+
* And one that reads the colour gets it afresh, with everything drawn before it — a full-screen
|
|
12578
|
+
* pass drawn last reads the whole frame and an effect drawn mid-frame reads the frame so far.
|
|
12579
|
+
* A copy a reading pass, where the depth above is one a frame: what a colour reader draws is
|
|
12580
|
+
* itself part of what the next one should see.
|
|
12581
|
+
*/
|
|
12582
|
+
if ((reads & COLOR_SNAPSHOT) !== 0 && !this.framePresented && this.openPass() !== null) {
|
|
12583
|
+
this.takePassColour();
|
|
12584
|
+
}
|
|
12027
12585
|
if (!this.quality.frameGraph) {
|
|
12028
12586
|
const pass = this.openPass();
|
|
12029
12587
|
if (pass === null)
|
|
@@ -12031,7 +12589,7 @@ export class WebGPURenderer {
|
|
|
12031
12589
|
definition.draw(this.fillPassContext(pass));
|
|
12032
12590
|
return;
|
|
12033
12591
|
}
|
|
12034
|
-
recordNode(this.arena, VERB_PASS,
|
|
12592
|
+
recordNode(this.arena, VERB_PASS, reads, this.currentTarget(), handle, 0);
|
|
12035
12593
|
if (this.framePresented)
|
|
12036
12594
|
this.queueOverlayFlush();
|
|
12037
12595
|
}
|
|
@@ -12140,8 +12698,8 @@ export class WebGPURenderer {
|
|
|
12140
12698
|
bindClusters(camera, env, f, i, at) {
|
|
12141
12699
|
const binner = this.clusterBinner;
|
|
12142
12700
|
i[at('uClustered')] = binner === null ? 0 : 1;
|
|
12143
|
-
|
|
12144
|
-
|
|
12701
|
+
/* Written whether or not the lights are clustered: a surface overlay's rim finds its place on
|
|
12702
|
+
the screen through the same two (\`overlayScreen\`). */
|
|
12145
12703
|
const p = camera.projection;
|
|
12146
12704
|
const tanHalfFovY = 1 / (p[5] ?? 1);
|
|
12147
12705
|
const aspect = (p[5] ?? 1) / (p[0] ?? 1);
|
|
@@ -12157,6 +12715,8 @@ export class WebGPURenderer {
|
|
|
12157
12715
|
f[frustumAt + 2] = tanHalfFovY;
|
|
12158
12716
|
f[frustumAt + 3] = aspect;
|
|
12159
12717
|
f.set(camera.view, at('uView'));
|
|
12718
|
+
if (binner === null)
|
|
12719
|
+
return;
|
|
12160
12720
|
/* The rectangles past the fixed arm's go into the table. See `ClusterLightSet.areas`. */
|
|
12161
12721
|
const set = fillClusterLightSet(env, this.clusterLights, this.shadedAreaLights);
|
|
12162
12722
|
/* A slot is read only under `POINT_SHADOWS`; see `lightHasFixture`. */
|
|
@@ -12457,6 +13017,43 @@ export class WebGPURenderer {
|
|
|
12457
13017
|
}
|
|
12458
13018
|
/** This frame's exposure into the tone curve. Starts at the profile's own. */
|
|
12459
13019
|
exposure;
|
|
13020
|
+
/**
|
|
13021
|
+
* The `filmic` curve every later frame is graded through, until it is set again. See
|
|
13022
|
+
* `RendererApi.setFilmicCurve`; the constants are solved here once, not per frame or per pixel.
|
|
13023
|
+
*/
|
|
13024
|
+
setFilmicCurve(curve) {
|
|
13025
|
+
this.filmCurve = curve;
|
|
13026
|
+
if (resolveFilmicCurve(curve, this.filmConstants, this.headroom) && !this.warnedFilmClamped) {
|
|
13027
|
+
this.warnedFilmClamped = true;
|
|
13028
|
+
console.warn(FILMIC_CLAMPED);
|
|
13029
|
+
}
|
|
13030
|
+
}
|
|
13031
|
+
warnedFilmClamped = false;
|
|
13032
|
+
/** The curve last set, so a new headroom can solve it again. */
|
|
13033
|
+
filmCurve = DEFAULT_FILMIC_CURVE;
|
|
13034
|
+
/** The display's peak over paper white; 1 on a standard range. See `setDisplayLuminance`. */
|
|
13035
|
+
headroom = 1;
|
|
13036
|
+
/** The range the frame goes out in. See `RendererApi.displayRange`. */
|
|
13037
|
+
get displayRange() {
|
|
13038
|
+
return this.surface.displayRange;
|
|
13039
|
+
}
|
|
13040
|
+
/** Why it is that range, in words. */
|
|
13041
|
+
get displayRangeReason() {
|
|
13042
|
+
return this.surface.displayRangeReason;
|
|
13043
|
+
}
|
|
13044
|
+
/**
|
|
13045
|
+
* How bright paper white and the display's peak are, in any one unit (nits, usually), held until
|
|
13046
|
+
* changed: their ratio is how far above white the highlights may run on a high range display,
|
|
13047
|
+
* and nothing changes on a standard one. See `RendererApi.setDisplayLuminance`.
|
|
13048
|
+
*/
|
|
13049
|
+
setDisplayLuminance(paperWhite, peak) {
|
|
13050
|
+
this.headroom = displayHeadroom(this.surface.displayRange, paperWhite, peak);
|
|
13051
|
+
resolveFilmicCurve(this.filmCurve, this.filmConstants, this.headroom);
|
|
13052
|
+
}
|
|
13053
|
+
/** The curve's eight constants, and the two views the composite uploads them through. */
|
|
13054
|
+
filmConstants = new Float32Array(FILMIC_CONSTANTS);
|
|
13055
|
+
filmA = this.filmConstants.subarray(0, 4);
|
|
13056
|
+
filmB = this.filmConstants.subarray(4, 8);
|
|
12460
13057
|
/*
|
|
12461
13058
|
* -----------------------------------------------------------------------------------------
|
|
12462
13059
|
* The three screen effects, which this backend cannot yet honour — and says so.
|
|
@@ -12500,6 +13097,8 @@ export class WebGPURenderer {
|
|
|
12500
13097
|
veilColor = [0, 0, 0];
|
|
12501
13098
|
veilAlpha = 0;
|
|
12502
13099
|
vignette = 0;
|
|
13100
|
+
/** The lens's colour fringe: red's and green's pull and where it begins. See `fringe.ts`. */
|
|
13101
|
+
fringe = new Float32Array(FRINGE_FLOATS);
|
|
12503
13102
|
grain = 0;
|
|
12504
13103
|
grainSeed = 0;
|
|
12505
13104
|
warnedLookWithoutComposite = false;
|
|
@@ -12521,6 +13120,20 @@ export class WebGPURenderer {
|
|
|
12521
13120
|
}
|
|
12522
13121
|
this.vignette = clampVignette(strength);
|
|
12523
13122
|
}
|
|
13123
|
+
/**
|
|
13124
|
+
* The lens's colour fringe, held until changed: `intensity` is a percentage, 0 is none, and red
|
|
13125
|
+
* and green are pulled toward the centre by their wavelength's distance from blue, growing from
|
|
13126
|
+
* `start` — a share of the half-frame, 0 the centre — to the whole of it at the edge. Applied to
|
|
13127
|
+
* scene light at the composite's first read. Needs `screenEffects`, and says so once rather than
|
|
13128
|
+
* doing nothing. See `shaders/fringe.ts`.
|
|
13129
|
+
*/
|
|
13130
|
+
setChromaticAberration(intensity, start = 0) {
|
|
13131
|
+
if (!this.hasComposite) {
|
|
13132
|
+
this.warnLookWithoutComposite(intensity);
|
|
13133
|
+
return;
|
|
13134
|
+
}
|
|
13135
|
+
resolveFringe(intensity, start, this.fringe);
|
|
13136
|
+
}
|
|
12524
13137
|
/**
|
|
12525
13138
|
* Film grain, held until changed: an amplitude in display values (0 is none, 0.03 is a fine
|
|
12526
13139
|
* grain) and this frame's seed. **The seed is the caller's**, because the engine takes time from
|
|
@@ -12654,9 +13267,26 @@ export class WebGPURenderer {
|
|
|
12654
13267
|
setSpeedRush(strength) {
|
|
12655
13268
|
this.rushStrength = Math.min(Math.max(strength, 0), 1);
|
|
12656
13269
|
}
|
|
12657
|
-
/**
|
|
12658
|
-
|
|
13270
|
+
/**
|
|
13271
|
+
* How much of the frame's motion blur to apply, 0 to 1, and the longest smear as a share of the
|
|
13272
|
+
* frame, both held until changed. See `RendererApi.setCameraMotionBlur`.
|
|
13273
|
+
*/
|
|
13274
|
+
setCameraMotionBlur(scale, maxShare = MOTION_BLUR_MAX_UV) {
|
|
12659
13275
|
this.motionBlurScale = Math.min(Math.max(scale, 0), 1);
|
|
13276
|
+
this.motionBlurMax = clampMotionShare(maxShare);
|
|
13277
|
+
}
|
|
13278
|
+
/** The longest smear, as a share of the frame. See `setCameraMotionBlur`. */
|
|
13279
|
+
motionBlurMax = MOTION_BLUR_MAX_UV;
|
|
13280
|
+
warnedObjectBlur = false;
|
|
13281
|
+
/**
|
|
13282
|
+
* Said once: a draw named where it was last frame while the frame blurs, and this frame keeps no
|
|
13283
|
+
* motion target to blur it by — the camera's motion alone smears it. See `OBJECT_BLUR_UNKEPT`.
|
|
13284
|
+
*/
|
|
13285
|
+
warnObjectBlur() {
|
|
13286
|
+
if (this.warnedObjectBlur)
|
|
13287
|
+
return;
|
|
13288
|
+
this.warnedObjectBlur = true;
|
|
13289
|
+
console.warn(OBJECT_BLUR_UNKEPT);
|
|
12660
13290
|
}
|
|
12661
13291
|
/**
|
|
12662
13292
|
* The camera has cut: this frame is a new shot, not the next moment of the last one.
|
|
@@ -13203,18 +13833,19 @@ export class WebGPURenderer {
|
|
|
13203
13833
|
* `bindMeshPass` and `drawMesh`, which record into whatever `this.pass` is. That is the same
|
|
13204
13834
|
* arrangement `bakeFace` uses for a point light's cube, and it is why a bake must not be
|
|
13205
13835
|
* opened inside a frame's pass by a caller who then expects the frame to continue.
|
|
13206
|
-
|
|
13207
|
-
const savedPass = this.pass;
|
|
13208
|
-
/*
|
|
13836
|
+
*
|
|
13209
13837
|
* **A bake is a boundary, and boundaries flush.** The mirror says why in its own words: what
|
|
13210
13838
|
* has been recorded and not yet replayed belongs to the pass that is about to end, and leaving
|
|
13211
13839
|
* it replays it into whichever pass opens next — a draw in the wrong target rather than a
|
|
13212
13840
|
* missing one. This is the fourth such boundary in the backend and the only one that was never
|
|
13213
13841
|
* given the treatment, which is what let a bake carry the frame's pending draws into a cube
|
|
13214
|
-
* face.
|
|
13842
|
+
* face. **The pass is saved after the flush**, which opens it where none was: saved before,
|
|
13843
|
+
* the null given back left the flushed pass open and the frame's encoder locked. See
|
|
13844
|
+
* `captureScene`.
|
|
13215
13845
|
*/
|
|
13216
13846
|
if (this.quality.frameGraph)
|
|
13217
13847
|
this.flushGraph();
|
|
13848
|
+
const savedPass = this.pass;
|
|
13218
13849
|
const savedEncoder = this.encoder;
|
|
13219
13850
|
/*
|
|
13220
13851
|
* **The rings are marked here and given back after the submit below**, because this bake is its
|
|
@@ -13799,6 +14430,7 @@ export class WebGPURenderer {
|
|
|
13799
14430
|
this.sceneColor,
|
|
13800
14431
|
this.sceneColorMsaa,
|
|
13801
14432
|
this.refractSnapshot,
|
|
14433
|
+
this.passColour,
|
|
13802
14434
|
this.resolvedDepth,
|
|
13803
14435
|
this.aoTarget,
|
|
13804
14436
|
this.aoScratch,
|
|
@@ -13864,6 +14496,10 @@ export class WebGPURenderer {
|
|
|
13864
14496
|
usage: 0x4 | 0x2 | 0x10, // TEXTURE_BINDING | COPY_DST | RENDER_ATTACHMENT
|
|
13865
14497
|
});
|
|
13866
14498
|
this.refractSnapshotView = this.refractSnapshot.createView();
|
|
14499
|
+
this.passColour = null;
|
|
14500
|
+
this.passColourView = null;
|
|
14501
|
+
if ((this.passReadsUnion & COLOR_SNAPSHOT) !== 0)
|
|
14502
|
+
this.allocatePassColour();
|
|
13867
14503
|
this.refractMips = new SnapshotMips(device, this.pipelines, this.refractSnapshot, reconstructed ? RECON_HISTORY_FORMAT : format);
|
|
13868
14504
|
/*
|
|
13869
14505
|
* **Every cached flat bind group is stale the moment this view changes**, and the failure is
|
|
@@ -14079,6 +14715,10 @@ export class WebGPURenderer {
|
|
|
14079
14715
|
usage: USAGE,
|
|
14080
14716
|
});
|
|
14081
14717
|
this.ssrReflectionView = this.ssrReflection.createView();
|
|
14718
|
+
/* The frame's materials, at the scene's size; a stand-in where nothing traces them. */
|
|
14719
|
+
this.reflectionSurface?.size(width, height);
|
|
14720
|
+
const probeMap = this.reflectionSurface?.probeView ?? this.blankView;
|
|
14721
|
+
const tintMap = this.reflectionSurface?.tintView ?? this.blankView;
|
|
14082
14722
|
this.ssrBindGroup =
|
|
14083
14723
|
this.resolvedDepthView === null || this.sceneColorView === null
|
|
14084
14724
|
? null
|
|
@@ -14092,8 +14732,23 @@ export class WebGPURenderer {
|
|
|
14092
14732
|
},
|
|
14093
14733
|
...entry(SSR_TEXTURES.uSsrDepth, this.resolvedDepthView, true),
|
|
14094
14734
|
...entry(SSR_TEXTURES.uSsrScene, this.sceneColorView),
|
|
14735
|
+
...entry(SSR_TEXTURES.uSsrProbeMap, probeMap),
|
|
14736
|
+
...entry(SSR_TEXTURES.uSsrTintMap, tintMap),
|
|
14095
14737
|
],
|
|
14096
14738
|
});
|
|
14739
|
+
this.ssrMaterialGroup = device.createBindGroup({
|
|
14740
|
+
label: 'post.ssrMaterialGroup',
|
|
14741
|
+
layout: this.ssrMaterialLayout,
|
|
14742
|
+
entries: [
|
|
14743
|
+
{
|
|
14744
|
+
binding: SSR_MATERIAL_UNIFORMS,
|
|
14745
|
+
resource: { buffer: this.ssrMaterialUniforms, size: SSR_MATERIAL_SIZE },
|
|
14746
|
+
},
|
|
14747
|
+
...entry(SSR_MATERIAL_TEXTURES.uSsrReflection, this.ssrReflectionView),
|
|
14748
|
+
...entry(SSR_MATERIAL_TEXTURES.uSsrProbeMap, probeMap),
|
|
14749
|
+
...entry(SSR_MATERIAL_TEXTURES.uSsrTintMap, tintMap),
|
|
14750
|
+
],
|
|
14751
|
+
});
|
|
14097
14752
|
this.ssrResolveGroup = device.createBindGroup({
|
|
14098
14753
|
label: 'post.ssrResolveGroup',
|
|
14099
14754
|
layout: this.ssrResolveLayout,
|