@driftengine/core 3.63.0 → 4.0.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 +111 -0
- package/NOTICE +20 -0
- package/README.md +1 -1
- package/dist/core/loop.js +18 -2
- package/dist/core/storage.js +5 -0
- package/dist/geometry/meshBuilder.d.ts +1 -1
- package/dist/geometry/meshBuilder.js +6 -2
- package/dist/index.d.ts +69 -4
- package/dist/index.js +112 -2
- package/dist/input/input.js +6 -1
- package/dist/math/exact.d.ts +13 -3
- package/dist/math/exact.js +68 -3
- package/dist/math/frustum.d.ts +17 -2
- package/dist/math/frustum.js +24 -0
- package/dist/render/backend/api.d.ts +34 -1
- package/dist/render/backend/api.js +22 -1
- package/dist/render/backend/budget.d.ts +7 -6
- package/dist/render/backend/budget.js +7 -6
- package/dist/render/backend/createRenderer.d.ts +57 -0
- package/dist/render/backend/createRenderer.js +51 -2
- package/dist/render/backend/materialChanges.d.ts +41 -0
- package/dist/render/backend/materialChanges.js +38 -0
- package/dist/render/backend/select.js +8 -0
- package/dist/render/backend/webgl2/renderer.d.ts +80 -1
- package/dist/render/backend/webgl2/renderer.js +208 -10
- package/dist/render/backend/webgpu/buffers.d.ts +20 -1
- package/dist/render/backend/webgpu/buffers.js +116 -58
- package/dist/render/backend/webgpu/fieldCompose.d.ts +156 -0
- package/dist/render/backend/webgpu/fieldCompose.js +396 -0
- package/dist/render/backend/webgpu/giFieldPass.d.ts +66 -0
- package/dist/render/backend/webgpu/giFieldPass.js +137 -0
- package/dist/render/backend/webgpu/gpuDrivenPass.d.ts +728 -0
- package/dist/render/backend/webgpu/gpuDrivenPass.js +2473 -0
- package/dist/render/backend/webgpu/linePass.d.ts +4 -14
- package/dist/render/backend/webgpu/linePass.js +4 -33
- package/dist/render/backend/webgpu/probeBake.d.ts +148 -0
- package/dist/render/backend/webgpu/probeBake.js +493 -0
- package/dist/render/backend/webgpu/renderer.d.ts +379 -24
- package/dist/render/backend/webgpu/renderer.js +1369 -76
- package/dist/render/backend/webgpu/scatterPass.js +15 -1
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +19 -9
- package/dist/render/backend/webgpu/surfaceTexturePass.js +58 -32
- package/dist/render/causticsRenderer.d.ts +3 -1
- package/dist/render/causticsRenderer.js +4 -1
- package/dist/render/depthConvention.d.ts +12 -0
- package/dist/render/depthConvention.js +14 -0
- package/dist/render/deviceFeatures.d.ts +32 -0
- package/dist/render/deviceFeatures.js +41 -0
- package/dist/render/fog.d.ts +79 -0
- package/dist/render/fog.js +138 -0
- package/dist/render/frame/alias.d.ts +30 -0
- package/dist/render/frame/alias.js +61 -0
- package/dist/render/frame/deps.d.ts +37 -0
- package/dist/render/frame/deps.js +122 -0
- package/dist/render/frame/flushGraph.d.ts +43 -0
- package/dist/render/frame/flushGraph.js +84 -0
- package/dist/render/frame/graphSchedule.d.ts +49 -0
- package/dist/render/frame/graphSchedule.js +120 -0
- package/dist/render/frame/index.d.ts +15 -0
- package/dist/render/frame/index.js +8 -0
- package/dist/render/frame/lifetime.d.ts +24 -0
- package/dist/render/frame/lifetime.js +36 -0
- package/dist/render/frame/passTimings.d.ts +42 -0
- package/dist/render/frame/passTimings.js +38 -0
- package/dist/render/frame/validate.d.ts +15 -0
- package/dist/render/frame/validate.js +22 -0
- package/dist/render/frame/virtual.d.ts +44 -0
- package/dist/render/frame/virtual.js +61 -0
- package/dist/render/gi/chain.d.ts +82 -0
- package/dist/render/gi/chain.js +135 -0
- package/dist/render/gi/denoise.d.ts +114 -0
- package/dist/render/gi/denoise.js +169 -0
- package/dist/render/gi/fieldScene.d.ts +125 -0
- package/dist/render/gi/fieldScene.js +219 -0
- package/dist/render/gi/globalField.d.ts +157 -0
- package/dist/render/gi/globalField.js +373 -0
- package/dist/render/gi/probeGridFit.d.ts +24 -0
- package/dist/render/gi/probeGridFit.js +71 -0
- package/dist/render/gi/probeTrace.d.ts +82 -0
- package/dist/render/gi/probeTrace.js +128 -0
- package/dist/render/gi/probeVolume.d.ts +136 -0
- package/dist/render/gi/probeVolume.js +191 -0
- package/dist/render/gi/reflection.d.ts +73 -0
- package/dist/render/gi/reflection.js +109 -0
- package/dist/render/gi/traceField.d.ts +91 -0
- package/dist/render/gi/traceField.js +129 -0
- package/dist/render/gi/traceScreen.d.ts +68 -0
- package/dist/render/gi/traceScreen.js +82 -0
- package/dist/render/gpuScene.d.ts +38 -0
- package/dist/render/gpuScene.js +41 -0
- package/dist/render/gpudriven/alphaTest.d.ts +28 -0
- package/dist/render/gpudriven/alphaTest.js +32 -0
- package/dist/render/gpudriven/clusterUpload.d.ts +59 -0
- package/dist/render/gpudriven/clusterUpload.js +75 -0
- package/dist/render/gpudriven/clusterWorld.d.ts +57 -0
- package/dist/render/gpudriven/clusterWorld.js +155 -0
- package/dist/render/gpudriven/compact.d.ts +51 -0
- package/dist/render/gpudriven/compact.js +68 -0
- package/dist/render/gpudriven/cullClusters.d.ts +129 -0
- package/dist/render/gpudriven/cullClusters.js +237 -0
- package/dist/render/gpudriven/decodeTables.d.ts +64 -0
- package/dist/render/gpudriven/decodeTables.js +219 -0
- package/dist/render/gpudriven/frustum.d.ts +25 -0
- package/dist/render/gpudriven/frustum.js +59 -0
- package/dist/render/gpudriven/geometryHarness.d.ts +15 -0
- package/dist/render/gpudriven/geometryHarness.js +31 -0
- package/dist/render/gpudriven/hzb.d.ts +60 -0
- package/dist/render/gpudriven/hzb.js +97 -0
- package/dist/render/gpudriven/ibl.d.ts +85 -0
- package/dist/render/gpudriven/ibl.js +129 -0
- package/dist/render/gpudriven/indirect.d.ts +67 -0
- package/dist/render/gpudriven/indirect.js +77 -0
- package/dist/render/gpudriven/instances.d.ts +43 -0
- package/dist/render/gpudriven/instances.js +172 -0
- package/dist/render/gpudriven/lit.d.ts +165 -0
- package/dist/render/gpudriven/lit.js +165 -0
- package/dist/render/gpudriven/lodCut.d.ts +28 -0
- package/dist/render/gpudriven/lodCut.js +38 -0
- package/dist/render/gpudriven/materialBin.d.ts +60 -0
- package/dist/render/gpudriven/materialBin.js +108 -0
- package/dist/render/gpudriven/materialTable.d.ts +88 -0
- package/dist/render/gpudriven/materialTable.js +115 -0
- package/dist/render/gpudriven/pipeline.d.ts +135 -0
- package/dist/render/gpudriven/pipeline.js +237 -0
- package/dist/render/gpudriven/rangeAlloc.d.ts +50 -0
- package/dist/render/gpudriven/rangeAlloc.js +112 -0
- package/dist/render/gpudriven/sceneUpload.d.ts +96 -0
- package/dist/render/gpudriven/sceneUpload.js +115 -0
- package/dist/render/gpudriven/shadeBins.d.ts +92 -0
- package/dist/render/gpudriven/shadeBins.js +182 -0
- package/dist/render/gpudriven/shadow.d.ts +136 -0
- package/dist/render/gpudriven/shadow.js +224 -0
- package/dist/render/gpudriven/shadowCamera.d.ts +87 -0
- package/dist/render/gpudriven/shadowCamera.js +345 -0
- package/dist/render/gpudriven/streamScene.d.ts +194 -0
- package/dist/render/gpudriven/streamScene.js +459 -0
- package/dist/render/gpudriven/surfaceFrame.d.ts +25 -0
- package/dist/render/gpudriven/surfaceFrame.js +51 -0
- package/dist/render/gpudriven/twoPhase.d.ts +122 -0
- package/dist/render/gpudriven/twoPhase.js +160 -0
- package/dist/render/gpudriven/visbuffer.d.ts +25 -0
- package/dist/render/gpudriven/visbuffer.js +37 -0
- package/dist/render/halfFloat.d.ts +10 -0
- package/dist/render/halfFloat.js +23 -4
- package/dist/render/inference/attentionTiled.d.ts +27 -0
- package/dist/render/inference/attentionTiled.js +117 -0
- package/dist/render/inference/denseKernels.d.ts +29 -0
- package/dist/render/inference/denseKernels.js +312 -0
- package/dist/render/inference/device.d.ts +21 -0
- package/dist/render/inference/device.js +32 -0
- package/dist/render/inference/deviceGraph.d.ts +34 -0
- package/dist/render/inference/deviceGraph.js +17 -0
- package/dist/render/inference/fuse.d.ts +15 -0
- package/dist/render/inference/fuse.js +37 -0
- package/dist/render/inference/kernelKit.d.ts +52 -0
- package/dist/render/inference/kernelKit.js +70 -0
- package/dist/render/inference/kernels.d.ts +3 -0
- package/dist/render/inference/kernels.js +11 -0
- package/dist/render/inference/plan.d.ts +13 -0
- package/dist/render/inference/plan.js +18 -0
- package/dist/render/inference/reuse.d.ts +39 -0
- package/dist/render/inference/reuse.js +65 -0
- package/dist/render/inference/runner.d.ts +31 -0
- package/dist/render/inference/runner.js +175 -0
- package/dist/render/inference/schedule.d.ts +39 -0
- package/dist/render/inference/schedule.js +59 -0
- package/dist/render/inference/shapeKernels.d.ts +10 -0
- package/dist/render/inference/shapeKernels.js +80 -0
- package/dist/render/inference/spatialKernels.d.ts +21 -0
- package/dist/render/inference/spatialKernels.js +179 -0
- package/dist/render/inference/splatRaster.wgsl.d.ts +75 -0
- package/dist/render/inference/splatRaster.wgsl.js +337 -0
- package/dist/render/inference/splatTiles.d.ts +79 -0
- package/dist/render/inference/splatTiles.js +161 -0
- package/dist/render/lineBatch.d.ts +2 -5
- package/dist/render/lineBatch.js +4 -24
- package/dist/render/mesh.d.ts +4 -0
- package/dist/render/mesh.js +45 -1
- package/dist/render/pass.d.ts +145 -8
- package/dist/render/prefilterEnvMap.d.ts +22 -0
- package/dist/render/prefilterEnvMap.js +31 -0
- package/dist/render/readback.d.ts +30 -0
- package/dist/render/readback.js +47 -0
- package/dist/render/recon/clamp.d.ts +36 -0
- package/dist/render/recon/clamp.js +92 -0
- package/dist/render/recon/disocclusion.d.ts +60 -0
- package/dist/render/recon/disocclusion.js +109 -0
- package/dist/render/recon/frameGen.d.ts +81 -0
- package/dist/render/recon/frameGen.js +150 -0
- package/dist/render/recon/frameSizes.d.ts +34 -0
- package/dist/render/recon/frameSizes.js +58 -0
- package/dist/render/recon/jitter.d.ts +60 -0
- package/dist/render/recon/jitter.js +118 -0
- package/dist/render/recon/motionVectors.d.ts +55 -0
- package/dist/render/recon/motionVectors.js +107 -0
- package/dist/render/recon/reproject.d.ts +41 -0
- package/dist/render/recon/reproject.js +131 -0
- package/dist/render/recon/resolve.d.ts +90 -0
- package/dist/render/recon/resolve.js +313 -0
- package/dist/render/recon/tier.d.ts +24 -0
- package/dist/render/recon/tier.js +8 -0
- package/dist/render/reflectionProbe.d.ts +26 -0
- package/dist/render/reflectionProbe.js +33 -0
- package/dist/render/renderQuality.d.ts +69 -0
- package/dist/render/renderQuality.js +22 -0
- package/dist/render/screenSpaceReflection.d.ts +23 -0
- package/dist/render/screenSpaceReflection.js +22 -3
- package/dist/render/sdfFont.js +2 -0
- package/dist/render/segmentQuads.d.ts +13 -0
- package/dist/render/segmentQuads.js +32 -0
- package/dist/render/shaders/flat/lobes.d.ts +27 -0
- package/dist/render/shaders/flat/lobes.js +33 -3
- package/dist/render/shaders/flat/preamble.d.ts +7 -0
- package/dist/render/shaders/flat/preamble.js +24 -1
- package/dist/render/shaders/flat/probeGrid.d.ts +1 -1
- package/dist/render/shaders/flat/probeGrid.js +71 -2
- package/dist/render/shaders/generated/flat.wgsl.d.ts +48 -8
- package/dist/render/shaders/generated/flat.wgsl.js +64 -24
- package/dist/render/shaders/gi/composeField.wgsl.d.ts +56 -0
- package/dist/render/shaders/gi/composeField.wgsl.js +231 -0
- package/dist/render/shaders/gi/marchField.wgsl.d.ts +30 -0
- package/dist/render/shaders/gi/marchField.wgsl.js +141 -0
- package/dist/render/shaders/gi/probeBake.wgsl.d.ts +113 -0
- package/dist/render/shaders/gi/probeBake.wgsl.js +635 -0
- package/dist/render/shaders/gi/probeTrace.wgsl.d.ts +72 -0
- package/dist/render/shaders/gi/probeTrace.wgsl.js +261 -0
- package/dist/render/shaders/gi/probeVolume.wgsl.d.ts +49 -0
- package/dist/render/shaders/gi/probeVolume.wgsl.js +242 -0
- package/dist/render/shaders/gi/sampleField.wgsl.d.ts +32 -0
- package/dist/render/shaders/gi/sampleField.wgsl.js +134 -0
- package/dist/render/shaders/gpudriven/blendRaster.wgsl.d.ts +26 -0
- package/dist/render/shaders/gpudriven/blendRaster.wgsl.js +339 -0
- package/dist/render/shaders/gpudriven/compact.wgsl.d.ts +29 -0
- package/dist/render/shaders/gpudriven/compact.wgsl.js +58 -0
- package/dist/render/shaders/gpudriven/cull.wgsl.d.ts +90 -0
- package/dist/render/shaders/gpudriven/cull.wgsl.js +423 -0
- package/dist/render/shaders/gpudriven/decode.wgsl.d.ts +72 -0
- package/dist/render/shaders/gpudriven/decode.wgsl.js +349 -0
- package/dist/render/shaders/gpudriven/environment.wgsl.d.ts +44 -0
- package/dist/render/shaders/gpudriven/environment.wgsl.js +137 -0
- package/dist/render/shaders/gpudriven/fog.wgsl.d.ts +28 -0
- package/dist/render/shaders/gpudriven/fog.wgsl.js +107 -0
- package/dist/render/shaders/gpudriven/frameBlock.wgsl.d.ts +13 -0
- package/dist/render/shaders/gpudriven/frameBlock.wgsl.js +62 -0
- package/dist/render/shaders/gpudriven/materialBin.wgsl.d.ts +51 -0
- package/dist/render/shaders/gpudriven/materialBin.wgsl.js +112 -0
- package/dist/render/shaders/gpudriven/materialTable.wgsl.d.ts +25 -0
- package/dist/render/shaders/gpudriven/materialTable.wgsl.js +43 -0
- package/dist/render/shaders/gpudriven/meshTransform.wgsl.d.ts +18 -0
- package/dist/render/shaders/gpudriven/meshTransform.wgsl.js +37 -0
- package/dist/render/shaders/gpudriven/present.wgsl.d.ts +43 -0
- package/dist/render/shaders/gpudriven/present.wgsl.js +127 -0
- package/dist/render/shaders/gpudriven/shade.wgsl.d.ts +127 -0
- package/dist/render/shaders/gpudriven/shade.wgsl.js +874 -0
- package/dist/render/shaders/gpudriven/shadow.wgsl.d.ts +44 -0
- package/dist/render/shaders/gpudriven/shadow.wgsl.js +308 -0
- package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.d.ts +28 -0
- package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.js +245 -0
- package/dist/render/shaders/network.wgsl.d.ts +101 -0
- package/dist/render/shaders/network.wgsl.js +206 -0
- package/dist/render/shaders/recon/motion.wgsl.d.ts +44 -0
- package/dist/render/shaders/recon/motion.wgsl.js +96 -0
- package/dist/render/shaders/recon/resolve.wgsl.d.ts +51 -0
- package/dist/render/shaders/recon/resolve.wgsl.js +733 -0
- package/dist/render/temporalAa.js +8 -33
- package/dist/render/textLayout.js +12 -6
- package/dist/render/textRenderer.d.ts +3 -1
- package/dist/render/textRenderer.js +4 -1
- package/dist/render/translucentQueue.d.ts +6 -0
- package/dist/render/translucentQueue.js +11 -0
- package/dist/render/windStreakRenderer.d.ts +2 -1
- package/dist/render/windStreakRenderer.js +3 -1
- package/dist/world/cell.d.ts +63 -0
- package/dist/world/cell.js +181 -0
- package/dist/world/cellStream.d.ts +103 -0
- package/dist/world/cellStream.js +198 -0
- package/dist/world/freeze.d.ts +81 -0
- package/dist/world/freeze.js +114 -0
- package/dist/world/rebase.d.ts +29 -0
- package/dist/world/rebase.js +41 -0
- package/package.json +8 -3
- package/scripts/frames.mjs +92 -0
- package/scripts/gpuCompute.mjs +323 -0
- package/scripts/png.d.mts +16 -0
- package/scripts/png.mjs +25 -0
- package/src/core/loop.ts +19 -2
- package/src/core/storage.ts +5 -0
- package/src/geometry/meshBuilder.ts +5 -1
- package/src/index.ts +246 -4
- package/src/input/input.ts +6 -1
- package/src/math/exact.ts +69 -3
- package/src/math/frustum.ts +52 -3
- package/src/render/backend/api.ts +42 -1
- package/src/render/backend/budget.ts +7 -6
- package/src/render/backend/createRenderer.ts +92 -3
- package/src/render/backend/materialChanges.ts +54 -0
- package/src/render/backend/select.ts +8 -0
- package/src/render/backend/webgl2/renderer.ts +211 -9
- package/src/render/backend/webgpu/buffers.ts +182 -69
- package/src/render/backend/webgpu/fieldCompose.ts +482 -0
- package/src/render/backend/webgpu/giFieldPass.ts +200 -0
- package/src/render/backend/webgpu/gpuDrivenPass.ts +2993 -0
- package/src/render/backend/webgpu/linePass.ts +4 -39
- package/src/render/backend/webgpu/probeBake.ts +588 -0
- package/src/render/backend/webgpu/renderer.ts +1481 -76
- package/src/render/backend/webgpu/scatterPass.ts +15 -1
- package/src/render/backend/webgpu/surfaceTexturePass.ts +63 -36
- package/src/render/causticsRenderer.ts +5 -2
- package/src/render/depthConvention.ts +15 -0
- package/src/render/deviceFeatures.ts +77 -0
- package/src/render/fog.ts +185 -0
- package/src/render/frame/alias.ts +97 -0
- package/src/render/frame/deps.ts +155 -0
- package/src/render/frame/flushGraph.ts +135 -0
- package/src/render/frame/graphSchedule.ts +170 -0
- package/src/render/frame/index.ts +52 -0
- package/src/render/frame/lifetime.ts +61 -0
- package/src/render/frame/passTimings.ts +69 -0
- package/src/render/frame/validate.ts +40 -0
- package/src/render/frame/virtual.ts +91 -0
- package/src/render/gi/chain.ts +225 -0
- package/src/render/gi/denoise.ts +276 -0
- package/src/render/gi/fieldScene.ts +264 -0
- package/src/render/gi/globalField.ts +543 -0
- package/src/render/gi/probeGridFit.ts +89 -0
- package/src/render/gi/probeTrace.ts +143 -0
- package/src/render/gi/probeVolume.ts +288 -0
- package/src/render/gi/reflection.ts +163 -0
- package/src/render/gi/traceField.ts +223 -0
- package/src/render/gi/traceScreen.ts +106 -0
- package/src/render/gpuScene.ts +80 -0
- package/src/render/gpudriven/alphaTest.ts +33 -0
- package/src/render/gpudriven/clusterUpload.ts +123 -0
- package/src/render/gpudriven/clusterWorld.ts +239 -0
- package/src/render/gpudriven/compact.ts +80 -0
- package/src/render/gpudriven/cullClusters.ts +333 -0
- package/src/render/gpudriven/decodeTables.ts +332 -0
- package/src/render/gpudriven/frustum.ts +70 -0
- package/src/render/gpudriven/geometryHarness.ts +51 -0
- package/src/render/gpudriven/hzb.ts +113 -0
- package/src/render/gpudriven/ibl.ts +147 -0
- package/src/render/gpudriven/indirect.ts +89 -0
- package/src/render/gpudriven/instances.ts +234 -0
- package/src/render/gpudriven/lit.ts +329 -0
- package/src/render/gpudriven/lodCut.ts +62 -0
- package/src/render/gpudriven/materialBin.ts +131 -0
- package/src/render/gpudriven/materialTable.ts +164 -0
- package/src/render/gpudriven/pipeline.ts +336 -0
- package/src/render/gpudriven/rangeAlloc.ts +122 -0
- package/src/render/gpudriven/sceneUpload.ts +173 -0
- package/src/render/gpudriven/shadeBins.ts +222 -0
- package/src/render/gpudriven/shadow.ts +285 -0
- package/src/render/gpudriven/shadowCamera.ts +406 -0
- package/src/render/gpudriven/streamScene.ts +575 -0
- package/src/render/gpudriven/surfaceFrame.ts +68 -0
- package/src/render/gpudriven/twoPhase.ts +214 -0
- package/src/render/gpudriven/visbuffer.ts +41 -0
- package/src/render/halfFloat.ts +22 -4
- package/src/render/inference/attentionTiled.ts +132 -0
- package/src/render/inference/denseKernels.ts +369 -0
- package/src/render/inference/device.ts +44 -0
- package/src/render/inference/deviceGraph.ts +39 -0
- package/src/render/inference/fuse.ts +47 -0
- package/src/render/inference/kernelKit.ts +120 -0
- package/src/render/inference/kernels.ts +18 -0
- package/src/render/inference/plan.ts +26 -0
- package/src/render/inference/reuse.ts +79 -0
- package/src/render/inference/runner.ts +204 -0
- package/src/render/inference/schedule.ts +80 -0
- package/src/render/inference/shapeKernels.ts +112 -0
- package/src/render/inference/spatialKernels.ts +220 -0
- package/src/render/inference/splatRaster.wgsl.ts +348 -0
- package/src/render/inference/splatTiles.ts +186 -0
- package/src/render/lineBatch.ts +4 -24
- package/src/render/mesh.ts +46 -1
- package/src/render/pass.ts +155 -10
- package/src/render/prefilterEnvMap.ts +33 -0
- package/src/render/readback.ts +73 -0
- package/src/render/recon/clamp.ts +113 -0
- package/src/render/recon/disocclusion.ts +143 -0
- package/src/render/recon/frameGen.ts +191 -0
- package/src/render/recon/frameSizes.ts +64 -0
- package/src/render/recon/jitter.ts +126 -0
- package/src/render/recon/motionVectors.ts +138 -0
- package/src/render/recon/reproject.ts +179 -0
- package/src/render/recon/resolve.ts +444 -0
- package/src/render/recon/tier.ts +31 -0
- package/src/render/reflectionProbe.ts +34 -0
- package/src/render/renderQuality.ts +87 -0
- package/src/render/screenSpaceReflection.ts +37 -3
- package/src/render/sdfFont.ts +2 -0
- package/src/render/segmentQuads.ts +39 -0
- package/src/render/shaders/flat/lobes.ts +35 -3
- package/src/render/shaders/flat/preamble.ts +25 -1
- package/src/render/shaders/flat/probeGrid.ts +77 -2
- package/src/render/shaders/generated/flat.wgsl.ts +64 -24
- package/src/render/shaders/gi/composeField.wgsl.ts +239 -0
- package/src/render/shaders/gi/marchField.wgsl.ts +146 -0
- package/src/render/shaders/gi/probeBake.wgsl.ts +649 -0
- package/src/render/shaders/gi/probeTrace.wgsl.ts +269 -0
- package/src/render/shaders/gi/probeVolume.wgsl.ts +246 -0
- package/src/render/shaders/gi/sampleField.wgsl.ts +140 -0
- package/src/render/shaders/gpudriven/blendRaster.wgsl.ts +343 -0
- package/src/render/shaders/gpudriven/compact.wgsl.ts +59 -0
- package/src/render/shaders/gpudriven/cull.wgsl.ts +433 -0
- package/src/render/shaders/gpudriven/decode.wgsl.ts +373 -0
- package/src/render/shaders/gpudriven/environment.wgsl.ts +141 -0
- package/src/render/shaders/gpudriven/fog.wgsl.ts +110 -0
- package/src/render/shaders/gpudriven/frameBlock.wgsl.ts +62 -0
- package/src/render/shaders/gpudriven/materialBin.wgsl.ts +116 -0
- package/src/render/shaders/gpudriven/materialTable.wgsl.ts +46 -0
- package/src/render/shaders/gpudriven/meshTransform.wgsl.ts +37 -0
- package/src/render/shaders/gpudriven/present.wgsl.ts +129 -0
- package/src/render/shaders/gpudriven/shade.wgsl.ts +884 -0
- package/src/render/shaders/gpudriven/shadow.wgsl.ts +322 -0
- package/src/render/shaders/gpudriven/visbufferRaster.wgsl.ts +251 -0
- package/src/render/shaders/network.wgsl.ts +238 -0
- package/src/render/shaders/recon/motion.wgsl.ts +101 -0
- package/src/render/shaders/recon/resolve.wgsl.ts +741 -0
- package/src/render/temporalAa.ts +9 -33
- package/src/render/textLayout.ts +12 -6
- package/src/render/textRenderer.ts +5 -2
- package/src/render/translucentQueue.ts +11 -0
- package/src/render/windStreakRenderer.ts +4 -2
- package/src/world/cell.ts +231 -0
- package/src/world/cellStream.ts +278 -0
- package/src/world/freeze.ts +157 -0
- package/src/world/rebase.ts +56 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DriftTR's resolve: a jittered frame at the render size into a picture at the output size.
|
|
3
|
+
*
|
|
4
|
+
* **This is the reference the compute shader is held to**, operation for operation, and it is built
|
|
5
|
+
* from the pieces before it: the jitter taken out of where the current frame was observed
|
|
6
|
+
* (`motionVectors.ts`), the history read bicubically where the surface was (`reproject.ts`), clipped
|
|
7
|
+
* to the render neighbourhood's variance box in YCoCg (`clamp.ts`), weighted by whether it is the
|
|
8
|
+
* same surface (`disocclusion.ts`), accumulated, and then sharpened into the picture that is shown.
|
|
9
|
+
*
|
|
10
|
+
* **Per output pixel, in this order:**
|
|
11
|
+
*
|
|
12
|
+
* 1. The render texels around where this frame observed the output pixel each land, unjittered, some
|
|
13
|
+
* distance from its centre. **Each contributes by that distance** — a Gaussian over output pixels —
|
|
14
|
+
* and the sum of their weights is how much this frame knows about the pixel. This is what makes the
|
|
15
|
+
* accumulation converge on the output-size picture rather than on the render's own, blurred to its
|
|
16
|
+
* texel size: an interpolated read of the render, averaged over every jitter, is exactly that blur.
|
|
17
|
+
* 2. The same texels bound the history, and give the **nearest depth**, whose texel's motion is the one
|
|
18
|
+
* taken — the dilation every temporal upscaler uses, because an edge's own motion belongs to the
|
|
19
|
+
* nearer of the two surfaces meeting there. The centre texel wins a tie.
|
|
20
|
+
* 3. That texel's motion is a drawn object's where the motion target flags one, carrying the object's
|
|
21
|
+
* previous view depth in its third channel; otherwise it is the camera's, through the surface's
|
|
22
|
+
* world position.
|
|
23
|
+
* 4. Where the surface was is read from the history — its colour and the weight it had gathered — and
|
|
24
|
+
* last frame's depth there says whether it is the same surface; the two frames' normals, from their
|
|
25
|
+
* depths, catch thin geometry. The disocclusion scales the history's weight.
|
|
26
|
+
* 5. The history is clipped to the box, its weight capped so a tenth of each frame is always new, and
|
|
27
|
+
* the two are mixed by their weights. Where the pixel has gathered less than one sample's worth,
|
|
28
|
+
* the render read bicubically fills the gap. The history keeps colour and weight; the picture shown
|
|
29
|
+
* is it sharpened.
|
|
30
|
+
*
|
|
31
|
+
* **Conventions.** Images are rows from `v = 0` upward. Depths are clip depths in the matrices'
|
|
32
|
+
* convention, OpenGL's here — the nearest is the smallest — and the renderer converts its reversed
|
|
33
|
+
* buffer before a sample reaches this arithmetic, as the temporal resolve already does.
|
|
34
|
+
*/
|
|
35
|
+
import { clipToBox, rgbToYCoCg, varianceBox, yCoCgToRgb } from './clamp.js';
|
|
36
|
+
import { disocclusionWeight, normalFromPositions, worldPositionFromDepth } from './disocclusion.js';
|
|
37
|
+
import { sampleBilinear, sampleHistoryBicubic5 } from './reproject.js';
|
|
38
|
+
/**
|
|
39
|
+
* A tenth of each frame kept, as the temporal resolve keeps; a box of a standard deviation and a
|
|
40
|
+
* quarter; a light sharpen. At two thirds of the output a side.
|
|
41
|
+
*/
|
|
42
|
+
export const DEFAULT_RECON_QUALITY = {
|
|
43
|
+
ratio: 1.5,
|
|
44
|
+
alpha: 0.1,
|
|
45
|
+
sharpen: 0.25,
|
|
46
|
+
clampGamma: 1.25,
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* The current colour and the clipped history mixed: the history's share is `weight × (1 − alpha)`.
|
|
50
|
+
*
|
|
51
|
+
* A weight of zero is the current colour exactly — no history — and a weight of one with an alpha of
|
|
52
|
+
* zero is the history exactly. A convex mixture, so repeated accumulation of one colour converges to
|
|
53
|
+
* it and never overshoots.
|
|
54
|
+
*/
|
|
55
|
+
export function accumulate(out, current, history, weight, alpha) {
|
|
56
|
+
const kept = Math.min(1, Math.max(0, weight)) * (1 - Math.min(1, Math.max(0, alpha)));
|
|
57
|
+
for (let c = 0; c < 3; c += 1) {
|
|
58
|
+
out[c] = current[c] * (1 - kept) + history[c] * kept;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The centre pushed away from its four neighbours' mean by `amount`, and **held inside the range of
|
|
63
|
+
* the five** — the halo a plain unsharp mask draws beside every edge is a value outside that range.
|
|
64
|
+
* A flat region has no difference to push, so it is unchanged.
|
|
65
|
+
*/
|
|
66
|
+
export function sharpen(out, centre, neighbours, amount) {
|
|
67
|
+
for (let c = 0; c < 3; c += 1) {
|
|
68
|
+
const middle = centre[c];
|
|
69
|
+
let low = middle;
|
|
70
|
+
let high = middle;
|
|
71
|
+
let sum = 0;
|
|
72
|
+
for (let n = 0; n < 4; n += 1) {
|
|
73
|
+
const value = neighbours[n * 3 + c];
|
|
74
|
+
sum += value;
|
|
75
|
+
low = Math.min(low, value);
|
|
76
|
+
high = Math.max(high, value);
|
|
77
|
+
}
|
|
78
|
+
const pushed = middle + amount * (middle - sum / 4);
|
|
79
|
+
out[c] = Math.min(high, Math.max(low, pushed));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* How far a render sample's contribution reaches, in output pixels: a Gaussian's standard deviation.
|
|
84
|
+
*
|
|
85
|
+
* Just under half a pixel, so a sample on a pixel's centre counts fully and one a pixel away counts a
|
|
86
|
+
* tenth — the width a reconstruction filter needs to avoid both aliasing and a soft picture.
|
|
87
|
+
*/
|
|
88
|
+
export const SAMPLE_SPREAD = 0.47;
|
|
89
|
+
/** A render sample's weight at `(dx, dy)` output pixels from an output pixel's centre. */
|
|
90
|
+
export function reconstructionWeight(dx, dy) {
|
|
91
|
+
return Math.exp(-(dx * dx + dy * dy) / (2 * SAMPLE_SPREAD * SAMPLE_SPREAD));
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* How much gathered weight counts as knowing a pixel. Below it, the render's bicubic read fills in —
|
|
95
|
+
* which is every pixel on the first frame at a large ratio, where the nearest sample can be most of a
|
|
96
|
+
* pixel away.
|
|
97
|
+
*/
|
|
98
|
+
export const CONFIDENT_WEIGHT = 1;
|
|
99
|
+
const SAMPLE = new Float32Array(3);
|
|
100
|
+
const GATHERED = new Float64Array(3);
|
|
101
|
+
const HISTORY = new Float32Array(4);
|
|
102
|
+
const NEIGHBOURS = new Float64Array(27);
|
|
103
|
+
const LOW = new Float64Array(3);
|
|
104
|
+
const HIGH = new Float64Array(3);
|
|
105
|
+
const YCOCG = new Float64Array(3);
|
|
106
|
+
const CLIPPED = new Float64Array(3);
|
|
107
|
+
const MIXED = new Float64Array(3);
|
|
108
|
+
const WORLD = new Float64Array(3);
|
|
109
|
+
const RIGHT = new Float64Array(3);
|
|
110
|
+
const UP = new Float64Array(3);
|
|
111
|
+
const NORMAL = new Float64Array(3);
|
|
112
|
+
const PREVIOUS_NORMAL = new Float64Array(3);
|
|
113
|
+
const CLIP = new Float64Array(4);
|
|
114
|
+
function clampTexel(value, size) {
|
|
115
|
+
return value < 0 ? 0 : value >= size ? size - 1 : value;
|
|
116
|
+
}
|
|
117
|
+
/** A texel's world position from a depth image, at its unjittered uv. Returns the view depth, or 0. */
|
|
118
|
+
function worldAt(depths, inverse, jitter, width, height, x, y, out) {
|
|
119
|
+
const tx = clampTexel(x, width);
|
|
120
|
+
const ty = clampTexel(y, height);
|
|
121
|
+
const u = (tx + 0.5 - jitter[0]) / width;
|
|
122
|
+
const v = (ty + 0.5 - jitter[1]) / height;
|
|
123
|
+
const z = depths[ty * width + tx];
|
|
124
|
+
if (!worldPositionFromDepth(inverse, u, v, z, out))
|
|
125
|
+
return 0;
|
|
126
|
+
/* The view depth is the clip w, which the inverse's w is the reciprocal of. */
|
|
127
|
+
const m = inverse;
|
|
128
|
+
const w = m[3] * (u * 2 - 1) +
|
|
129
|
+
m[7] * (v * 2 - 1) +
|
|
130
|
+
m[11] * z +
|
|
131
|
+
m[15];
|
|
132
|
+
return 1 / w;
|
|
133
|
+
}
|
|
134
|
+
/** The world normal at a texel, from its depth and its right and upper neighbours'. */
|
|
135
|
+
function normalAt(depths, inverse, jitter, eye, width, height, x, y, out) {
|
|
136
|
+
if (worldAt(depths, inverse, jitter, width, height, x, y, WORLD) <= 0)
|
|
137
|
+
return false;
|
|
138
|
+
/* The neighbour on the inside of the border, so the edge texels have a normal too. */
|
|
139
|
+
const dx = x + 1 < width ? 1 : -1;
|
|
140
|
+
const dy = y + 1 < height ? 1 : -1;
|
|
141
|
+
if (worldAt(depths, inverse, jitter, width, height, x + dx, y, RIGHT) <= 0)
|
|
142
|
+
return false;
|
|
143
|
+
if (worldAt(depths, inverse, jitter, width, height, x, y + dy, UP) <= 0)
|
|
144
|
+
return false;
|
|
145
|
+
return normalFromPositions(WORLD, RIGHT, UP, eye, out);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* The weight a pixel's history still deserves this frame: none on the first frame, where the surface
|
|
149
|
+
* was behind last frame's eye or off its picture, and otherwise what it gathered, scaled by whether it
|
|
150
|
+
* is the same surface. Reads `HISTORY`; writes the history's colour there.
|
|
151
|
+
*/
|
|
152
|
+
function historyWeight(frame, u, v, dx, dy) {
|
|
153
|
+
if (!frame.hasHistory)
|
|
154
|
+
return 0;
|
|
155
|
+
const rw = frame.renderWidth;
|
|
156
|
+
const rh = frame.renderHeight;
|
|
157
|
+
const flagAt = (dy * rw + dx) * 4;
|
|
158
|
+
let mu;
|
|
159
|
+
let mv;
|
|
160
|
+
let expected;
|
|
161
|
+
if (frame.motion[flagAt + 3] > 0.5) {
|
|
162
|
+
mu = frame.motion[flagAt];
|
|
163
|
+
mv = frame.motion[flagAt + 1];
|
|
164
|
+
expected = frame.motion[flagAt + 2];
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
if (worldAt(frame.depth, frame.inverseViewProj, frame.jitter, rw, rh, dx, dy, WORLD) <= 0) {
|
|
168
|
+
return 0;
|
|
169
|
+
}
|
|
170
|
+
const m = frame.previousViewProj;
|
|
171
|
+
for (let r = 0; r < 4; r += 1) {
|
|
172
|
+
CLIP[r] =
|
|
173
|
+
m[r] * WORLD[0] +
|
|
174
|
+
m[4 + r] * WORLD[1] +
|
|
175
|
+
m[8 + r] * WORLD[2] +
|
|
176
|
+
m[12 + r];
|
|
177
|
+
}
|
|
178
|
+
/*
|
|
179
|
+
* A surface behind last frame's eye has a w that is not positive: the disocclusion trusts no
|
|
180
|
+
* such depth, so it needs no refusal of its own here.
|
|
181
|
+
*/
|
|
182
|
+
expected = CLIP[3];
|
|
183
|
+
const du = (dx + 0.5 - frame.jitter[0]) / rw;
|
|
184
|
+
const dv = (dy + 0.5 - frame.jitter[1]) / rh;
|
|
185
|
+
mu = (CLIP[0] / expected) * 0.5 + 0.5 - du;
|
|
186
|
+
mv = (CLIP[1] / expected) * 0.5 + 0.5 - dv;
|
|
187
|
+
}
|
|
188
|
+
const pu = u + mu;
|
|
189
|
+
const pv = v + mv;
|
|
190
|
+
if (!(pu >= 0 && pu <= 1 && pv >= 0 && pv <= 1))
|
|
191
|
+
return 0;
|
|
192
|
+
sampleHistoryBicubic5(frame.history, pu * frame.outputWidth - 0.5, pv * frame.outputHeight - 0.5, frame.outputWidth, frame.outputHeight, 4, HISTORY);
|
|
193
|
+
const px = clampTexel(Math.round(pu * rw + frame.previousJitter[0] - 0.5), rw);
|
|
194
|
+
const py = clampTexel(Math.round(pv * rh + frame.previousJitter[1] - 0.5), rh);
|
|
195
|
+
const held = worldAt(frame.previousDepth, frame.previousInverseViewProj, frame.previousJitter, rw, rh, px, py, RIGHT);
|
|
196
|
+
let normalDot = 1;
|
|
197
|
+
if (normalAt(frame.depth, frame.inverseViewProj, frame.jitter, frame.eye, rw, rh, dx, dy, NORMAL) &&
|
|
198
|
+
normalAt(frame.previousDepth, frame.previousInverseViewProj, frame.previousJitter, frame.previousEye, rw, rh, px, py, PREVIOUS_NORMAL)) {
|
|
199
|
+
normalDot =
|
|
200
|
+
NORMAL[0] * PREVIOUS_NORMAL[0] +
|
|
201
|
+
NORMAL[1] * PREVIOUS_NORMAL[1] +
|
|
202
|
+
NORMAL[2] * PREVIOUS_NORMAL[2];
|
|
203
|
+
}
|
|
204
|
+
const trust = disocclusionWeight(expected, held, Math.hypot(mu, mv), normalDot, frame.disocclusion);
|
|
205
|
+
return HISTORY[3] * trust;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* One output pixel: the history's next colour, before sharpening, and the weight it has gathered —
|
|
209
|
+
* four values into `out`.
|
|
210
|
+
*/
|
|
211
|
+
export function resolvePixel(frame, ox, oy, out) {
|
|
212
|
+
const rw = frame.renderWidth;
|
|
213
|
+
const rh = frame.renderHeight;
|
|
214
|
+
const ow = frame.outputWidth;
|
|
215
|
+
const oh = frame.outputHeight;
|
|
216
|
+
const u = (ox + 0.5) / ow;
|
|
217
|
+
const v = (oy + 0.5) / oh;
|
|
218
|
+
const jx = frame.jitter[0];
|
|
219
|
+
const jy = frame.jitter[1];
|
|
220
|
+
/* Where this frame observed the pixel, and the render read there for the gaps. */
|
|
221
|
+
const rx = u * rw + jx - 0.5;
|
|
222
|
+
const ry = v * rh + jy - 0.5;
|
|
223
|
+
sampleHistoryBicubic5(frame.scene, rx, ry, rw, rh, 3, SAMPLE);
|
|
224
|
+
const nx = clampTexel(Math.round(rx), rw);
|
|
225
|
+
const ny = clampTexel(Math.round(ry), rh);
|
|
226
|
+
/* 1 and 2. Each texel around: its weight, its colour in the box, and its depth. */
|
|
227
|
+
let nearest = frame.depth[ny * rw + nx];
|
|
228
|
+
let dx = nx;
|
|
229
|
+
let dy = ny;
|
|
230
|
+
let gathered = 0;
|
|
231
|
+
GATHERED.fill(0);
|
|
232
|
+
for (let j = -1; j <= 1; j += 1) {
|
|
233
|
+
for (let i = -1; i <= 1; i += 1) {
|
|
234
|
+
const tx = clampTexel(nx + i, rw);
|
|
235
|
+
const ty = clampTexel(ny + j, rh);
|
|
236
|
+
const at = (ty * rw + tx) * 3;
|
|
237
|
+
const r = frame.scene[at];
|
|
238
|
+
const g = frame.scene[at + 1];
|
|
239
|
+
const b = frame.scene[at + 2];
|
|
240
|
+
const weight = reconstructionWeight(((tx + 0.5 - jx) / rw) * ow - (ox + 0.5), ((ty + 0.5 - jy) / rh) * oh - (oy + 0.5));
|
|
241
|
+
gathered += weight;
|
|
242
|
+
GATHERED[0] = GATHERED[0] + r * weight;
|
|
243
|
+
GATHERED[1] = GATHERED[1] + g * weight;
|
|
244
|
+
GATHERED[2] = GATHERED[2] + b * weight;
|
|
245
|
+
rgbToYCoCg(YCOCG, r, g, b);
|
|
246
|
+
NEIGHBOURS.set(YCOCG, ((j + 1) * 3 + (i + 1)) * 3);
|
|
247
|
+
const z = frame.depth[ty * rw + tx];
|
|
248
|
+
if (z < nearest) {
|
|
249
|
+
nearest = z;
|
|
250
|
+
dx = tx;
|
|
251
|
+
dy = ty;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
varianceBox(NEIGHBOURS, 9, frame.quality.clampGamma, LOW, HIGH);
|
|
256
|
+
for (let c = 0; c < 3; c += 1)
|
|
257
|
+
GATHERED[c] = GATHERED[c] / gathered;
|
|
258
|
+
/* 3 and 4. The history, and the weight it still deserves. */
|
|
259
|
+
const cap = (1 - frame.quality.alpha) / frame.quality.alpha;
|
|
260
|
+
const kept = Math.min(cap, historyWeight(frame, u, v, dx, dy));
|
|
261
|
+
/* 5. Clipped, mixed by weight, and the gaps filled from the render. */
|
|
262
|
+
const total = kept + gathered;
|
|
263
|
+
if (kept > 0) {
|
|
264
|
+
rgbToYCoCg(YCOCG, HISTORY[0], HISTORY[1], HISTORY[2]);
|
|
265
|
+
clipToBox(CLIPPED, YCOCG, LOW, HIGH);
|
|
266
|
+
yCoCgToRgb(YCOCG, CLIPPED[0], CLIPPED[1], CLIPPED[2]);
|
|
267
|
+
accumulate(MIXED, GATHERED, YCOCG, 1, gathered / total);
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
MIXED.set(GATHERED);
|
|
271
|
+
}
|
|
272
|
+
const known = Math.min(1, total / CONFIDENT_WEIGHT);
|
|
273
|
+
for (let c = 0; c < 3; c += 1) {
|
|
274
|
+
out[c] = SAMPLE[c] * (1 - known) + MIXED[c] * known;
|
|
275
|
+
}
|
|
276
|
+
out[3] = total;
|
|
277
|
+
}
|
|
278
|
+
const TAP = new Float32Array(4);
|
|
279
|
+
const CENTRE = new Float32Array(4);
|
|
280
|
+
const AROUND = new Float32Array(12);
|
|
281
|
+
const SHOWN = new Float32Array(3);
|
|
282
|
+
const OFFSETS = [
|
|
283
|
+
[-1, 0],
|
|
284
|
+
[1, 0],
|
|
285
|
+
[0, -1],
|
|
286
|
+
[0, 1],
|
|
287
|
+
];
|
|
288
|
+
/**
|
|
289
|
+
* The whole frame: the next history into `history`, four floats a pixel, and the picture shown into
|
|
290
|
+
* `shown`, three — the history sharpened against the four direct neighbours it has just been given.
|
|
291
|
+
*/
|
|
292
|
+
export function resolveFrame(frame, history, shown) {
|
|
293
|
+
const ow = frame.outputWidth;
|
|
294
|
+
const oh = frame.outputHeight;
|
|
295
|
+
for (let y = 0; y < oh; y += 1) {
|
|
296
|
+
for (let x = 0; x < ow; x += 1) {
|
|
297
|
+
resolvePixel(frame, x, y, TAP);
|
|
298
|
+
history.set(TAP, (y * ow + x) * 4);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
for (let y = 0; y < oh; y += 1) {
|
|
302
|
+
for (let x = 0; x < ow; x += 1) {
|
|
303
|
+
sampleBilinear(history, x, y, ow, oh, 4, CENTRE);
|
|
304
|
+
for (let n = 0; n < 4; n += 1) {
|
|
305
|
+
const offset = OFFSETS[n];
|
|
306
|
+
sampleBilinear(history, x + offset[0], y + offset[1], ow, oh, 4, TAP);
|
|
307
|
+
AROUND.set(TAP.subarray(0, 3), n * 3);
|
|
308
|
+
}
|
|
309
|
+
sharpen(SHOWN, CENTRE, AROUND, frame.quality.sharpen);
|
|
310
|
+
shown.set(SHOWN, (y * ow + x) * 3);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which reconstruction tier a request gets.
|
|
3
|
+
*
|
|
4
|
+
* **Tier 0, DriftTR, is analytic** and runs wherever compute does. **Tier 2, DriftFG**, generates
|
|
5
|
+
* frames between rendered ones, over tier 0.
|
|
6
|
+
*
|
|
7
|
+
* **Tier 1, DriftNeural, was withdrawn before 4.0.0 by the spec's own stop condition**, and a request
|
|
8
|
+
* for it is tier 0 on every device. Measured (the plan's Tasks 8 and 9): a network refining the
|
|
9
|
+
* finished frame averaged +0.01 dB on scenes it had not seen, and lost the one clear difference in a
|
|
10
|
+
* blind comparison. The analytic tier was designed to stand without it, so this is the degradation
|
|
11
|
+
* the design was built around rather than a gap in it.
|
|
12
|
+
*
|
|
13
|
+
* **Tier 2 used to fall to tier 0 wherever tier 1 could not run**, so that a device lacking half
|
|
14
|
+
* precision could not choose frame generation over the analytic tier for a consumer who had asked
|
|
15
|
+
* for the learned base. With no learned base there is nothing to choose between, and a request for
|
|
16
|
+
* tier 2 is a request for exactly that. **What would make this wrong** is a learned tier that
|
|
17
|
+
* measures better — a network inside the resolve rather than after it — which brings the rule back.
|
|
18
|
+
*/
|
|
19
|
+
import type { GpuCapabilities } from '../deviceFeatures.ts';
|
|
20
|
+
export type ReconTier = 0 | 1 | 2;
|
|
21
|
+
/** Whether this device can reconstruct at all: the tiers are compute dispatches. */
|
|
22
|
+
export declare function reconstructionSupported(caps: GpuCapabilities): boolean;
|
|
23
|
+
/** The tier a request gets, wherever `reconstructionSupported` says reconstruction runs at all. */
|
|
24
|
+
export declare function selectTier(requested: ReconTier): ReconTier;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Whether this device can reconstruct at all: the tiers are compute dispatches. */
|
|
2
|
+
export function reconstructionSupported(caps) {
|
|
3
|
+
return caps.indirect;
|
|
4
|
+
}
|
|
5
|
+
/** The tier a request gets, wherever `reconstructionSupported` says reconstruction runs at all. */
|
|
6
|
+
export function selectTier(requested) {
|
|
7
|
+
return requested === 1 ? 0 : requested;
|
|
8
|
+
}
|
|
@@ -1,6 +1,32 @@
|
|
|
1
1
|
/** A cubemap of the scene from one point, for surfaces that mirror the room they stand in. */
|
|
2
2
|
import type { Vec3 } from '../math/color.ts';
|
|
3
3
|
import { Camera } from './camera.ts';
|
|
4
|
+
/**
|
|
5
|
+
* The same view-projection with clip y negated, which is what rendering into a cube face wants.
|
|
6
|
+
*
|
|
7
|
+
* **A cubemap face's rows run downward and a GL framebuffer's run upward.** `framebufferTexture2D`
|
|
8
|
+
* on `TEXTURE_CUBE_MAP_POSITIVE_X + face` makes framebuffer row 0 the face's texel row 0, and the
|
|
9
|
+
* face's own direction mapping — the specification's `sc`, `tc`, `ma` table — says texel row 0 is
|
|
10
|
+
* the +Y end. A camera aimed along the face with its up at +Y puts +Y at the top of the render
|
|
11
|
+
* area, which in GL is the *last* row, so the face is stored upside down and every reflection and
|
|
12
|
+
* every irradiance sample taken from it has up and down the wrong way round.
|
|
13
|
+
*
|
|
14
|
+
* **Almost nothing can see it**, which is why it stood until 2026-09-16. A face that is flat, or
|
|
15
|
+
* chequered, or bright across its whole width is symmetric under a vertical flip, and so was every
|
|
16
|
+
* room this repository could bake into a probe. `showroom` is not: its walls are a lit cove and
|
|
17
|
+
* ceiling above and a dark floor below, and its ceiling came out 22 levels of 255 from what the
|
|
18
|
+
* other backend drew, with its floor 7 the other way and its walls exactly equal — a difference
|
|
19
|
+
* that reverses between a surface facing up and one facing down and leaves a horizontal one alone
|
|
20
|
+
* is a probe with its rows the wrong way up. `demo/dev/ibl.html?band=1` is the room that shows it
|
|
21
|
+
* directly: four side walls bright above and dark below, and without this correction the spheres
|
|
22
|
+
* in it are lit from underneath.
|
|
23
|
+
*
|
|
24
|
+
* **The winding goes with it.** Negating one clip axis mirrors every triangle, so a caller has to
|
|
25
|
+
* flip the front face for the duration or the room turns inside out — `bakeProbe` does.
|
|
26
|
+
*
|
|
27
|
+
* Writing into the source is allowed: each output element reads only the element at its own index.
|
|
28
|
+
*/
|
|
29
|
+
export declare function cubeFaceProjection(out: Float32Array, viewProj: ArrayLike<number>): Float32Array;
|
|
4
30
|
/**
|
|
5
31
|
* What a bake is for, and specifically whether it also replaces the scene's diffuse ambient.
|
|
6
32
|
*
|
|
@@ -1,6 +1,39 @@
|
|
|
1
1
|
/** A cubemap of the scene from one point, for surfaces that mirror the room they stand in. */
|
|
2
2
|
import { Camera } from './camera.js';
|
|
3
3
|
import { roughnessForLevel } from './prefilterEnvMap.js';
|
|
4
|
+
/**
|
|
5
|
+
* The same view-projection with clip y negated, which is what rendering into a cube face wants.
|
|
6
|
+
*
|
|
7
|
+
* **A cubemap face's rows run downward and a GL framebuffer's run upward.** `framebufferTexture2D`
|
|
8
|
+
* on `TEXTURE_CUBE_MAP_POSITIVE_X + face` makes framebuffer row 0 the face's texel row 0, and the
|
|
9
|
+
* face's own direction mapping — the specification's `sc`, `tc`, `ma` table — says texel row 0 is
|
|
10
|
+
* the +Y end. A camera aimed along the face with its up at +Y puts +Y at the top of the render
|
|
11
|
+
* area, which in GL is the *last* row, so the face is stored upside down and every reflection and
|
|
12
|
+
* every irradiance sample taken from it has up and down the wrong way round.
|
|
13
|
+
*
|
|
14
|
+
* **Almost nothing can see it**, which is why it stood until 2026-09-16. A face that is flat, or
|
|
15
|
+
* chequered, or bright across its whole width is symmetric under a vertical flip, and so was every
|
|
16
|
+
* room this repository could bake into a probe. `showroom` is not: its walls are a lit cove and
|
|
17
|
+
* ceiling above and a dark floor below, and its ceiling came out 22 levels of 255 from what the
|
|
18
|
+
* other backend drew, with its floor 7 the other way and its walls exactly equal — a difference
|
|
19
|
+
* that reverses between a surface facing up and one facing down and leaves a horizontal one alone
|
|
20
|
+
* is a probe with its rows the wrong way up. `demo/dev/ibl.html?band=1` is the room that shows it
|
|
21
|
+
* directly: four side walls bright above and dark below, and without this correction the spheres
|
|
22
|
+
* in it are lit from underneath.
|
|
23
|
+
*
|
|
24
|
+
* **The winding goes with it.** Negating one clip axis mirrors every triangle, so a caller has to
|
|
25
|
+
* flip the front face for the duration or the room turns inside out — `bakeProbe` does.
|
|
26
|
+
*
|
|
27
|
+
* Writing into the source is allowed: each output element reads only the element at its own index.
|
|
28
|
+
*/
|
|
29
|
+
export function cubeFaceProjection(out, viewProj) {
|
|
30
|
+
for (let i = 0; i < 16; i++) {
|
|
31
|
+
const value = viewProj[i];
|
|
32
|
+
/* Row one of a column-major 4x4 is indices 1, 5, 9, 13 — the row that produces clip y. */
|
|
33
|
+
out[i] = i % 4 === 1 ? -value : value;
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
4
37
|
/**
|
|
5
38
|
* The six directions of a cubemap, as the yaw and pitch a `Camera` aims with.
|
|
6
39
|
*
|
|
@@ -319,6 +319,23 @@ export interface RenderQuality {
|
|
|
319
319
|
* rather than to a release. `?graph=0` turns it off in one reload.
|
|
320
320
|
*/
|
|
321
321
|
readonly frameGraph: boolean;
|
|
322
|
+
/**
|
|
323
|
+
* Schedule each flush through the identifier graph rather than through masks. WebGPU only.
|
|
324
|
+
*
|
|
325
|
+
* **Off, and nothing a consumer can see changes with it on.** The graph `frameGraph` records is
|
|
326
|
+
* the same either way; this chooses which scheduler groups it into passes and derives what each
|
|
327
|
+
* attachment loads and stores. The mask scheduler is exactly right for a frame whose composition
|
|
328
|
+
* is fixed, and the identifier graph is what can also express a frame whose composition is not —
|
|
329
|
+
* the second pipeline's — so this is the switch that says the second is a generalisation of the
|
|
330
|
+
* first: the eighteen scenes the harness walks are pixel-identical with it on, measured
|
|
331
|
+
* 2026-09-17 — `showroom` in six captures a side, because it varies between runs of one build
|
|
332
|
+
* whatever this says. `frame/flushGraph.ts` says where the two can part, and why no flush the
|
|
333
|
+
* renderer makes reaches it.
|
|
334
|
+
*
|
|
335
|
+
* Ignored where `frameGraph` is off, which records nothing to schedule, and on WebGL2, which has no
|
|
336
|
+
* verb-level graph. `?idgraph=1` turns it on in one reload.
|
|
337
|
+
*/
|
|
338
|
+
readonly identifierGraph: boolean;
|
|
322
339
|
/**
|
|
323
340
|
* Light the world from a froxel table rather than from a fixed set of uniform-array slots.
|
|
324
341
|
*
|
|
@@ -485,6 +502,53 @@ export interface RenderQuality {
|
|
|
485
502
|
* it be identical on both backends and asserted without one.
|
|
486
503
|
*/
|
|
487
504
|
readonly occlusionCulling: number;
|
|
505
|
+
/**
|
|
506
|
+
* Reconstruction: how many output pixels the renderer draws for each render pixel, each axis.
|
|
507
|
+
* **0 is off and is the default.**
|
|
508
|
+
*
|
|
509
|
+
* On, the scene and everything that reads it before the resolve are drawn at
|
|
510
|
+
* `ceil(output / ratio)` and a compute dispatch reconstructs the output-size picture from this
|
|
511
|
+
* frame and the accumulated history — DriftTR, `render/recon/`. What it saves is `1 - 1/r²` of
|
|
512
|
+
* the scene's fragment work, which at 1.5 is 56%; what it costs is one motion target and one
|
|
513
|
+
* previous depth at render size, two output-size histories and one dispatch.
|
|
514
|
+
*
|
|
515
|
+
* **Off is byte-for-byte what the renderer does today**, which is what the published scenes are
|
|
516
|
+
* gated at. WebGPU only: WebGL2 has no compute stage, and it keeps the temporal resolve at native
|
|
517
|
+
* size, which is what it does now.
|
|
518
|
+
*
|
|
519
|
+
* **Zero is off rather than one, because one is a thing somebody might mean** — the resolve at
|
|
520
|
+
* the output size with no upscaling, which is a temporal antialiaser with a better neighbourhood
|
|
521
|
+
* rule than `temporalAa`'s. A sentinel that collided with it would make that unaskable.
|
|
522
|
+
*
|
|
523
|
+
* The range is 1.3 to 2 and a value inside it is clamped to the end it is nearest. Below 1.3 the
|
|
524
|
+
* render saves less than a third of the fragment work and the resolve's own cost eats it; above 2
|
|
525
|
+
* the render is a quarter of the output and no reconstruction holds an edge through that. A
|
|
526
|
+
* negative number is off, not a clamp to the bottom: it cannot mean "a little".
|
|
527
|
+
*
|
|
528
|
+
* `?recon=` is how it gets A/B'd in one reload.
|
|
529
|
+
*/
|
|
530
|
+
readonly reconstruction: number;
|
|
531
|
+
/**
|
|
532
|
+
* Light a scene from what bounces, rather than from what was baked. **Off by default.**
|
|
533
|
+
*
|
|
534
|
+
* On, the probe grid the shading already samples is filled by tracing `render/gi/`'s three-level
|
|
535
|
+
* chain — the world distance field, and behind it the probes themselves, which is what makes the
|
|
536
|
+
* solution converge on several bounces over a few refreshes. Off, the probes hold one bounce of
|
|
537
|
+
* the rasterised scene, which is what this engine has always baked.
|
|
538
|
+
*
|
|
539
|
+
* **A switch rather than a strength.** Half an indirect bounce is not a thing to ask for: a probe
|
|
540
|
+
* either holds what the chain traced or it holds what the cube held, and mixing them is two
|
|
541
|
+
* solutions averaged rather than one at a lower quality. What is adjustable is how many probes
|
|
542
|
+
* refresh a frame, and that belongs to the grid.
|
|
543
|
+
*
|
|
544
|
+
* **WebGPU only**, and refused in words elsewhere — `INDIRECT_LIGHT_WEBGL2_REFUSAL`. A probe is a
|
|
545
|
+
* few hundred rays and that is a compute dispatch; WebGL2 has no stage for one, and the
|
|
546
|
+
* alternative — the two backends lighting one scene differently — is the disagreement the
|
|
547
|
+
* published pixel gate exists to prevent.
|
|
548
|
+
*
|
|
549
|
+
* `?indirect=1` turns it on in one reload.
|
|
550
|
+
*/
|
|
551
|
+
readonly indirectLight: boolean;
|
|
488
552
|
/**
|
|
489
553
|
* Ambient occlusion, 0 to 1. Off by default.
|
|
490
554
|
*
|
|
@@ -696,4 +760,9 @@ export declare const DIRECTIONAL_SHADOW_FADE_START = 0.65;
|
|
|
696
760
|
*/
|
|
697
761
|
export declare const DEFAULT_RENDER_QUALITY: Readonly<RenderQuality>;
|
|
698
762
|
/** Resolve and validate once at renderer construction, never in a hot path. */
|
|
763
|
+
/** The narrowest and widest ratio a reconstruction runs at; outside them a caller means the end. */
|
|
764
|
+
export declare const RECONSTRUCTION_RANGE: {
|
|
765
|
+
readonly least: 1.3;
|
|
766
|
+
readonly most: 2;
|
|
767
|
+
};
|
|
699
768
|
export declare function resolveRenderQuality(options?: RenderQualityOptions): Readonly<RenderQuality>;
|
|
@@ -81,6 +81,8 @@ export const DEFAULT_RENDER_QUALITY = Object.freeze({
|
|
|
81
81
|
temporalAa: false,
|
|
82
82
|
orderIndependent: false,
|
|
83
83
|
depthOfField: 0,
|
|
84
|
+
reconstruction: 0,
|
|
85
|
+
indirectLight: false,
|
|
84
86
|
occlusionCulling: 0,
|
|
85
87
|
ambientOcclusion: 0,
|
|
86
88
|
nightEmissive: false,
|
|
@@ -158,6 +160,7 @@ export const DEFAULT_RENDER_QUALITY = Object.freeze({
|
|
|
158
160
|
discardResolvedAttachments: true,
|
|
159
161
|
deferFramePass: true,
|
|
160
162
|
frameGraph: true,
|
|
163
|
+
identifierGraph: false,
|
|
161
164
|
clusteredLights: false,
|
|
162
165
|
cullDraws: false,
|
|
163
166
|
/*
|
|
@@ -168,6 +171,21 @@ export const DEFAULT_RENDER_QUALITY = Object.freeze({
|
|
|
168
171
|
gpuTiming: false,
|
|
169
172
|
});
|
|
170
173
|
/** Resolve and validate once at renderer construction, never in a hot path. */
|
|
174
|
+
/** The narrowest and widest ratio a reconstruction runs at; outside them a caller means the end. */
|
|
175
|
+
export const RECONSTRUCTION_RANGE = { least: 1.3, most: 2 };
|
|
176
|
+
/**
|
|
177
|
+
* Off, or a ratio inside the range.
|
|
178
|
+
*
|
|
179
|
+
* Anything that is not a positive number is off, which covers the default, a negative, and a
|
|
180
|
+
* `NaN` arriving from a query string somebody spelled wrong — none of those can mean "a little
|
|
181
|
+
* reconstruction", and a renderer that guessed one would be drawing at a size nobody asked for.
|
|
182
|
+
*/
|
|
183
|
+
function resolveReconstruction(asked) {
|
|
184
|
+
const value = asked ?? DEFAULT_RENDER_QUALITY.reconstruction;
|
|
185
|
+
if (!(value > 0))
|
|
186
|
+
return 0;
|
|
187
|
+
return Math.min(RECONSTRUCTION_RANGE.most, Math.max(RECONSTRUCTION_RANGE.least, value));
|
|
188
|
+
}
|
|
171
189
|
export function resolveRenderQuality(options = {}) {
|
|
172
190
|
const quality = {
|
|
173
191
|
outputTransform: options.outputTransform ?? DEFAULT_RENDER_QUALITY.outputTransform,
|
|
@@ -200,6 +218,9 @@ export function resolveRenderQuality(options = {}) {
|
|
|
200
218
|
cameraMotionBlur: Math.min(1, Math.max(0, options.cameraMotionBlur ?? DEFAULT_RENDER_QUALITY.cameraMotionBlur)),
|
|
201
219
|
temporalAa: options.temporalAa ?? DEFAULT_RENDER_QUALITY.temporalAa,
|
|
202
220
|
orderIndependent: options.orderIndependent ?? DEFAULT_RENDER_QUALITY.orderIndependent,
|
|
221
|
+
/* Off, or a ratio at least 1.3 and at most 2 — never a number between zero and the range. */
|
|
222
|
+
reconstruction: resolveReconstruction(options.reconstruction),
|
|
223
|
+
indirectLight: options.indirectLight ?? DEFAULT_RENDER_QUALITY.indirectLight,
|
|
203
224
|
depthOfField: Math.min(1, Math.max(0, options.depthOfField ?? DEFAULT_RENDER_QUALITY.depthOfField)),
|
|
204
225
|
occlusionCulling: Math.max(0, Math.round(options.occlusionCulling ?? DEFAULT_RENDER_QUALITY.occlusionCulling)),
|
|
205
226
|
ambientOcclusion: Math.min(1, Math.max(0, options.ambientOcclusion ?? DEFAULT_RENDER_QUALITY.ambientOcclusion)),
|
|
@@ -226,6 +247,7 @@ export function resolveRenderQuality(options = {}) {
|
|
|
226
247
|
discardResolvedAttachments: options.discardResolvedAttachments ?? DEFAULT_RENDER_QUALITY.discardResolvedAttachments,
|
|
227
248
|
deferFramePass: options.deferFramePass ?? DEFAULT_RENDER_QUALITY.deferFramePass,
|
|
228
249
|
frameGraph: options.frameGraph ?? DEFAULT_RENDER_QUALITY.frameGraph,
|
|
250
|
+
identifierGraph: options.identifierGraph ?? DEFAULT_RENDER_QUALITY.identifierGraph,
|
|
229
251
|
clusteredLights: options.clusteredLights ?? DEFAULT_RENDER_QUALITY.clusteredLights,
|
|
230
252
|
cullDraws: options.cullDraws ?? DEFAULT_RENDER_QUALITY.cullDraws,
|
|
231
253
|
waterReflectionFilterTaps: options.waterReflectionFilterTaps ?? DEFAULT_RENDER_QUALITY.waterReflectionFilterTaps,
|
|
@@ -76,6 +76,20 @@ export interface ScreenSpaceHit {
|
|
|
76
76
|
v: number;
|
|
77
77
|
/** How far along the ray it was found, world metres. */
|
|
78
78
|
distanceM: number;
|
|
79
|
+
/**
|
|
80
|
+
* The closest this ray's whole path came to the edge of the frame, 0 to 0.5. Zero if it left.
|
|
81
|
+
*
|
|
82
|
+
* **Fading the *hit* is not enough, and that is what this is for.** `screenEdgeFade` softens a
|
|
83
|
+
* hit near the border so a reflection leaves rather than being cut off along a line — but a ray
|
|
84
|
+
* that runs off the edge *before* it hits anything returns no hit at all, and that is still a
|
|
85
|
+
* line. The two cases are adjacent: one pixel's ray grazes the border and lands, the next
|
|
86
|
+
* pixel's leaves and answers nothing.
|
|
87
|
+
*
|
|
88
|
+
* So the fade has to be driven by how close the *path* came, which only the march can see. A
|
|
89
|
+
* ray that stayed in the middle of the frame reports 0.5; one that skimmed the border reports
|
|
90
|
+
* almost nothing and fades out before its neighbour disappears.
|
|
91
|
+
*/
|
|
92
|
+
margin: number;
|
|
79
93
|
}
|
|
80
94
|
export declare function newScreenSpaceHit(): ScreenSpaceHit;
|
|
81
95
|
/**
|
|
@@ -127,6 +141,15 @@ export declare function traceScreenSpaceRay(origin: ReadonlyVec3, direction: Rea
|
|
|
127
141
|
* `band` is how much of the frame each side is fade, as a fraction.
|
|
128
142
|
*/
|
|
129
143
|
export declare function screenEdgeFade(u: number, v: number, band: number): number;
|
|
144
|
+
/** How far a screen position is from the nearest border, 0 at it and 0.5 in the middle. */
|
|
145
|
+
export declare function edgeMargin(u: number, v: number): number;
|
|
146
|
+
/**
|
|
147
|
+
* A margin turned into a fade, so `screenEdgeFade` and `ScreenSpaceHit.margin` cannot disagree.
|
|
148
|
+
*
|
|
149
|
+
* One expression rather than two copies of it — the rule `REFLECTION_EDGE_FADE` states about the
|
|
150
|
+
* band applied one level down, to the arithmetic the band goes into.
|
|
151
|
+
*/
|
|
152
|
+
export declare function edgeFade(margin: number, band: number): number;
|
|
130
153
|
/**
|
|
131
154
|
* How much of a reflection survives its ray pointing back toward the eye.
|
|
132
155
|
*
|
|
@@ -75,7 +75,7 @@ export function projectToUv(viewProjection, x, y, z, uv) {
|
|
|
75
75
|
return true;
|
|
76
76
|
}
|
|
77
77
|
export function newScreenSpaceHit() {
|
|
78
|
-
return { hit: false, u: 0, v: 0, distanceM: 0 };
|
|
78
|
+
return { hit: false, u: 0, v: 0, distanceM: 0, margin: 0 };
|
|
79
79
|
}
|
|
80
80
|
/**
|
|
81
81
|
* How many times a bracketed crossing is halved.
|
|
@@ -131,6 +131,9 @@ export function traceScreenSpaceRay(origin, direction, eye, march, project, scen
|
|
|
131
131
|
out.u = 0;
|
|
132
132
|
out.v = 0;
|
|
133
133
|
out.distanceM = 0;
|
|
134
|
+
out.margin = 0;
|
|
135
|
+
/* The closest the path has come to the border so far. See `ScreenSpaceHit.margin`. */
|
|
136
|
+
let margin = 0.5;
|
|
134
137
|
const steps = Math.max(1, Math.round(march.steps));
|
|
135
138
|
const ox = origin[0] ?? 0;
|
|
136
139
|
const oy = origin[1] ?? 0;
|
|
@@ -160,6 +163,7 @@ export function traceScreenSpaceRay(origin, direction, eye, march, project, scen
|
|
|
160
163
|
const z = oz + dz * t;
|
|
161
164
|
if (!project(x, y, z, UV))
|
|
162
165
|
return false;
|
|
166
|
+
margin = Math.min(margin, edgeMargin(UV[0] ?? 0, UV[1] ?? 0));
|
|
163
167
|
const scene = sceneDistance(UV[0] ?? 0, UV[1] ?? 0);
|
|
164
168
|
/* Nothing was drawn here, so there is no surface to cross. Carried rather than treated as a
|
|
165
169
|
miss so a ray may pass over a gap in the geometry and hit what is beyond it. */
|
|
@@ -211,6 +215,7 @@ export function traceScreenSpaceRay(origin, direction, eye, march, project, scen
|
|
|
211
215
|
out.u = hitU;
|
|
212
216
|
out.v = hitV;
|
|
213
217
|
out.distanceM = far;
|
|
218
|
+
out.margin = margin;
|
|
214
219
|
return true;
|
|
215
220
|
}
|
|
216
221
|
behind = gap;
|
|
@@ -232,8 +237,22 @@ export function traceScreenSpaceRay(origin, direction, eye, march, project, scen
|
|
|
232
237
|
export function screenEdgeFade(u, v, band) {
|
|
233
238
|
if (band <= 0)
|
|
234
239
|
return u < 0 || u > 1 || v < 0 || v > 1 ? 0 : 1;
|
|
235
|
-
|
|
236
|
-
|
|
240
|
+
return edgeFade(edgeMargin(u, v), band);
|
|
241
|
+
}
|
|
242
|
+
/** How far a screen position is from the nearest border, 0 at it and 0.5 in the middle. */
|
|
243
|
+
export function edgeMargin(u, v) {
|
|
244
|
+
return Math.min(u, 1 - u, v, 1 - v);
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* A margin turned into a fade, so `screenEdgeFade` and `ScreenSpaceHit.margin` cannot disagree.
|
|
248
|
+
*
|
|
249
|
+
* One expression rather than two copies of it — the rule `REFLECTION_EDGE_FADE` states about the
|
|
250
|
+
* band applied one level down, to the arithmetic the band goes into.
|
|
251
|
+
*/
|
|
252
|
+
export function edgeFade(margin, band) {
|
|
253
|
+
if (band <= 0)
|
|
254
|
+
return margin < 0 ? 0 : 1;
|
|
255
|
+
return Math.min(1, Math.max(0, margin / band));
|
|
237
256
|
}
|
|
238
257
|
/**
|
|
239
258
|
* How much of a reflection survives its ray pointing back toward the eye.
|