@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.
|
|
@@ -44,6 +67,16 @@ import {
|
|
|
44
67
|
} from './disocclusion.ts';
|
|
45
68
|
import type { DisocclusionParams } from './disocclusion.ts';
|
|
46
69
|
import { sampleBilinear, sampleHistoryBicubic5 } from './reproject.ts';
|
|
70
|
+
import {
|
|
71
|
+
TEMPORAL_FLICKER,
|
|
72
|
+
flickerBlend,
|
|
73
|
+
flickerRule,
|
|
74
|
+
flickerWidening,
|
|
75
|
+
freshFlicker,
|
|
76
|
+
nextFlicker,
|
|
77
|
+
stillness,
|
|
78
|
+
} from '../temporalAa.ts';
|
|
79
|
+
import type { Flicker, FlickerRule } from '../temporalAa.ts';
|
|
47
80
|
|
|
48
81
|
/** What a reconstruction is asked for. */
|
|
49
82
|
export interface ReconQuality {
|
|
@@ -140,6 +173,12 @@ export interface ResolveFrame {
|
|
|
140
173
|
readonly inverseViewProj: ArrayLike<number>;
|
|
141
174
|
/** Last frame's view-projection and its inverse, unjittered. */
|
|
142
175
|
readonly previousViewProj: ArrayLike<number>;
|
|
176
|
+
/**
|
|
177
|
+
* Last frame's view-projection times this frame's inverse: where a texel's clip position was
|
|
178
|
+
* last frame. Derived from the two when absent; a caller holding its matrices at a lower precision
|
|
179
|
+
* than it multiplies at passes its own product, as the renderer does.
|
|
180
|
+
*/
|
|
181
|
+
readonly reprojection?: ArrayLike<number>;
|
|
143
182
|
readonly previousInverseViewProj: ArrayLike<number>;
|
|
144
183
|
readonly eye: ArrayLike<number>;
|
|
145
184
|
readonly previousEye: ArrayLike<number>;
|
|
@@ -148,6 +187,22 @@ export interface ResolveFrame {
|
|
|
148
187
|
readonly previousJitter: ArrayLike<number>;
|
|
149
188
|
readonly quality: ReconQuality;
|
|
150
189
|
readonly disocclusion: DisocclusionParams;
|
|
190
|
+
/**
|
|
191
|
+
* Last frame's flicker records, output size, four floats a pixel — the spread, this period's
|
|
192
|
+
* running sum, the last period's mean, and how many periods running repeated, as `Flicker` holds
|
|
193
|
+
* them — or absent for the resolve without its anti-flicker.
|
|
194
|
+
*/
|
|
195
|
+
readonly flicker?: ArrayLike<number> | null;
|
|
196
|
+
/**
|
|
197
|
+
* Last frame's depth swings, output size, one float a pixel: the largest miss, relative to the
|
|
198
|
+
* depth, of last frame's depth against the one this pixel's surface should have had, seen lately
|
|
199
|
+
* — held, and halving over a period. Carried with `flicker` and read only with it.
|
|
200
|
+
*/
|
|
201
|
+
readonly depthSwing?: ArrayLike<number> | null;
|
|
202
|
+
/** Whether this frame samples the first phase of the jitter's period. */
|
|
203
|
+
readonly periodStart?: boolean;
|
|
204
|
+
/** The jitter's period: the reconstruction's own phase count, not the temporal resolve's. */
|
|
205
|
+
readonly flickerPhases?: number;
|
|
151
206
|
}
|
|
152
207
|
|
|
153
208
|
/**
|
|
@@ -170,6 +225,13 @@ export function reconstructionWeight(dx: number, dy: number): number {
|
|
|
170
225
|
*/
|
|
171
226
|
export const CONFIDENT_WEIGHT = 1;
|
|
172
227
|
|
|
228
|
+
/**
|
|
229
|
+
* How many of its held depth swings a still pixel's depth test tolerates. The peak halves over a
|
|
230
|
+
* period, so the frame before the next once-a-period miss holds just over half of it: tolerating
|
|
231
|
+
* twice the peak passes that miss outright, and three leaves the same room again.
|
|
232
|
+
*/
|
|
233
|
+
export const DEPTH_SWING_WIDTH = 3;
|
|
234
|
+
|
|
173
235
|
const SAMPLE = new Float32Array(3);
|
|
174
236
|
const GATHERED = new Float64Array(3);
|
|
175
237
|
const HISTORY = new Float32Array(4);
|
|
@@ -185,6 +247,36 @@ const UP = new Float64Array(3);
|
|
|
185
247
|
const NORMAL = new Float64Array(3);
|
|
186
248
|
const PREVIOUS_NORMAL = new Float64Array(3);
|
|
187
249
|
const CLIP = new Float64Array(4);
|
|
250
|
+
/** The last frame's reprojection, derived once a frame rather than once a pixel. */
|
|
251
|
+
const REPROJECTION = new Float64Array(16);
|
|
252
|
+
let reprojected: ResolveFrame | null = null;
|
|
253
|
+
|
|
254
|
+
/** `frame.reprojection`, or the product of its two matrices, multiplied once per frame. */
|
|
255
|
+
function reprojectionOf(frame: ResolveFrame): ArrayLike<number> {
|
|
256
|
+
if (frame.reprojection !== undefined) return frame.reprojection;
|
|
257
|
+
if (reprojected !== frame) {
|
|
258
|
+
reprojected = frame;
|
|
259
|
+
const a = frame.previousViewProj;
|
|
260
|
+
const b = frame.inverseViewProj;
|
|
261
|
+
for (let c = 0; c < 4; c += 1) {
|
|
262
|
+
for (let r = 0; r < 4; r += 1) {
|
|
263
|
+
let sum = 0;
|
|
264
|
+
for (let k = 0; k < 4; k += 1) sum += (a[k * 4 + r] as number) * (b[c * 4 + k] as number);
|
|
265
|
+
REPROJECTION[c * 4 + r] = sum;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return REPROJECTION;
|
|
270
|
+
}
|
|
271
|
+
const HELD: Flicker = { spread: 0, sum: 0, mean: 0, repeated: 0 };
|
|
272
|
+
const CARRIED: Flicker = { spread: 0, sum: 0, mean: 0, repeated: 0 };
|
|
273
|
+
/** How far, in output pixels, the last `historyWeight` found its surface had moved. */
|
|
274
|
+
let moved = 0;
|
|
275
|
+
/** How far, relative, last frame's depth missed the one expected there; -1 where nothing was held. */
|
|
276
|
+
let missed = -1;
|
|
277
|
+
/** The rule for the last period asked for, kept: one is made per period, not per pixel. */
|
|
278
|
+
let rulePhases = 0;
|
|
279
|
+
let rule: FlickerRule = TEMPORAL_FLICKER;
|
|
188
280
|
|
|
189
281
|
function clampTexel(value: number, size: number): number {
|
|
190
282
|
return value < 0 ? 0 : value >= size ? size - 1 : value;
|
|
@@ -296,7 +388,15 @@ function normalAt(
|
|
|
296
388
|
* was behind last frame's eye or off its picture, and otherwise what it gathered, scaled by whether it
|
|
297
389
|
* is the same surface. Reads `HISTORY`; writes the history's colour there.
|
|
298
390
|
*/
|
|
299
|
-
function historyWeight(
|
|
391
|
+
function historyWeight(
|
|
392
|
+
frame: ResolveFrame,
|
|
393
|
+
u: number,
|
|
394
|
+
v: number,
|
|
395
|
+
dx: number,
|
|
396
|
+
dy: number,
|
|
397
|
+
swing: number,
|
|
398
|
+
): number {
|
|
399
|
+
missed = -1;
|
|
300
400
|
if (!frame.hasHistory) return 0;
|
|
301
401
|
const rw = frame.renderWidth;
|
|
302
402
|
const rh = frame.renderHeight;
|
|
@@ -309,28 +409,46 @@ function historyWeight(frame: ResolveFrame, u: number, v: number, dx: number, dy
|
|
|
309
409
|
mv = frame.motion[flagAt + 1] as number;
|
|
310
410
|
expected = frame.motion[flagAt + 2] as number;
|
|
311
411
|
} else {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
412
|
+
/*
|
|
413
|
+
* **Through one matrix, last frame's view-projection times this frame's inverse**, rather than
|
|
414
|
+
* out to a world position and back. The two are the same arithmetic in exact numbers; in single
|
|
415
|
+
* precision the round trip is not the identity — a near plane a quarter of a metre from a far one
|
|
416
|
+
* five kilometres off — and a still camera measured a few hundredths of a pixel of motion growing
|
|
417
|
+
* out from the middle of the frame, which crept every still history and kept every pixel from
|
|
418
|
+
* the anti-flicker's stillness. The product is taken where the precision is (`reprojection`),
|
|
419
|
+
* and for a still camera it is the identity.
|
|
420
|
+
*/
|
|
421
|
+
const du = (dx + 0.5 - (frame.jitter[0] as number)) / rw;
|
|
422
|
+
const dv = (dy + 0.5 - (frame.jitter[1] as number)) / rh;
|
|
423
|
+
const x = du * 2 - 1;
|
|
424
|
+
const y = dv * 2 - 1;
|
|
425
|
+
const z = frame.depth[dy * rw + dx] as number;
|
|
426
|
+
const inverse = frame.inverseViewProj;
|
|
427
|
+
/* The homogeneous w of the world position: none, or behind the eye, is no surface. */
|
|
428
|
+
const hw =
|
|
429
|
+
(inverse[3] as number) * x +
|
|
430
|
+
(inverse[7] as number) * y +
|
|
431
|
+
(inverse[11] as number) * z +
|
|
432
|
+
(inverse[15] as number);
|
|
433
|
+
if (!(hw > 1e-12)) return 0;
|
|
434
|
+
const m = reprojectionOf(frame);
|
|
316
435
|
for (let r = 0; r < 4; r += 1) {
|
|
317
436
|
CLIP[r] =
|
|
318
|
-
(m[r] as number) *
|
|
319
|
-
(m[4 + r] as number) *
|
|
320
|
-
(m[8 + r] as number) *
|
|
437
|
+
(m[r] as number) * x +
|
|
438
|
+
(m[4 + r] as number) * y +
|
|
439
|
+
(m[8 + r] as number) * z +
|
|
321
440
|
(m[12 + r] as number);
|
|
322
441
|
}
|
|
323
442
|
/*
|
|
324
443
|
* A surface behind last frame's eye has a w that is not positive: the disocclusion trusts no
|
|
325
444
|
* such depth, so it needs no refusal of its own here.
|
|
326
445
|
*/
|
|
327
|
-
expected = CLIP[3] as number;
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
mu = ((CLIP[0] as number) / expected) * 0.5 + 0.5 - du;
|
|
331
|
-
mv = ((CLIP[1] as number) / expected) * 0.5 + 0.5 - dv;
|
|
446
|
+
expected = (CLIP[3] as number) / hw;
|
|
447
|
+
mu = ((CLIP[0] as number) / (CLIP[3] as number)) * 0.5 + 0.5 - du;
|
|
448
|
+
mv = ((CLIP[1] as number) / (CLIP[3] as number)) * 0.5 + 0.5 - dv;
|
|
332
449
|
}
|
|
333
450
|
|
|
451
|
+
moved = Math.hypot(mu * frame.outputWidth, mv * frame.outputHeight);
|
|
334
452
|
const pu = u + mu;
|
|
335
453
|
const pv = v + mv;
|
|
336
454
|
if (!(pu >= 0 && pu <= 1 && pv >= 0 && pv <= 1)) return 0;
|
|
@@ -440,22 +558,32 @@ function historyWeight(frame: ResolveFrame, u: number, v: number, dx: number, dy
|
|
|
440
558
|
(NORMAL[2] as number) * (PREVIOUS_NORMAL[2] as number);
|
|
441
559
|
normalDot = 1 - planar * (1 - cosine);
|
|
442
560
|
}
|
|
561
|
+
if (held > 0 && expected > 0) missed = Math.abs(held - expected) / expected;
|
|
562
|
+
/* A still pixel's own depth swing, as wide as the anti-flicker's box: see the header. */
|
|
563
|
+
const swung = swing > 0 ? DEPTH_SWING_WIDTH * swing * stillness(moved) : 0;
|
|
443
564
|
const trust = disocclusionWeight(
|
|
444
565
|
expected,
|
|
445
566
|
held,
|
|
446
567
|
Math.hypot(mu, mv),
|
|
447
568
|
normalDot,
|
|
448
569
|
frame.disocclusion,
|
|
449
|
-
slope,
|
|
570
|
+
slope + swung,
|
|
450
571
|
);
|
|
451
572
|
return (HISTORY[3] as number) * trust;
|
|
452
573
|
}
|
|
453
574
|
|
|
454
575
|
/**
|
|
455
576
|
* One output pixel: the history's next colour, before sharpening, and the weight it has gathered —
|
|
456
|
-
* four values into `out`.
|
|
577
|
+
* four values into `out` — and, where the frame carries records, the pixel's next one into `record`.
|
|
457
578
|
*/
|
|
458
|
-
export function resolvePixel(
|
|
579
|
+
export function resolvePixel(
|
|
580
|
+
frame: ResolveFrame,
|
|
581
|
+
ox: number,
|
|
582
|
+
oy: number,
|
|
583
|
+
out: Float32Array,
|
|
584
|
+
record?: Float32Array,
|
|
585
|
+
swingOut?: Float32Array,
|
|
586
|
+
): void {
|
|
459
587
|
const rw = frame.renderWidth;
|
|
460
588
|
const rh = frame.renderHeight;
|
|
461
589
|
const ow = frame.outputWidth;
|
|
@@ -508,8 +636,58 @@ export function resolvePixel(frame: ResolveFrame, ox: number, oy: number, out: F
|
|
|
508
636
|
for (let c = 0; c < 3; c += 1) GATHERED[c] = (GATHERED[c] as number) / gathered;
|
|
509
637
|
|
|
510
638
|
/* 3 and 4. The history, and the weight it still deserves. */
|
|
511
|
-
const
|
|
512
|
-
const
|
|
639
|
+
const alpha = frame.quality.alpha;
|
|
640
|
+
const records = frame.flicker;
|
|
641
|
+
const anti = records !== undefined && records !== null;
|
|
642
|
+
if (anti) {
|
|
643
|
+
const phases = frame.flickerPhases ?? 0;
|
|
644
|
+
if (phases !== rulePhases) {
|
|
645
|
+
rulePhases = phases;
|
|
646
|
+
rule = phases > 0 ? flickerRule(phases) : TEMPORAL_FLICKER;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
const swings = frame.depthSwing;
|
|
650
|
+
const swing =
|
|
651
|
+
anti && swings !== undefined && swings !== null ? (swings[oy * ow + ox] as number) : 0;
|
|
652
|
+
const weight = historyWeight(frame, u, v, dx, dy, swing);
|
|
653
|
+
let cap = (1 - alpha) / alpha;
|
|
654
|
+
/* The anti-flicker: a proven pixel's box widened by its spread, and more of its history kept. */
|
|
655
|
+
if (anti) {
|
|
656
|
+
const sample =
|
|
657
|
+
(GATHERED[0] as number) * 0.2126 +
|
|
658
|
+
(GATHERED[1] as number) * 0.7152 +
|
|
659
|
+
(GATHERED[2] as number) * 0.0722;
|
|
660
|
+
const at = (oy * ow + ox) * 4;
|
|
661
|
+
if (weight > 0) {
|
|
662
|
+
HELD.spread = records[at] as number;
|
|
663
|
+
HELD.sum = records[at + 1] as number;
|
|
664
|
+
HELD.mean = records[at + 2] as number;
|
|
665
|
+
HELD.repeated = records[at + 3] as number;
|
|
666
|
+
} else {
|
|
667
|
+
freshFlicker(HELD, sample, rule);
|
|
668
|
+
}
|
|
669
|
+
const still = weight > 0 ? stillness(moved) : 0;
|
|
670
|
+
const widen = flickerWidening(HELD, sample, still, rule);
|
|
671
|
+
for (let c = 0; c < 3; c += 1) {
|
|
672
|
+
LOW[c] = (LOW[c] as number) - widen;
|
|
673
|
+
HIGH[c] = (HIGH[c] as number) + widen;
|
|
674
|
+
}
|
|
675
|
+
const keep = flickerBlend(1 - alpha, HELD, sample, still, rule);
|
|
676
|
+
cap = keep / (1 - keep);
|
|
677
|
+
nextFlicker(CARRIED, HELD, sample, still, frame.periodStart === true, rule);
|
|
678
|
+
if (record !== undefined) {
|
|
679
|
+
record[0] = CARRIED.spread;
|
|
680
|
+
record[1] = CARRIED.sum;
|
|
681
|
+
record[2] = CARRIED.mean;
|
|
682
|
+
record[3] = CARRIED.repeated;
|
|
683
|
+
}
|
|
684
|
+
/* The peak carried on, halving over a period, whatever the test said; none where nothing was
|
|
685
|
+
held to miss. */
|
|
686
|
+
if (swingOut !== undefined) {
|
|
687
|
+
swingOut[0] = missed >= 0 ? Math.max(missed, swing * 0.5 ** rule.memory) : 0;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
const kept = Math.min(cap, weight);
|
|
513
691
|
|
|
514
692
|
/* 5. Clipped, mixed by weight, and the gaps filled from the render. */
|
|
515
693
|
const total = kept + gathered;
|
|
@@ -529,6 +707,8 @@ export function resolvePixel(frame: ResolveFrame, ox: number, oy: number, out: F
|
|
|
529
707
|
}
|
|
530
708
|
|
|
531
709
|
const TAP = new Float32Array(4);
|
|
710
|
+
const RECORD = new Float32Array(4);
|
|
711
|
+
const SWING = new Float32Array(1);
|
|
532
712
|
const CENTRE = new Float32Array(4);
|
|
533
713
|
const AROUND = new Float32Array(12);
|
|
534
714
|
const SHOWN = new Float32Array(3);
|
|
@@ -541,19 +721,25 @@ const OFFSETS: readonly (readonly [number, number])[] = [
|
|
|
541
721
|
|
|
542
722
|
/**
|
|
543
723
|
* The whole frame: the next history into `history`, four floats a pixel, and the picture shown into
|
|
544
|
-
* `shown`, three — the history sharpened against the four direct neighbours it has just been given
|
|
724
|
+
* `shown`, three — the history sharpened against the four direct neighbours it has just been given —
|
|
725
|
+
* and, where the frame carries records, the next ones into `records`, four floats a pixel, and the
|
|
726
|
+
* next depth swings into `swings`, one.
|
|
545
727
|
*/
|
|
546
728
|
export function resolveFrame(
|
|
547
729
|
frame: ResolveFrame,
|
|
548
730
|
history: Float32Array,
|
|
549
731
|
shown: Float32Array,
|
|
732
|
+
records?: Float32Array,
|
|
733
|
+
swings?: Float32Array,
|
|
550
734
|
): void {
|
|
551
735
|
const ow = frame.outputWidth;
|
|
552
736
|
const oh = frame.outputHeight;
|
|
553
737
|
for (let y = 0; y < oh; y += 1) {
|
|
554
738
|
for (let x = 0; x < ow; x += 1) {
|
|
555
|
-
resolvePixel(frame, x, y, TAP);
|
|
739
|
+
resolvePixel(frame, x, y, TAP, RECORD, SWING);
|
|
556
740
|
history.set(TAP, (y * ow + x) * 4);
|
|
741
|
+
if (records !== undefined) records.set(RECORD, (y * ow + x) * 4);
|
|
742
|
+
if (swings !== undefined) swings[y * ow + x] = SWING[0] as number;
|
|
557
743
|
}
|
|
558
744
|
}
|
|
559
745
|
for (let y = 0; y < oh; y += 1) {
|
|
@@ -37,6 +37,9 @@ export type ShadowFilterTaps = 4 | 8 | 12;
|
|
|
37
37
|
export type DirectionalShadowDepthLayers = 1 | 2;
|
|
38
38
|
export type WaterReflectionFilterTaps = 1 | 5 | 9;
|
|
39
39
|
|
|
40
|
+
/** How light through glass is coloured: see `RenderQuality.glassShadows`. */
|
|
41
|
+
export type GlassShadows = 'full' | 'half' | 'off';
|
|
42
|
+
|
|
40
43
|
export interface RenderQuality {
|
|
41
44
|
/**
|
|
42
45
|
* The transfer curve applied at the end of every shaded pass.
|
|
@@ -149,6 +152,25 @@ export interface RenderQuality {
|
|
|
149
152
|
readonly lightVolumeSamples: number;
|
|
150
153
|
/** Independently composited static sun depths; two preserves common overlaps. */
|
|
151
154
|
readonly directionalShadowDepthLayers: DirectionalShadowDepthLayers;
|
|
155
|
+
/**
|
|
156
|
+
* Whether light through glass takes the glass's colour, and at what size.
|
|
157
|
+
*
|
|
158
|
+
* **`'full'`, the default**: every shadow a glass caster reaches gains a layer holding the depth of
|
|
159
|
+
* the nearest pane and a tint layer holding what the panes let through, at the size of the depth
|
|
160
|
+
* map beside it — 8 bytes a texel, so 32 MB for a 2048 sun map and 8 MB for a 1024 lamp layer.
|
|
161
|
+
* **`'half'`** halves the tint layers only (the glass depth must match its array): 20 MB and 5 MB,
|
|
162
|
+
* and a coloured patch softer than an opaque shadow's edge. **`'off'`** is glass before this
|
|
163
|
+
* existed: a pane casts nothing at all.
|
|
164
|
+
*
|
|
165
|
+
* Nothing is allocated until a glass caster first reaches a shadow, so a scene with no glass pays
|
|
166
|
+
* no memory and no pass whatever this says. **The lit shader is the exception**: `'full'` and
|
|
167
|
+
* `'half'` compile the glass lookup into it, and a lookup that finds no glass and returns at its
|
|
168
|
+
* first line still costs the lit pass its registers — measured on the courtyard at night, 0.27 ms
|
|
169
|
+
* at 720p on WebGPU, 0.42 ms on WebGL2 and 1.2 ms at 4K. `'off'` compiles none of it, so a world
|
|
170
|
+
* that never casts through glass is that much faster for saying so. What would make `'full'` wrong
|
|
171
|
+
* is a phone budget with a lot of glass; that is what `'half'` is for. See `glassShadow.ts`.
|
|
172
|
+
*/
|
|
173
|
+
readonly glassShadows: GlassShadows;
|
|
152
174
|
/** Horizontal world-space reach over which a directional shadow dissolves. */
|
|
153
175
|
readonly directionalShadowMaxDistance: number;
|
|
154
176
|
/** Maximum horizontal projection per vertical metre before low-angle shadows vanish. */
|
|
@@ -556,6 +578,20 @@ export interface RenderQuality {
|
|
|
556
578
|
* the render is a quarter of the output and no reconstruction holds an edge through that. A
|
|
557
579
|
* negative number is off, not a clamp to the bottom: it cannot mean "a little".
|
|
558
580
|
*
|
|
581
|
+
* **What it needs from you.** A `createMover()` per moving object, passed with that object's
|
|
582
|
+
* draws where `drawMesh` takes last frame's matrix — the renderer keeps last frame's model and,
|
|
583
|
+
* for a skinned draw, last frame's pose. Instanced batches and meshes rewritten with
|
|
584
|
+
* `updateMesh` need nothing: the batch and the mesh are their own identity. And `cameraCut()` at
|
|
585
|
+
* every cut, because nothing detects one: without it a cut blends the previous shot into the
|
|
586
|
+
* next. Blended draws — translucent meshes, particles, plumes, film, bolts, lines, world text —
|
|
587
|
+
* are drawn after the upscale at the output size and need nothing either; light volumes stay in
|
|
588
|
+
* the reconstructed picture, being a march that keeps its history.
|
|
589
|
+
*
|
|
590
|
+
* **What it cannot give back** is the resolution it draws at: an edge that never moves is softer
|
|
591
|
+
* than a native frame's at any ratio, because two thirds of the pixels at 1.5 are a guess made
|
|
592
|
+
* sharp. Measured on `demo/dev/ghost.html` against a native frame at four samples, still edges
|
|
593
|
+
* err by about 8 levels where a native frame at one sample errs by 3.
|
|
594
|
+
*
|
|
559
595
|
* `?recon=` is how it gets A/B'd in one reload.
|
|
560
596
|
*/
|
|
561
597
|
readonly reconstruction: number;
|
|
@@ -838,6 +874,7 @@ export const DEFAULT_RENDER_QUALITY: Readonly<RenderQuality> = Object.freeze({
|
|
|
838
874
|
shadowFilterTaps: MAX_SHADOW_FILTER_TAPS,
|
|
839
875
|
lightVolumeSamples: 32,
|
|
840
876
|
directionalShadowDepthLayers: 2,
|
|
877
|
+
glassShadows: 'full',
|
|
841
878
|
directionalShadowMaxDistance: 6,
|
|
842
879
|
directionalShadowMaxSlope: 3,
|
|
843
880
|
pointShadowRebakeDistance: 0.001,
|
|
@@ -998,6 +1035,7 @@ export function resolveRenderQuality(options: RenderQualityOptions = {}): Readon
|
|
|
998
1035
|
lightVolumeSamples: options.lightVolumeSamples ?? DEFAULT_RENDER_QUALITY.lightVolumeSamples,
|
|
999
1036
|
directionalShadowDepthLayers:
|
|
1000
1037
|
options.directionalShadowDepthLayers ?? DEFAULT_RENDER_QUALITY.directionalShadowDepthLayers,
|
|
1038
|
+
glassShadows: options.glassShadows ?? DEFAULT_RENDER_QUALITY.glassShadows,
|
|
1001
1039
|
directionalShadowMaxDistance:
|
|
1002
1040
|
options.directionalShadowMaxDistance ?? DEFAULT_RENDER_QUALITY.directionalShadowMaxDistance,
|
|
1003
1041
|
directionalShadowMaxSlope:
|
|
@@ -1128,6 +1166,15 @@ export function resolveRenderQuality(options: RenderQualityOptions = {}): Readon
|
|
|
1128
1166
|
`RenderQuality.directionalShadowDepthLayers must be 1 or 2, got ${quality.directionalShadowDepthLayers}`,
|
|
1129
1167
|
);
|
|
1130
1168
|
}
|
|
1169
|
+
if (
|
|
1170
|
+
quality.glassShadows !== 'full' &&
|
|
1171
|
+
quality.glassShadows !== 'half' &&
|
|
1172
|
+
quality.glassShadows !== 'off'
|
|
1173
|
+
) {
|
|
1174
|
+
throw new Error(
|
|
1175
|
+
`RenderQuality.glassShadows must be 'full', 'half' or 'off', got ${String(quality.glassShadows)}`,
|
|
1176
|
+
);
|
|
1177
|
+
}
|
|
1131
1178
|
if (quality.plumeNoiseOctaves > 3) {
|
|
1132
1179
|
throw new Error(
|
|
1133
1180
|
`RenderQuality.plumeNoiseOctaves must be <= 3, got ${quality.plumeNoiseOctaves}`,
|
|
@@ -72,6 +72,10 @@ export function recordingGl(
|
|
|
72
72
|
const programFragments = new WeakMap<object, string>();
|
|
73
73
|
const calls: { name: string; args: unknown[] }[] = [];
|
|
74
74
|
const constants: Record<string, number> = {
|
|
75
|
+
/* The canvas's size, as a real context reports its drawing buffer; a function here made every
|
|
76
|
+
jitter and every per-pixel offset divided by it a NaN. */
|
|
77
|
+
drawingBufferWidth: 640,
|
|
78
|
+
drawingBufferHeight: 480,
|
|
75
79
|
ACTIVE_UNIFORMS: 0x8b86,
|
|
76
80
|
ACTIVE_ATTRIBUTES: 0x8b89,
|
|
77
81
|
COMPILE_STATUS: 0x8b81,
|
|
@@ -129,6 +133,18 @@ export function recordingGl(
|
|
|
129
133
|
COLOR_BUFFER_BIT: 0x00004000,
|
|
130
134
|
/* Named so a test can tell a mesh that declared itself deforming from one that did not. */
|
|
131
135
|
ARRAY_BUFFER: 0x8892,
|
|
136
|
+
/* Named so a blend a pass sets can be read back as numbers: glass multiplies what it lets
|
|
137
|
+
through, and a test that cannot tell DST_COLOR from a recorder cannot tell it multiplies. */
|
|
138
|
+
ZERO: 0,
|
|
139
|
+
DST_ALPHA: 0x0304,
|
|
140
|
+
DST_COLOR: 0x0306,
|
|
141
|
+
FUNC_ADD: 0x8006,
|
|
142
|
+
COLOR: 0x1800,
|
|
143
|
+
DEPTH: 0x1801,
|
|
144
|
+
/* Named so a copy can be read as from-and-to: a blit's two framebuffers are the whole claim. */
|
|
145
|
+
READ_FRAMEBUFFER: 0x8ca8,
|
|
146
|
+
DRAW_FRAMEBUFFER: 0x8ca9,
|
|
147
|
+
BLEND: 0x0be2,
|
|
132
148
|
STATIC_DRAW: 0x88e4,
|
|
133
149
|
DYNAMIC_DRAW: 0x88e8,
|
|
134
150
|
};
|
|
@@ -82,6 +82,8 @@ export class SceneTarget {
|
|
|
82
82
|
private colorCopyFramebuffer: WebGLFramebuffer | null = null;
|
|
83
83
|
/** Whether this frame has filled it. One snapshot serves every refracting draw in a frame. */
|
|
84
84
|
private colorCopyFilled = false;
|
|
85
|
+
/** Whether this frame's copy has its mip chain, which frosted glass reads. See `snapshotColor`. */
|
|
86
|
+
private colorCopyMips = false;
|
|
85
87
|
/**
|
|
86
88
|
* The colour grade, as a 3D texture on unit 4, and the table it was built from.
|
|
87
89
|
*
|
|
@@ -275,12 +277,24 @@ export class SceneTarget {
|
|
|
275
277
|
*/
|
|
276
278
|
if (this.colorCopy === null) this.colorCopy = gl.createTexture();
|
|
277
279
|
gl.bindTexture(gl.TEXTURE_2D, this.colorCopy);
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
280
|
+
/*
|
|
281
|
+
* **Every level, so glass can read a blurrier one.** Frosted glass reads the copy at a level its
|
|
282
|
+
* frost picks, and a texture sampled through a mipmapping filter with a level missing is
|
|
283
|
+
* incomplete and reads black — so the chain is allocated here, once per size, and filled only on
|
|
284
|
+
* a frame that draws glass (`snapshotColor`). Plain refraction reads level 0, as it always has.
|
|
285
|
+
*/
|
|
286
|
+
const levels = Math.floor(Math.log2(Math.max(width, height))) + 1;
|
|
287
|
+
for (let level = 0; level < levels; level += 1) {
|
|
288
|
+
const w = Math.max(1, width >> level);
|
|
289
|
+
const h = Math.max(1, height >> level);
|
|
290
|
+
if (this.floatColor) {
|
|
291
|
+
gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA16F, w, h, 0, gl.RGBA, gl.HALF_FLOAT, null);
|
|
292
|
+
} else {
|
|
293
|
+
gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA, w, h, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
|
294
|
+
}
|
|
282
295
|
}
|
|
283
|
-
gl.texParameteri(gl.TEXTURE_2D, gl.
|
|
296
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL, levels - 1);
|
|
297
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
|
|
284
298
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
285
299
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
286
300
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
@@ -419,6 +433,7 @@ export class SceneTarget {
|
|
|
419
433
|
/* Last frame's snapshot is last frame's scene. See `snapshotDepth`. */
|
|
420
434
|
this.depthCopyFilled = false;
|
|
421
435
|
this.colorCopyFilled = false;
|
|
436
|
+
this.colorCopyMips = false;
|
|
422
437
|
this.bind();
|
|
423
438
|
}
|
|
424
439
|
|
|
@@ -516,12 +531,21 @@ export class SceneTarget {
|
|
|
516
531
|
* Null where the copy could not be made, and a caller must read that as "do not refract" rather
|
|
517
532
|
* than as a black texture — refracting against black paints every pane the colour of a hole.
|
|
518
533
|
*/
|
|
519
|
-
snapshotColor(
|
|
534
|
+
snapshotColor(
|
|
535
|
+
afresh = false,
|
|
536
|
+
/** Fill the copy's mip chain as well, once a frame: what frosted glass reads. */
|
|
537
|
+
mips = false,
|
|
538
|
+
): WebGLTexture | null {
|
|
520
539
|
if (this.colorCopy === null || this.colorCopyFramebuffer === null) return null;
|
|
521
|
-
if (this.colorCopyFilled && !afresh)
|
|
540
|
+
if (this.colorCopyFilled && !afresh) {
|
|
541
|
+
if (mips) this.fillColorCopyMips();
|
|
542
|
+
return this.colorCopy;
|
|
543
|
+
}
|
|
522
544
|
const { gl } = this;
|
|
523
545
|
const source = this.msaaFramebuffer ?? this.framebuffer;
|
|
524
546
|
if (source === null) return null;
|
|
547
|
+
const drawing = gl.getParameter(gl.DRAW_FRAMEBUFFER_BINDING) as WebGLFramebuffer | null;
|
|
548
|
+
const reading = gl.getParameter(gl.READ_FRAMEBUFFER_BINDING) as WebGLFramebuffer | null;
|
|
525
549
|
|
|
526
550
|
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, source);
|
|
527
551
|
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this.colorCopyFramebuffer);
|
|
@@ -546,13 +570,33 @@ export class SceneTarget {
|
|
|
546
570
|
gl.NEAREST,
|
|
547
571
|
);
|
|
548
572
|
const refused = gl.getError() !== gl.NO_ERROR;
|
|
549
|
-
/*
|
|
550
|
-
|
|
573
|
+
/*
|
|
574
|
+
* **Back to what the caller was drawing into, whichever that was** — read before the copy and
|
|
575
|
+
* handed back, never assumed. It was the scene's framebuffer, assumed, and a pane drawn into a
|
|
576
|
+
* reflection probe's face then left the scene's bound: the probe attached its next face to it,
|
|
577
|
+
* a face-sized colour beside a screen-sized depth, and every draw after refused as incomplete.
|
|
578
|
+
*/
|
|
579
|
+
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, drawing);
|
|
580
|
+
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, reading);
|
|
551
581
|
if (refused) return null;
|
|
552
582
|
this.colorCopyFilled = true;
|
|
583
|
+
this.colorCopyMips = false;
|
|
584
|
+
if (mips) this.fillColorCopyMips();
|
|
553
585
|
return this.colorCopy;
|
|
554
586
|
}
|
|
555
587
|
|
|
588
|
+
/** The copy's mip chain from its level 0, once per copy. */
|
|
589
|
+
private fillColorCopyMips(): void {
|
|
590
|
+
if (this.colorCopyMips || this.colorCopy === null) return;
|
|
591
|
+
const { gl } = this;
|
|
592
|
+
/* Bound on whichever unit is active and handed back, so the caller's texture there survives. */
|
|
593
|
+
const previous = gl.getParameter(gl.TEXTURE_BINDING_2D) as WebGLTexture | null;
|
|
594
|
+
gl.bindTexture(gl.TEXTURE_2D, this.colorCopy);
|
|
595
|
+
gl.generateMipmap(gl.TEXTURE_2D);
|
|
596
|
+
gl.bindTexture(gl.TEXTURE_2D, previous);
|
|
597
|
+
this.colorCopyMips = true;
|
|
598
|
+
}
|
|
599
|
+
|
|
556
600
|
snapshotDepth(
|
|
557
601
|
/**
|
|
558
602
|
* Take the copy again even though one has already been taken this frame.
|
|
@@ -573,6 +617,8 @@ export class SceneTarget {
|
|
|
573
617
|
const { gl } = this;
|
|
574
618
|
const source = this.msaaFramebuffer ?? this.framebuffer;
|
|
575
619
|
if (source === null) return null;
|
|
620
|
+
const drawing = gl.getParameter(gl.DRAW_FRAMEBUFFER_BINDING) as WebGLFramebuffer | null;
|
|
621
|
+
const reading = gl.getParameter(gl.READ_FRAMEBUFFER_BINDING) as WebGLFramebuffer | null;
|
|
576
622
|
|
|
577
623
|
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, source);
|
|
578
624
|
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this.depthCopyFramebuffer);
|
|
@@ -595,8 +641,9 @@ export class SceneTarget {
|
|
|
595
641
|
gl.NEAREST,
|
|
596
642
|
);
|
|
597
643
|
const refused = gl.getError() !== gl.NO_ERROR;
|
|
598
|
-
/* Back to what the caller was drawing into,
|
|
599
|
-
gl.bindFramebuffer(gl.
|
|
644
|
+
/* Back to what the caller was drawing into, read rather than assumed: see `snapshotColor`. */
|
|
645
|
+
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, drawing);
|
|
646
|
+
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, reading);
|
|
600
647
|
if (refused) return null;
|
|
601
648
|
this.depthCopyFilled = true;
|
|
602
649
|
return this.depthCopy;
|
|
@@ -708,6 +755,8 @@ export class SceneTarget {
|
|
|
708
755
|
/** The projection's x and y scales, which turn a world radius into a screen one. */
|
|
709
756
|
readonly projScale: Float32Array;
|
|
710
757
|
readonly invProjection: Float32Array;
|
|
758
|
+
/** Where the frame's jitter put the occlusion, in uv: see the shader's `uAoOffset`. */
|
|
759
|
+
readonly offset?: Float32Array;
|
|
711
760
|
},
|
|
712
761
|
bloom?: {
|
|
713
762
|
readonly strength: number;
|
|
@@ -976,6 +1025,8 @@ export class SceneTarget {
|
|
|
976
1025
|
this.uniforms['uAoStrength'] ?? null,
|
|
977
1026
|
aoTexture === null ? 0 : (ao?.strength ?? 0),
|
|
978
1027
|
);
|
|
1028
|
+
const offset = ao?.offset;
|
|
1029
|
+
gl.uniform2f(this.uniforms['uAoOffset'] ?? null, offset?.[0] ?? 0, offset?.[1] ?? 0);
|
|
979
1030
|
|
|
980
1031
|
/* Bloom on its own unit, and the placeholder for the same reason as the one above: a
|
|
981
1032
|
driver may fetch a texture's descriptor before it evaluates the branch that skips the
|