@driftengine/core 4.4.1 → 4.5.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 +72 -0
- package/README.md +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +7 -0
- package/dist/render/backend/api.d.ts +17 -11
- package/dist/render/backend/webgl2/glassTintPrograms.d.ts +13 -0
- package/dist/render/backend/webgl2/glassTintPrograms.js +38 -0
- package/dist/render/backend/webgl2/renderer.d.ts +82 -8
- package/dist/render/backend/webgl2/renderer.js +373 -105
- package/dist/render/backend/webgpu/buffers.d.ts +19 -1
- package/dist/render/backend/webgpu/buffers.js +27 -1
- package/dist/render/backend/webgpu/depthPass.d.ts +1 -1
- package/dist/render/backend/webgpu/depthPass.js +2 -1
- package/dist/render/backend/webgpu/flatPass.d.ts +2 -0
- package/dist/render/backend/webgpu/flatPass.js +20 -1
- package/dist/render/backend/webgpu/glassTintPass.d.ts +28 -0
- package/dist/render/backend/webgpu/glassTintPass.js +205 -0
- package/dist/render/backend/webgpu/gpuDrivenPass.d.ts +60 -1
- package/dist/render/backend/webgpu/gpuDrivenPass.js +305 -5
- package/dist/render/backend/webgpu/instanced.d.ts +12 -1
- package/dist/render/backend/webgpu/instanced.js +20 -1
- package/dist/render/backend/webgpu/latePass.d.ts +26 -0
- package/dist/render/backend/webgpu/latePass.js +133 -0
- package/dist/render/backend/webgpu/lightVolumePass.js +5 -1
- package/dist/render/backend/webgpu/motionPass.d.ts +95 -0
- package/dist/render/backend/webgpu/motionPass.js +491 -0
- package/dist/render/backend/webgpu/pipelineCache.d.ts +10 -1
- package/dist/render/backend/webgpu/pipelineCache.js +11 -1
- package/dist/render/backend/webgpu/pointGlassTintPass.d.ts +32 -0
- package/dist/render/backend/webgpu/pointGlassTintPass.js +193 -0
- package/dist/render/backend/webgpu/pointShadowPass.d.ts +35 -6
- package/dist/render/backend/webgpu/pointShadowPass.js +105 -20
- package/dist/render/backend/webgpu/postPass.d.ts +21 -15
- package/dist/render/backend/webgpu/postPass.js +17 -4
- package/dist/render/backend/webgpu/renderer.d.ts +181 -54
- package/dist/render/backend/webgpu/renderer.js +1011 -394
- package/dist/render/backend/webgpu/scatterPass.js +2 -1
- package/dist/render/backend/webgpu/skinPaletteRing.d.ts +4 -0
- package/dist/render/backend/webgpu/skinPaletteRing.js +4 -0
- package/dist/render/backend/webgpu/snapshotMips.d.ts +29 -0
- package/dist/render/backend/webgpu/snapshotMips.js +52 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +5 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.js +1 -1
- package/dist/render/flameLight.js +15 -2
- package/dist/render/glass.d.ts +45 -0
- package/dist/render/glass.js +39 -0
- package/dist/render/glassCasters.d.ts +40 -0
- package/dist/render/glassCasters.js +84 -0
- package/dist/render/glassShadow.d.ts +78 -0
- package/dist/render/glassShadow.js +125 -0
- package/dist/render/globalMediumPass.d.ts +2 -3
- package/dist/render/globalMediumPass.js +2 -8
- package/dist/render/gpudriven/lit.d.ts +12 -0
- package/dist/render/gpudriven/lit.js +23 -0
- package/dist/render/gpudriven/materialTable.d.ts +15 -5
- package/dist/render/gpudriven/materialTable.js +26 -6
- package/dist/render/gpudriven/pipeline.d.ts +1 -1
- package/dist/render/gpudriven/pipeline.js +4 -3
- package/dist/render/gpudriven/sunGlass.d.ts +35 -0
- package/dist/render/gpudriven/sunGlass.js +89 -0
- package/dist/render/lightBudget.d.ts +17 -5
- package/dist/render/lightBudget.js +17 -5
- package/dist/render/pointGlassTint.d.ts +59 -0
- package/dist/render/pointGlassTint.js +198 -0
- package/dist/render/pointShadowArray.d.ts +56 -3
- package/dist/render/pointShadowArray.js +127 -16
- package/dist/render/pointShadowBudget.js +51 -41
- package/dist/render/pointShadowMap.d.ts +23 -1
- package/dist/render/pointShadowMap.js +27 -1
- package/dist/render/recon/changeFrames.d.ts +15 -0
- package/dist/render/recon/changeFrames.js +21 -0
- package/dist/render/recon/disocclusion.d.ts +36 -3
- package/dist/render/recon/disocclusion.js +56 -4
- package/dist/render/recon/lateRouting.d.ts +14 -0
- package/dist/render/recon/lateRouting.js +18 -0
- package/dist/render/recon/mover.d.ts +57 -0
- package/dist/render/recon/mover.js +86 -0
- package/dist/render/recon/resolve.js +95 -12
- package/dist/render/renderQuality.d.ts +35 -0
- package/dist/render/renderQuality.js +7 -0
- package/dist/render/rendererHarness.js +12 -0
- package/dist/render/sceneTarget.d.ts +9 -1
- package/dist/render/sceneTarget.js +59 -13
- package/dist/render/shaders/depth.d.ts +16 -1
- package/dist/render/shaders/depth.js +85 -9
- package/dist/render/shaders/flat/directionalShadow.js +91 -6
- package/dist/render/shaders/flat/glassTint.d.ts +6 -0
- package/dist/render/shaders/flat/glassTint.js +25 -0
- package/dist/render/shaders/flat/index.d.ts +13 -0
- package/dist/render/shaders/flat/index.js +4 -0
- package/dist/render/shaders/flat/lampGlass.d.ts +1 -0
- package/dist/render/shaders/flat/lampGlass.js +183 -0
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +115 -15
- package/dist/render/shaders/flat/pointShadow.d.ts +1 -1
- package/dist/render/shaders/flat/pointShadow.js +2 -0
- package/dist/render/shaders/flat/preamble.js +21 -6
- package/dist/render/shaders/generated/depth.wgsl.d.ts +227 -4
- package/dist/render/shaders/generated/depth.wgsl.js +227 -4
- package/dist/render/shaders/generated/flat.wgsl.d.ts +360 -312
- package/dist/render/shaders/generated/flat.wgsl.js +376 -328
- package/dist/render/shaders/generated/globalMedium.wgsl.d.ts +3 -13
- package/dist/render/shaders/generated/globalMedium.wgsl.js +3 -13
- package/dist/render/shaders/generated/lightVolume.wgsl.d.ts +3 -13
- package/dist/render/shaders/generated/lightVolume.wgsl.js +4 -14
- package/dist/render/shaders/generated/octahedralResolve.wgsl.d.ts +29 -0
- package/dist/render/shaders/generated/octahedralResolve.wgsl.js +29 -0
- package/dist/render/shaders/generated/temporalResolve.wgsl.d.ts +31 -6
- package/dist/render/shaders/generated/temporalResolve.wgsl.js +31 -6
- package/dist/render/shaders/globalMedium.js +6 -6
- package/dist/render/shaders/gpudriven/blendRaster.wgsl.d.ts +7 -0
- package/dist/render/shaders/gpudriven/blendRaster.wgsl.js +86 -1
- package/dist/render/shaders/gpudriven/cull.wgsl.d.ts +1 -1
- package/dist/render/shaders/gpudriven/cull.wgsl.js +11 -2
- package/dist/render/shaders/gpudriven/materialTable.wgsl.d.ts +1 -1
- package/dist/render/shaders/gpudriven/materialTable.wgsl.js +3 -1
- package/dist/render/shaders/gpudriven/shade.wgsl.js +50 -1
- package/dist/render/shaders/gpudriven/sunGlass.wgsl.d.ts +9 -0
- package/dist/render/shaders/gpudriven/sunGlass.wgsl.js +133 -0
- package/dist/render/shaders/lightVolume.js +6 -6
- package/dist/render/shaders/octahedralResolve.d.ts +8 -0
- package/dist/render/shaders/octahedralResolve.js +48 -17
- package/dist/render/shaders/recon/lateDepth.wgsl.d.ts +1 -0
- package/dist/render/shaders/recon/lateDepth.wgsl.js +44 -0
- package/dist/render/shaders/recon/motion.wgsl.d.ts +1 -1
- package/dist/render/shaders/recon/motion.wgsl.js +84 -0
- package/dist/render/shaders/recon/resolve.wgsl.d.ts +1 -1
- package/dist/render/shaders/recon/resolve.wgsl.js +112 -8
- package/dist/render/shaders/temporalResolve.js +133 -5
- package/dist/render/shadowCasters.d.ts +20 -1
- package/dist/render/shadowCasters.js +15 -1
- package/dist/render/shadowMap.d.ts +54 -5
- package/dist/render/shadowMap.js +190 -36
- package/dist/render/sunGlassTint.d.ts +25 -0
- package/dist/render/sunGlassTint.js +79 -0
- package/dist/render/temporalAa.d.ts +145 -0
- package/dist/render/temporalAa.js +190 -0
- package/dist/render/temporalPass.d.ts +16 -14
- package/dist/render/temporalPass.js +59 -14
- package/dist/render/translucentQueue.d.ts +6 -0
- package/dist/render/translucentQueue.js +15 -1
- package/package.json +6 -6
- package/src/index.ts +10 -0
- package/src/render/backend/api.ts +15 -9
- package/src/render/backend/webgl2/glassTintPrograms.ts +58 -0
- package/src/render/backend/webgl2/renderer.ts +422 -109
- package/src/render/backend/webgpu/buffers.ts +50 -2
- package/src/render/backend/webgpu/depthPass.ts +2 -1
- package/src/render/backend/webgpu/flatPass.ts +22 -1
- package/src/render/backend/webgpu/glassTintPass.ts +244 -0
- package/src/render/backend/webgpu/gpuDrivenPass.ts +349 -5
- package/src/render/backend/webgpu/instanced.ts +21 -1
- package/src/render/backend/webgpu/latePass.ts +152 -0
- package/src/render/backend/webgpu/lightVolumePass.ts +5 -1
- package/src/render/backend/webgpu/motionPass.ts +535 -0
- package/src/render/backend/webgpu/pipelineCache.ts +12 -1
- package/src/render/backend/webgpu/pointGlassTintPass.ts +233 -0
- package/src/render/backend/webgpu/pointShadowPass.ts +158 -26
- package/src/render/backend/webgpu/postPass.ts +34 -4
- package/src/render/backend/webgpu/renderer.ts +1178 -425
- package/src/render/backend/webgpu/scatterPass.ts +2 -1
- package/src/render/backend/webgpu/skinPaletteRing.ts +4 -0
- package/src/render/backend/webgpu/snapshotMips.ts +80 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +1 -1
- package/src/render/flameLight.ts +15 -2
- package/src/render/glass.ts +74 -0
- package/src/render/glassCasters.ts +164 -0
- package/src/render/glassShadow.ts +153 -0
- package/src/render/globalMediumPass.ts +4 -11
- package/src/render/gpudriven/lit.ts +31 -0
- package/src/render/gpudriven/materialTable.ts +28 -6
- package/src/render/gpudriven/pipeline.ts +4 -3
- package/src/render/gpudriven/sunGlass.ts +117 -0
- package/src/render/lightBudget.ts +19 -5
- package/src/render/pointGlassTint.ts +250 -0
- package/src/render/pointShadowArray.ts +188 -22
- package/src/render/pointShadowBudget.ts +49 -39
- package/src/render/pointShadowMap.ts +41 -0
- package/src/render/recon/changeFrames.ts +24 -0
- package/src/render/recon/disocclusion.ts +54 -3
- package/src/render/recon/lateRouting.ts +29 -0
- package/src/render/recon/mover.ts +118 -0
- package/src/render/recon/resolve.ts +137 -10
- package/src/render/renderQuality.ts +47 -0
- package/src/render/rendererHarness.ts +12 -0
- package/src/render/sceneTarget.ts +66 -12
- package/src/render/shaders/depth.ts +109 -9
- package/src/render/shaders/flat/directionalShadow.ts +97 -6
- package/src/render/shaders/flat/glassTint.ts +25 -0
- package/src/render/shaders/flat/index.ts +17 -0
- package/src/render/shaders/flat/lampGlass.ts +185 -0
- package/src/render/shaders/flat/main.ts +115 -15
- package/src/render/shaders/flat/pointShadow.ts +2 -0
- package/src/render/shaders/flat/preamble.ts +21 -6
- package/src/render/shaders/generated/depth.wgsl.ts +234 -4
- package/src/render/shaders/generated/flat.wgsl.ts +376 -328
- package/src/render/shaders/generated/globalMedium.wgsl.ts +3 -13
- package/src/render/shaders/generated/lightVolume.wgsl.ts +4 -14
- package/src/render/shaders/generated/octahedralResolve.wgsl.ts +30 -0
- package/src/render/shaders/generated/temporalResolve.wgsl.ts +31 -6
- package/src/render/shaders/globalMedium.ts +6 -6
- package/src/render/shaders/gpudriven/blendRaster.wgsl.ts +88 -1
- package/src/render/shaders/gpudriven/cull.wgsl.ts +11 -2
- package/src/render/shaders/gpudriven/materialTable.wgsl.ts +3 -1
- package/src/render/shaders/gpudriven/shade.wgsl.ts +50 -1
- package/src/render/shaders/gpudriven/sunGlass.wgsl.ts +139 -0
- package/src/render/shaders/lightVolume.ts +6 -6
- package/src/render/shaders/octahedralResolve.ts +50 -17
- package/src/render/shaders/recon/lateDepth.wgsl.ts +47 -0
- package/src/render/shaders/recon/motion.wgsl.ts +84 -0
- package/src/render/shaders/recon/resolve.wgsl.ts +112 -8
- package/src/render/shaders/temporalResolve.ts +141 -5
- package/src/render/shadowCasters.ts +28 -1
- package/src/render/shadowMap.ts +231 -37
- package/src/render/sunGlassTint.ts +86 -0
- package/src/render/temporalAa.ts +227 -0
- package/src/render/temporalPass.ts +88 -12
- package/src/render/translucentQueue.ts +20 -1
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which object a draw is, so a reconstruction can find where that object was last frame.
|
|
3
|
+
*
|
|
4
|
+
* **The engine cannot infer this and does not try.** `drawMesh` is immediate: a mesh drawn three
|
|
5
|
+
* times is three objects, and nothing in the call says which of last frame's three each one is. A
|
|
6
|
+
* consumer that makes one `Mover` per moving object and passes it with every draw of that object
|
|
7
|
+
* says exactly that, and the renderer keeps the rest — last frame's model and, for a skinned
|
|
8
|
+
* draw, last frame's palette.
|
|
9
|
+
*
|
|
10
|
+
* **Plain data, advanced by the renderer.** No renderer is needed to make one, so a consumer holds
|
|
11
|
+
* it beside the object it belongs to. Only a reconstructing WebGPU renderer reads or writes it; it
|
|
12
|
+
* costs nothing anywhere else. The fields past `kind` are the renderer's and a caller leaves them
|
|
13
|
+
* alone.
|
|
14
|
+
*
|
|
15
|
+
* **What would make this wrong** is a consumer whose objects are rebuilt every frame with no state
|
|
16
|
+
* of their own to hold a mover in; the identity then has to live in whatever *does* persist.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export const MOVER_KIND = 'drift.mover' as const;
|
|
20
|
+
|
|
21
|
+
export interface Mover {
|
|
22
|
+
readonly kind: typeof MOVER_KIND;
|
|
23
|
+
/** The renderer that last drew it, from `claimRendererId`; -1 before its first draw. */
|
|
24
|
+
renderer: number;
|
|
25
|
+
/** That renderer's frame serial at the last draw. */
|
|
26
|
+
frame: number;
|
|
27
|
+
readonly model: Float32Array;
|
|
28
|
+
readonly previousModel: Float32Array;
|
|
29
|
+
/**
|
|
30
|
+
* This frame's palette, exactly `paletteLength` floats long once a skinned draw has set it.
|
|
31
|
+
* Swapped with the previous one each frame and reallocated only when the rig changes.
|
|
32
|
+
*/
|
|
33
|
+
palette: Float32Array;
|
|
34
|
+
previousPalette: Float32Array;
|
|
35
|
+
paletteLength: number;
|
|
36
|
+
previousPaletteLength: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Nothing to reproject from: first draw, a missed frame, another renderer, or a changed rig. */
|
|
40
|
+
export const MOVER_NONE = 0;
|
|
41
|
+
/** Drawn on the frame before by this renderer: `previousModel` and `previousPalette` are real. */
|
|
42
|
+
export const MOVER_MOTION = 1;
|
|
43
|
+
/** Drawn already this frame. The second draw is a different object and the caller's error. */
|
|
44
|
+
export const MOVER_TWICE = 2;
|
|
45
|
+
export type MoverVerdict = typeof MOVER_NONE | typeof MOVER_MOTION | typeof MOVER_TWICE;
|
|
46
|
+
|
|
47
|
+
let nextRendererId = 1;
|
|
48
|
+
|
|
49
|
+
/** A number no other renderer in this page holds, so a mover can tell renderers apart. */
|
|
50
|
+
export function claimRendererId(): number {
|
|
51
|
+
const id = nextRendererId;
|
|
52
|
+
nextRendererId += 1;
|
|
53
|
+
return id;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function createMover(): Mover {
|
|
57
|
+
return {
|
|
58
|
+
kind: MOVER_KIND,
|
|
59
|
+
renderer: -1,
|
|
60
|
+
frame: -2,
|
|
61
|
+
model: new Float32Array(16),
|
|
62
|
+
previousModel: new Float32Array(16),
|
|
63
|
+
palette: new Float32Array(0),
|
|
64
|
+
previousPalette: new Float32Array(0),
|
|
65
|
+
paletteLength: 0,
|
|
66
|
+
previousPaletteLength: 0,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function isMover(value: unknown): value is Mover {
|
|
71
|
+
return (
|
|
72
|
+
typeof value === 'object' && value !== null && (value as { kind?: unknown }).kind === MOVER_KIND
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Advance a mover to this draw, and say whether last frame's state is usable.
|
|
78
|
+
*
|
|
79
|
+
* **A second draw in one frame changes nothing**, so the first draw's motion stands; the caller
|
|
80
|
+
* warns. Otherwise what the mover held becomes its previous and this draw becomes its current —
|
|
81
|
+
* the palettes by swapping two arrays, so a steady rig allocates nothing after its first frame.
|
|
82
|
+
*
|
|
83
|
+
* **A changed joint count is no motion**: skinning a vertex with a palette of a different rig is
|
|
84
|
+
* not an old pose of this one.
|
|
85
|
+
*/
|
|
86
|
+
export function advanceMover(
|
|
87
|
+
mover: Mover,
|
|
88
|
+
renderer: number,
|
|
89
|
+
frame: number,
|
|
90
|
+
model: ArrayLike<number>,
|
|
91
|
+
palette: Float32Array | null,
|
|
92
|
+
): MoverVerdict {
|
|
93
|
+
if (mover.renderer === renderer && mover.frame === frame) return MOVER_TWICE;
|
|
94
|
+
const drawnLast = mover.renderer === renderer && mover.frame === frame - 1;
|
|
95
|
+
|
|
96
|
+
mover.previousModel.set(mover.model);
|
|
97
|
+
const spare = mover.previousPalette;
|
|
98
|
+
mover.previousPalette = mover.palette;
|
|
99
|
+
mover.palette = spare;
|
|
100
|
+
mover.previousPaletteLength = mover.paletteLength;
|
|
101
|
+
|
|
102
|
+
mover.model.set(model);
|
|
103
|
+
if (palette === null) {
|
|
104
|
+
mover.paletteLength = 0;
|
|
105
|
+
} else {
|
|
106
|
+
/* Exactly the rig's length, because the array is uploaded as it stands; reallocated only when
|
|
107
|
+
the rig changes, which a steady character never does. */
|
|
108
|
+
if (mover.palette.length !== palette.length) mover.palette = new Float32Array(palette.length);
|
|
109
|
+
mover.palette.set(palette);
|
|
110
|
+
mover.paletteLength = palette.length;
|
|
111
|
+
}
|
|
112
|
+
mover.renderer = renderer;
|
|
113
|
+
mover.frame = frame;
|
|
114
|
+
|
|
115
|
+
if (!drawnLast) return MOVER_NONE;
|
|
116
|
+
if (mover.paletteLength !== mover.previousPaletteLength) return MOVER_NONE;
|
|
117
|
+
return MOVER_MOTION;
|
|
118
|
+
}
|
|
@@ -21,8 +21,10 @@
|
|
|
21
21
|
* previous view depth in its third channel; otherwise it is the camera's, through the surface's
|
|
22
22
|
* world position.
|
|
23
23
|
* 4. Where the surface was is read from the history — its colour and the weight it had gathered — and
|
|
24
|
-
* last frame's depth
|
|
25
|
-
*
|
|
24
|
+
* last frame's depth says whether it is the same surface: the nearest of the nine texels around
|
|
25
|
+
* where the dilated texel was, forgiven its own slope across a texel. The two frames' normals, from
|
|
26
|
+
* their depths, catch thin geometry, each with the say its planarity gives it. The disocclusion
|
|
27
|
+
* scales the history's weight.
|
|
26
28
|
* 5. The history is clipped to the box, its weight capped so a tenth of each frame is always new, and
|
|
27
29
|
* the two are mixed by their weights. Where the pixel has gathered less than one sample's worth,
|
|
28
30
|
* the render read bicubically fills the gap. The history keeps colour and weight; the picture shown
|
|
@@ -33,7 +35,13 @@
|
|
|
33
35
|
* buffer before a sample reaches this arithmetic, as the temporal resolve already does.
|
|
34
36
|
*/
|
|
35
37
|
import { clipToBox, rgbToYCoCg, varianceBox, yCoCgToRgb } from './clamp.ts';
|
|
36
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
disocclusionWeight,
|
|
40
|
+
normalFromPositions,
|
|
41
|
+
planarity,
|
|
42
|
+
surfaceSlope,
|
|
43
|
+
worldPositionFromDepth,
|
|
44
|
+
} from './disocclusion.ts';
|
|
37
45
|
import type { DisocclusionParams } from './disocclusion.ts';
|
|
38
46
|
import { sampleBilinear, sampleHistoryBicubic5 } from './reproject.ts';
|
|
39
47
|
|
|
@@ -209,6 +217,59 @@ function worldAt(
|
|
|
209
217
|
return 1 / w;
|
|
210
218
|
}
|
|
211
219
|
|
|
220
|
+
/**
|
|
221
|
+
* A texel's view depth at its unjittered uv, or 0 — `worldAt` without the position, which is all a
|
|
222
|
+
* search over depths needs: one row of the inverse rather than four. Zero off the picture, where
|
|
223
|
+
* `worldAt` would clamp, because a neighbour that is not there is not a step.
|
|
224
|
+
*/
|
|
225
|
+
function viewDepthAt(
|
|
226
|
+
depths: ArrayLike<number>,
|
|
227
|
+
inverse: ArrayLike<number>,
|
|
228
|
+
jitter: ArrayLike<number>,
|
|
229
|
+
width: number,
|
|
230
|
+
height: number,
|
|
231
|
+
x: number,
|
|
232
|
+
y: number,
|
|
233
|
+
): number {
|
|
234
|
+
if (x < 0 || y < 0 || x >= width || y >= height) return 0;
|
|
235
|
+
const u = (x + 0.5 - (jitter[0] as number)) / width;
|
|
236
|
+
const v = (y + 0.5 - (jitter[1] as number)) / height;
|
|
237
|
+
const m = inverse;
|
|
238
|
+
const w =
|
|
239
|
+
(m[3] as number) * (u * 2 - 1) +
|
|
240
|
+
(m[7] as number) * (v * 2 - 1) +
|
|
241
|
+
(m[11] as number) * (depths[y * width + x] as number) +
|
|
242
|
+
(m[15] as number);
|
|
243
|
+
return Math.abs(w) > 1e-12 ? 1 / w : 0;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** What a texel's four neighbours say about its surface: see `planarity` and `surfaceSlope`. */
|
|
247
|
+
const SURFACE = { planar: 1, slope: 0 };
|
|
248
|
+
|
|
249
|
+
function surfaceAt(
|
|
250
|
+
depths: ArrayLike<number>,
|
|
251
|
+
inverse: ArrayLike<number>,
|
|
252
|
+
jitter: ArrayLike<number>,
|
|
253
|
+
width: number,
|
|
254
|
+
height: number,
|
|
255
|
+
x: number,
|
|
256
|
+
y: number,
|
|
257
|
+
centre: number,
|
|
258
|
+
): void {
|
|
259
|
+
let planar = 1;
|
|
260
|
+
let slope = 0;
|
|
261
|
+
for (let axis = 0; axis < 2; axis += 1) {
|
|
262
|
+
const ax = 1 - axis;
|
|
263
|
+
const ay = axis;
|
|
264
|
+
const after = viewDepthAt(depths, inverse, jitter, width, height, x + ax, y + ay);
|
|
265
|
+
const before = viewDepthAt(depths, inverse, jitter, width, height, x - ax, y - ay);
|
|
266
|
+
planar *= planarity(before, centre, after);
|
|
267
|
+
slope = Math.max(slope, surfaceSlope(before, centre, after));
|
|
268
|
+
}
|
|
269
|
+
SURFACE.planar = planar;
|
|
270
|
+
SURFACE.slope = slope;
|
|
271
|
+
}
|
|
272
|
+
|
|
212
273
|
/** The world normal at a texel, from its depth and its right and upper neighbours'. */
|
|
213
274
|
function normalAt(
|
|
214
275
|
depths: ArrayLike<number>,
|
|
@@ -282,20 +343,84 @@ function historyWeight(frame: ResolveFrame, u: number, v: number, dx: number, dy
|
|
|
282
343
|
4,
|
|
283
344
|
HISTORY,
|
|
284
345
|
);
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
346
|
+
/*
|
|
347
|
+
* **Last frame's depth is searched for this surface, around where the dilated texel was.** The
|
|
348
|
+
* surface being judged is the dilated texel's, which beside an edge is a neighbour's, so judging
|
|
349
|
+
* it by the texel under the pixel compares two surfaces: the side of every silhouette away from the
|
|
350
|
+
* eye dropped its history on every frame of a still picture. And a new jitter samples a surface
|
|
351
|
+
* somewhere else, so the nearest of last frame's nine is taken rather than the one the rounding
|
|
352
|
+
* picks. What it gives up is a disocclusion narrower than a texel, which keeps a history that the
|
|
353
|
+
* neighbourhood clip then bounds.
|
|
354
|
+
*/
|
|
355
|
+
const su = (dx + 0.5 - (frame.jitter[0] as number)) / rw + mu;
|
|
356
|
+
const sv = (dy + 0.5 - (frame.jitter[1] as number)) / rh + mv;
|
|
357
|
+
const cx = clampTexel(Math.round(su * rw + (frame.previousJitter[0] as number) - 0.5), rw);
|
|
358
|
+
const cy = clampTexel(Math.round(sv * rh + (frame.previousJitter[1] as number) - 0.5), rh);
|
|
359
|
+
/*
|
|
360
|
+
* The first texel looked at answers alone when it agrees within the tolerance, which it does
|
|
361
|
+
* everywhere but at an edge: the depth test trusts it fully whichever texel would have won, so
|
|
362
|
+
* only an edge pays for the other eight.
|
|
363
|
+
*/
|
|
364
|
+
let held = viewDepthAt(
|
|
288
365
|
frame.previousDepth,
|
|
289
366
|
frame.previousInverseViewProj,
|
|
290
367
|
frame.previousJitter,
|
|
291
368
|
rw,
|
|
292
369
|
rh,
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
RIGHT,
|
|
370
|
+
cx,
|
|
371
|
+
cy,
|
|
296
372
|
);
|
|
373
|
+
let px = cx;
|
|
374
|
+
let py = cy;
|
|
375
|
+
if (!(held > 0 && Math.abs(held - expected) <= frame.disocclusion.depthTolerance * expected)) {
|
|
376
|
+
held = 0;
|
|
377
|
+
let nearest = Number.POSITIVE_INFINITY;
|
|
378
|
+
for (let j = -1; j <= 1; j += 1) {
|
|
379
|
+
for (let i = -1; i <= 1; i += 1) {
|
|
380
|
+
const tx = clampTexel(cx + i, rw);
|
|
381
|
+
const ty = clampTexel(cy + j, rh);
|
|
382
|
+
const depth = viewDepthAt(
|
|
383
|
+
frame.previousDepth,
|
|
384
|
+
frame.previousInverseViewProj,
|
|
385
|
+
frame.previousJitter,
|
|
386
|
+
rw,
|
|
387
|
+
rh,
|
|
388
|
+
tx,
|
|
389
|
+
ty,
|
|
390
|
+
);
|
|
391
|
+
if (!(depth > 0)) continue;
|
|
392
|
+
const off = Math.abs(depth - expected);
|
|
393
|
+
if (off < nearest) {
|
|
394
|
+
nearest = off;
|
|
395
|
+
held = depth;
|
|
396
|
+
px = tx;
|
|
397
|
+
py = ty;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const here = viewDepthAt(frame.depth, frame.inverseViewProj, frame.jitter, rw, rh, dx, dy);
|
|
404
|
+
surfaceAt(frame.depth, frame.inverseViewProj, frame.jitter, rw, rh, dx, dy, here);
|
|
405
|
+
const slope = SURFACE.slope;
|
|
406
|
+
let planar = SURFACE.planar;
|
|
407
|
+
if (planar > 0) {
|
|
408
|
+
surfaceAt(
|
|
409
|
+
frame.previousDepth,
|
|
410
|
+
frame.previousInverseViewProj,
|
|
411
|
+
frame.previousJitter,
|
|
412
|
+
rw,
|
|
413
|
+
rh,
|
|
414
|
+
px,
|
|
415
|
+
py,
|
|
416
|
+
held,
|
|
417
|
+
);
|
|
418
|
+
planar *= SURFACE.planar;
|
|
419
|
+
}
|
|
420
|
+
/* A normal has the say its planarity gives it; at an edge or a sliver, none, and none is computed. */
|
|
297
421
|
let normalDot = 1;
|
|
298
422
|
if (
|
|
423
|
+
planar > 0 &&
|
|
299
424
|
normalAt(frame.depth, frame.inverseViewProj, frame.jitter, frame.eye, rw, rh, dx, dy, NORMAL) &&
|
|
300
425
|
normalAt(
|
|
301
426
|
frame.previousDepth,
|
|
@@ -309,10 +434,11 @@ function historyWeight(frame: ResolveFrame, u: number, v: number, dx: number, dy
|
|
|
309
434
|
PREVIOUS_NORMAL,
|
|
310
435
|
)
|
|
311
436
|
) {
|
|
312
|
-
|
|
437
|
+
const cosine =
|
|
313
438
|
(NORMAL[0] as number) * (PREVIOUS_NORMAL[0] as number) +
|
|
314
439
|
(NORMAL[1] as number) * (PREVIOUS_NORMAL[1] as number) +
|
|
315
440
|
(NORMAL[2] as number) * (PREVIOUS_NORMAL[2] as number);
|
|
441
|
+
normalDot = 1 - planar * (1 - cosine);
|
|
316
442
|
}
|
|
317
443
|
const trust = disocclusionWeight(
|
|
318
444
|
expected,
|
|
@@ -320,6 +446,7 @@ function historyWeight(frame: ResolveFrame, u: number, v: number, dx: number, dy
|
|
|
320
446
|
Math.hypot(mu, mv),
|
|
321
447
|
normalDot,
|
|
322
448
|
frame.disocclusion,
|
|
449
|
+
slope,
|
|
323
450
|
);
|
|
324
451
|
return (HISTORY[3] as number) * trust;
|
|
325
452
|
}
|
|
@@ -37,6 +37,9 @@ export type ShadowFilterTaps = 4 | 8 | 12;
|
|
|
37
37
|
export type DirectionalShadowDepthLayers = 1 | 2;
|
|
38
38
|
export type WaterReflectionFilterTaps = 1 | 5 | 9;
|
|
39
39
|
|
|
40
|
+
/** How light through glass is coloured: see `RenderQuality.glassShadows`. */
|
|
41
|
+
export type GlassShadows = 'full' | 'half' | 'off';
|
|
42
|
+
|
|
40
43
|
export interface RenderQuality {
|
|
41
44
|
/**
|
|
42
45
|
* The transfer curve applied at the end of every shaded pass.
|
|
@@ -149,6 +152,25 @@ export interface RenderQuality {
|
|
|
149
152
|
readonly lightVolumeSamples: number;
|
|
150
153
|
/** Independently composited static sun depths; two preserves common overlaps. */
|
|
151
154
|
readonly directionalShadowDepthLayers: DirectionalShadowDepthLayers;
|
|
155
|
+
/**
|
|
156
|
+
* Whether light through glass takes the glass's colour, and at what size.
|
|
157
|
+
*
|
|
158
|
+
* **`'full'`, the default**: every shadow a glass caster reaches gains a layer holding the depth of
|
|
159
|
+
* the nearest pane and a tint layer holding what the panes let through, at the size of the depth
|
|
160
|
+
* map beside it — 8 bytes a texel, so 32 MB for a 2048 sun map and 8 MB for a 1024 lamp layer.
|
|
161
|
+
* **`'half'`** halves the tint layers only (the glass depth must match its array): 20 MB and 5 MB,
|
|
162
|
+
* and a coloured patch softer than an opaque shadow's edge. **`'off'`** is glass before this
|
|
163
|
+
* existed: a pane casts nothing at all.
|
|
164
|
+
*
|
|
165
|
+
* Nothing is allocated until a glass caster first reaches a shadow, so a scene with no glass pays
|
|
166
|
+
* no memory and no pass whatever this says. **The lit shader is the exception**: `'full'` and
|
|
167
|
+
* `'half'` compile the glass lookup into it, and a lookup that finds no glass and returns at its
|
|
168
|
+
* first line still costs the lit pass its registers — measured on the courtyard at night, 0.27 ms
|
|
169
|
+
* at 720p on WebGPU, 0.42 ms on WebGL2 and 1.2 ms at 4K. `'off'` compiles none of it, so a world
|
|
170
|
+
* that never casts through glass is that much faster for saying so. What would make `'full'` wrong
|
|
171
|
+
* is a phone budget with a lot of glass; that is what `'half'` is for. See `glassShadow.ts`.
|
|
172
|
+
*/
|
|
173
|
+
readonly glassShadows: GlassShadows;
|
|
152
174
|
/** Horizontal world-space reach over which a directional shadow dissolves. */
|
|
153
175
|
readonly directionalShadowMaxDistance: number;
|
|
154
176
|
/** Maximum horizontal projection per vertical metre before low-angle shadows vanish. */
|
|
@@ -556,6 +578,20 @@ export interface RenderQuality {
|
|
|
556
578
|
* the render is a quarter of the output and no reconstruction holds an edge through that. A
|
|
557
579
|
* negative number is off, not a clamp to the bottom: it cannot mean "a little".
|
|
558
580
|
*
|
|
581
|
+
* **What it needs from you.** A `createMover()` per moving object, passed with that object's
|
|
582
|
+
* draws where `drawMesh` takes last frame's matrix — the renderer keeps last frame's model and,
|
|
583
|
+
* for a skinned draw, last frame's pose. Instanced batches and meshes rewritten with
|
|
584
|
+
* `updateMesh` need nothing: the batch and the mesh are their own identity. And `cameraCut()` at
|
|
585
|
+
* every cut, because nothing detects one: without it a cut blends the previous shot into the
|
|
586
|
+
* next. Blended draws — translucent meshes, particles, plumes, film, bolts, lines, world text —
|
|
587
|
+
* are drawn after the upscale at the output size and need nothing either; light volumes stay in
|
|
588
|
+
* the reconstructed picture, being a march that keeps its history.
|
|
589
|
+
*
|
|
590
|
+
* **What it cannot give back** is the resolution it draws at: an edge that never moves is softer
|
|
591
|
+
* than a native frame's at any ratio, because two thirds of the pixels at 1.5 are a guess made
|
|
592
|
+
* sharp. Measured on `demo/dev/ghost.html` against a native frame at four samples, still edges
|
|
593
|
+
* err by about 8 levels where a native frame at one sample errs by 3.
|
|
594
|
+
*
|
|
559
595
|
* `?recon=` is how it gets A/B'd in one reload.
|
|
560
596
|
*/
|
|
561
597
|
readonly reconstruction: number;
|
|
@@ -838,6 +874,7 @@ export const DEFAULT_RENDER_QUALITY: Readonly<RenderQuality> = Object.freeze({
|
|
|
838
874
|
shadowFilterTaps: MAX_SHADOW_FILTER_TAPS,
|
|
839
875
|
lightVolumeSamples: 32,
|
|
840
876
|
directionalShadowDepthLayers: 2,
|
|
877
|
+
glassShadows: 'full',
|
|
841
878
|
directionalShadowMaxDistance: 6,
|
|
842
879
|
directionalShadowMaxSlope: 3,
|
|
843
880
|
pointShadowRebakeDistance: 0.001,
|
|
@@ -998,6 +1035,7 @@ export function resolveRenderQuality(options: RenderQualityOptions = {}): Readon
|
|
|
998
1035
|
lightVolumeSamples: options.lightVolumeSamples ?? DEFAULT_RENDER_QUALITY.lightVolumeSamples,
|
|
999
1036
|
directionalShadowDepthLayers:
|
|
1000
1037
|
options.directionalShadowDepthLayers ?? DEFAULT_RENDER_QUALITY.directionalShadowDepthLayers,
|
|
1038
|
+
glassShadows: options.glassShadows ?? DEFAULT_RENDER_QUALITY.glassShadows,
|
|
1001
1039
|
directionalShadowMaxDistance:
|
|
1002
1040
|
options.directionalShadowMaxDistance ?? DEFAULT_RENDER_QUALITY.directionalShadowMaxDistance,
|
|
1003
1041
|
directionalShadowMaxSlope:
|
|
@@ -1128,6 +1166,15 @@ export function resolveRenderQuality(options: RenderQualityOptions = {}): Readon
|
|
|
1128
1166
|
`RenderQuality.directionalShadowDepthLayers must be 1 or 2, got ${quality.directionalShadowDepthLayers}`,
|
|
1129
1167
|
);
|
|
1130
1168
|
}
|
|
1169
|
+
if (
|
|
1170
|
+
quality.glassShadows !== 'full' &&
|
|
1171
|
+
quality.glassShadows !== 'half' &&
|
|
1172
|
+
quality.glassShadows !== 'off'
|
|
1173
|
+
) {
|
|
1174
|
+
throw new Error(
|
|
1175
|
+
`RenderQuality.glassShadows must be 'full', 'half' or 'off', got ${String(quality.glassShadows)}`,
|
|
1176
|
+
);
|
|
1177
|
+
}
|
|
1131
1178
|
if (quality.plumeNoiseOctaves > 3) {
|
|
1132
1179
|
throw new Error(
|
|
1133
1180
|
`RenderQuality.plumeNoiseOctaves must be <= 3, got ${quality.plumeNoiseOctaves}`,
|
|
@@ -129,6 +129,18 @@ export function recordingGl(
|
|
|
129
129
|
COLOR_BUFFER_BIT: 0x00004000,
|
|
130
130
|
/* Named so a test can tell a mesh that declared itself deforming from one that did not. */
|
|
131
131
|
ARRAY_BUFFER: 0x8892,
|
|
132
|
+
/* Named so a blend a pass sets can be read back as numbers: glass multiplies what it lets
|
|
133
|
+
through, and a test that cannot tell DST_COLOR from a recorder cannot tell it multiplies. */
|
|
134
|
+
ZERO: 0,
|
|
135
|
+
DST_ALPHA: 0x0304,
|
|
136
|
+
DST_COLOR: 0x0306,
|
|
137
|
+
FUNC_ADD: 0x8006,
|
|
138
|
+
COLOR: 0x1800,
|
|
139
|
+
DEPTH: 0x1801,
|
|
140
|
+
/* Named so a copy can be read as from-and-to: a blit's two framebuffers are the whole claim. */
|
|
141
|
+
READ_FRAMEBUFFER: 0x8ca8,
|
|
142
|
+
DRAW_FRAMEBUFFER: 0x8ca9,
|
|
143
|
+
BLEND: 0x0be2,
|
|
132
144
|
STATIC_DRAW: 0x88e4,
|
|
133
145
|
DYNAMIC_DRAW: 0x88e8,
|
|
134
146
|
};
|
|
@@ -82,6 +82,8 @@ export class SceneTarget {
|
|
|
82
82
|
private colorCopyFramebuffer: WebGLFramebuffer | null = null;
|
|
83
83
|
/** Whether this frame has filled it. One snapshot serves every refracting draw in a frame. */
|
|
84
84
|
private colorCopyFilled = false;
|
|
85
|
+
/** Whether this frame's copy has its mip chain, which frosted glass reads. See `snapshotColor`. */
|
|
86
|
+
private colorCopyMips = false;
|
|
85
87
|
/**
|
|
86
88
|
* The colour grade, as a 3D texture on unit 4, and the table it was built from.
|
|
87
89
|
*
|
|
@@ -275,12 +277,24 @@ export class SceneTarget {
|
|
|
275
277
|
*/
|
|
276
278
|
if (this.colorCopy === null) this.colorCopy = gl.createTexture();
|
|
277
279
|
gl.bindTexture(gl.TEXTURE_2D, this.colorCopy);
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
280
|
+
/*
|
|
281
|
+
* **Every level, so glass can read a blurrier one.** Frosted glass reads the copy at a level its
|
|
282
|
+
* frost picks, and a texture sampled through a mipmapping filter with a level missing is
|
|
283
|
+
* incomplete and reads black — so the chain is allocated here, once per size, and filled only on
|
|
284
|
+
* a frame that draws glass (`snapshotColor`). Plain refraction reads level 0, as it always has.
|
|
285
|
+
*/
|
|
286
|
+
const levels = Math.floor(Math.log2(Math.max(width, height))) + 1;
|
|
287
|
+
for (let level = 0; level < levels; level += 1) {
|
|
288
|
+
const w = Math.max(1, width >> level);
|
|
289
|
+
const h = Math.max(1, height >> level);
|
|
290
|
+
if (this.floatColor) {
|
|
291
|
+
gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA16F, w, h, 0, gl.RGBA, gl.HALF_FLOAT, null);
|
|
292
|
+
} else {
|
|
293
|
+
gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA, w, h, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
|
294
|
+
}
|
|
282
295
|
}
|
|
283
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.
|
|
296
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL, levels - 1);
|
|
297
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
|
|
284
298
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
285
299
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
286
300
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
@@ -419,6 +433,7 @@ export class SceneTarget {
|
|
|
419
433
|
/* Last frame's snapshot is last frame's scene. See `snapshotDepth`. */
|
|
420
434
|
this.depthCopyFilled = false;
|
|
421
435
|
this.colorCopyFilled = false;
|
|
436
|
+
this.colorCopyMips = false;
|
|
422
437
|
this.bind();
|
|
423
438
|
}
|
|
424
439
|
|
|
@@ -516,12 +531,21 @@ export class SceneTarget {
|
|
|
516
531
|
* Null where the copy could not be made, and a caller must read that as "do not refract" rather
|
|
517
532
|
* than as a black texture — refracting against black paints every pane the colour of a hole.
|
|
518
533
|
*/
|
|
519
|
-
snapshotColor(
|
|
534
|
+
snapshotColor(
|
|
535
|
+
afresh = false,
|
|
536
|
+
/** Fill the copy's mip chain as well, once a frame: what frosted glass reads. */
|
|
537
|
+
mips = false,
|
|
538
|
+
): WebGLTexture | null {
|
|
520
539
|
if (this.colorCopy === null || this.colorCopyFramebuffer === null) return null;
|
|
521
|
-
if (this.colorCopyFilled && !afresh)
|
|
540
|
+
if (this.colorCopyFilled && !afresh) {
|
|
541
|
+
if (mips) this.fillColorCopyMips();
|
|
542
|
+
return this.colorCopy;
|
|
543
|
+
}
|
|
522
544
|
const { gl } = this;
|
|
523
545
|
const source = this.msaaFramebuffer ?? this.framebuffer;
|
|
524
546
|
if (source === null) return null;
|
|
547
|
+
const drawing = gl.getParameter(gl.DRAW_FRAMEBUFFER_BINDING) as WebGLFramebuffer | null;
|
|
548
|
+
const reading = gl.getParameter(gl.READ_FRAMEBUFFER_BINDING) as WebGLFramebuffer | null;
|
|
525
549
|
|
|
526
550
|
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, source);
|
|
527
551
|
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this.colorCopyFramebuffer);
|
|
@@ -546,13 +570,33 @@ export class SceneTarget {
|
|
|
546
570
|
gl.NEAREST,
|
|
547
571
|
);
|
|
548
572
|
const refused = gl.getError() !== gl.NO_ERROR;
|
|
549
|
-
/*
|
|
550
|
-
|
|
573
|
+
/*
|
|
574
|
+
* **Back to what the caller was drawing into, whichever that was** — read before the copy and
|
|
575
|
+
* handed back, never assumed. It was the scene's framebuffer, assumed, and a pane drawn into a
|
|
576
|
+
* reflection probe's face then left the scene's bound: the probe attached its next face to it,
|
|
577
|
+
* a face-sized colour beside a screen-sized depth, and every draw after refused as incomplete.
|
|
578
|
+
*/
|
|
579
|
+
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, drawing);
|
|
580
|
+
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, reading);
|
|
551
581
|
if (refused) return null;
|
|
552
582
|
this.colorCopyFilled = true;
|
|
583
|
+
this.colorCopyMips = false;
|
|
584
|
+
if (mips) this.fillColorCopyMips();
|
|
553
585
|
return this.colorCopy;
|
|
554
586
|
}
|
|
555
587
|
|
|
588
|
+
/** The copy's mip chain from its level 0, once per copy. */
|
|
589
|
+
private fillColorCopyMips(): void {
|
|
590
|
+
if (this.colorCopyMips || this.colorCopy === null) return;
|
|
591
|
+
const { gl } = this;
|
|
592
|
+
/* Bound on whichever unit is active and handed back, so the caller's texture there survives. */
|
|
593
|
+
const previous = gl.getParameter(gl.TEXTURE_BINDING_2D) as WebGLTexture | null;
|
|
594
|
+
gl.bindTexture(gl.TEXTURE_2D, this.colorCopy);
|
|
595
|
+
gl.generateMipmap(gl.TEXTURE_2D);
|
|
596
|
+
gl.bindTexture(gl.TEXTURE_2D, previous);
|
|
597
|
+
this.colorCopyMips = true;
|
|
598
|
+
}
|
|
599
|
+
|
|
556
600
|
snapshotDepth(
|
|
557
601
|
/**
|
|
558
602
|
* Take the copy again even though one has already been taken this frame.
|
|
@@ -573,6 +617,8 @@ export class SceneTarget {
|
|
|
573
617
|
const { gl } = this;
|
|
574
618
|
const source = this.msaaFramebuffer ?? this.framebuffer;
|
|
575
619
|
if (source === null) return null;
|
|
620
|
+
const drawing = gl.getParameter(gl.DRAW_FRAMEBUFFER_BINDING) as WebGLFramebuffer | null;
|
|
621
|
+
const reading = gl.getParameter(gl.READ_FRAMEBUFFER_BINDING) as WebGLFramebuffer | null;
|
|
576
622
|
|
|
577
623
|
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, source);
|
|
578
624
|
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this.depthCopyFramebuffer);
|
|
@@ -595,8 +641,9 @@ export class SceneTarget {
|
|
|
595
641
|
gl.NEAREST,
|
|
596
642
|
);
|
|
597
643
|
const refused = gl.getError() !== gl.NO_ERROR;
|
|
598
|
-
/* Back to what the caller was drawing into,
|
|
599
|
-
gl.bindFramebuffer(gl.
|
|
644
|
+
/* Back to what the caller was drawing into, read rather than assumed: see `snapshotColor`. */
|
|
645
|
+
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, drawing);
|
|
646
|
+
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, reading);
|
|
600
647
|
if (refused) return null;
|
|
601
648
|
this.depthCopyFilled = true;
|
|
602
649
|
return this.depthCopy;
|
|
@@ -743,7 +790,12 @@ export class SceneTarget {
|
|
|
743
790
|
* `veilColor` and `veilAlpha` are positional defaults and every existing caller passes them
|
|
744
791
|
* by position.
|
|
745
792
|
*/
|
|
746
|
-
temporal?: {
|
|
793
|
+
temporal?: {
|
|
794
|
+
readonly reprojection: Float32Array;
|
|
795
|
+
readonly blend: number;
|
|
796
|
+
readonly periodStart: boolean;
|
|
797
|
+
readonly depthToViewZ: Float32Array;
|
|
798
|
+
},
|
|
747
799
|
): void {
|
|
748
800
|
const { gl } = this;
|
|
749
801
|
this.depthWanted =
|
|
@@ -883,6 +935,8 @@ export class SceneTarget {
|
|
|
883
935
|
this.width,
|
|
884
936
|
this.height,
|
|
885
937
|
temporal.blend,
|
|
938
|
+
temporal.periodStart,
|
|
939
|
+
temporal.depthToViewZ,
|
|
886
940
|
);
|
|
887
941
|
}
|
|
888
942
|
|