@driftengine/core 4.4.2 → 4.6.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 +97 -0
- package/README.md +1 -1
- package/dist/geometry/boxCsg.d.ts +28 -0
- package/dist/geometry/boxCsg.js +139 -0
- package/dist/geometry/csg.d.ts +36 -0
- package/dist/geometry/csg.js +329 -0
- package/dist/geometry/meshBuilder.js +13 -0
- package/dist/geometry/meshConcat.js +1 -0
- package/dist/geometry/pixelFont.js +2 -0
- package/dist/geometry/solid.d.ts +61 -0
- package/dist/geometry/solid.js +152 -0
- package/dist/geometry/solidFlat.d.ts +35 -0
- package/dist/geometry/solidFlat.js +428 -0
- package/dist/geometry/solidHarness.d.ts +19 -0
- package/dist/geometry/solidHarness.js +66 -0
- package/dist/geometry/solidNormals.d.ts +17 -0
- package/dist/geometry/solidNormals.js +86 -0
- package/dist/geometry/solidRound.d.ts +41 -0
- package/dist/geometry/solidRound.js +323 -0
- package/dist/geometry/solidWriter.d.ts +16 -0
- package/dist/geometry/solidWriter.js +42 -0
- package/dist/index.d.ts +28 -3
- package/dist/index.js +20 -1
- package/dist/render/backend/api.d.ts +34 -14
- package/dist/render/backend/drawFog.d.ts +26 -0
- package/dist/render/backend/drawFog.js +30 -0
- package/dist/render/backend/materialChanges.d.ts +2 -1
- package/dist/render/backend/materialChanges.js +6 -1
- package/dist/render/backend/webgl2/glassTintPrograms.d.ts +13 -0
- package/dist/render/backend/webgl2/glassTintPrograms.js +38 -0
- package/dist/render/backend/webgl2/instanced.d.ts +24 -5
- package/dist/render/backend/webgl2/instanced.js +72 -15
- package/dist/render/backend/webgl2/renderer.d.ts +201 -16
- package/dist/render/backend/webgl2/renderer.js +596 -224
- package/dist/render/backend/webgpu/buffers.d.ts +29 -1
- package/dist/render/backend/webgpu/buffers.js +61 -4
- package/dist/render/backend/webgpu/depthPass.d.ts +1 -1
- package/dist/render/backend/webgpu/depthPass.js +7 -2
- package/dist/render/backend/webgpu/drawCommand.d.ts +5 -0
- package/dist/render/backend/webgpu/drawCommand.js +1 -0
- package/dist/render/backend/webgpu/flatPass.d.ts +6 -1
- package/dist/render/backend/webgpu/flatPass.js +57 -9
- package/dist/render/backend/webgpu/glassTintPass.d.ts +28 -0
- package/dist/render/backend/webgpu/glassTintPass.js +206 -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/instanceCullPass.d.ts +34 -0
- package/dist/render/backend/webgpu/instanceCullPass.js +144 -0
- package/dist/render/backend/webgpu/instanced.d.ts +23 -2
- package/dist/render/backend/webgpu/instanced.js +41 -4
- package/dist/render/backend/webgpu/latePass.d.ts +30 -0
- package/dist/render/backend/webgpu/latePass.js +140 -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 +3 -13
- package/dist/render/backend/webgpu/renderer.d.ts +271 -64
- package/dist/render/backend/webgpu/renderer.js +1395 -580
- package/dist/render/backend/webgpu/scatterPass.js +2 -1
- package/dist/render/backend/webgpu/sdfTextPass.js +6 -1
- package/dist/render/backend/webgpu/shaders/instanceCull.wgsl.d.ts +18 -0
- package/dist/render/backend/webgpu/shaders/instanceCull.wgsl.js +59 -0
- 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 +22 -3
- package/dist/render/backend/webgpu/surfaceTexturePass.js +78 -39
- package/dist/render/driftLight/bake.d.ts +7 -0
- package/dist/render/driftLight/bake.js +61 -56
- package/dist/render/driftLight/denseField.d.ts +46 -0
- package/dist/render/driftLight/denseField.js +109 -0
- package/dist/render/driftLight/lightField.d.ts +10 -5
- package/dist/render/driftLight/lightField.js +25 -19
- package/dist/render/driftLight/presence.d.ts +50 -0
- package/dist/render/driftLight/presence.js +43 -0
- package/dist/render/driftLight/uniforms.d.ts +3 -3
- package/dist/render/driftLight/uniforms.js +5 -6
- package/dist/render/driftLight/worldLightField.d.ts +41 -0
- package/dist/render/driftLight/worldLightField.js +66 -0
- package/dist/render/fixtureAtlas.d.ts +20 -0
- package/dist/render/fixtureAtlas.js +49 -0
- 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/instanceCull.d.ts +63 -0
- package/dist/render/instanceCull.js +160 -0
- package/dist/render/instances.d.ts +15 -0
- package/dist/render/lightBudget.d.ts +36 -35
- package/dist/render/lightBudget.js +38 -37
- package/dist/render/lightGrid.d.ts +42 -0
- package/dist/render/lightGrid.js +87 -0
- package/dist/render/mesh.d.ts +18 -24
- package/dist/render/mesh.js +51 -68
- package/dist/render/occlusion.d.ts +10 -0
- package/dist/render/occlusion.js +36 -7
- package/dist/render/pointGlassTint.d.ts +59 -0
- package/dist/render/pointGlassTint.js +198 -0
- package/dist/render/pointLightSelection.d.ts +7 -0
- package/dist/render/pointLightSelection.js +29 -4
- package/dist/render/pointShadowArray.d.ts +56 -3
- package/dist/render/pointShadowArray.js +127 -16
- package/dist/render/pointShadowMap.d.ts +23 -1
- package/dist/render/pointShadowMap.js +27 -1
- package/dist/render/rainField.d.ts +13 -0
- package/dist/render/rainField.js +27 -5
- package/dist/render/recon/changeFrames.d.ts +15 -0
- package/dist/render/recon/changeFrames.js +21 -0
- package/dist/render/recon/lateDepth.d.ts +11 -0
- package/dist/render/recon/lateDepth.js +70 -0
- 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.d.ts +34 -4
- package/dist/render/recon/resolve.js +161 -20
- package/dist/render/renderQuality.d.ts +35 -0
- package/dist/render/renderQuality.js +7 -0
- package/dist/render/rendererHarness.js +16 -0
- package/dist/render/sceneTarget.d.ts +9 -1
- package/dist/render/sceneTarget.js +60 -12
- package/dist/render/shaders/depth.d.ts +16 -1
- package/dist/render/shaders/depth.js +94 -15
- package/dist/render/shaders/flat/directionalShadow.js +91 -6
- package/dist/render/shaders/flat/driftLight.d.ts +1 -1
- package/dist/render/shaders/flat/driftLight.js +32 -7
- 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 +9 -3
- 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 +190 -38
- 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 +96 -26
- package/dist/render/shaders/flat/surfaceEffects.d.ts +29 -0
- package/dist/render/shaders/flat/surfaceEffects.js +152 -0
- package/dist/render/shaders/generated/depth.wgsl.d.ts +230 -7
- package/dist/render/shaders/generated/depth.wgsl.js +230 -7
- package/dist/render/shaders/generated/flat.wgsl.d.ts +1644 -1596
- package/dist/render/shaders/generated/flat.wgsl.js +1683 -1621
- 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 +21 -17
- 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/rush.wgsl.d.ts +21 -16
- package/dist/render/shaders/generated/rush.wgsl.js +21 -16
- package/dist/render/shaders/generated/sdfText.wgsl.d.ts +2 -2
- package/dist/render/shaders/generated/sdfText.wgsl.js +2 -2
- package/dist/render/shaders/generated/text.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/text.wgsl.js +1 -1
- package/dist/render/shaders/gi/driftLight.wgsl.d.ts +1 -1
- package/dist/render/shaders/gi/driftLight.wgsl.js +27 -4
- 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 +69 -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 +8 -7
- package/dist/render/shaders/recon/resolve.wgsl.js +161 -27
- package/dist/render/shaders/rush.js +11 -1
- package/dist/render/shaders/sdfText.d.ts +1 -1
- package/dist/render/shaders/sdfText.js +3 -2
- package/dist/render/shaders/text.d.ts +1 -1
- package/dist/render/shaders/text.js +18 -9
- 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/surfaceEffects.d.ts +83 -0
- package/dist/render/surfaceEffects.js +75 -0
- package/dist/render/surfaceScene.d.ts +8 -0
- package/dist/render/surfaceScene.js +8 -0
- package/dist/render/surfaceTexture.d.ts +18 -2
- package/dist/render/surfaceTexture.js +68 -15
- package/dist/render/temporalAa.d.ts +25 -6
- package/dist/render/temporalAa.js +45 -15
- package/dist/render/textureSource.d.ts +30 -0
- package/dist/render/textureSource.js +53 -0
- package/dist/render/translucentQueue.d.ts +6 -0
- package/dist/render/translucentQueue.js +15 -1
- package/dist/render/vertexDefaults.js +2 -0
- package/dist/world/hlod.d.ts +104 -0
- package/dist/world/hlod.js +214 -0
- package/package.json +6 -6
- package/src/geometry/boxCsg.ts +186 -0
- package/src/geometry/csg.ts +384 -0
- package/src/geometry/meshBuilder.ts +15 -0
- package/src/geometry/meshConcat.ts +1 -0
- package/src/geometry/pixelFont.ts +2 -0
- package/src/geometry/solid.ts +189 -0
- package/src/geometry/solidFlat.ts +522 -0
- package/src/geometry/solidHarness.ts +83 -0
- package/src/geometry/solidNormals.ts +103 -0
- package/src/geometry/solidRound.ts +443 -0
- package/src/geometry/solidWriter.ts +58 -0
- package/src/index.ts +61 -1
- package/src/render/backend/api.ts +36 -11
- package/src/render/backend/drawFog.ts +31 -0
- package/src/render/backend/materialChanges.ts +9 -2
- package/src/render/backend/webgl2/glassTintPrograms.ts +58 -0
- package/src/render/backend/webgl2/instanced.ts +80 -14
- package/src/render/backend/webgl2/renderer.ts +700 -250
- package/src/render/backend/webgpu/buffers.ts +92 -5
- package/src/render/backend/webgpu/depthPass.ts +7 -2
- package/src/render/backend/webgpu/drawCommand.ts +6 -0
- package/src/render/backend/webgpu/flatPass.ts +62 -11
- package/src/render/backend/webgpu/glassTintPass.ts +245 -0
- package/src/render/backend/webgpu/gpuDrivenPass.ts +349 -5
- package/src/render/backend/webgpu/instanceCullPass.ts +165 -0
- package/src/render/backend/webgpu/instanced.ts +42 -4
- package/src/render/backend/webgpu/latePass.ts +161 -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/renderer.ts +1671 -670
- package/src/render/backend/webgpu/scatterPass.ts +2 -1
- package/src/render/backend/webgpu/sdfTextPass.ts +6 -1
- package/src/render/backend/webgpu/shaders/instanceCull.wgsl.ts +61 -0
- 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 +91 -51
- package/src/render/driftLight/bake.ts +85 -62
- package/src/render/driftLight/denseField.ts +149 -0
- package/src/render/driftLight/lightField.ts +33 -18
- package/src/render/driftLight/presence.ts +69 -0
- package/src/render/driftLight/uniforms.ts +8 -9
- package/src/render/driftLight/worldLightField.ts +83 -0
- package/src/render/fixtureAtlas.ts +63 -0
- 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/instanceCull.ts +205 -0
- package/src/render/instances.ts +16 -0
- package/src/render/lightBudget.ts +41 -38
- package/src/render/lightGrid.ts +130 -0
- package/src/render/mesh.ts +59 -72
- package/src/render/occlusion.ts +51 -7
- package/src/render/pointGlassTint.ts +250 -0
- package/src/render/pointLightSelection.ts +72 -4
- package/src/render/pointShadowArray.ts +188 -22
- package/src/render/pointShadowMap.ts +41 -0
- package/src/render/rainField.ts +39 -5
- package/src/render/recon/changeFrames.ts +24 -0
- package/src/render/recon/lateDepth.ts +83 -0
- package/src/render/recon/lateRouting.ts +29 -0
- package/src/render/recon/mover.ts +118 -0
- package/src/render/recon/resolve.ts +206 -20
- package/src/render/renderQuality.ts +47 -0
- package/src/render/rendererHarness.ts +16 -0
- package/src/render/sceneTarget.ts +62 -11
- package/src/render/shaders/depth.ts +118 -15
- package/src/render/shaders/flat/directionalShadow.ts +97 -6
- package/src/render/shaders/flat/driftLight.ts +32 -7
- package/src/render/shaders/flat/glassTint.ts +25 -0
- package/src/render/shaders/flat/index.ts +22 -3
- package/src/render/shaders/flat/lampGlass.ts +185 -0
- package/src/render/shaders/flat/main.ts +190 -38
- package/src/render/shaders/flat/pointShadow.ts +2 -0
- package/src/render/shaders/flat/preamble.ts +96 -26
- package/src/render/shaders/flat/surfaceEffects.ts +152 -0
- package/src/render/shaders/generated/depth.wgsl.ts +237 -7
- package/src/render/shaders/generated/flat.wgsl.ts +1685 -1621
- package/src/render/shaders/generated/globalMedium.wgsl.ts +3 -13
- package/src/render/shaders/generated/lightVolume.wgsl.ts +23 -17
- package/src/render/shaders/generated/octahedralResolve.wgsl.ts +30 -0
- package/src/render/shaders/generated/rush.wgsl.ts +21 -16
- package/src/render/shaders/generated/sdfText.wgsl.ts +2 -2
- package/src/render/shaders/generated/text.wgsl.ts +1 -1
- package/src/render/shaders/gi/driftLight.wgsl.ts +27 -4
- 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 +72 -0
- package/src/render/shaders/recon/motion.wgsl.ts +84 -0
- package/src/render/shaders/recon/resolve.wgsl.ts +161 -27
- package/src/render/shaders/rush.ts +11 -1
- package/src/render/shaders/sdfText.ts +3 -2
- package/src/render/shaders/text.ts +18 -9
- package/src/render/shadowCasters.ts +28 -1
- package/src/render/shadowMap.ts +231 -37
- package/src/render/sunGlassTint.ts +86 -0
- package/src/render/surfaceEffects.ts +132 -0
- package/src/render/surfaceScene.ts +16 -0
- package/src/render/surfaceTexture.ts +110 -19
- package/src/render/temporalAa.ts +78 -14
- package/src/render/textureSource.ts +72 -0
- package/src/render/translucentQueue.ts +20 -1
- package/src/render/vertexDefaults.ts +2 -0
- package/src/world/hlod.ts +290 -0
|
@@ -10,6 +10,29 @@ void main() {
|
|
|
10
10
|
vec2 p = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));
|
|
11
11
|
gl_Position = vec4(p * 2.0 - 1.0, 0.0, 1.0);
|
|
12
12
|
}`;
|
|
13
|
+
/**
|
|
14
|
+
* Which cube face owns this octahedral texel, and where in that face it looks: `d`, `forward` and
|
|
15
|
+
* `faceUv`, with every texel another face owns discarded. **One text for both resolves**, depth
|
|
16
|
+
* and tint, because the tie-breaking has to agree with itself as well as with the CPU: a sliver
|
|
17
|
+
* of texels given to one face's depth and another face's colour is a tinted seam.
|
|
18
|
+
*/
|
|
19
|
+
const FACE_REGION = ` vec3 d = octDecode(gl_FragCoord.xy / uEdge);
|
|
20
|
+
|
|
21
|
+
/*
|
|
22
|
+
* Which face owns this texel. The tie-breaking is x, then y, then z, and it has to match the
|
|
23
|
+
* CPU's exactly or a sliver of texels along a boundary belongs to two faces or to none.
|
|
24
|
+
*/
|
|
25
|
+
vec3 a = abs(d);
|
|
26
|
+
int face;
|
|
27
|
+
if (a.x >= a.y && a.x >= a.z) face = d.x >= 0.0 ? 0 : 1;
|
|
28
|
+
else if (a.y >= a.z) face = d.y >= 0.0 ? 2 : 3;
|
|
29
|
+
else face = d.z >= 0.0 ? 4 : 5;
|
|
30
|
+
if (face != uFaceIndex) discard;
|
|
31
|
+
|
|
32
|
+
/* lookAt looks down -z, so a direction inside this face has a negative z here. */
|
|
33
|
+
vec3 v = uFaceRotation * d;
|
|
34
|
+
float forward = -v.z;
|
|
35
|
+
vec2 faceUv = (v.xy / forward) * 0.5 + 0.5;`;
|
|
13
36
|
/**
|
|
14
37
|
* One cube face into its region of an octahedral map, as linear radial distance.
|
|
15
38
|
*
|
|
@@ -44,23 +67,7 @@ uniform float uEdge;
|
|
|
44
67
|
${OCTAHEDRAL_GLSL}
|
|
45
68
|
|
|
46
69
|
void main() {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
/*
|
|
50
|
-
* Which face owns this texel. The tie-breaking is x, then y, then z, and it has to match the
|
|
51
|
-
* CPU's exactly or a sliver of texels along a boundary belongs to two faces or to none.
|
|
52
|
-
*/
|
|
53
|
-
vec3 a = abs(d);
|
|
54
|
-
int face;
|
|
55
|
-
if (a.x >= a.y && a.x >= a.z) face = d.x >= 0.0 ? 0 : 1;
|
|
56
|
-
else if (a.y >= a.z) face = d.y >= 0.0 ? 2 : 3;
|
|
57
|
-
else face = d.z >= 0.0 ? 4 : 5;
|
|
58
|
-
if (face != uFaceIndex) discard;
|
|
59
|
-
|
|
60
|
-
/* lookAt looks down -z, so a direction inside this face has a negative z here. */
|
|
61
|
-
vec3 v = uFaceRotation * d;
|
|
62
|
-
float forward = -v.z;
|
|
63
|
-
vec2 faceUv = (v.xy / forward) * 0.5 + 0.5;
|
|
70
|
+
${FACE_REGION}
|
|
64
71
|
|
|
65
72
|
/*
|
|
66
73
|
* textureLod at level zero rather than plain texture. The scratch carries one storage level and
|
|
@@ -79,3 +86,27 @@ void main() {
|
|
|
79
86
|
/* d is a unit vector, so forward is its cosine to the axis and this is the true radius. */
|
|
80
87
|
gl_FragDepth = clamp((faceLocalZ / forward) / uFar, 0.0, 1.0);
|
|
81
88
|
}`;
|
|
89
|
+
/**
|
|
90
|
+
* One face of a lamp's glass tint into its region of the tint layer: the depth resolve's region,
|
|
91
|
+
* and a colour copied rather than a depth decoded.
|
|
92
|
+
*
|
|
93
|
+
* **Nearest, at level zero**, like the depth beside it: the tint's outline has to land on the
|
|
94
|
+
* glass depth's texels, and a filtered copy would smear a pane's colour a texel past its edge.
|
|
95
|
+
*/
|
|
96
|
+
export const OCTAHEDRAL_RESOLVE_TINT_FRAG = `#version 300 es
|
|
97
|
+
precision highp float;
|
|
98
|
+
|
|
99
|
+
/** The face the bake just tinted, as colour. */
|
|
100
|
+
uniform highp sampler2D uFace;
|
|
101
|
+
uniform mat3 uFaceRotation;
|
|
102
|
+
uniform int uFaceIndex;
|
|
103
|
+
uniform float uEdge;
|
|
104
|
+
|
|
105
|
+
out vec4 outTint;
|
|
106
|
+
|
|
107
|
+
${OCTAHEDRAL_GLSL}
|
|
108
|
+
|
|
109
|
+
void main() {
|
|
110
|
+
${FACE_REGION}
|
|
111
|
+
outTint = textureLod(uFace, faceUv, 0.0);
|
|
112
|
+
}`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const LATE_DEPTH_WGSL: string;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The render's depth, taken up to the output size for the draws that land after the upscale.
|
|
3
|
+
*
|
|
4
|
+
* `recon/lateDepth.ts` is the same arithmetic and says why: **each of the four render texels about
|
|
5
|
+
* the pixel carries its surface on to the pixel's unjittered centre** along a slope the minmod
|
|
6
|
+
* limiter takes — the smaller of its two differences on an axis, none where they disagree — and the
|
|
7
|
+
* nearest of the four so carried stands, less a quarter of its slope. On a plane that is the plane's
|
|
8
|
+
* own depth at the pixel whatever the jitter; at an edge no slope crosses the jump, so a
|
|
9
|
+
* translucent draw is never let through an opaque edge the render covered.
|
|
10
|
+
*
|
|
11
|
+
* Hand-written, and built by concatenation rather than a template literal: the reconstruction's
|
|
12
|
+
* passes have no WebGL2 twin (the AGENTS.md exception for them), and a `${}` inside WGSL source is
|
|
13
|
+
* the thing that rule forbids.
|
|
14
|
+
*/
|
|
15
|
+
import { REVERSED_DEPTH } from '../../depthConvention.js';
|
|
16
|
+
/** The nearer of two stored depths: the larger in a reversed buffer. */
|
|
17
|
+
const NEARER = REVERSED_DEPTH ? 'max' : 'min';
|
|
18
|
+
/** Toward the far side: down in a reversed buffer. */
|
|
19
|
+
const FARWARD = REVERSED_DEPTH ? '-' : '+';
|
|
20
|
+
/** Where the search for the nearest starts: the far plane. */
|
|
21
|
+
const FARTHEST = REVERSED_DEPTH ? '0.0' : '1.0';
|
|
22
|
+
export const LATE_DEPTH_WGSL = '@group(0) @binding(0) var source: texture_depth_2d;\n' +
|
|
23
|
+
'/* The render size and the output size; then the frame jitter, in render texels. */\n' +
|
|
24
|
+
'@group(0) @binding(1) var<uniform> sizes: array<vec4<f32>, 2>;\n' +
|
|
25
|
+
'\n' +
|
|
26
|
+
'@vertex\n' +
|
|
27
|
+
'fn lateDepthVert(@builtin(vertex_index) index: u32) -> @builtin(position) vec4<f32> {\n' +
|
|
28
|
+
' let x = f32((index << 1u) & 2u);\n' +
|
|
29
|
+
' let y = f32(index & 2u);\n' +
|
|
30
|
+
' return vec4<f32>(x * 2.0 - 1.0, 1.0 - y * 2.0, 0.0, 1.0);\n' +
|
|
31
|
+
'}\n' +
|
|
32
|
+
'\n' +
|
|
33
|
+
'fn tap(base: vec2<i32>, x: i32, y: i32) -> f32 {\n' +
|
|
34
|
+
' let top = vec2<i32>(sizes[0].xy) - vec2<i32>(1);\n' +
|
|
35
|
+
' return textureLoad(source, clamp(base + vec2<i32>(x, y), vec2<i32>(0), top), 0);\n' +
|
|
36
|
+
'}\n' +
|
|
37
|
+
'\n' +
|
|
38
|
+
'/* The smaller of two differences, or none where they disagree in sign. */\n' +
|
|
39
|
+
'fn minmod(a: f32, b: f32) -> f32 {\n' +
|
|
40
|
+
' if (a * b <= 0.0) {\n' +
|
|
41
|
+
' return 0.0;\n' +
|
|
42
|
+
' }\n' +
|
|
43
|
+
' return select(b, a, abs(a) < abs(b));\n' +
|
|
44
|
+
'}\n' +
|
|
45
|
+
'\n' +
|
|
46
|
+
'@fragment\n' +
|
|
47
|
+
'fn lateDepthFrag(@builtin(position) at: vec4<f32>) -> @builtin(frag_depth) f32 {\n' +
|
|
48
|
+
' /* A texel stands at its index plus a half less the jitter, as the resolve places it. */\n' +
|
|
49
|
+
' let centre = at.xy * sizes[0].xy / sizes[0].zw - vec2<f32>(0.5) + sizes[1].xy;\n' +
|
|
50
|
+
' let base = vec2<i32>(floor(centre));\n' +
|
|
51
|
+
' let f = centre - floor(centre);\n' +
|
|
52
|
+
' var depth = ' +
|
|
53
|
+
FARTHEST +
|
|
54
|
+
';\n' +
|
|
55
|
+
' for (var oy = 0; oy < 2; oy += 1) {\n' +
|
|
56
|
+
' for (var ox = 0; ox < 2; ox += 1) {\n' +
|
|
57
|
+
' let d = tap(base, ox, oy);\n' +
|
|
58
|
+
' let gx = minmod(tap(base, ox + 1, oy) - d, d - tap(base, ox - 1, oy));\n' +
|
|
59
|
+
' let gy = minmod(tap(base, ox, oy + 1) - d, d - tap(base, ox, oy - 1));\n' +
|
|
60
|
+
' let carried = d + gx * (f.x - f32(ox)) + gy * (f.y - f32(oy));\n' +
|
|
61
|
+
' depth = ' +
|
|
62
|
+
NEARER +
|
|
63
|
+
'(depth, carried ' +
|
|
64
|
+
FARWARD +
|
|
65
|
+
' 0.25 * max(abs(gx), abs(gy)));\n' +
|
|
66
|
+
' }\n' +
|
|
67
|
+
' }\n' +
|
|
68
|
+
' return clamp(depth, 0.0, 1.0);\n' +
|
|
69
|
+
'}\n';
|
|
@@ -41,4 +41,4 @@ export declare const MOTION_DRAW_STRIDE = 256;
|
|
|
41
41
|
export declare const MOTION_DRAW_FLOATS = 32;
|
|
42
42
|
/** The motion target's format. Half float holds a uv to a hundredth of a pixel and a view depth. */
|
|
43
43
|
export declare const MOTION_TARGET_FORMAT: GPUTextureFormat;
|
|
44
|
-
export declare const RECON_MOTION_WGSL = "\nstruct MotionFrame {\n /* The scene's own clip transform, with the generated shaders' y negation already folded in. */\n raster: mat4x4<f32>,\n /* This frame's and last frame's view-projections, unjittered, in the matrices' own convention. */\n viewProj: mat4x4<f32>,\n previousViewProj: mat4x4<f32>,\n}\n\nstruct MotionDraw {\n model: mat4x4<f32>,\n previousModel: mat4x4<f32>,\n}\n\n@group(0) @binding(0) var<uniform> frame: MotionFrame;\n@group(0) @binding(1) var<uniform> draw: MotionDraw;\n\nstruct Varyings {\n @builtin(position) clip: vec4<f32>,\n /* Clip positions rather than uv: an interpolator divides by w for us, so dividing these by their\n own interpolated w in the fragment stage is the perspective-correct answer at every pixel. */\n @location(0) now: vec4<f32>,\n @location(1) was: vec4<f32>,\n}\n\n@vertex\nfn motionVert(@location(0) position: vec3<f32>) -> Varyings {\n let world = draw.model * vec4<f32>(position, 1.0);\n let wasWorld = draw.previousModel * vec4<f32>(position, 1.0);\n var out: Varyings;\n out.clip = frame.raster * world;\n out.now = frame.viewProj * world;\n out.was = frame.previousViewProj * wasWorld;\n return out;\n}\n\n/* Normalised device coordinates to the uv the resolve measures in: v increases upward, as the\n references' images do, and the accessor that reads this target is what flips the texture's rows. */\nfn motionUv(clip: vec4<f32>) -> vec2<f32> {\n return clip.xy / clip.w * 0.5 + vec2<f32>(0.5);\n}\n\n@fragment\nfn motionFrag(in: Varyings) -> @location(0) vec4<f32> {\n /*\n * A surface behind last frame's eye has a w that is not positive, and there is no uv for it. The\n * flag stays set: the disocclusion refuses a depth that is not positive, so the resolve drops the\n * history rather than reading it from wherever a divide by a negative w would have pointed.\n */\n let offset = select(vec2<f32>(0.0), motionUv(in.was) - motionUv(in.now), in.was.w > 0.0);\n return vec4<f32>(offset, in.was.w, 1.0);\n}\n";
|
|
44
|
+
export declare const RECON_MOTION_WGSL = "\nstruct MotionFrame {\n /* The scene's own clip transform, with the generated shaders' y negation already folded in. */\n raster: mat4x4<f32>,\n /* This frame's and last frame's view-projections, unjittered, in the matrices' own convention. */\n viewProj: mat4x4<f32>,\n previousViewProj: mat4x4<f32>,\n}\n\nstruct MotionDraw {\n model: mat4x4<f32>,\n previousModel: mat4x4<f32>,\n}\n\n@group(0) @binding(0) var<uniform> frame: MotionFrame;\n@group(0) @binding(1) var<uniform> draw: MotionDraw;\n\nstruct Varyings {\n @builtin(position) clip: vec4<f32>,\n /* Clip positions rather than uv: an interpolator divides by w for us, so dividing these by their\n own interpolated w in the fragment stage is the perspective-correct answer at every pixel. */\n @location(0) now: vec4<f32>,\n @location(1) was: vec4<f32>,\n}\n\n@vertex\nfn motionVert(@location(0) position: vec3<f32>) -> Varyings {\n let world = draw.model * vec4<f32>(position, 1.0);\n let wasWorld = draw.previousModel * vec4<f32>(position, 1.0);\n var out: Varyings;\n out.clip = frame.raster * world;\n out.now = frame.viewProj * world;\n out.was = frame.previousViewProj * wasWorld;\n return out;\n}\n\n/* Normalised device coordinates to the uv the resolve measures in: v increases upward, as the\n references' images do, and the accessor that reads this target is what flips the texture's rows. */\nfn motionUv(clip: vec4<f32>) -> vec2<f32> {\n return clip.xy / clip.w * 0.5 + vec2<f32>(0.5);\n}\n\n/*\n * The skinned stage: the joints this frame and last frame, read from two palette textures of the\n * frame's ring. The same four weighted products in the same order as the scene's own skinning, so\n * the depth this stage produces is the depth the scene wrote.\n */\n@group(1) @binding(0) var palette: texture_2d<f32>;\n@group(1) @binding(1) var previousPalette: texture_2d<f32>;\n\nfn jointOf(source: texture_2d<f32>, index: i32) -> mat4x4<f32> {\n let x = index * 4;\n return mat4x4<f32>(\n textureLoad(source, vec2<i32>(x, 0), 0),\n textureLoad(source, vec2<i32>(x + 1, 0), 0),\n textureLoad(source, vec2<i32>(x + 2, 0), 0),\n textureLoad(source, vec2<i32>(x + 3, 0), 0));\n}\n\nfn skinOf(source: texture_2d<f32>, joints: vec4<f32>, weights: vec4<f32>) -> mat4x4<f32> {\n return jointOf(source, i32(joints.x)) * weights.x\n + jointOf(source, i32(joints.y)) * weights.y\n + jointOf(source, i32(joints.z)) * weights.z\n + jointOf(source, i32(joints.w)) * weights.w;\n}\n\n@vertex\nfn motionSkinnedVert(\n @location(0) position: vec3<f32>,\n @location(11) joints: vec4<f32>,\n @location(12) weights: vec4<f32>,\n) -> Varyings {\n let world = draw.model * (skinOf(palette, joints, weights) * vec4<f32>(position, 1.0));\n let wasWorld =\n draw.previousModel * (skinOf(previousPalette, joints, weights) * vec4<f32>(position, 1.0));\n var out: Varyings;\n out.clip = frame.raster * world;\n out.now = frame.viewProj * world;\n out.was = frame.previousViewProj * wasWorld;\n return out;\n}\n\n/*\n * The rewritten stage: a mesh whose vertices were rewritten this frame reads last frame's rows from\n * a second buffer of the same stride, so each vertex carries both of its positions.\n */\n@vertex\nfn motionDynamicVert(\n @location(0) position: vec3<f32>,\n @location(1) previousPosition: vec3<f32>,\n) -> Varyings {\n let world = draw.model * vec4<f32>(position, 1.0);\n let wasWorld = draw.previousModel * vec4<f32>(previousPosition, 1.0);\n var out: Varyings;\n out.clip = frame.raster * world;\n out.now = frame.viewProj * world;\n out.was = frame.previousViewProj * wasWorld;\n return out;\n}\n\n/*\n * The instanced stage: each instance's matrix this frame and last frame, read from the batch's\n * placement and the copy of last frame's it kept. Slot for slot, so an instance is paired with\n * itself; the per-draw matrices are not read.\n */\n@vertex\nfn motionInstancedVert(\n @location(0) position: vec3<f32>,\n @location(2) m0: vec4<f32>,\n @location(3) m1: vec4<f32>,\n @location(4) m2: vec4<f32>,\n @location(5) m3: vec4<f32>,\n @location(6) p0: vec4<f32>,\n @location(7) p1: vec4<f32>,\n @location(8) p2: vec4<f32>,\n @location(9) p3: vec4<f32>,\n) -> Varyings {\n let world = mat4x4<f32>(m0, m1, m2, m3) * vec4<f32>(position, 1.0);\n let wasWorld = mat4x4<f32>(p0, p1, p2, p3) * vec4<f32>(position, 1.0);\n var out: Varyings;\n out.clip = frame.raster * world;\n out.now = frame.viewProj * world;\n out.was = frame.previousViewProj * wasWorld;\n return out;\n}\n\n@fragment\nfn motionFrag(in: Varyings) -> @location(0) vec4<f32> {\n /*\n * A surface behind last frame's eye has a w that is not positive, and there is no uv for it. The\n * flag stays set: the disocclusion refuses a depth that is not positive, so the resolve drops the\n * history rather than reading it from wherever a divide by a negative w would have pointed.\n */\n let offset = select(vec2<f32>(0.0), motionUv(in.was) - motionUv(in.now), in.was.w > 0.0);\n return vec4<f32>(offset, in.was.w, 1.0);\n}\n";
|
|
@@ -83,6 +83,90 @@ fn motionUv(clip: vec4<f32>) -> vec2<f32> {
|
|
|
83
83
|
return clip.xy / clip.w * 0.5 + vec2<f32>(0.5);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
/*
|
|
87
|
+
* The skinned stage: the joints this frame and last frame, read from two palette textures of the
|
|
88
|
+
* frame's ring. The same four weighted products in the same order as the scene's own skinning, so
|
|
89
|
+
* the depth this stage produces is the depth the scene wrote.
|
|
90
|
+
*/
|
|
91
|
+
@group(1) @binding(0) var palette: texture_2d<f32>;
|
|
92
|
+
@group(1) @binding(1) var previousPalette: texture_2d<f32>;
|
|
93
|
+
|
|
94
|
+
fn jointOf(source: texture_2d<f32>, index: i32) -> mat4x4<f32> {
|
|
95
|
+
let x = index * 4;
|
|
96
|
+
return mat4x4<f32>(
|
|
97
|
+
textureLoad(source, vec2<i32>(x, 0), 0),
|
|
98
|
+
textureLoad(source, vec2<i32>(x + 1, 0), 0),
|
|
99
|
+
textureLoad(source, vec2<i32>(x + 2, 0), 0),
|
|
100
|
+
textureLoad(source, vec2<i32>(x + 3, 0), 0));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
fn skinOf(source: texture_2d<f32>, joints: vec4<f32>, weights: vec4<f32>) -> mat4x4<f32> {
|
|
104
|
+
return jointOf(source, i32(joints.x)) * weights.x
|
|
105
|
+
+ jointOf(source, i32(joints.y)) * weights.y
|
|
106
|
+
+ jointOf(source, i32(joints.z)) * weights.z
|
|
107
|
+
+ jointOf(source, i32(joints.w)) * weights.w;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@vertex
|
|
111
|
+
fn motionSkinnedVert(
|
|
112
|
+
@location(0) position: vec3<f32>,
|
|
113
|
+
@location(11) joints: vec4<f32>,
|
|
114
|
+
@location(12) weights: vec4<f32>,
|
|
115
|
+
) -> Varyings {
|
|
116
|
+
let world = draw.model * (skinOf(palette, joints, weights) * vec4<f32>(position, 1.0));
|
|
117
|
+
let wasWorld =
|
|
118
|
+
draw.previousModel * (skinOf(previousPalette, joints, weights) * vec4<f32>(position, 1.0));
|
|
119
|
+
var out: Varyings;
|
|
120
|
+
out.clip = frame.raster * world;
|
|
121
|
+
out.now = frame.viewProj * world;
|
|
122
|
+
out.was = frame.previousViewProj * wasWorld;
|
|
123
|
+
return out;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/*
|
|
127
|
+
* The rewritten stage: a mesh whose vertices were rewritten this frame reads last frame's rows from
|
|
128
|
+
* a second buffer of the same stride, so each vertex carries both of its positions.
|
|
129
|
+
*/
|
|
130
|
+
@vertex
|
|
131
|
+
fn motionDynamicVert(
|
|
132
|
+
@location(0) position: vec3<f32>,
|
|
133
|
+
@location(1) previousPosition: vec3<f32>,
|
|
134
|
+
) -> Varyings {
|
|
135
|
+
let world = draw.model * vec4<f32>(position, 1.0);
|
|
136
|
+
let wasWorld = draw.previousModel * vec4<f32>(previousPosition, 1.0);
|
|
137
|
+
var out: Varyings;
|
|
138
|
+
out.clip = frame.raster * world;
|
|
139
|
+
out.now = frame.viewProj * world;
|
|
140
|
+
out.was = frame.previousViewProj * wasWorld;
|
|
141
|
+
return out;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/*
|
|
145
|
+
* The instanced stage: each instance's matrix this frame and last frame, read from the batch's
|
|
146
|
+
* placement and the copy of last frame's it kept. Slot for slot, so an instance is paired with
|
|
147
|
+
* itself; the per-draw matrices are not read.
|
|
148
|
+
*/
|
|
149
|
+
@vertex
|
|
150
|
+
fn motionInstancedVert(
|
|
151
|
+
@location(0) position: vec3<f32>,
|
|
152
|
+
@location(2) m0: vec4<f32>,
|
|
153
|
+
@location(3) m1: vec4<f32>,
|
|
154
|
+
@location(4) m2: vec4<f32>,
|
|
155
|
+
@location(5) m3: vec4<f32>,
|
|
156
|
+
@location(6) p0: vec4<f32>,
|
|
157
|
+
@location(7) p1: vec4<f32>,
|
|
158
|
+
@location(8) p2: vec4<f32>,
|
|
159
|
+
@location(9) p3: vec4<f32>,
|
|
160
|
+
) -> Varyings {
|
|
161
|
+
let world = mat4x4<f32>(m0, m1, m2, m3) * vec4<f32>(position, 1.0);
|
|
162
|
+
let wasWorld = mat4x4<f32>(p0, p1, p2, p3) * vec4<f32>(position, 1.0);
|
|
163
|
+
var out: Varyings;
|
|
164
|
+
out.clip = frame.raster * world;
|
|
165
|
+
out.now = frame.viewProj * world;
|
|
166
|
+
out.was = frame.previousViewProj * wasWorld;
|
|
167
|
+
return out;
|
|
168
|
+
}
|
|
169
|
+
|
|
86
170
|
@fragment
|
|
87
171
|
fn motionFrag(in: Varyings) -> @location(0) vec4<f32> {
|
|
88
172
|
/*
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* `hasHistory` is a `u32` because a uniform buffer has no `bool`. The three matrices are the
|
|
5
5
|
* unjittered ones, in the same convention as the depths the accessors return.
|
|
6
6
|
*/
|
|
7
|
-
export declare const RECON_PARAMS_WGSL = "\nstruct ReconParams {\n renderSize: vec2<u32>,\n outputSize: vec2<u32>,\n jitter: vec2<f32>,\n previousJitter: vec2<f32>,\n inverseViewProj: mat4x4<f32>,\n previousViewProj: mat4x4<f32>,\n previousInverseViewProj: mat4x4<f32>,\n eye: vec4<f32>,\n previousEye: vec4<f32>,\n alpha: f32,\n sharpenAmount: f32,\n clampGamma: f32,\n hasHistory: u32,\n depthTolerance: f32,\n motionScale: f32,\n normalFloor: f32,\n normalCeiling: f32,\n}\n";
|
|
7
|
+
export declare const RECON_PARAMS_WGSL = "\nstruct ReconParams {\n renderSize: vec2<u32>,\n outputSize: vec2<u32>,\n jitter: vec2<f32>,\n previousJitter: vec2<f32>,\n inverseViewProj: mat4x4<f32>,\n previousViewProj: mat4x4<f32>,\n previousInverseViewProj: mat4x4<f32>,\n eye: vec4<f32>,\n previousEye: vec4<f32>,\n alpha: f32,\n sharpenAmount: f32,\n clampGamma: f32,\n hasHistory: u32,\n depthTolerance: f32,\n motionScale: f32,\n normalFloor: f32,\n normalCeiling: f32,\n periodStart: u32,\n antiFlicker: u32,\n flickerMemory: f32,\n flickerWidth: f32,\n reprojection: mat4x4<f32>,\n}\n";
|
|
8
8
|
/** Floats in `ReconParams`, which is what a caller sizes its uniform buffer by. */
|
|
9
|
-
export declare const RECON_PARAM_FLOATS =
|
|
9
|
+
export declare const RECON_PARAM_FLOATS = 92;
|
|
10
10
|
/** `SAMPLE_SPREAD` and `CONFIDENT_WEIGHT`, which the reference exports and this must not restate. */
|
|
11
11
|
export declare const RECON_SAMPLE_SPREAD = 0.47;
|
|
12
12
|
export declare const RECON_CONFIDENT_WEIGHT = 1;
|
|
@@ -23,18 +23,19 @@ export declare const RECON_WORKGROUP = 8;
|
|
|
23
23
|
*/
|
|
24
24
|
export declare const RECON_HISTORY_FORMAT: GPUTextureFormat;
|
|
25
25
|
/**
|
|
26
|
-
* The arithmetic, as text. Defines `reconResolvePixel(ox, oy) ->
|
|
27
|
-
* colour and the weight it has gathered — and `reconSharpenAt(ox, oy) -> vec3<f32>`, the picture
|
|
26
|
+
* The arithmetic, as text. Defines `reconResolvePixel(ox, oy) -> ReconResolved` — the next
|
|
27
|
+
* history's colour and the weight it has gathered, and the pixel's next flicker record — and `reconSharpenAt(ox, oy) -> vec3<f32>`, the picture
|
|
28
28
|
* shown, which reads the history the first has already written.
|
|
29
29
|
*/
|
|
30
|
-
export declare const RECON_RESOLVE_CORE_WGSL = "\nconst RECON_SAMPLE_SPREAD: f32 = 0.47;\nconst RECON_CONFIDENT_WEIGHT: f32 = 1.0;\nconst RECON_PLANAR_FROM: f32 = 0.25;\nconst RECON_PLANAR_TO: f32 = 0.5;\n\nfn reconClampTexel(value: i32, size: i32) -> i32 {\n return clamp(value, 0, size - 1);\n}\n\n/* JavaScript's Math.round, which is not WGSL's round: a half goes up, not to even. */\nfn reconRound(value: f32) -> f32 {\n return floor(value + 0.5);\n}\n\n/* Written out rather than the built-in, whose result is undefined where the edges are equal. */\nfn reconSmoothstep(edge0: f32, edge1: f32, x: f32) -> f32 {\n let t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);\n return t * t * (3.0 - 2.0 * t);\n}\n\nfn reconWeightAt(dx: f32, dy: f32) -> f32 {\n return exp(-(dx * dx + dy * dy) / (2.0 * RECON_SAMPLE_SPREAD * RECON_SAMPLE_SPREAD));\n}\n\nfn reconToYCoCg(c: vec3<f32>) -> vec3<f32> {\n return vec3<f32>(\n c.r * 0.25 + c.g * 0.5 + c.b * 0.25,\n c.r * 0.5 - c.b * 0.5,\n -c.r * 0.25 + c.g * 0.5 - c.b * 0.25,\n );\n}\n\nfn reconFromYCoCg(c: vec3<f32>) -> vec3<f32> {\n let base = c.x - c.z;\n return vec3<f32>(base + c.y, c.x + c.z, base - c.y);\n}\n\nstruct ReconBox {\n low: vec3<f32>,\n high: vec3<f32>,\n}\n\n/*\n * Each channel's mean plus or minus gamma of its standard deviation \u2014 and a channel whose nine\n * samples all agree is that value exactly, because a mean of nine equal floats is not always one\n * of them and a box a rounding wide lets a history through by a rounding.\n */\nfn reconVarianceBox(samples: array<vec3<f32>, 9>, gamma: f32) -> ReconBox {\n var s = samples;\n var box: ReconBox;\n for (var c = 0u; c < 3u; c = c + 1u) {\n let first = s[0][c];\n var same = true;\n var sum = 0.0;\n for (var i = 0u; i < 9u; i = i + 1u) {\n let value = s[i][c];\n sum = sum + value;\n if (value != first) { same = false; }\n }\n if (same) {\n box.low[c] = first;\n box.high[c] = first;\n continue;\n }\n let mean = sum / 9.0;\n var spread = 0.0;\n for (var i = 0u; i < 9u; i = i + 1u) {\n let d = s[i][c] - mean;\n spread = spread + d * d;\n }\n let deviation = sqrt(spread / 9.0);\n box.low[c] = mean - gamma * deviation;\n box.high[c] = mean + gamma * deviation;\n }\n return box;\n}\n\n/*\n * The history pulled towards the box's centre until it is inside, by one ratio for all three\n * channels \u2014 a clamp per channel would land it on a corner and change its hue, which is coloured\n * fringing on every moving edge. A channel with no extent bounds anything that differs from it\n * infinitely, so such a history collapses onto the centre.\n */\nfn reconClipToBox(history: vec3<f32>, low: vec3<f32>, high: vec3<f32>) -> vec3<f32> {\n let centre = (low + high) * 0.5;\n var offset = history - centre;\n var extent = (high - low) * 0.5;\n var ratio = 0.0;\n var infinite = false;\n for (var c = 0u; c < 3u; c = c + 1u) {\n if (extent[c] > 1e-7) {\n ratio = max(ratio, abs(offset[c]) / extent[c]);\n } else if (abs(offset[c]) > 1e-7) {\n infinite = true;\n }\n }\n if (!infinite && !(ratio > 1.0)) { return history; }\n var scale = 0.0;\n if (!infinite) { scale = 1.0 / ratio; }\n return centre + offset * scale;\n}\n\nfn reconAccumulate(current: vec3<f32>, history: vec3<f32>, weight: f32, alpha: f32) -> vec3<f32> {\n let kept = clamp(weight, 0.0, 1.0) * (1.0 - clamp(alpha, 0.0, 1.0));\n return current * (1.0 - kept) + history * kept;\n}\n\nfn reconCatmullRom(t: f32) -> vec4<f32> {\n let t2 = t * t;\n let t3 = t2 * t;\n return vec4<f32>(\n -0.5 * t3 + t2 - 0.5 * t,\n 1.5 * t3 - 2.5 * t2 + 1.0,\n -1.5 * t3 + 2.0 * t2 + 0.5 * t,\n 0.5 * t3 - 0.5 * t2,\n );\n}\n\n/*\n * Five bilinear taps standing for the sixteen-texel Catmull-Rom: each axis's middle pair folded\n * into one tap placed between them, the corners dropped, the five weights renormalised. It is the\n * five-tap form because the taps are the expensive part \u2014 which is only true while the bilinear is\n * the sampler's, so the production module's accessor is a filtered sample and the check's is the\n * reference's own arithmetic over loaded texels.\n */\nstruct ReconTaps {\n at0: vec2<f32>,\n at1: vec2<f32>,\n at2: vec2<f32>,\n at3: vec2<f32>,\n at4: vec2<f32>,\n weights: array<f32, 5>,\n total: f32,\n}\n\nfn reconBicubicTaps(x: f32, y: f32) -> ReconTaps {\n let x1 = floor(x);\n let y1 = floor(y);\n let wx = reconCatmullRom(x - x1);\n let wy = reconCatmullRom(y - y1);\n let wx12 = wx[1] + wx[2];\n let wy12 = wy[1] + wy[2];\n let x12 = x1 + wx[2] / wx12;\n let y12 = y1 + wy[2] / wy12;\n var taps: ReconTaps;\n taps.at0 = vec2<f32>(x12, y1 - 1.0);\n taps.at1 = vec2<f32>(x1 - 1.0, y12);\n taps.at2 = vec2<f32>(x12, y12);\n taps.at3 = vec2<f32>(x1 + 2.0, y12);\n taps.at4 = vec2<f32>(x12, y1 + 2.0);\n taps.weights[0] = wx12 * wy[0];\n taps.weights[1] = wx[0] * wy12;\n taps.weights[2] = wx12 * wy12;\n taps.weights[3] = wx[3] * wy12;\n taps.weights[4] = wx12 * wy[3];\n taps.total =\n taps.weights[0] + taps.weights[1] + taps.weights[2] + taps.weights[3] + taps.weights[4];\n return taps;\n}\n\n/* A colour cannot be negative, and Catmull-Rom's negative lobes undershoot beside a bright edge. */\nfn reconSceneBicubic5(x: f32, y: f32) -> vec3<f32> {\n let taps = reconBicubicTaps(x, y);\n var sum = reconSceneBilinear(taps.at0) * taps.weights[0];\n sum = sum + reconSceneBilinear(taps.at1) * taps.weights[1];\n sum = sum + reconSceneBilinear(taps.at2) * taps.weights[2];\n sum = sum + reconSceneBilinear(taps.at3) * taps.weights[3];\n sum = sum + reconSceneBilinear(taps.at4) * taps.weights[4];\n return max(sum / taps.total, vec3<f32>(0.0));\n}\n\nfn reconHistoryBicubic5(x: f32, y: f32) -> vec4<f32> {\n let taps = reconBicubicTaps(x, y);\n var sum = reconHistoryBilinear(taps.at0) * taps.weights[0];\n sum = sum + reconHistoryBilinear(taps.at1) * taps.weights[1];\n sum = sum + reconHistoryBilinear(taps.at2) * taps.weights[2];\n sum = sum + reconHistoryBilinear(taps.at3) * taps.weights[3];\n sum = sum + reconHistoryBilinear(taps.at4) * taps.weights[4];\n return max(sum / taps.total, vec4<f32>(0.0));\n}\n\nstruct ReconWorld {\n ok: bool,\n position: vec3<f32>,\n}\n\n/* The world position a clip depth stands for, and nothing where the matrix has no answer there. */\nfn reconWorldFromDepth(m: mat4x4<f32>, u: f32, v: f32, clipDepth: f32) -> ReconWorld {\n var out: ReconWorld;\n out.position = vec3<f32>(0.0);\n out.ok = false;\n let x = u * 2.0 - 1.0;\n let y = v * 2.0 - 1.0;\n let w = m[0][3] * x + m[1][3] * y + m[2][3] * clipDepth + m[3][3];\n if (!(abs(w) > 1e-12)) { return out; }\n let p = m * vec4<f32>(x, y, clipDepth, 1.0);\n out.position = p.xyz / w;\n out.ok = all(abs(out.position) < vec3<f32>(3.4e38));\n return out;\n}\n\nstruct ReconNormal {\n ok: bool,\n normal: vec3<f32>,\n}\n\n/* Turned to face the eye, so the answer is the same whichever way the target's rows run. */\nfn reconNormalFrom(\n centre: vec3<f32>,\n right: vec3<f32>,\n up: vec3<f32>,\n eye: vec3<f32>,\n) -> ReconNormal {\n var out: ReconNormal;\n out.normal = vec3<f32>(0.0);\n out.ok = false;\n let a = right - centre;\n let b = up - centre;\n let n = cross(a, b);\n let length2 = dot(n, n);\n let scale2 = dot(a, a) * dot(b, b);\n if (!(length2 > 1e-12 * scale2)) { return out; }\n let toEye = dot(n, eye - centre);\n var sign = 1.0;\n if (toEye < 0.0) { sign = -1.0; }\n out.normal = n * (sign / sqrt(length2));\n out.ok = true;\n return out;\n}\n\nfn reconDisocclusion(\n currDepth: f32,\n histDepth: f32,\n motionLength: f32,\n normalDot: f32,\n slope: f32,\n) -> f32 {\n if (!(currDepth > 0.0) || !(histDepth > 0.0) || !(motionLength >= 0.0)) { return 0.0; }\n let disagreement = abs(currDepth - histDepth) / currDepth;\n let tolerance = recon.depthTolerance + recon.motionScale * motionLength + slope;\n let depth = 1.0 - reconSmoothstep(tolerance, 2.0 * tolerance, disagreement);\n let normal = reconSmoothstep(recon.normalFloor, recon.normalCeiling, normalDot);\n return depth * normal;\n}\n\nstruct ReconAt {\n depth: f32,\n position: vec3<f32>,\n}\n\n/* A texel's world position at its unjittered uv, and the view depth it stands at, or zero. */\nfn reconWorldAt(previous: bool, x: i32, y: i32) -> ReconAt {\n let w = i32(recon.renderSize.x);\n let h = i32(recon.renderSize.y);\n let tx = reconClampTexel(x, w);\n let ty = reconClampTexel(y, h);\n var jitter = recon.jitter;\n var m = recon.inverseViewProj;\n var z = reconDepth(tx, ty);\n if (previous) {\n jitter = recon.previousJitter;\n m = recon.previousInverseViewProj;\n z = reconPreviousDepth(tx, ty);\n }\n let u = (f32(tx) + 0.5 - jitter.x) / f32(w);\n let v = (f32(ty) + 0.5 - jitter.y) / f32(h);\n var out: ReconAt;\n out.depth = 0.0;\n out.position = vec3<f32>(0.0);\n let world = reconWorldFromDepth(m, u, v, z);\n if (!world.ok) { return out; }\n out.position = world.position;\n /* The view depth is the clip w, which the inverse's w is the reciprocal of. */\n let cw = m[0][3] * (u * 2.0 - 1.0) + m[1][3] * (v * 2.0 - 1.0) + m[2][3] * z + m[3][3];\n out.depth = 1.0 / cw;\n return out;\n}\n\n/* The neighbour on the inside of the border, so the edge texels have a normal too. */\nfn reconNormalAt(previous: bool, x: i32, y: i32) -> ReconNormal {\n var out: ReconNormal;\n out.normal = vec3<f32>(0.0);\n out.ok = false;\n let w = i32(recon.renderSize.x);\n let h = i32(recon.renderSize.y);\n let centre = reconWorldAt(previous, x, y);\n if (!(centre.depth > 0.0)) { return out; }\n var dx = -1;\n if (x + 1 < w) { dx = 1; }\n var dy = -1;\n if (y + 1 < h) { dy = 1; }\n let right = reconWorldAt(previous, x + dx, y);\n if (!(right.depth > 0.0)) { return out; }\n let up = reconWorldAt(previous, x, y + dy);\n if (!(up.depth > 0.0)) { return out; }\n var eye = recon.eye.xyz;\n if (previous) { eye = recon.previousEye.xyz; }\n return reconNormalFrom(centre.position, right.position, up.position, eye);\n}\n\n/* resolve.ts's viewDepthAt. */\nfn reconViewDepthAt(previous: bool, x: i32, y: i32) -> f32 {\n let w = i32(recon.renderSize.x);\n let h = i32(recon.renderSize.y);\n if (x < 0 || y < 0 || x >= w || y >= h) { return 0.0; }\n var jitter = recon.jitter;\n var m = recon.inverseViewProj;\n var z = 0.0;\n if (previous) {\n jitter = recon.previousJitter;\n m = recon.previousInverseViewProj;\n z = reconPreviousDepth(x, y);\n } else {\n z = reconDepth(x, y);\n }\n let u = (f32(x) + 0.5 - jitter.x) / f32(w);\n let v = (f32(y) + 0.5 - jitter.y) / f32(h);\n let cw = m[0][3] * (u * 2.0 - 1.0) + m[1][3] * (v * 2.0 - 1.0) + m[2][3] * z + m[3][3];\n if (!(abs(cw) > 1e-12)) { return 0.0; }\n return 1.0 / cw;\n}\n\n/* disocclusion.ts's planarity. */\nfn reconPlanarity(before: f32, centre: f32, after: f32) -> f32 {\n if (!(before > 0.0) || !(centre > 0.0) || !(after > 0.0)) { return 1.0; }\n let a = 1.0 / after - 1.0 / centre;\n let b = 1.0 / before - 1.0 / centre;\n let spread = abs(a) + abs(b);\n if (!(spread > 0.0)) { return 1.0; }\n return 1.0 - reconSmoothstep(RECON_PLANAR_FROM, RECON_PLANAR_TO, abs(a + b) / spread);\n}\n\n/* disocclusion.ts's surfaceSlope. */\nfn reconSurfaceSlope(before: f32, centre: f32, after: f32) -> f32 {\n let hasAfter = after > 0.0;\n let hasBefore = before > 0.0;\n if (!hasAfter) {\n if (!hasBefore) { return 0.0; }\n return abs(before - centre) / centre;\n }\n if (!hasBefore) { return abs(after - centre) / centre; }\n let a = after - centre;\n let b = before - centre;\n if (a * b > 0.0) { return 0.0; }\n return min(abs(a), abs(b)) / centre;\n}\n\nstruct ReconSurface {\n planar: f32,\n slope: f32,\n}\n\n/* resolve.ts's surfaceAt. */\nfn reconSurfaceAt(previous: bool, x: i32, y: i32, centre: f32) -> ReconSurface {\n var out: ReconSurface;\n out.planar = 1.0;\n out.slope = 0.0;\n for (var axis = 0; axis < 2; axis = axis + 1) {\n let ax = 1 - axis;\n let ay = axis;\n let after = reconViewDepthAt(previous, x + ax, y + ay);\n let before = reconViewDepthAt(previous, x - ax, y - ay);\n out.planar = out.planar * reconPlanarity(before, centre, after);\n out.slope = max(out.slope, reconSurfaceSlope(before, centre, after));\n }\n return out;\n}\n\nstruct ReconHistory {\n weight: f32,\n colour: vec4<f32>,\n}\n\n/*\n * The weight a pixel's history still deserves: none on the first frame, none where the surface was\n * behind last frame's eye or off its picture, and otherwise what it gathered, scaled by whether it\n * is the same surface.\n */\nfn reconHistoryWeight(u: f32, v: f32, dx: i32, dy: i32) -> ReconHistory {\n var out: ReconHistory;\n out.weight = 0.0;\n out.colour = vec4<f32>(0.0);\n if (recon.hasHistory == 0u) { return out; }\n let rw = f32(recon.renderSize.x);\n let rh = f32(recon.renderSize.y);\n let motion = reconMotion(dx, dy);\n var mu = 0.0;\n var mv = 0.0;\n var expected = 0.0;\n if (motion.w > 0.5) {\n mu = motion.x;\n mv = motion.y;\n expected = motion.z;\n } else {\n let here = reconWorldAt(false, dx, dy);\n if (!(here.depth > 0.0)) { return out; }\n let clip = recon.previousViewProj * vec4<f32>(here.position, 1.0);\n /*\n * A surface behind last frame's eye has a w that is not positive: the disocclusion trusts no\n * such depth, so it needs no refusal of its own here.\n */\n expected = clip.w;\n let du = (f32(dx) + 0.5 - recon.jitter.x) / rw;\n let dv = (f32(dy) + 0.5 - recon.jitter.y) / rh;\n mu = (clip.x / expected) * 0.5 + 0.5 - du;\n mv = (clip.y / expected) * 0.5 + 0.5 - dv;\n }\n\n let pu = u + mu;\n let pv = v + mv;\n if (!(pu >= 0.0 && pu <= 1.0 && pv >= 0.0 && pv <= 1.0)) { return out; }\n out.colour = reconHistoryBicubic5(\n pu * f32(recon.outputSize.x) - 0.5,\n pv * f32(recon.outputSize.y) - 0.5,\n );\n /* Last frame's depth, searched around where the dilated texel was: see resolve.ts. */\n let su = (f32(dx) + 0.5 - recon.jitter.x) / rw + mu;\n let sv = (f32(dy) + 0.5 - recon.jitter.y) / rh + mv;\n let cx = reconClampTexel(i32(reconRound(su * rw + recon.previousJitter.x - 0.5)), i32(rw));\n let cy = reconClampTexel(i32(reconRound(sv * rh + recon.previousJitter.y - 0.5)), i32(rh));\n var held = reconViewDepthAt(true, cx, cy);\n var px = cx;\n var py = cy;\n if (!(held > 0.0 && abs(held - expected) <= recon.depthTolerance * expected)) {\n held = 0.0;\n var nearest = 3.4e38;\n for (var j = -1; j <= 1; j = j + 1) {\n for (var i = -1; i <= 1; i = i + 1) {\n let tx = reconClampTexel(cx + i, i32(rw));\n let ty = reconClampTexel(cy + j, i32(rh));\n let depth = reconViewDepthAt(true, tx, ty);\n if (!(depth > 0.0)) { continue; }\n let off = abs(depth - expected);\n if (off < nearest) {\n nearest = off;\n held = depth;\n px = tx;\n py = ty;\n }\n }\n }\n }\n\n let here = reconSurfaceAt(false, dx, dy, reconViewDepthAt(false, dx, dy));\n var planar = here.planar;\n if (planar > 0.0) { planar = planar * reconSurfaceAt(true, px, py, held).planar; }\n var normalDot = 1.0;\n if (planar > 0.0) {\n let hereNormal = reconNormalAt(false, dx, dy);\n let thereNormal = reconNormalAt(true, px, py);\n if (hereNormal.ok && thereNormal.ok) {\n normalDot = 1.0 - planar * (1.0 - dot(hereNormal.normal, thereNormal.normal));\n }\n }\n let trust = reconDisocclusion(expected, held, length(vec2<f32>(mu, mv)), normalDot, here.slope);\n out.weight = out.colour.w * trust;\n return out;\n}\n\n/* One output pixel: the history's next colour, before sharpening, and the weight it has gathered. */\nfn reconResolvePixel(ox: i32, oy: i32) -> vec4<f32> {\n let rw = i32(recon.renderSize.x);\n let rh = i32(recon.renderSize.y);\n let ow = i32(recon.outputSize.x);\n let oh = i32(recon.outputSize.y);\n let u = (f32(ox) + 0.5) / f32(ow);\n let v = (f32(oy) + 0.5) / f32(oh);\n let jx = recon.jitter.x;\n let jy = recon.jitter.y;\n\n /* Where this frame observed the pixel, and the render read there for the gaps. */\n let rx = u * f32(rw) + jx - 0.5;\n let ry = v * f32(rh) + jy - 0.5;\n let sample = reconSceneBicubic5(rx, ry);\n let nx = reconClampTexel(i32(reconRound(rx)), rw);\n let ny = reconClampTexel(i32(reconRound(ry)), rh);\n\n /* Each texel around: its weight, its colour in the box, and its depth. */\n var nearest = reconDepth(nx, ny);\n var dx = nx;\n var dy = ny;\n var gathered = 0.0;\n var accum = vec3<f32>(0.0);\n var neighbours: array<vec3<f32>, 9>;\n for (var j = -1; j <= 1; j = j + 1) {\n for (var i = -1; i <= 1; i = i + 1) {\n let tx = reconClampTexel(nx + i, rw);\n let ty = reconClampTexel(ny + j, rh);\n let colour = reconScene(tx, ty);\n let weight = reconWeightAt(\n ((f32(tx) + 0.5 - jx) / f32(rw)) * f32(ow) - (f32(ox) + 0.5),\n ((f32(ty) + 0.5 - jy) / f32(rh)) * f32(oh) - (f32(oy) + 0.5),\n );\n gathered = gathered + weight;\n accum = accum + colour * weight;\n neighbours[(j + 1) * 3 + (i + 1)] = reconToYCoCg(colour);\n let z = reconDepth(tx, ty);\n if (z < nearest) {\n nearest = z;\n dx = tx;\n dy = ty;\n }\n }\n }\n let box = reconVarianceBox(neighbours, recon.clampGamma);\n accum = accum / gathered;\n\n /* The history, and the weight it still deserves; a tenth of each frame is always new. */\n let cap = (1.0 - recon.alpha) / recon.alpha;\n let history = reconHistoryWeight(u, v, dx, dy);\n let kept = min(cap, history.weight);\n\n let total = kept + gathered;\n var mixed = accum;\n if (kept > 0.0) {\n let clipped = reconClipToBox(reconToYCoCg(history.colour.xyz), box.low, box.high);\n mixed = reconAccumulate(accum, reconFromYCoCg(clipped), 1.0, gathered / total);\n }\n let known = min(1.0, total / RECON_CONFIDENT_WEIGHT);\n return vec4<f32>(sample * (1.0 - known) + mixed * known, total);\n}\n\n/*\n * The centre pushed away from its four neighbours' mean, and held inside the range of the five \u2014\n * the halo a plain unsharp mask draws beside every edge is a value outside that range.\n */\nfn reconSharpenAt(ox: i32, oy: i32) -> vec3<f32> {\n let ow = i32(recon.outputSize.x);\n let oh = i32(recon.outputSize.y);\n let centre = reconHistory(reconClampTexel(ox, ow), reconClampTexel(oy, oh)).xyz;\n var offsets = array<vec2<i32>, 4>(\n vec2<i32>(-1, 0),\n vec2<i32>(1, 0),\n vec2<i32>(0, -1),\n vec2<i32>(0, 1),\n );\n var low = centre;\n var high = centre;\n var sum = vec3<f32>(0.0);\n for (var n = 0; n < 4; n = n + 1) {\n let at = offsets[n];\n let value = reconHistory(\n reconClampTexel(ox + at.x, ow),\n reconClampTexel(oy + at.y, oh),\n ).xyz;\n sum = sum + value;\n low = min(low, value);\n high = max(high, value);\n }\n let pushed = centre + recon.sharpenAmount * (centre - sum / 4.0);\n return min(high, max(low, pushed));\n}\n";
|
|
30
|
+
export declare const RECON_RESOLVE_CORE_WGSL = "\nconst RECON_SAMPLE_SPREAD: f32 = 0.47;\nconst RECON_CONFIDENT_WEIGHT: f32 = 1.0;\nconst RECON_PLANAR_FROM: f32 = 0.25;\nconst RECON_PLANAR_TO: f32 = 0.5;\nconst RECON_FLICKER_BLEND: f32 = 0.97;\nconst RECON_FLICKER_REPEAT: f32 = 0.05;\nconst RECON_STILL_FROM: f32 = 0.01;\nconst RECON_STILL_TO: f32 = 0.05;\nconst RECON_DEPTH_SWING_WIDTH: f32 = 3.0;\n\nfn reconClampTexel(value: i32, size: i32) -> i32 {\n return clamp(value, 0, size - 1);\n}\n\n/* JavaScript's Math.round, which is not WGSL's round: a half goes up, not to even. */\nfn reconRound(value: f32) -> f32 {\n return floor(value + 0.5);\n}\n\n/* Written out rather than the built-in, whose result is undefined where the edges are equal. */\nfn reconSmoothstep(edge0: f32, edge1: f32, x: f32) -> f32 {\n let t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);\n return t * t * (3.0 - 2.0 * t);\n}\n\nfn reconWeightAt(dx: f32, dy: f32) -> f32 {\n return exp(-(dx * dx + dy * dy) / (2.0 * RECON_SAMPLE_SPREAD * RECON_SAMPLE_SPREAD));\n}\n\nfn reconToYCoCg(c: vec3<f32>) -> vec3<f32> {\n return vec3<f32>(\n c.r * 0.25 + c.g * 0.5 + c.b * 0.25,\n c.r * 0.5 - c.b * 0.5,\n -c.r * 0.25 + c.g * 0.5 - c.b * 0.25,\n );\n}\n\nfn reconFromYCoCg(c: vec3<f32>) -> vec3<f32> {\n let base = c.x - c.z;\n return vec3<f32>(base + c.y, c.x + c.z, base - c.y);\n}\n\nstruct ReconBox {\n low: vec3<f32>,\n high: vec3<f32>,\n}\n\n/*\n * Each channel's mean plus or minus gamma of its standard deviation \u2014 and a channel whose nine\n * samples all agree is that value exactly, because a mean of nine equal floats is not always one\n * of them and a box a rounding wide lets a history through by a rounding.\n */\nfn reconVarianceBox(samples: array<vec3<f32>, 9>, gamma: f32) -> ReconBox {\n var s = samples;\n var box: ReconBox;\n for (var c = 0u; c < 3u; c = c + 1u) {\n let first = s[0][c];\n var same = true;\n var sum = 0.0;\n for (var i = 0u; i < 9u; i = i + 1u) {\n let value = s[i][c];\n sum = sum + value;\n if (value != first) { same = false; }\n }\n if (same) {\n box.low[c] = first;\n box.high[c] = first;\n continue;\n }\n let mean = sum / 9.0;\n var spread = 0.0;\n for (var i = 0u; i < 9u; i = i + 1u) {\n let d = s[i][c] - mean;\n spread = spread + d * d;\n }\n let deviation = sqrt(spread / 9.0);\n box.low[c] = mean - gamma * deviation;\n box.high[c] = mean + gamma * deviation;\n }\n return box;\n}\n\n/*\n * The history pulled towards the box's centre until it is inside, by one ratio for all three\n * channels \u2014 a clamp per channel would land it on a corner and change its hue, which is coloured\n * fringing on every moving edge. A channel with no extent bounds anything that differs from it\n * infinitely, so such a history collapses onto the centre.\n */\nfn reconClipToBox(history: vec3<f32>, low: vec3<f32>, high: vec3<f32>) -> vec3<f32> {\n let centre = (low + high) * 0.5;\n var offset = history - centre;\n var extent = (high - low) * 0.5;\n var ratio = 0.0;\n var infinite = false;\n for (var c = 0u; c < 3u; c = c + 1u) {\n if (extent[c] > 1e-7) {\n ratio = max(ratio, abs(offset[c]) / extent[c]);\n } else if (abs(offset[c]) > 1e-7) {\n infinite = true;\n }\n }\n if (!infinite && !(ratio > 1.0)) { return history; }\n var scale = 0.0;\n if (!infinite) { scale = 1.0 / ratio; }\n return centre + offset * scale;\n}\n\nfn reconAccumulate(current: vec3<f32>, history: vec3<f32>, weight: f32, alpha: f32) -> vec3<f32> {\n let kept = clamp(weight, 0.0, 1.0) * (1.0 - clamp(alpha, 0.0, 1.0));\n return current * (1.0 - kept) + history * kept;\n}\n\nfn reconCatmullRom(t: f32) -> vec4<f32> {\n let t2 = t * t;\n let t3 = t2 * t;\n return vec4<f32>(\n -0.5 * t3 + t2 - 0.5 * t,\n 1.5 * t3 - 2.5 * t2 + 1.0,\n -1.5 * t3 + 2.0 * t2 + 0.5 * t,\n 0.5 * t3 - 0.5 * t2,\n );\n}\n\n/*\n * Five bilinear taps standing for the sixteen-texel Catmull-Rom: each axis's middle pair folded\n * into one tap placed between them, the corners dropped, the five weights renormalised. It is the\n * five-tap form because the taps are the expensive part \u2014 which is only true while the bilinear is\n * the sampler's, so the production module's accessor is a filtered sample and the check's is the\n * reference's own arithmetic over loaded texels.\n */\nstruct ReconTaps {\n at0: vec2<f32>,\n at1: vec2<f32>,\n at2: vec2<f32>,\n at3: vec2<f32>,\n at4: vec2<f32>,\n weights: array<f32, 5>,\n total: f32,\n}\n\nfn reconBicubicTaps(x: f32, y: f32) -> ReconTaps {\n let x1 = floor(x);\n let y1 = floor(y);\n let wx = reconCatmullRom(x - x1);\n let wy = reconCatmullRom(y - y1);\n let wx12 = wx[1] + wx[2];\n let wy12 = wy[1] + wy[2];\n let x12 = x1 + wx[2] / wx12;\n let y12 = y1 + wy[2] / wy12;\n var taps: ReconTaps;\n taps.at0 = vec2<f32>(x12, y1 - 1.0);\n taps.at1 = vec2<f32>(x1 - 1.0, y12);\n taps.at2 = vec2<f32>(x12, y12);\n taps.at3 = vec2<f32>(x1 + 2.0, y12);\n taps.at4 = vec2<f32>(x12, y1 + 2.0);\n taps.weights[0] = wx12 * wy[0];\n taps.weights[1] = wx[0] * wy12;\n taps.weights[2] = wx12 * wy12;\n taps.weights[3] = wx[3] * wy12;\n taps.weights[4] = wx12 * wy[3];\n taps.total =\n taps.weights[0] + taps.weights[1] + taps.weights[2] + taps.weights[3] + taps.weights[4];\n return taps;\n}\n\n/* A colour cannot be negative, and Catmull-Rom's negative lobes undershoot beside a bright edge. */\nfn reconSceneBicubic5(x: f32, y: f32) -> vec3<f32> {\n let taps = reconBicubicTaps(x, y);\n var sum = reconSceneBilinear(taps.at0) * taps.weights[0];\n sum = sum + reconSceneBilinear(taps.at1) * taps.weights[1];\n sum = sum + reconSceneBilinear(taps.at2) * taps.weights[2];\n sum = sum + reconSceneBilinear(taps.at3) * taps.weights[3];\n sum = sum + reconSceneBilinear(taps.at4) * taps.weights[4];\n return max(sum / taps.total, vec3<f32>(0.0));\n}\n\nfn reconHistoryBicubic5(x: f32, y: f32) -> vec4<f32> {\n let taps = reconBicubicTaps(x, y);\n var sum = reconHistoryBilinear(taps.at0) * taps.weights[0];\n sum = sum + reconHistoryBilinear(taps.at1) * taps.weights[1];\n sum = sum + reconHistoryBilinear(taps.at2) * taps.weights[2];\n sum = sum + reconHistoryBilinear(taps.at3) * taps.weights[3];\n sum = sum + reconHistoryBilinear(taps.at4) * taps.weights[4];\n return max(sum / taps.total, vec4<f32>(0.0));\n}\n\nstruct ReconWorld {\n ok: bool,\n position: vec3<f32>,\n}\n\n/* The world position a clip depth stands for, and nothing where the matrix has no answer there. */\nfn reconWorldFromDepth(m: mat4x4<f32>, u: f32, v: f32, clipDepth: f32) -> ReconWorld {\n var out: ReconWorld;\n out.position = vec3<f32>(0.0);\n out.ok = false;\n let x = u * 2.0 - 1.0;\n let y = v * 2.0 - 1.0;\n let w = m[0][3] * x + m[1][3] * y + m[2][3] * clipDepth + m[3][3];\n if (!(abs(w) > 1e-12)) { return out; }\n let p = m * vec4<f32>(x, y, clipDepth, 1.0);\n out.position = p.xyz / w;\n out.ok = all(abs(out.position) < vec3<f32>(3.4e38));\n return out;\n}\n\nstruct ReconNormal {\n ok: bool,\n normal: vec3<f32>,\n}\n\n/* Turned to face the eye, so the answer is the same whichever way the target's rows run. */\nfn reconNormalFrom(\n centre: vec3<f32>,\n right: vec3<f32>,\n up: vec3<f32>,\n eye: vec3<f32>,\n) -> ReconNormal {\n var out: ReconNormal;\n out.normal = vec3<f32>(0.0);\n out.ok = false;\n let a = right - centre;\n let b = up - centre;\n let n = cross(a, b);\n let length2 = dot(n, n);\n let scale2 = dot(a, a) * dot(b, b);\n if (!(length2 > 1e-12 * scale2)) { return out; }\n let toEye = dot(n, eye - centre);\n var sign = 1.0;\n if (toEye < 0.0) { sign = -1.0; }\n out.normal = n * (sign / sqrt(length2));\n out.ok = true;\n return out;\n}\n\nfn reconDisocclusion(\n currDepth: f32,\n histDepth: f32,\n motionLength: f32,\n normalDot: f32,\n slope: f32,\n) -> f32 {\n if (!(currDepth > 0.0) || !(histDepth > 0.0) || !(motionLength >= 0.0)) { return 0.0; }\n let disagreement = abs(currDepth - histDepth) / currDepth;\n let tolerance = recon.depthTolerance + recon.motionScale * motionLength + slope;\n let depth = 1.0 - reconSmoothstep(tolerance, 2.0 * tolerance, disagreement);\n let normal = reconSmoothstep(recon.normalFloor, recon.normalCeiling, normalDot);\n return depth * normal;\n}\n\nstruct ReconAt {\n depth: f32,\n position: vec3<f32>,\n}\n\n/* A texel's world position at its unjittered uv, and the view depth it stands at, or zero. */\nfn reconWorldAt(previous: bool, x: i32, y: i32) -> ReconAt {\n let w = i32(recon.renderSize.x);\n let h = i32(recon.renderSize.y);\n let tx = reconClampTexel(x, w);\n let ty = reconClampTexel(y, h);\n var jitter = recon.jitter;\n var m = recon.inverseViewProj;\n var z = reconDepth(tx, ty);\n if (previous) {\n jitter = recon.previousJitter;\n m = recon.previousInverseViewProj;\n z = reconPreviousDepth(tx, ty);\n }\n let u = (f32(tx) + 0.5 - jitter.x) / f32(w);\n let v = (f32(ty) + 0.5 - jitter.y) / f32(h);\n var out: ReconAt;\n out.depth = 0.0;\n out.position = vec3<f32>(0.0);\n let world = reconWorldFromDepth(m, u, v, z);\n if (!world.ok) { return out; }\n out.position = world.position;\n /* The view depth is the clip w, which the inverse's w is the reciprocal of. */\n let cw = m[0][3] * (u * 2.0 - 1.0) + m[1][3] * (v * 2.0 - 1.0) + m[2][3] * z + m[3][3];\n out.depth = 1.0 / cw;\n return out;\n}\n\n/* The neighbour on the inside of the border, so the edge texels have a normal too. */\nfn reconNormalAt(previous: bool, x: i32, y: i32) -> ReconNormal {\n var out: ReconNormal;\n out.normal = vec3<f32>(0.0);\n out.ok = false;\n let w = i32(recon.renderSize.x);\n let h = i32(recon.renderSize.y);\n let centre = reconWorldAt(previous, x, y);\n if (!(centre.depth > 0.0)) { return out; }\n var dx = -1;\n if (x + 1 < w) { dx = 1; }\n var dy = -1;\n if (y + 1 < h) { dy = 1; }\n let right = reconWorldAt(previous, x + dx, y);\n if (!(right.depth > 0.0)) { return out; }\n let up = reconWorldAt(previous, x, y + dy);\n if (!(up.depth > 0.0)) { return out; }\n var eye = recon.eye.xyz;\n if (previous) { eye = recon.previousEye.xyz; }\n return reconNormalFrom(centre.position, right.position, up.position, eye);\n}\n\n/* resolve.ts's viewDepthAt. */\nfn reconViewDepthAt(previous: bool, x: i32, y: i32) -> f32 {\n let w = i32(recon.renderSize.x);\n let h = i32(recon.renderSize.y);\n if (x < 0 || y < 0 || x >= w || y >= h) { return 0.0; }\n var jitter = recon.jitter;\n var m = recon.inverseViewProj;\n var z = 0.0;\n if (previous) {\n jitter = recon.previousJitter;\n m = recon.previousInverseViewProj;\n z = reconPreviousDepth(x, y);\n } else {\n z = reconDepth(x, y);\n }\n let u = (f32(x) + 0.5 - jitter.x) / f32(w);\n let v = (f32(y) + 0.5 - jitter.y) / f32(h);\n let cw = m[0][3] * (u * 2.0 - 1.0) + m[1][3] * (v * 2.0 - 1.0) + m[2][3] * z + m[3][3];\n if (!(abs(cw) > 1e-12)) { return 0.0; }\n return 1.0 / cw;\n}\n\n/* disocclusion.ts's planarity. */\nfn reconPlanarity(before: f32, centre: f32, after: f32) -> f32 {\n if (!(before > 0.0) || !(centre > 0.0) || !(after > 0.0)) { return 1.0; }\n let a = 1.0 / after - 1.0 / centre;\n let b = 1.0 / before - 1.0 / centre;\n let spread = abs(a) + abs(b);\n if (!(spread > 0.0)) { return 1.0; }\n return 1.0 - reconSmoothstep(RECON_PLANAR_FROM, RECON_PLANAR_TO, abs(a + b) / spread);\n}\n\n/* disocclusion.ts's surfaceSlope. */\nfn reconSurfaceSlope(before: f32, centre: f32, after: f32) -> f32 {\n let hasAfter = after > 0.0;\n let hasBefore = before > 0.0;\n if (!hasAfter) {\n if (!hasBefore) { return 0.0; }\n return abs(before - centre) / centre;\n }\n if (!hasBefore) { return abs(after - centre) / centre; }\n let a = after - centre;\n let b = before - centre;\n if (a * b > 0.0) { return 0.0; }\n return min(abs(a), abs(b)) / centre;\n}\n\nstruct ReconSurface {\n planar: f32,\n slope: f32,\n}\n\n/* resolve.ts's surfaceAt. */\nfn reconSurfaceAt(previous: bool, x: i32, y: i32, centre: f32) -> ReconSurface {\n var out: ReconSurface;\n out.planar = 1.0;\n out.slope = 0.0;\n for (var axis = 0; axis < 2; axis = axis + 1) {\n let ax = 1 - axis;\n let ay = axis;\n let after = reconViewDepthAt(previous, x + ax, y + ay);\n let before = reconViewDepthAt(previous, x - ax, y - ay);\n out.planar = out.planar * reconPlanarity(before, centre, after);\n out.slope = max(out.slope, reconSurfaceSlope(before, centre, after));\n }\n return out;\n}\n\nstruct ReconHistory {\n weight: f32,\n colour: vec4<f32>,\n /* How far the surface moved, in output pixels; read only where the weight is not zero. */\n moved: f32,\n /* How far, relative, last frame's depth missed the one expected there; -1 where none was held. */\n missed: f32,\n}\n\n/*\n * The weight a pixel's history still deserves: none on the first frame, none where the surface was\n * behind last frame's eye or off its picture, and otherwise what it gathered, scaled by whether it\n * is the same surface.\n */\nfn reconHistoryWeight(u: f32, v: f32, dx: i32, dy: i32, swing: f32) -> ReconHistory {\n var out: ReconHistory;\n out.weight = 0.0;\n out.colour = vec4<f32>(0.0);\n out.moved = 0.0;\n out.missed = -1.0;\n if (recon.hasHistory == 0u) { return out; }\n let rw = f32(recon.renderSize.x);\n let rh = f32(recon.renderSize.y);\n let motion = reconMotion(dx, dy);\n var mu = 0.0;\n var mv = 0.0;\n var expected = 0.0;\n if (motion.w > 0.5) {\n mu = motion.x;\n mv = motion.y;\n expected = motion.z;\n } else {\n /* Through the one reprojection matrix the renderer multiplied: see resolve.ts. */\n let du = (f32(dx) + 0.5 - recon.jitter.x) / rw;\n let dv = (f32(dy) + 0.5 - recon.jitter.y) / rh;\n let ndc = vec4<f32>(du * 2.0 - 1.0, dv * 2.0 - 1.0, reconDepth(dx, dy), 1.0);\n let m = recon.inverseViewProj;\n let hw = m[0][3] * ndc.x + m[1][3] * ndc.y + m[2][3] * ndc.z + m[3][3];\n if (!(hw > 1e-12)) { return out; }\n let clip = recon.reprojection * ndc;\n /*\n * A surface behind last frame's eye has a w that is not positive: the disocclusion trusts no\n * such depth, so it needs no refusal of its own here.\n */\n expected = clip.w / hw;\n mu = (clip.x / clip.w) * 0.5 + 0.5 - du;\n mv = (clip.y / clip.w) * 0.5 + 0.5 - dv;\n }\n\n out.moved = length(vec2<f32>(mu * f32(recon.outputSize.x), mv * f32(recon.outputSize.y)));\n let pu = u + mu;\n let pv = v + mv;\n if (!(pu >= 0.0 && pu <= 1.0 && pv >= 0.0 && pv <= 1.0)) { return out; }\n out.colour = reconHistoryBicubic5(\n pu * f32(recon.outputSize.x) - 0.5,\n pv * f32(recon.outputSize.y) - 0.5,\n );\n /* Last frame's depth, searched around where the dilated texel was: see resolve.ts. */\n let su = (f32(dx) + 0.5 - recon.jitter.x) / rw + mu;\n let sv = (f32(dy) + 0.5 - recon.jitter.y) / rh + mv;\n let cx = reconClampTexel(i32(reconRound(su * rw + recon.previousJitter.x - 0.5)), i32(rw));\n let cy = reconClampTexel(i32(reconRound(sv * rh + recon.previousJitter.y - 0.5)), i32(rh));\n var held = reconViewDepthAt(true, cx, cy);\n var px = cx;\n var py = cy;\n if (!(held > 0.0 && abs(held - expected) <= recon.depthTolerance * expected)) {\n held = 0.0;\n var nearest = 3.4e38;\n for (var j = -1; j <= 1; j = j + 1) {\n for (var i = -1; i <= 1; i = i + 1) {\n let tx = reconClampTexel(cx + i, i32(rw));\n let ty = reconClampTexel(cy + j, i32(rh));\n let depth = reconViewDepthAt(true, tx, ty);\n if (!(depth > 0.0)) { continue; }\n let off = abs(depth - expected);\n if (off < nearest) {\n nearest = off;\n held = depth;\n px = tx;\n py = ty;\n }\n }\n }\n }\n\n let here = reconSurfaceAt(false, dx, dy, reconViewDepthAt(false, dx, dy));\n var planar = here.planar;\n if (planar > 0.0) { planar = planar * reconSurfaceAt(true, px, py, held).planar; }\n var normalDot = 1.0;\n if (planar > 0.0) {\n let hereNormal = reconNormalAt(false, dx, dy);\n let thereNormal = reconNormalAt(true, px, py);\n if (hereNormal.ok && thereNormal.ok) {\n normalDot = 1.0 - planar * (1.0 - dot(hereNormal.normal, thereNormal.normal));\n }\n }\n if (held > 0.0 && expected > 0.0) { out.missed = abs(held - expected) / expected; }\n /* A still pixel's own held depth swing: see resolve.ts. */\n var swung = 0.0;\n if (swing > 0.0) {\n swung = RECON_DEPTH_SWING_WIDTH * swing *\n (1.0 - reconSmoothstep(RECON_STILL_FROM, RECON_STILL_TO, out.moved));\n }\n let trust = reconDisocclusion(\n expected,\n held,\n length(vec2<f32>(mu, mv)),\n normalDot,\n here.slope + swung,\n );\n out.weight = out.colour.w * trust;\n return out;\n}\n\n/* temporalAa.ts's repeats: whether two periods' means are the same, one to zero. */\nfn reconRepeats(mean: f32, previous: f32) -> f32 {\n let apart = abs(mean - previous) / max(max(mean, previous), 1.0 / 255.0);\n return 1.0 - reconSmoothstep(RECON_FLICKER_REPEAT, 2.0 * RECON_FLICKER_REPEAT, apart);\n}\n\nstruct ReconResolved {\n /* The history's next colour, before sharpening, and the weight it has gathered. */\n colour: vec4<f32>,\n /* The pixel's next flicker record: spread, this period's sum, last period's mean, repeats. */\n record: vec4<f32>,\n /* And its next held depth swing. */\n swing: f32,\n}\n\n/* One output pixel: the history's next colour and weight, and its next flicker record. */\nfn reconResolvePixel(ox: i32, oy: i32) -> ReconResolved {\n let rw = i32(recon.renderSize.x);\n let rh = i32(recon.renderSize.y);\n let ow = i32(recon.outputSize.x);\n let oh = i32(recon.outputSize.y);\n let u = (f32(ox) + 0.5) / f32(ow);\n let v = (f32(oy) + 0.5) / f32(oh);\n let jx = recon.jitter.x;\n let jy = recon.jitter.y;\n\n /* Where this frame observed the pixel, and the render read there for the gaps. */\n let rx = u * f32(rw) + jx - 0.5;\n let ry = v * f32(rh) + jy - 0.5;\n let sample = reconSceneBicubic5(rx, ry);\n let nx = reconClampTexel(i32(reconRound(rx)), rw);\n let ny = reconClampTexel(i32(reconRound(ry)), rh);\n\n /* Each texel around: its weight, its colour in the box, and its depth. */\n var nearest = reconDepth(nx, ny);\n var dx = nx;\n var dy = ny;\n var gathered = 0.0;\n var accum = vec3<f32>(0.0);\n var neighbours: array<vec3<f32>, 9>;\n for (var j = -1; j <= 1; j = j + 1) {\n for (var i = -1; i <= 1; i = i + 1) {\n let tx = reconClampTexel(nx + i, rw);\n let ty = reconClampTexel(ny + j, rh);\n let colour = reconScene(tx, ty);\n let weight = reconWeightAt(\n ((f32(tx) + 0.5 - jx) / f32(rw)) * f32(ow) - (f32(ox) + 0.5),\n ((f32(ty) + 0.5 - jy) / f32(rh)) * f32(oh) - (f32(oy) + 0.5),\n );\n gathered = gathered + weight;\n accum = accum + colour * weight;\n neighbours[(j + 1) * 3 + (i + 1)] = reconToYCoCg(colour);\n let z = reconDepth(tx, ty);\n if (z < nearest) {\n nearest = z;\n dx = tx;\n dy = ty;\n }\n }\n }\n let box = reconVarianceBox(neighbours, recon.clampGamma);\n accum = accum / gathered;\n\n /* The history, and the weight it still deserves; a tenth of each frame is always new. */\n var swing = 0.0;\n if (recon.antiFlicker != 0u) { swing = reconSwing(ox, oy); }\n let history = reconHistoryWeight(u, v, dx, dy, swing);\n var cap = (1.0 - recon.alpha) / recon.alpha;\n var low = box.low;\n var high = box.high;\n var out: ReconResolved;\n out.record = vec4<f32>(0.0);\n out.swing = 0.0;\n /* The anti-flicker, operation for operation resolve.ts's: see temporalAa.ts. */\n if (recon.antiFlicker != 0u) {\n let seen = dot(accum, vec3<f32>(0.2126, 0.7152, 0.0722));\n let memory = recon.flickerMemory;\n let width = recon.flickerWidth;\n var held = vec4<f32>(0.0, seen * memory, seen, 0.0);\n if (history.weight > 0.0) { held = reconFlicker(ox, oy); }\n var still = 0.0;\n if (history.weight > 0.0) {\n still = 1.0 - reconSmoothstep(RECON_STILL_FROM, RECON_STILL_TO, history.moved);\n }\n let apart = abs(seen - held.z);\n let within = apart <= width * held.x + 1.0 / 255.0;\n let proven = held.w > 0.75 && within;\n var widen = 0.0;\n let base = 1.0 - recon.alpha;\n var keep = base;\n if (proven) {\n widen = width * held.x * still;\n keep = base + (RECON_FLICKER_BLEND - base) * still;\n }\n low = low - vec3<f32>(widen);\n high = high + vec3<f32>(widen);\n cap = keep / (1.0 - keep);\n var sum = held.y;\n var mean = held.z;\n var repeated = 0.0;\n if (within) { repeated = held.w; }\n if (recon.periodStart != 0u) {\n if (reconRepeats(sum, mean) > 0.5) { repeated = min(1.0, repeated + 0.5); } else { repeated = 0.0; }\n mean = sum;\n sum = 0.0;\n }\n out.record = vec4<f32>(\n held.x + (apart - held.x) * memory,\n sum + seen * memory,\n mean,\n repeated * still,\n );\n /* The peak carried on, halving over a period, whatever the test said. */\n if (history.missed >= 0.0) { out.swing = max(history.missed, swing * pow(0.5, memory)); }\n }\n let kept = min(cap, history.weight);\n\n let total = kept + gathered;\n var mixed = accum;\n if (kept > 0.0) {\n let clipped = reconClipToBox(reconToYCoCg(history.colour.xyz), low, high);\n mixed = reconAccumulate(accum, reconFromYCoCg(clipped), 1.0, gathered / total);\n }\n let known = min(1.0, total / RECON_CONFIDENT_WEIGHT);\n out.colour = vec4<f32>(sample * (1.0 - known) + mixed * known, total);\n return out;\n}\n\n/*\n * The centre pushed away from its four neighbours' mean, and held inside the range of the five \u2014\n * the halo a plain unsharp mask draws beside every edge is a value outside that range.\n */\nfn reconSharpenAt(ox: i32, oy: i32) -> vec3<f32> {\n let ow = i32(recon.outputSize.x);\n let oh = i32(recon.outputSize.y);\n let centre = reconHistory(reconClampTexel(ox, ow), reconClampTexel(oy, oh)).xyz;\n var offsets = array<vec2<i32>, 4>(\n vec2<i32>(-1, 0),\n vec2<i32>(1, 0),\n vec2<i32>(0, -1),\n vec2<i32>(0, 1),\n );\n var low = centre;\n var high = centre;\n var sum = vec3<f32>(0.0);\n for (var n = 0; n < 4; n = n + 1) {\n let at = offsets[n];\n let value = reconHistory(\n reconClampTexel(ox + at.x, ow),\n reconClampTexel(oy + at.y, oh),\n ).xyz;\n sum = sum + value;\n low = min(low, value);\n high = max(high, value);\n }\n let pushed = centre + recon.sharpenAmount * (centre - sum / 4.0);\n return min(high, max(low, pushed));\n}\n";
|
|
31
31
|
/**
|
|
32
32
|
* The parity module: the core over plain float textures written in the reference's own row order,
|
|
33
33
|
* doing its own bilinear so the comparison is arithmetic rather than a tolerance on the device's
|
|
34
34
|
* sampler.
|
|
35
35
|
*
|
|
36
|
-
* Two entry points over one set of bindings. `resolveMain` writes
|
|
37
|
-
* history — and `sharpenMain` reads that history back at binding 4
|
|
36
|
+
* Two entry points over one set of bindings. `resolveMain` writes nine floats a pixel — the next
|
|
37
|
+
* history, the next flicker record and the next depth swing — and `sharpenMain` reads that history back at binding 4
|
|
38
|
+
* and writes three.
|
|
38
39
|
*/
|
|
39
40
|
export declare function reconResolveParityWgsl(): string;
|
|
40
41
|
/**
|