@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
|
@@ -30,6 +30,29 @@
|
|
|
30
30
|
* the render read bicubically fills the gap. The history keeps colour and weight; the picture shown
|
|
31
31
|
* is it sharpened.
|
|
32
32
|
*
|
|
33
|
+
* **And the temporal resolve's anti-flicker, where the caller carries its records** (`temporalAa.ts`
|
|
34
|
+
* says why it works and what it gives up). A thread finer than a render texel is caught in some
|
|
35
|
+
* phases of the jitter and missed in others, and in a phase that misses it the nine texels can all
|
|
36
|
+
* miss it: the box collapses onto what is behind, the history is clipped out, and a still picture
|
|
37
|
+
* flashes at every fine edge each period. A still camera over a still scene gathers every pixel the
|
|
38
|
+
* same way each period, so each pixel keeps a record of this frame's gathered luma over the period,
|
|
39
|
+
* and once two periods running have repeated its box widens by its own spread and it keeps more of
|
|
40
|
+
* its history. **The record stays at the pixel rather than following the reprojection**, because the
|
|
41
|
+
* proof is only ever for a pixel that has not moved; it starts over wherever the history did not
|
|
42
|
+
* survive, and is forgotten in proportion to motion.
|
|
43
|
+
*
|
|
44
|
+
* **And a still pixel whose depth has been seen to swing is not a disocclusion.** A rail finer than
|
|
45
|
+
* a texel in front of a wall is caught in some phases and missed in others, so last frame can hold
|
|
46
|
+
* the wall where this frame holds the rail with nothing moved — and the depth test threw the history
|
|
47
|
+
* away, leaving the record nothing to keep. So each pixel also keeps the largest relative miss of
|
|
48
|
+
* last frame's depth it has seen lately, kept whatever the test says, and while still its tolerance
|
|
49
|
+
* widens to `DEPTH_SWING_WIDTH` of it — the temporal resolve's answer to a crack of light round a
|
|
50
|
+
* door, as a held peak rather than its running mean: a rail caught in one phase of eighteen misses
|
|
51
|
+
* once a period, and a mean has worn that miss down to a thirtieth by the next. **What it gives up**
|
|
52
|
+
* is the depth test's word at such a pixel while still: a surface arriving there inside the swing
|
|
53
|
+
* keeps the history, and the neighbourhood clip, which the unproven sample of an arrival leaves
|
|
54
|
+
* unwidened, is what takes it out.
|
|
55
|
+
*
|
|
33
56
|
* **Conventions.** Images are rows from `v = 0` upward. Depths are clip depths in the matrices'
|
|
34
57
|
* convention, OpenGL's here — the nearest is the smallest — and the renderer converts its reversed
|
|
35
58
|
* buffer before a sample reaches this arithmetic, as the temporal resolve already does.
|
|
@@ -37,6 +60,7 @@
|
|
|
37
60
|
import { clipToBox, rgbToYCoCg, varianceBox, yCoCgToRgb } from './clamp.js';
|
|
38
61
|
import { disocclusionWeight, normalFromPositions, planarity, surfaceSlope, worldPositionFromDepth, } from './disocclusion.js';
|
|
39
62
|
import { sampleBilinear, sampleHistoryBicubic5 } from './reproject.js';
|
|
63
|
+
import { TEMPORAL_FLICKER, flickerBlend, flickerRule, flickerWidening, freshFlicker, nextFlicker, stillness, } from '../temporalAa.js';
|
|
40
64
|
/**
|
|
41
65
|
* A tenth of each frame kept, as the temporal resolve keeps; a box of a standard deviation and a
|
|
42
66
|
* quarter; a light sharpen. At two thirds of the output a side.
|
|
@@ -98,6 +122,12 @@ export function reconstructionWeight(dx, dy) {
|
|
|
98
122
|
* pixel away.
|
|
99
123
|
*/
|
|
100
124
|
export const CONFIDENT_WEIGHT = 1;
|
|
125
|
+
/**
|
|
126
|
+
* How many of its held depth swings a still pixel's depth test tolerates. The peak halves over a
|
|
127
|
+
* period, so the frame before the next once-a-period miss holds just over half of it: tolerating
|
|
128
|
+
* twice the peak passes that miss outright, and three leaves the same room again.
|
|
129
|
+
*/
|
|
130
|
+
export const DEPTH_SWING_WIDTH = 3;
|
|
101
131
|
const SAMPLE = new Float32Array(3);
|
|
102
132
|
const GATHERED = new Float64Array(3);
|
|
103
133
|
const HISTORY = new Float32Array(4);
|
|
@@ -113,6 +143,37 @@ const UP = new Float64Array(3);
|
|
|
113
143
|
const NORMAL = new Float64Array(3);
|
|
114
144
|
const PREVIOUS_NORMAL = new Float64Array(3);
|
|
115
145
|
const CLIP = new Float64Array(4);
|
|
146
|
+
/** The last frame's reprojection, derived once a frame rather than once a pixel. */
|
|
147
|
+
const REPROJECTION = new Float64Array(16);
|
|
148
|
+
let reprojected = null;
|
|
149
|
+
/** `frame.reprojection`, or the product of its two matrices, multiplied once per frame. */
|
|
150
|
+
function reprojectionOf(frame) {
|
|
151
|
+
if (frame.reprojection !== undefined)
|
|
152
|
+
return frame.reprojection;
|
|
153
|
+
if (reprojected !== frame) {
|
|
154
|
+
reprojected = frame;
|
|
155
|
+
const a = frame.previousViewProj;
|
|
156
|
+
const b = frame.inverseViewProj;
|
|
157
|
+
for (let c = 0; c < 4; c += 1) {
|
|
158
|
+
for (let r = 0; r < 4; r += 1) {
|
|
159
|
+
let sum = 0;
|
|
160
|
+
for (let k = 0; k < 4; k += 1)
|
|
161
|
+
sum += a[k * 4 + r] * b[c * 4 + k];
|
|
162
|
+
REPROJECTION[c * 4 + r] = sum;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return REPROJECTION;
|
|
167
|
+
}
|
|
168
|
+
const HELD = { spread: 0, sum: 0, mean: 0, repeated: 0 };
|
|
169
|
+
const CARRIED = { spread: 0, sum: 0, mean: 0, repeated: 0 };
|
|
170
|
+
/** How far, in output pixels, the last `historyWeight` found its surface had moved. */
|
|
171
|
+
let moved = 0;
|
|
172
|
+
/** How far, relative, last frame's depth missed the one expected there; -1 where nothing was held. */
|
|
173
|
+
let missed = -1;
|
|
174
|
+
/** The rule for the last period asked for, kept: one is made per period, not per pixel. */
|
|
175
|
+
let rulePhases = 0;
|
|
176
|
+
let rule = TEMPORAL_FLICKER;
|
|
116
177
|
function clampTexel(value, size) {
|
|
117
178
|
return value < 0 ? 0 : value >= size ? size - 1 : value;
|
|
118
179
|
}
|
|
@@ -184,7 +245,8 @@ function normalAt(depths, inverse, jitter, eye, width, height, x, y, out) {
|
|
|
184
245
|
* was behind last frame's eye or off its picture, and otherwise what it gathered, scaled by whether it
|
|
185
246
|
* is the same surface. Reads `HISTORY`; writes the history's colour there.
|
|
186
247
|
*/
|
|
187
|
-
function historyWeight(frame, u, v, dx, dy) {
|
|
248
|
+
function historyWeight(frame, u, v, dx, dy, swing) {
|
|
249
|
+
missed = -1;
|
|
188
250
|
if (!frame.hasHistory)
|
|
189
251
|
return 0;
|
|
190
252
|
const rw = frame.renderWidth;
|
|
@@ -199,27 +261,45 @@ function historyWeight(frame, u, v, dx, dy) {
|
|
|
199
261
|
expected = frame.motion[flagAt + 2];
|
|
200
262
|
}
|
|
201
263
|
else {
|
|
202
|
-
|
|
264
|
+
/*
|
|
265
|
+
* **Through one matrix, last frame's view-projection times this frame's inverse**, rather than
|
|
266
|
+
* out to a world position and back. The two are the same arithmetic in exact numbers; in single
|
|
267
|
+
* precision the round trip is not the identity — a near plane a quarter of a metre from a far one
|
|
268
|
+
* five kilometres off — and a still camera measured a few hundredths of a pixel of motion growing
|
|
269
|
+
* out from the middle of the frame, which crept every still history and kept every pixel from
|
|
270
|
+
* the anti-flicker's stillness. The product is taken where the precision is (`reprojection`),
|
|
271
|
+
* and for a still camera it is the identity.
|
|
272
|
+
*/
|
|
273
|
+
const du = (dx + 0.5 - frame.jitter[0]) / rw;
|
|
274
|
+
const dv = (dy + 0.5 - frame.jitter[1]) / rh;
|
|
275
|
+
const x = du * 2 - 1;
|
|
276
|
+
const y = dv * 2 - 1;
|
|
277
|
+
const z = frame.depth[dy * rw + dx];
|
|
278
|
+
const inverse = frame.inverseViewProj;
|
|
279
|
+
/* The homogeneous w of the world position: none, or behind the eye, is no surface. */
|
|
280
|
+
const hw = inverse[3] * x +
|
|
281
|
+
inverse[7] * y +
|
|
282
|
+
inverse[11] * z +
|
|
283
|
+
inverse[15];
|
|
284
|
+
if (!(hw > 1e-12))
|
|
203
285
|
return 0;
|
|
204
|
-
|
|
205
|
-
const m = frame.previousViewProj;
|
|
286
|
+
const m = reprojectionOf(frame);
|
|
206
287
|
for (let r = 0; r < 4; r += 1) {
|
|
207
288
|
CLIP[r] =
|
|
208
|
-
m[r] *
|
|
209
|
-
m[4 + r] *
|
|
210
|
-
m[8 + r] *
|
|
289
|
+
m[r] * x +
|
|
290
|
+
m[4 + r] * y +
|
|
291
|
+
m[8 + r] * z +
|
|
211
292
|
m[12 + r];
|
|
212
293
|
}
|
|
213
294
|
/*
|
|
214
295
|
* A surface behind last frame's eye has a w that is not positive: the disocclusion trusts no
|
|
215
296
|
* such depth, so it needs no refusal of its own here.
|
|
216
297
|
*/
|
|
217
|
-
expected = CLIP[3];
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
mu = (CLIP[0] / expected) * 0.5 + 0.5 - du;
|
|
221
|
-
mv = (CLIP[1] / expected) * 0.5 + 0.5 - dv;
|
|
298
|
+
expected = CLIP[3] / hw;
|
|
299
|
+
mu = (CLIP[0] / CLIP[3]) * 0.5 + 0.5 - du;
|
|
300
|
+
mv = (CLIP[1] / CLIP[3]) * 0.5 + 0.5 - dv;
|
|
222
301
|
}
|
|
302
|
+
moved = Math.hypot(mu * frame.outputWidth, mv * frame.outputHeight);
|
|
223
303
|
const pu = u + mu;
|
|
224
304
|
const pv = v + mv;
|
|
225
305
|
if (!(pu >= 0 && pu <= 1 && pv >= 0 && pv <= 1))
|
|
@@ -284,14 +364,18 @@ function historyWeight(frame, u, v, dx, dy) {
|
|
|
284
364
|
NORMAL[2] * PREVIOUS_NORMAL[2];
|
|
285
365
|
normalDot = 1 - planar * (1 - cosine);
|
|
286
366
|
}
|
|
287
|
-
|
|
367
|
+
if (held > 0 && expected > 0)
|
|
368
|
+
missed = Math.abs(held - expected) / expected;
|
|
369
|
+
/* A still pixel's own depth swing, as wide as the anti-flicker's box: see the header. */
|
|
370
|
+
const swung = swing > 0 ? DEPTH_SWING_WIDTH * swing * stillness(moved) : 0;
|
|
371
|
+
const trust = disocclusionWeight(expected, held, Math.hypot(mu, mv), normalDot, frame.disocclusion, slope + swung);
|
|
288
372
|
return HISTORY[3] * trust;
|
|
289
373
|
}
|
|
290
374
|
/**
|
|
291
375
|
* One output pixel: the history's next colour, before sharpening, and the weight it has gathered —
|
|
292
|
-
* four values into `out`.
|
|
376
|
+
* four values into `out` — and, where the frame carries records, the pixel's next one into `record`.
|
|
293
377
|
*/
|
|
294
|
-
export function resolvePixel(frame, ox, oy, out) {
|
|
378
|
+
export function resolvePixel(frame, ox, oy, out, record, swingOut) {
|
|
295
379
|
const rw = frame.renderWidth;
|
|
296
380
|
const rh = frame.renderHeight;
|
|
297
381
|
const ow = frame.outputWidth;
|
|
@@ -339,8 +423,57 @@ export function resolvePixel(frame, ox, oy, out) {
|
|
|
339
423
|
for (let c = 0; c < 3; c += 1)
|
|
340
424
|
GATHERED[c] = GATHERED[c] / gathered;
|
|
341
425
|
/* 3 and 4. The history, and the weight it still deserves. */
|
|
342
|
-
const
|
|
343
|
-
const
|
|
426
|
+
const alpha = frame.quality.alpha;
|
|
427
|
+
const records = frame.flicker;
|
|
428
|
+
const anti = records !== undefined && records !== null;
|
|
429
|
+
if (anti) {
|
|
430
|
+
const phases = frame.flickerPhases ?? 0;
|
|
431
|
+
if (phases !== rulePhases) {
|
|
432
|
+
rulePhases = phases;
|
|
433
|
+
rule = phases > 0 ? flickerRule(phases) : TEMPORAL_FLICKER;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
const swings = frame.depthSwing;
|
|
437
|
+
const swing = anti && swings !== undefined && swings !== null ? swings[oy * ow + ox] : 0;
|
|
438
|
+
const weight = historyWeight(frame, u, v, dx, dy, swing);
|
|
439
|
+
let cap = (1 - alpha) / alpha;
|
|
440
|
+
/* The anti-flicker: a proven pixel's box widened by its spread, and more of its history kept. */
|
|
441
|
+
if (anti) {
|
|
442
|
+
const sample = GATHERED[0] * 0.2126 +
|
|
443
|
+
GATHERED[1] * 0.7152 +
|
|
444
|
+
GATHERED[2] * 0.0722;
|
|
445
|
+
const at = (oy * ow + ox) * 4;
|
|
446
|
+
if (weight > 0) {
|
|
447
|
+
HELD.spread = records[at];
|
|
448
|
+
HELD.sum = records[at + 1];
|
|
449
|
+
HELD.mean = records[at + 2];
|
|
450
|
+
HELD.repeated = records[at + 3];
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
freshFlicker(HELD, sample, rule);
|
|
454
|
+
}
|
|
455
|
+
const still = weight > 0 ? stillness(moved) : 0;
|
|
456
|
+
const widen = flickerWidening(HELD, sample, still, rule);
|
|
457
|
+
for (let c = 0; c < 3; c += 1) {
|
|
458
|
+
LOW[c] = LOW[c] - widen;
|
|
459
|
+
HIGH[c] = HIGH[c] + widen;
|
|
460
|
+
}
|
|
461
|
+
const keep = flickerBlend(1 - alpha, HELD, sample, still, rule);
|
|
462
|
+
cap = keep / (1 - keep);
|
|
463
|
+
nextFlicker(CARRIED, HELD, sample, still, frame.periodStart === true, rule);
|
|
464
|
+
if (record !== undefined) {
|
|
465
|
+
record[0] = CARRIED.spread;
|
|
466
|
+
record[1] = CARRIED.sum;
|
|
467
|
+
record[2] = CARRIED.mean;
|
|
468
|
+
record[3] = CARRIED.repeated;
|
|
469
|
+
}
|
|
470
|
+
/* The peak carried on, halving over a period, whatever the test said; none where nothing was
|
|
471
|
+
held to miss. */
|
|
472
|
+
if (swingOut !== undefined) {
|
|
473
|
+
swingOut[0] = missed >= 0 ? Math.max(missed, swing * 0.5 ** rule.memory) : 0;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
const kept = Math.min(cap, weight);
|
|
344
477
|
/* 5. Clipped, mixed by weight, and the gaps filled from the render. */
|
|
345
478
|
const total = kept + gathered;
|
|
346
479
|
if (kept > 0) {
|
|
@@ -359,6 +492,8 @@ export function resolvePixel(frame, ox, oy, out) {
|
|
|
359
492
|
out[3] = total;
|
|
360
493
|
}
|
|
361
494
|
const TAP = new Float32Array(4);
|
|
495
|
+
const RECORD = new Float32Array(4);
|
|
496
|
+
const SWING = new Float32Array(1);
|
|
362
497
|
const CENTRE = new Float32Array(4);
|
|
363
498
|
const AROUND = new Float32Array(12);
|
|
364
499
|
const SHOWN = new Float32Array(3);
|
|
@@ -370,15 +505,21 @@ const OFFSETS = [
|
|
|
370
505
|
];
|
|
371
506
|
/**
|
|
372
507
|
* The whole frame: the next history into `history`, four floats a pixel, and the picture shown into
|
|
373
|
-
* `shown`, three — the history sharpened against the four direct neighbours it has just been given
|
|
508
|
+
* `shown`, three — the history sharpened against the four direct neighbours it has just been given —
|
|
509
|
+
* and, where the frame carries records, the next ones into `records`, four floats a pixel, and the
|
|
510
|
+
* next depth swings into `swings`, one.
|
|
374
511
|
*/
|
|
375
|
-
export function resolveFrame(frame, history, shown) {
|
|
512
|
+
export function resolveFrame(frame, history, shown, records, swings) {
|
|
376
513
|
const ow = frame.outputWidth;
|
|
377
514
|
const oh = frame.outputHeight;
|
|
378
515
|
for (let y = 0; y < oh; y += 1) {
|
|
379
516
|
for (let x = 0; x < ow; x += 1) {
|
|
380
|
-
resolvePixel(frame, x, y, TAP);
|
|
517
|
+
resolvePixel(frame, x, y, TAP, RECORD, SWING);
|
|
381
518
|
history.set(TAP, (y * ow + x) * 4);
|
|
519
|
+
if (records !== undefined)
|
|
520
|
+
records.set(RECORD, (y * ow + x) * 4);
|
|
521
|
+
if (swings !== undefined)
|
|
522
|
+
swings[y * ow + x] = SWING[0];
|
|
382
523
|
}
|
|
383
524
|
}
|
|
384
525
|
for (let y = 0; y < oh; y += 1) {
|
|
@@ -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
|
}
|
|
@@ -29,6 +29,10 @@ export function recordingGl(options = {}) {
|
|
|
29
29
|
const programFragments = new WeakMap();
|
|
30
30
|
const calls = [];
|
|
31
31
|
const constants = {
|
|
32
|
+
/* The canvas's size, as a real context reports its drawing buffer; a function here made every
|
|
33
|
+
jitter and every per-pixel offset divided by it a NaN. */
|
|
34
|
+
drawingBufferWidth: 640,
|
|
35
|
+
drawingBufferHeight: 480,
|
|
32
36
|
ACTIVE_UNIFORMS: 0x8b86,
|
|
33
37
|
ACTIVE_ATTRIBUTES: 0x8b89,
|
|
34
38
|
COMPILE_STATUS: 0x8b81,
|
|
@@ -86,6 +90,18 @@ export function recordingGl(options = {}) {
|
|
|
86
90
|
COLOR_BUFFER_BIT: 0x00004000,
|
|
87
91
|
/* Named so a test can tell a mesh that declared itself deforming from one that did not. */
|
|
88
92
|
ARRAY_BUFFER: 0x8892,
|
|
93
|
+
/* Named so a blend a pass sets can be read back as numbers: glass multiplies what it lets
|
|
94
|
+
through, and a test that cannot tell DST_COLOR from a recorder cannot tell it multiplies. */
|
|
95
|
+
ZERO: 0,
|
|
96
|
+
DST_ALPHA: 0x0304,
|
|
97
|
+
DST_COLOR: 0x0306,
|
|
98
|
+
FUNC_ADD: 0x8006,
|
|
99
|
+
COLOR: 0x1800,
|
|
100
|
+
DEPTH: 0x1801,
|
|
101
|
+
/* Named so a copy can be read as from-and-to: a blit's two framebuffers are the whole claim. */
|
|
102
|
+
READ_FRAMEBUFFER: 0x8ca8,
|
|
103
|
+
DRAW_FRAMEBUFFER: 0x8ca9,
|
|
104
|
+
BLEND: 0x0be2,
|
|
89
105
|
STATIC_DRAW: 0x88e4,
|
|
90
106
|
DYNAMIC_DRAW: 0x88e8,
|
|
91
107
|
};
|
|
@@ -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.
|
|
@@ -287,6 +293,8 @@ export declare class SceneTarget {
|
|
|
287
293
|
/** The projection's x and y scales, which turn a world radius into a screen one. */
|
|
288
294
|
readonly projScale: Float32Array;
|
|
289
295
|
readonly invProjection: Float32Array;
|
|
296
|
+
/** Where the frame's jitter put the occlusion, in uv: see the shader's `uAoOffset`. */
|
|
297
|
+
readonly offset?: Float32Array;
|
|
290
298
|
}, bloom?: {
|
|
291
299
|
readonly strength: number;
|
|
292
300
|
/** In scene units, which is why this wants `keepsRange`. See `BloomPass`. */
|
|
@@ -76,6 +76,8 @@ export class SceneTarget {
|
|
|
76
76
|
colorCopyFramebuffer = null;
|
|
77
77
|
/** Whether this frame has filled it. One snapshot serves every refracting draw in a frame. */
|
|
78
78
|
colorCopyFilled = false;
|
|
79
|
+
/** Whether this frame's copy has its mip chain, which frosted glass reads. See `snapshotColor`. */
|
|
80
|
+
colorCopyMips = false;
|
|
79
81
|
/**
|
|
80
82
|
* The colour grade, as a 3D texture on unit 4, and the table it was built from.
|
|
81
83
|
*
|
|
@@ -241,13 +243,25 @@ export class SceneTarget {
|
|
|
241
243
|
if (this.colorCopy === null)
|
|
242
244
|
this.colorCopy = gl.createTexture();
|
|
243
245
|
gl.bindTexture(gl.TEXTURE_2D, this.colorCopy);
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
246
|
+
/*
|
|
247
|
+
* **Every level, so glass can read a blurrier one.** Frosted glass reads the copy at a level its
|
|
248
|
+
* frost picks, and a texture sampled through a mipmapping filter with a level missing is
|
|
249
|
+
* incomplete and reads black — so the chain is allocated here, once per size, and filled only on
|
|
250
|
+
* a frame that draws glass (`snapshotColor`). Plain refraction reads level 0, as it always has.
|
|
251
|
+
*/
|
|
252
|
+
const levels = Math.floor(Math.log2(Math.max(width, height))) + 1;
|
|
253
|
+
for (let level = 0; level < levels; level += 1) {
|
|
254
|
+
const w = Math.max(1, width >> level);
|
|
255
|
+
const h = Math.max(1, height >> level);
|
|
256
|
+
if (this.floatColor) {
|
|
257
|
+
gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA16F, w, h, 0, gl.RGBA, gl.HALF_FLOAT, null);
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA, w, h, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
|
261
|
+
}
|
|
249
262
|
}
|
|
250
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.
|
|
263
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL, levels - 1);
|
|
264
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
|
|
251
265
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
252
266
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
253
267
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
@@ -359,6 +373,7 @@ export class SceneTarget {
|
|
|
359
373
|
/* Last frame's snapshot is last frame's scene. See `snapshotDepth`. */
|
|
360
374
|
this.depthCopyFilled = false;
|
|
361
375
|
this.colorCopyFilled = false;
|
|
376
|
+
this.colorCopyMips = false;
|
|
362
377
|
this.bind();
|
|
363
378
|
}
|
|
364
379
|
/**
|
|
@@ -449,15 +464,22 @@ export class SceneTarget {
|
|
|
449
464
|
* Null where the copy could not be made, and a caller must read that as "do not refract" rather
|
|
450
465
|
* than as a black texture — refracting against black paints every pane the colour of a hole.
|
|
451
466
|
*/
|
|
452
|
-
snapshotColor(afresh = false
|
|
467
|
+
snapshotColor(afresh = false,
|
|
468
|
+
/** Fill the copy's mip chain as well, once a frame: what frosted glass reads. */
|
|
469
|
+
mips = false) {
|
|
453
470
|
if (this.colorCopy === null || this.colorCopyFramebuffer === null)
|
|
454
471
|
return null;
|
|
455
|
-
if (this.colorCopyFilled && !afresh)
|
|
472
|
+
if (this.colorCopyFilled && !afresh) {
|
|
473
|
+
if (mips)
|
|
474
|
+
this.fillColorCopyMips();
|
|
456
475
|
return this.colorCopy;
|
|
476
|
+
}
|
|
457
477
|
const { gl } = this;
|
|
458
478
|
const source = this.msaaFramebuffer ?? this.framebuffer;
|
|
459
479
|
if (source === null)
|
|
460
480
|
return null;
|
|
481
|
+
const drawing = gl.getParameter(gl.DRAW_FRAMEBUFFER_BINDING);
|
|
482
|
+
const reading = gl.getParameter(gl.READ_FRAMEBUFFER_BINDING);
|
|
461
483
|
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, source);
|
|
462
484
|
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this.colorCopyFramebuffer);
|
|
463
485
|
/* Drained first, for the reason `resolve` gives at length: `getError` reports whatever is
|
|
@@ -470,13 +492,34 @@ export class SceneTarget {
|
|
|
470
492
|
choice only decides which refusals are possible. */
|
|
471
493
|
gl.blitFramebuffer(0, 0, this.width, this.height, 0, 0, this.width, this.height, gl.COLOR_BUFFER_BIT, gl.NEAREST);
|
|
472
494
|
const refused = gl.getError() !== gl.NO_ERROR;
|
|
473
|
-
/*
|
|
474
|
-
|
|
495
|
+
/*
|
|
496
|
+
* **Back to what the caller was drawing into, whichever that was** — read before the copy and
|
|
497
|
+
* handed back, never assumed. It was the scene's framebuffer, assumed, and a pane drawn into a
|
|
498
|
+
* reflection probe's face then left the scene's bound: the probe attached its next face to it,
|
|
499
|
+
* a face-sized colour beside a screen-sized depth, and every draw after refused as incomplete.
|
|
500
|
+
*/
|
|
501
|
+
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, drawing);
|
|
502
|
+
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, reading);
|
|
475
503
|
if (refused)
|
|
476
504
|
return null;
|
|
477
505
|
this.colorCopyFilled = true;
|
|
506
|
+
this.colorCopyMips = false;
|
|
507
|
+
if (mips)
|
|
508
|
+
this.fillColorCopyMips();
|
|
478
509
|
return this.colorCopy;
|
|
479
510
|
}
|
|
511
|
+
/** The copy's mip chain from its level 0, once per copy. */
|
|
512
|
+
fillColorCopyMips() {
|
|
513
|
+
if (this.colorCopyMips || this.colorCopy === null)
|
|
514
|
+
return;
|
|
515
|
+
const { gl } = this;
|
|
516
|
+
/* Bound on whichever unit is active and handed back, so the caller's texture there survives. */
|
|
517
|
+
const previous = gl.getParameter(gl.TEXTURE_BINDING_2D);
|
|
518
|
+
gl.bindTexture(gl.TEXTURE_2D, this.colorCopy);
|
|
519
|
+
gl.generateMipmap(gl.TEXTURE_2D);
|
|
520
|
+
gl.bindTexture(gl.TEXTURE_2D, previous);
|
|
521
|
+
this.colorCopyMips = true;
|
|
522
|
+
}
|
|
480
523
|
snapshotDepth(
|
|
481
524
|
/**
|
|
482
525
|
* Take the copy again even though one has already been taken this frame.
|
|
@@ -499,6 +542,8 @@ export class SceneTarget {
|
|
|
499
542
|
const source = this.msaaFramebuffer ?? this.framebuffer;
|
|
500
543
|
if (source === null)
|
|
501
544
|
return null;
|
|
545
|
+
const drawing = gl.getParameter(gl.DRAW_FRAMEBUFFER_BINDING);
|
|
546
|
+
const reading = gl.getParameter(gl.READ_FRAMEBUFFER_BINDING);
|
|
502
547
|
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, source);
|
|
503
548
|
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this.depthCopyFramebuffer);
|
|
504
549
|
/* Drained first, for the reason `resolve` gives at length: `getError` reports whatever is
|
|
@@ -509,8 +554,9 @@ export class SceneTarget {
|
|
|
509
554
|
}
|
|
510
555
|
gl.blitFramebuffer(0, 0, this.width, this.height, 0, 0, this.width, this.height, gl.DEPTH_BUFFER_BIT, gl.NEAREST);
|
|
511
556
|
const refused = gl.getError() !== gl.NO_ERROR;
|
|
512
|
-
/* Back to what the caller was drawing into,
|
|
513
|
-
gl.bindFramebuffer(gl.
|
|
557
|
+
/* Back to what the caller was drawing into, read rather than assumed: see `snapshotColor`. */
|
|
558
|
+
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, drawing);
|
|
559
|
+
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, reading);
|
|
514
560
|
if (refused)
|
|
515
561
|
return null;
|
|
516
562
|
this.depthCopyFilled = true;
|
|
@@ -788,6 +834,8 @@ export class SceneTarget {
|
|
|
788
834
|
gl.bindTexture(gl.TEXTURE_2D, aoTexture ?? scene);
|
|
789
835
|
gl.uniform1i(this.uniforms['uAo'] ?? null, 2);
|
|
790
836
|
gl.uniform1f(this.uniforms['uAoStrength'] ?? null, aoTexture === null ? 0 : (ao?.strength ?? 0));
|
|
837
|
+
const offset = ao?.offset;
|
|
838
|
+
gl.uniform2f(this.uniforms['uAoOffset'] ?? null, offset?.[0] ?? 0, offset?.[1] ?? 0);
|
|
791
839
|
/* Bloom on its own unit, and the placeholder for the same reason as the one above: a
|
|
792
840
|
driver may fetch a texture's descriptor before it evaluates the branch that skips the
|
|
793
841
|
read, and an incomplete texture is not a thing to hand it. */
|
|
@@ -23,7 +23,7 @@ export declare const DEPTH_INSTANCED_VERT: string;
|
|
|
23
23
|
*/
|
|
24
24
|
export declare const DEPTH_CUTOUT_VERT: string;
|
|
25
25
|
export declare const DEPTH_INSTANCED_CUTOUT_VERT: string;
|
|
26
|
-
export declare const DEPTH_FRAG = "#version 300 es\nprecision highp float;\nin vec4 vLightPosition;\n\nuniform highp
|
|
26
|
+
export declare const DEPTH_FRAG = "#version 300 es\nprecision highp float;\nin vec4 vLightPosition;\n\nuniform highp sampler2DArray uPreviousShadowMap;\nuniform int uPeelShadowLayer;\n\nvoid main() {\n if (uPeelShadowLayer != 0) {\n vec3 p = vLightPosition.xyz / vLightPosition.w;\n vec2 uv = p.xy * 0.5 + 0.5;\n float previousDepth = texture(uPreviousShadowMap, vec3(uv, 0.0)).r;\n // Discard the already-recorded surface and everything in front of it. The\n // ordinary depth test then stores the next independently fading occluder.\n if (gl_FragCoord.z <= previousDepth + 0.00001) discard;\n }\n}\n";
|
|
27
27
|
/**
|
|
28
28
|
* The cutout caster's depth: the plain pass, and a texel of alpha under the cutoff writes nothing.
|
|
29
29
|
*
|
|
@@ -37,3 +37,18 @@ export declare const DEPTH_FRAG = "#version 300 es\nprecision highp float;\nin v
|
|
|
37
37
|
* map takes the next.
|
|
38
38
|
*/
|
|
39
39
|
export declare const DEPTH_CUTOUT_FRAG: string;
|
|
40
|
+
/**
|
|
41
|
+
* The glass tint's vertex stages: every way a caster is drawn, with the world position handed on.
|
|
42
|
+
*
|
|
43
|
+
* **The same five as the depth pass, and for the same reason**: a pane must cast its colour from
|
|
44
|
+
* exactly where it casts its depth, deformed and placed the same way, or the coloured patch parts
|
|
45
|
+
* from the pane's own shadow edge. The only addition is `vGlassWorld`.
|
|
46
|
+
*/
|
|
47
|
+
export declare const GLASS_TINT_VERT: string;
|
|
48
|
+
export declare const GLASS_TINT_SKINNED_VERT: string;
|
|
49
|
+
export declare const GLASS_TINT_INSTANCED_VERT: string;
|
|
50
|
+
export declare const GLASS_TINT_CUTOUT_VERT: string;
|
|
51
|
+
export declare const GLASS_TINT_INSTANCED_CUTOUT_VERT: string;
|
|
52
|
+
export declare const GLASS_TINT_FRAG = "#version 300 es\nprecision highp float;\nin vec4 vLightPosition;\nin vec3 vGlassWorld;\n\n/* rgb: transmission times tint; a: clarity, one less the frost. */\nuniform vec4 uGlassPane;\n/* xyz: the light's position (w 1) or the direction toward it (w 0). */\nuniform vec4 uGlassLight;\n\nout vec4 outTint;\n\nvoid main() {\n\n vec3 n = normalize(cross(dFdx(vGlassWorld), dFdy(vGlassWorld)));\n vec3 toLight = uGlassLight.w > 0.5 ? normalize(uGlassLight.xyz - vGlassWorld) : uGlassLight.xyz;\n float cosLight = abs(dot(n, toLight));\n float reflected = 0.04 + 0.96 * pow(1.0 - cosLight, 5.0);\n outTint = vec4(uGlassPane.rgb * (1.0 - reflected), uGlassPane.a);\n\n}\n";
|
|
53
|
+
/** A cut-out pane: its colour lands only where its depth does, by the depth cutout's own test. */
|
|
54
|
+
export declare const GLASS_TINT_CUTOUT_FRAG: string;
|