@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
|
@@ -12,10 +12,15 @@
|
|
|
12
12
|
* a different part of a texel than the one it stands for, which at a grazing angle is a different
|
|
13
13
|
* depth; the tolerance grows with the motion's length.
|
|
14
14
|
*
|
|
15
|
+
* **And in proportion to the surface's own slope.** A new jitter samples a surface somewhere else, so
|
|
16
|
+
* on one turned from the eye two frames' samples of it differ by up to a texel's worth of its depth
|
|
17
|
+
* with nothing moved. `surfaceSlope` measures that step on the surface's own side of any silhouette.
|
|
18
|
+
*
|
|
15
19
|
* **Normals, for the case depth cannot see.** A leaf's two sides are at one depth, and the history of
|
|
16
20
|
* the front is not the back. The forward frame has no normal buffer, so both normals come from their
|
|
17
21
|
* frame's depth — `worldPositionFromDepth` and `normalFromPositions` — in world space, where the two
|
|
18
|
-
* frames agree.
|
|
22
|
+
* frames agree. **A normal differenced across an edge is not a normal**, so the caller weighs each by
|
|
23
|
+
* `planarity`: at an edge, a sliver, or anything finer than a texel the depth test decides alone.
|
|
19
24
|
*
|
|
20
25
|
* **Continuous in every input**, because a step is a line on screen along which the history is kept on
|
|
21
26
|
* one frame and dropped on the next: every threshold is a smoothstep, never a comparison.
|
|
@@ -37,19 +42,66 @@ function smoothstep(edge0, edge1, x) {
|
|
|
37
42
|
const t = Math.min(1, Math.max(0, (x - edge0) / (edge1 - edge0)));
|
|
38
43
|
return t * t * (3 - 2 * t);
|
|
39
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* The band over which three texels stop being one plane: how far their two steps in inverse depth
|
|
47
|
+
* fail to cancel, as a share of the two together. None of it is a plane at any angle; all of it is an
|
|
48
|
+
* edge — one side steps, the other does not — or a sliver, both stepping the same way.
|
|
49
|
+
*
|
|
50
|
+
* **A share and not a size**, which is the lesson of the rods in `resolve.test.ts`: a part a
|
|
51
|
+
* hundredth of the depth in front of a wall passes the depth test, and a normal differenced across it
|
|
52
|
+
* still leans by its height over a texel's width — nearly edge-on where texels are small. What would
|
|
53
|
+
* make this wrong is a curved surface judged at its apex, where both steps lean one way and the
|
|
54
|
+
* normal is sound; there it only gives the normals less say, which the depth test does not need.
|
|
55
|
+
*/
|
|
56
|
+
export const PLANAR_FROM = 0.25;
|
|
57
|
+
export const PLANAR_TO = 0.5;
|
|
58
|
+
/**
|
|
59
|
+
* How sure it is that `centre` and its neighbours either side along one axis lie on one plane, one to
|
|
60
|
+
* zero. Inverse depth is affine across a plane on screen, so a plane's two steps cancel whatever its
|
|
61
|
+
* angle. One where a neighbour is missing — zero, off the picture or undrawn — or where there is no
|
|
62
|
+
* step to compare: nothing to judge by, so a normal keeps the say it always had.
|
|
63
|
+
*/
|
|
64
|
+
export function planarity(before, centre, after) {
|
|
65
|
+
if (!(before > 0) || !(centre > 0) || !(after > 0))
|
|
66
|
+
return 1;
|
|
67
|
+
const a = 1 / after - 1 / centre;
|
|
68
|
+
const b = 1 / before - 1 / centre;
|
|
69
|
+
const spread = Math.abs(a) + Math.abs(b);
|
|
70
|
+
if (!(spread > 0))
|
|
71
|
+
return 1;
|
|
72
|
+
return 1 - smoothstep(PLANAR_FROM, PLANAR_TO, Math.abs(a + b) / spread);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* How far a surface's depth moves across one texel along an axis, relative to the depth: the smaller
|
|
76
|
+
* of the steps to its two neighbours, because beside a silhouette one of them is the surface behind.
|
|
77
|
+
* Zero at a sliver — nearer or farther than both, so each step is to another surface — and one
|
|
78
|
+
* neighbour's step where only one is there.
|
|
79
|
+
*/
|
|
80
|
+
export function surfaceSlope(before, centre, after) {
|
|
81
|
+
const a = after > 0 ? after - centre : Number.NaN;
|
|
82
|
+
const b = before > 0 ? before - centre : Number.NaN;
|
|
83
|
+
if (Number.isNaN(a))
|
|
84
|
+
return Number.isNaN(b) ? 0 : Math.abs(b) / centre;
|
|
85
|
+
if (Number.isNaN(b))
|
|
86
|
+
return Math.abs(a) / centre;
|
|
87
|
+
if (a * b > 0)
|
|
88
|
+
return 0;
|
|
89
|
+
return Math.min(Math.abs(a), Math.abs(b)) / centre;
|
|
90
|
+
}
|
|
40
91
|
/**
|
|
41
92
|
* One where the history is trustworthy, zero where it is not, and continuous between.
|
|
42
93
|
*
|
|
43
94
|
* `currDepth` is the view depth this surface should have had last frame; `histDepth` the view depth
|
|
44
95
|
* last frame held where it lands, or zero where it landed off the screen; `motionLength` the motion's
|
|
45
|
-
* length in uv; `normalDot` the cosine between the two frames' world normals there
|
|
96
|
+
* length in uv; `normalDot` the cosine between the two frames' world normals there; `slope` the
|
|
97
|
+
* surface's `surfaceSlope`, which widens the tolerance one for one.
|
|
46
98
|
*/
|
|
47
|
-
export function disocclusionWeight(currDepth, histDepth, motionLength, normalDot, params) {
|
|
99
|
+
export function disocclusionWeight(currDepth, histDepth, motionLength, normalDot, params, slope = 0) {
|
|
48
100
|
if (!(currDepth > 0) || !(histDepth > 0) || !(motionLength >= 0) || Number.isNaN(normalDot)) {
|
|
49
101
|
return 0;
|
|
50
102
|
}
|
|
51
103
|
const disagreement = Math.abs(currDepth - histDepth) / currDepth;
|
|
52
|
-
const tolerance = params.depthTolerance + params.motionScale * motionLength;
|
|
104
|
+
const tolerance = params.depthTolerance + params.motionScale * motionLength + slope;
|
|
53
105
|
const depth = 1 - smoothstep(tolerance, 2 * tolerance, disagreement);
|
|
54
106
|
const normal = smoothstep(params.normalFloor, params.normalCeiling, normalDot);
|
|
55
107
|
return depth * normal;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Two decisions about one draw in a reconstructing frame, and why they are the same set.
|
|
3
|
+
*
|
|
4
|
+
* A blended draw goes **late** — after the reconstruction, at output resolution, unjittered — and a
|
|
5
|
+
* mover's motion is **tracked**, only for draws that land in the picture being reconstructed. A
|
|
6
|
+
* planar mirror and a probe bake are other pictures drawn through the same verbs, and the overlay
|
|
7
|
+
* after `endFrame` is past the picture altogether; routing any of them late, or counting a mirrored
|
|
8
|
+
* draw as a mover's second draw of the frame, would be wrong in the way that reads as a ghost.
|
|
9
|
+
*
|
|
10
|
+
* Plain booleans rather than a state object, so the per-draw call allocates nothing.
|
|
11
|
+
*/
|
|
12
|
+
export declare function routesLate(reconstructing: boolean, mirror: boolean, probe: boolean, presented: boolean): boolean;
|
|
13
|
+
/** The same set as `routesLate`, named for the other reader. */
|
|
14
|
+
export declare function tracksMotion(reconstructing: boolean, mirror: boolean, probe: boolean, presented: boolean): boolean;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Two decisions about one draw in a reconstructing frame, and why they are the same set.
|
|
3
|
+
*
|
|
4
|
+
* A blended draw goes **late** — after the reconstruction, at output resolution, unjittered — and a
|
|
5
|
+
* mover's motion is **tracked**, only for draws that land in the picture being reconstructed. A
|
|
6
|
+
* planar mirror and a probe bake are other pictures drawn through the same verbs, and the overlay
|
|
7
|
+
* after `endFrame` is past the picture altogether; routing any of them late, or counting a mirrored
|
|
8
|
+
* draw as a mover's second draw of the frame, would be wrong in the way that reads as a ghost.
|
|
9
|
+
*
|
|
10
|
+
* Plain booleans rather than a state object, so the per-draw call allocates nothing.
|
|
11
|
+
*/
|
|
12
|
+
export function routesLate(reconstructing, mirror, probe, presented) {
|
|
13
|
+
return reconstructing && !mirror && !probe && !presented;
|
|
14
|
+
}
|
|
15
|
+
/** The same set as `routesLate`, named for the other reader. */
|
|
16
|
+
export function tracksMotion(reconstructing, mirror, probe, presented) {
|
|
17
|
+
return routesLate(reconstructing, mirror, probe, presented);
|
|
18
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
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
|
+
export declare const MOVER_KIND: 'drift.mover';
|
|
19
|
+
export interface Mover {
|
|
20
|
+
readonly kind: typeof MOVER_KIND;
|
|
21
|
+
/** The renderer that last drew it, from `claimRendererId`; -1 before its first draw. */
|
|
22
|
+
renderer: number;
|
|
23
|
+
/** That renderer's frame serial at the last draw. */
|
|
24
|
+
frame: number;
|
|
25
|
+
readonly model: Float32Array;
|
|
26
|
+
readonly previousModel: Float32Array;
|
|
27
|
+
/**
|
|
28
|
+
* This frame's palette, exactly `paletteLength` floats long once a skinned draw has set it.
|
|
29
|
+
* Swapped with the previous one each frame and reallocated only when the rig changes.
|
|
30
|
+
*/
|
|
31
|
+
palette: Float32Array;
|
|
32
|
+
previousPalette: Float32Array;
|
|
33
|
+
paletteLength: number;
|
|
34
|
+
previousPaletteLength: number;
|
|
35
|
+
}
|
|
36
|
+
/** Nothing to reproject from: first draw, a missed frame, another renderer, or a changed rig. */
|
|
37
|
+
export declare const MOVER_NONE = 0;
|
|
38
|
+
/** Drawn on the frame before by this renderer: `previousModel` and `previousPalette` are real. */
|
|
39
|
+
export declare const MOVER_MOTION = 1;
|
|
40
|
+
/** Drawn already this frame. The second draw is a different object and the caller's error. */
|
|
41
|
+
export declare const MOVER_TWICE = 2;
|
|
42
|
+
export type MoverVerdict = typeof MOVER_NONE | typeof MOVER_MOTION | typeof MOVER_TWICE;
|
|
43
|
+
/** A number no other renderer in this page holds, so a mover can tell renderers apart. */
|
|
44
|
+
export declare function claimRendererId(): number;
|
|
45
|
+
export declare function createMover(): Mover;
|
|
46
|
+
export declare function isMover(value: unknown): value is Mover;
|
|
47
|
+
/**
|
|
48
|
+
* Advance a mover to this draw, and say whether last frame's state is usable.
|
|
49
|
+
*
|
|
50
|
+
* **A second draw in one frame changes nothing**, so the first draw's motion stands; the caller
|
|
51
|
+
* warns. Otherwise what the mover held becomes its previous and this draw becomes its current —
|
|
52
|
+
* the palettes by swapping two arrays, so a steady rig allocates nothing after its first frame.
|
|
53
|
+
*
|
|
54
|
+
* **A changed joint count is no motion**: skinning a vertex with a palette of a different rig is
|
|
55
|
+
* not an old pose of this one.
|
|
56
|
+
*/
|
|
57
|
+
export declare function advanceMover(mover: Mover, renderer: number, frame: number, model: ArrayLike<number>, palette: Float32Array | null): MoverVerdict;
|
|
@@ -0,0 +1,86 @@
|
|
|
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
|
+
export const MOVER_KIND = 'drift.mover';
|
|
19
|
+
/** Nothing to reproject from: first draw, a missed frame, another renderer, or a changed rig. */
|
|
20
|
+
export const MOVER_NONE = 0;
|
|
21
|
+
/** Drawn on the frame before by this renderer: `previousModel` and `previousPalette` are real. */
|
|
22
|
+
export const MOVER_MOTION = 1;
|
|
23
|
+
/** Drawn already this frame. The second draw is a different object and the caller's error. */
|
|
24
|
+
export const MOVER_TWICE = 2;
|
|
25
|
+
let nextRendererId = 1;
|
|
26
|
+
/** A number no other renderer in this page holds, so a mover can tell renderers apart. */
|
|
27
|
+
export function claimRendererId() {
|
|
28
|
+
const id = nextRendererId;
|
|
29
|
+
nextRendererId += 1;
|
|
30
|
+
return id;
|
|
31
|
+
}
|
|
32
|
+
export function createMover() {
|
|
33
|
+
return {
|
|
34
|
+
kind: MOVER_KIND,
|
|
35
|
+
renderer: -1,
|
|
36
|
+
frame: -2,
|
|
37
|
+
model: new Float32Array(16),
|
|
38
|
+
previousModel: new Float32Array(16),
|
|
39
|
+
palette: new Float32Array(0),
|
|
40
|
+
previousPalette: new Float32Array(0),
|
|
41
|
+
paletteLength: 0,
|
|
42
|
+
previousPaletteLength: 0,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export function isMover(value) {
|
|
46
|
+
return (typeof value === 'object' && value !== null && value.kind === MOVER_KIND);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Advance a mover to this draw, and say whether last frame's state is usable.
|
|
50
|
+
*
|
|
51
|
+
* **A second draw in one frame changes nothing**, so the first draw's motion stands; the caller
|
|
52
|
+
* warns. Otherwise what the mover held becomes its previous and this draw becomes its current —
|
|
53
|
+
* the palettes by swapping two arrays, so a steady rig allocates nothing after its first frame.
|
|
54
|
+
*
|
|
55
|
+
* **A changed joint count is no motion**: skinning a vertex with a palette of a different rig is
|
|
56
|
+
* not an old pose of this one.
|
|
57
|
+
*/
|
|
58
|
+
export function advanceMover(mover, renderer, frame, model, palette) {
|
|
59
|
+
if (mover.renderer === renderer && mover.frame === frame)
|
|
60
|
+
return MOVER_TWICE;
|
|
61
|
+
const drawnLast = mover.renderer === renderer && mover.frame === frame - 1;
|
|
62
|
+
mover.previousModel.set(mover.model);
|
|
63
|
+
const spare = mover.previousPalette;
|
|
64
|
+
mover.previousPalette = mover.palette;
|
|
65
|
+
mover.palette = spare;
|
|
66
|
+
mover.previousPaletteLength = mover.paletteLength;
|
|
67
|
+
mover.model.set(model);
|
|
68
|
+
if (palette === null) {
|
|
69
|
+
mover.paletteLength = 0;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
/* Exactly the rig's length, because the array is uploaded as it stands; reallocated only when
|
|
73
|
+
the rig changes, which a steady character never does. */
|
|
74
|
+
if (mover.palette.length !== palette.length)
|
|
75
|
+
mover.palette = new Float32Array(palette.length);
|
|
76
|
+
mover.palette.set(palette);
|
|
77
|
+
mover.paletteLength = palette.length;
|
|
78
|
+
}
|
|
79
|
+
mover.renderer = renderer;
|
|
80
|
+
mover.frame = frame;
|
|
81
|
+
if (!drawnLast)
|
|
82
|
+
return MOVER_NONE;
|
|
83
|
+
if (mover.paletteLength !== mover.previousPaletteLength)
|
|
84
|
+
return MOVER_NONE;
|
|
85
|
+
return MOVER_MOTION;
|
|
86
|
+
}
|
|
@@ -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,7 @@
|
|
|
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.js';
|
|
36
|
-
import { disocclusionWeight, normalFromPositions, worldPositionFromDepth } from './disocclusion.js';
|
|
38
|
+
import { disocclusionWeight, normalFromPositions, planarity, surfaceSlope, worldPositionFromDepth, } from './disocclusion.js';
|
|
37
39
|
import { sampleBilinear, sampleHistoryBicubic5 } from './reproject.js';
|
|
38
40
|
/**
|
|
39
41
|
* A tenth of each frame kept, as the temporal resolve keeps; a box of a standard deviation and a
|
|
@@ -131,6 +133,39 @@ function worldAt(depths, inverse, jitter, width, height, x, y, out) {
|
|
|
131
133
|
m[15];
|
|
132
134
|
return 1 / w;
|
|
133
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* A texel's view depth at its unjittered uv, or 0 — `worldAt` without the position, which is all a
|
|
138
|
+
* search over depths needs: one row of the inverse rather than four. Zero off the picture, where
|
|
139
|
+
* `worldAt` would clamp, because a neighbour that is not there is not a step.
|
|
140
|
+
*/
|
|
141
|
+
function viewDepthAt(depths, inverse, jitter, width, height, x, y) {
|
|
142
|
+
if (x < 0 || y < 0 || x >= width || y >= height)
|
|
143
|
+
return 0;
|
|
144
|
+
const u = (x + 0.5 - jitter[0]) / width;
|
|
145
|
+
const v = (y + 0.5 - jitter[1]) / height;
|
|
146
|
+
const m = inverse;
|
|
147
|
+
const w = m[3] * (u * 2 - 1) +
|
|
148
|
+
m[7] * (v * 2 - 1) +
|
|
149
|
+
m[11] * depths[y * width + x] +
|
|
150
|
+
m[15];
|
|
151
|
+
return Math.abs(w) > 1e-12 ? 1 / w : 0;
|
|
152
|
+
}
|
|
153
|
+
/** What a texel's four neighbours say about its surface: see `planarity` and `surfaceSlope`. */
|
|
154
|
+
const SURFACE = { planar: 1, slope: 0 };
|
|
155
|
+
function surfaceAt(depths, inverse, jitter, width, height, x, y, centre) {
|
|
156
|
+
let planar = 1;
|
|
157
|
+
let slope = 0;
|
|
158
|
+
for (let axis = 0; axis < 2; axis += 1) {
|
|
159
|
+
const ax = 1 - axis;
|
|
160
|
+
const ay = axis;
|
|
161
|
+
const after = viewDepthAt(depths, inverse, jitter, width, height, x + ax, y + ay);
|
|
162
|
+
const before = viewDepthAt(depths, inverse, jitter, width, height, x - ax, y - ay);
|
|
163
|
+
planar *= planarity(before, centre, after);
|
|
164
|
+
slope = Math.max(slope, surfaceSlope(before, centre, after));
|
|
165
|
+
}
|
|
166
|
+
SURFACE.planar = planar;
|
|
167
|
+
SURFACE.slope = slope;
|
|
168
|
+
}
|
|
134
169
|
/** The world normal at a texel, from its depth and its right and upper neighbours'. */
|
|
135
170
|
function normalAt(depths, inverse, jitter, eye, width, height, x, y, out) {
|
|
136
171
|
if (worldAt(depths, inverse, jitter, width, height, x, y, WORLD) <= 0)
|
|
@@ -190,18 +225,66 @@ function historyWeight(frame, u, v, dx, dy) {
|
|
|
190
225
|
if (!(pu >= 0 && pu <= 1 && pv >= 0 && pv <= 1))
|
|
191
226
|
return 0;
|
|
192
227
|
sampleHistoryBicubic5(frame.history, pu * frame.outputWidth - 0.5, pv * frame.outputHeight - 0.5, frame.outputWidth, frame.outputHeight, 4, HISTORY);
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
228
|
+
/*
|
|
229
|
+
* **Last frame's depth is searched for this surface, around where the dilated texel was.** The
|
|
230
|
+
* surface being judged is the dilated texel's, which beside an edge is a neighbour's, so judging
|
|
231
|
+
* it by the texel under the pixel compares two surfaces: the side of every silhouette away from the
|
|
232
|
+
* eye dropped its history on every frame of a still picture. And a new jitter samples a surface
|
|
233
|
+
* somewhere else, so the nearest of last frame's nine is taken rather than the one the rounding
|
|
234
|
+
* picks. What it gives up is a disocclusion narrower than a texel, which keeps a history that the
|
|
235
|
+
* neighbourhood clip then bounds.
|
|
236
|
+
*/
|
|
237
|
+
const su = (dx + 0.5 - frame.jitter[0]) / rw + mu;
|
|
238
|
+
const sv = (dy + 0.5 - frame.jitter[1]) / rh + mv;
|
|
239
|
+
const cx = clampTexel(Math.round(su * rw + frame.previousJitter[0] - 0.5), rw);
|
|
240
|
+
const cy = clampTexel(Math.round(sv * rh + frame.previousJitter[1] - 0.5), rh);
|
|
241
|
+
/*
|
|
242
|
+
* The first texel looked at answers alone when it agrees within the tolerance, which it does
|
|
243
|
+
* everywhere but at an edge: the depth test trusts it fully whichever texel would have won, so
|
|
244
|
+
* only an edge pays for the other eight.
|
|
245
|
+
*/
|
|
246
|
+
let held = viewDepthAt(frame.previousDepth, frame.previousInverseViewProj, frame.previousJitter, rw, rh, cx, cy);
|
|
247
|
+
let px = cx;
|
|
248
|
+
let py = cy;
|
|
249
|
+
if (!(held > 0 && Math.abs(held - expected) <= frame.disocclusion.depthTolerance * expected)) {
|
|
250
|
+
held = 0;
|
|
251
|
+
let nearest = Number.POSITIVE_INFINITY;
|
|
252
|
+
for (let j = -1; j <= 1; j += 1) {
|
|
253
|
+
for (let i = -1; i <= 1; i += 1) {
|
|
254
|
+
const tx = clampTexel(cx + i, rw);
|
|
255
|
+
const ty = clampTexel(cy + j, rh);
|
|
256
|
+
const depth = viewDepthAt(frame.previousDepth, frame.previousInverseViewProj, frame.previousJitter, rw, rh, tx, ty);
|
|
257
|
+
if (!(depth > 0))
|
|
258
|
+
continue;
|
|
259
|
+
const off = Math.abs(depth - expected);
|
|
260
|
+
if (off < nearest) {
|
|
261
|
+
nearest = off;
|
|
262
|
+
held = depth;
|
|
263
|
+
px = tx;
|
|
264
|
+
py = ty;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
const here = viewDepthAt(frame.depth, frame.inverseViewProj, frame.jitter, rw, rh, dx, dy);
|
|
270
|
+
surfaceAt(frame.depth, frame.inverseViewProj, frame.jitter, rw, rh, dx, dy, here);
|
|
271
|
+
const slope = SURFACE.slope;
|
|
272
|
+
let planar = SURFACE.planar;
|
|
273
|
+
if (planar > 0) {
|
|
274
|
+
surfaceAt(frame.previousDepth, frame.previousInverseViewProj, frame.previousJitter, rw, rh, px, py, held);
|
|
275
|
+
planar *= SURFACE.planar;
|
|
276
|
+
}
|
|
277
|
+
/* A normal has the say its planarity gives it; at an edge or a sliver, none, and none is computed. */
|
|
196
278
|
let normalDot = 1;
|
|
197
|
-
if (
|
|
279
|
+
if (planar > 0 &&
|
|
280
|
+
normalAt(frame.depth, frame.inverseViewProj, frame.jitter, frame.eye, rw, rh, dx, dy, NORMAL) &&
|
|
198
281
|
normalAt(frame.previousDepth, frame.previousInverseViewProj, frame.previousJitter, frame.previousEye, rw, rh, px, py, PREVIOUS_NORMAL)) {
|
|
199
|
-
|
|
200
|
-
NORMAL[
|
|
201
|
-
|
|
202
|
-
|
|
282
|
+
const cosine = NORMAL[0] * PREVIOUS_NORMAL[0] +
|
|
283
|
+
NORMAL[1] * PREVIOUS_NORMAL[1] +
|
|
284
|
+
NORMAL[2] * PREVIOUS_NORMAL[2];
|
|
285
|
+
normalDot = 1 - planar * (1 - cosine);
|
|
203
286
|
}
|
|
204
|
-
const trust = disocclusionWeight(expected, held, Math.hypot(mu, mv), normalDot, frame.disocclusion);
|
|
287
|
+
const trust = disocclusionWeight(expected, held, Math.hypot(mu, mv), normalDot, frame.disocclusion, slope);
|
|
205
288
|
return HISTORY[3] * trust;
|
|
206
289
|
}
|
|
207
290
|
/**
|
|
@@ -29,6 +29,8 @@ export type OutputTransform = 'none' | 'srgb' | 'aces';
|
|
|
29
29
|
export type ShadowFilterTaps = 4 | 8 | 12;
|
|
30
30
|
export type DirectionalShadowDepthLayers = 1 | 2;
|
|
31
31
|
export type WaterReflectionFilterTaps = 1 | 5 | 9;
|
|
32
|
+
/** How light through glass is coloured: see `RenderQuality.glassShadows`. */
|
|
33
|
+
export type GlassShadows = 'full' | 'half' | 'off';
|
|
32
34
|
export interface RenderQuality {
|
|
33
35
|
/**
|
|
34
36
|
* The transfer curve applied at the end of every shaded pass.
|
|
@@ -141,6 +143,25 @@ export interface RenderQuality {
|
|
|
141
143
|
readonly lightVolumeSamples: number;
|
|
142
144
|
/** Independently composited static sun depths; two preserves common overlaps. */
|
|
143
145
|
readonly directionalShadowDepthLayers: DirectionalShadowDepthLayers;
|
|
146
|
+
/**
|
|
147
|
+
* Whether light through glass takes the glass's colour, and at what size.
|
|
148
|
+
*
|
|
149
|
+
* **`'full'`, the default**: every shadow a glass caster reaches gains a layer holding the depth of
|
|
150
|
+
* the nearest pane and a tint layer holding what the panes let through, at the size of the depth
|
|
151
|
+
* map beside it — 8 bytes a texel, so 32 MB for a 2048 sun map and 8 MB for a 1024 lamp layer.
|
|
152
|
+
* **`'half'`** halves the tint layers only (the glass depth must match its array): 20 MB and 5 MB,
|
|
153
|
+
* and a coloured patch softer than an opaque shadow's edge. **`'off'`** is glass before this
|
|
154
|
+
* existed: a pane casts nothing at all.
|
|
155
|
+
*
|
|
156
|
+
* Nothing is allocated until a glass caster first reaches a shadow, so a scene with no glass pays
|
|
157
|
+
* no memory and no pass whatever this says. **The lit shader is the exception**: `'full'` and
|
|
158
|
+
* `'half'` compile the glass lookup into it, and a lookup that finds no glass and returns at its
|
|
159
|
+
* first line still costs the lit pass its registers — measured on the courtyard at night, 0.27 ms
|
|
160
|
+
* at 720p on WebGPU, 0.42 ms on WebGL2 and 1.2 ms at 4K. `'off'` compiles none of it, so a world
|
|
161
|
+
* that never casts through glass is that much faster for saying so. What would make `'full'` wrong
|
|
162
|
+
* is a phone budget with a lot of glass; that is what `'half'` is for. See `glassShadow.ts`.
|
|
163
|
+
*/
|
|
164
|
+
readonly glassShadows: GlassShadows;
|
|
144
165
|
/** Horizontal world-space reach over which a directional shadow dissolves. */
|
|
145
166
|
readonly directionalShadowMaxDistance: number;
|
|
146
167
|
/** Maximum horizontal projection per vertical metre before low-angle shadows vanish. */
|
|
@@ -548,6 +569,20 @@ export interface RenderQuality {
|
|
|
548
569
|
* the render is a quarter of the output and no reconstruction holds an edge through that. A
|
|
549
570
|
* negative number is off, not a clamp to the bottom: it cannot mean "a little".
|
|
550
571
|
*
|
|
572
|
+
* **What it needs from you.** A `createMover()` per moving object, passed with that object's
|
|
573
|
+
* draws where `drawMesh` takes last frame's matrix — the renderer keeps last frame's model and,
|
|
574
|
+
* for a skinned draw, last frame's pose. Instanced batches and meshes rewritten with
|
|
575
|
+
* `updateMesh` need nothing: the batch and the mesh are their own identity. And `cameraCut()` at
|
|
576
|
+
* every cut, because nothing detects one: without it a cut blends the previous shot into the
|
|
577
|
+
* next. Blended draws — translucent meshes, particles, plumes, film, bolts, lines, world text —
|
|
578
|
+
* are drawn after the upscale at the output size and need nothing either; light volumes stay in
|
|
579
|
+
* the reconstructed picture, being a march that keeps its history.
|
|
580
|
+
*
|
|
581
|
+
* **What it cannot give back** is the resolution it draws at: an edge that never moves is softer
|
|
582
|
+
* than a native frame's at any ratio, because two thirds of the pixels at 1.5 are a guess made
|
|
583
|
+
* sharp. Measured on `demo/dev/ghost.html` against a native frame at four samples, still edges
|
|
584
|
+
* err by about 8 levels where a native frame at one sample errs by 3.
|
|
585
|
+
*
|
|
551
586
|
* `?recon=` is how it gets A/B'd in one reload.
|
|
552
587
|
*/
|
|
553
588
|
readonly reconstruction: number;
|
|
@@ -56,6 +56,7 @@ export const DEFAULT_RENDER_QUALITY = Object.freeze({
|
|
|
56
56
|
shadowFilterTaps: MAX_SHADOW_FILTER_TAPS,
|
|
57
57
|
lightVolumeSamples: 32,
|
|
58
58
|
directionalShadowDepthLayers: 2,
|
|
59
|
+
glassShadows: 'full',
|
|
59
60
|
directionalShadowMaxDistance: 6,
|
|
60
61
|
directionalShadowMaxSlope: 3,
|
|
61
62
|
pointShadowRebakeDistance: 0.001,
|
|
@@ -205,6 +206,7 @@ export function resolveRenderQuality(options = {}) {
|
|
|
205
206
|
shadowFilterTaps: options.shadowFilterTaps ?? DEFAULT_RENDER_QUALITY.shadowFilterTaps,
|
|
206
207
|
lightVolumeSamples: options.lightVolumeSamples ?? DEFAULT_RENDER_QUALITY.lightVolumeSamples,
|
|
207
208
|
directionalShadowDepthLayers: options.directionalShadowDepthLayers ?? DEFAULT_RENDER_QUALITY.directionalShadowDepthLayers,
|
|
209
|
+
glassShadows: options.glassShadows ?? DEFAULT_RENDER_QUALITY.glassShadows,
|
|
208
210
|
directionalShadowMaxDistance: options.directionalShadowMaxDistance ?? DEFAULT_RENDER_QUALITY.directionalShadowMaxDistance,
|
|
209
211
|
directionalShadowMaxSlope: options.directionalShadowMaxSlope ?? DEFAULT_RENDER_QUALITY.directionalShadowMaxSlope,
|
|
210
212
|
pointShadowRebakeDistance: options.pointShadowRebakeDistance ?? DEFAULT_RENDER_QUALITY.pointShadowRebakeDistance,
|
|
@@ -286,6 +288,11 @@ export function resolveRenderQuality(options = {}) {
|
|
|
286
288
|
if (quality.directionalShadowDepthLayers !== 1 && quality.directionalShadowDepthLayers !== 2) {
|
|
287
289
|
throw new Error(`RenderQuality.directionalShadowDepthLayers must be 1 or 2, got ${quality.directionalShadowDepthLayers}`);
|
|
288
290
|
}
|
|
291
|
+
if (quality.glassShadows !== 'full' &&
|
|
292
|
+
quality.glassShadows !== 'half' &&
|
|
293
|
+
quality.glassShadows !== 'off') {
|
|
294
|
+
throw new Error(`RenderQuality.glassShadows must be 'full', 'half' or 'off', got ${String(quality.glassShadows)}`);
|
|
295
|
+
}
|
|
289
296
|
if (quality.plumeNoiseOctaves > 3) {
|
|
290
297
|
throw new Error(`RenderQuality.plumeNoiseOctaves must be <= 3, got ${quality.plumeNoiseOctaves}`);
|
|
291
298
|
}
|
|
@@ -86,6 +86,18 @@ export function recordingGl(options = {}) {
|
|
|
86
86
|
COLOR_BUFFER_BIT: 0x00004000,
|
|
87
87
|
/* Named so a test can tell a mesh that declared itself deforming from one that did not. */
|
|
88
88
|
ARRAY_BUFFER: 0x8892,
|
|
89
|
+
/* Named so a blend a pass sets can be read back as numbers: glass multiplies what it lets
|
|
90
|
+
through, and a test that cannot tell DST_COLOR from a recorder cannot tell it multiplies. */
|
|
91
|
+
ZERO: 0,
|
|
92
|
+
DST_ALPHA: 0x0304,
|
|
93
|
+
DST_COLOR: 0x0306,
|
|
94
|
+
FUNC_ADD: 0x8006,
|
|
95
|
+
COLOR: 0x1800,
|
|
96
|
+
DEPTH: 0x1801,
|
|
97
|
+
/* Named so a copy can be read as from-and-to: a blit's two framebuffers are the whole claim. */
|
|
98
|
+
READ_FRAMEBUFFER: 0x8ca8,
|
|
99
|
+
DRAW_FRAMEBUFFER: 0x8ca9,
|
|
100
|
+
BLEND: 0x0be2,
|
|
89
101
|
STATIC_DRAW: 0x88e4,
|
|
90
102
|
DYNAMIC_DRAW: 0x88e8,
|
|
91
103
|
};
|
|
@@ -72,6 +72,8 @@ export declare class SceneTarget {
|
|
|
72
72
|
private colorCopyFramebuffer;
|
|
73
73
|
/** Whether this frame has filled it. One snapshot serves every refracting draw in a frame. */
|
|
74
74
|
private colorCopyFilled;
|
|
75
|
+
/** Whether this frame's copy has its mip chain, which frosted glass reads. See `snapshotColor`. */
|
|
76
|
+
private colorCopyMips;
|
|
75
77
|
/**
|
|
76
78
|
* The colour grade, as a 3D texture on unit 4, and the table it was built from.
|
|
77
79
|
*
|
|
@@ -221,7 +223,11 @@ export declare class SceneTarget {
|
|
|
221
223
|
* Null where the copy could not be made, and a caller must read that as "do not refract" rather
|
|
222
224
|
* than as a black texture — refracting against black paints every pane the colour of a hole.
|
|
223
225
|
*/
|
|
224
|
-
snapshotColor(afresh?: boolean
|
|
226
|
+
snapshotColor(afresh?: boolean,
|
|
227
|
+
/** Fill the copy's mip chain as well, once a frame: what frosted glass reads. */
|
|
228
|
+
mips?: boolean): WebGLTexture | null;
|
|
229
|
+
/** The copy's mip chain from its level 0, once per copy. */
|
|
230
|
+
private fillColorCopyMips;
|
|
225
231
|
snapshotDepth(
|
|
226
232
|
/**
|
|
227
233
|
* Take the copy again even though one has already been taken this frame.
|
|
@@ -303,6 +309,8 @@ export declare class SceneTarget {
|
|
|
303
309
|
}, veilColor?: Vec3, veilAlpha?: number, temporal?: {
|
|
304
310
|
readonly reprojection: Float32Array;
|
|
305
311
|
readonly blend: number;
|
|
312
|
+
readonly periodStart: boolean;
|
|
313
|
+
readonly depthToViewZ: Float32Array;
|
|
306
314
|
}): void;
|
|
307
315
|
dispose(): void;
|
|
308
316
|
}
|