@driftengine/core 3.63.0 → 4.1.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 +122 -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,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The frame's declared distance fields — what the renderer's indirect light may be traced against.
|
|
3
|
+
*
|
|
4
|
+
* **A consumer declares this, and the seam is `addOccluder`'s for `addOccluder`'s reason.** A
|
|
5
|
+
* distance field is the shape of a thing it is *safe* to be traced against, and only the consumer
|
|
6
|
+
* knows which of its objects that is true of: the terrain and the walls, not the leaves, not the
|
|
7
|
+
* water, not the thing that is about to be deleted. `quality.occlusionCulling` already makes that
|
|
8
|
+
* sentence about occluders and culls nothing until somebody declares one. This makes it about
|
|
9
|
+
* light, and lights nothing until somebody declares a field.
|
|
10
|
+
*
|
|
11
|
+
* **A field and its placement are separate arguments on purpose.** Forty identical pillars are one
|
|
12
|
+
* baked field and forty matrices: the samples are megabytes and the matrices are bytes. So a source
|
|
13
|
+
* declared twice in a frame is two instances sharing one packed copy of its samples, and the queue
|
|
14
|
+
* is what knows the difference.
|
|
15
|
+
*
|
|
16
|
+
* **The transform is copied and the samples are not.** A consumer holds a matrix and moves it —
|
|
17
|
+
* that is what a matrix is for — so a queue that kept the reference would replay every field
|
|
18
|
+
* wearing the last pose written, which is `DecalQueue`'s recorded mistake and is the dangerous
|
|
19
|
+
* kind: a plausible picture rather than a broken one. A `FieldSource`, by contrast, is baked and
|
|
20
|
+
* does not move, and copying its samples once a frame would move the whole field across the bus to
|
|
21
|
+
* say the same thing.
|
|
22
|
+
*
|
|
23
|
+
* **Records are pooled and never released**, per `AGENTS.md`: `reset` returns the count to zero
|
|
24
|
+
* and keeps the storage, so a steady scene allocates on its first frame and never again.
|
|
25
|
+
*/
|
|
26
|
+
import type { ReadonlyMat4 } from 'gl-matrix';
|
|
27
|
+
import type { FieldSource } from './globalField.ts';
|
|
28
|
+
/**
|
|
29
|
+
* How many placed fields one frame may declare.
|
|
30
|
+
*
|
|
31
|
+
* **Thirty-two, which is what `composeGlobalField` was measured against** — 707 ms on the CPU for
|
|
32
|
+
* thirty-two instances over four cascades, and the number the device composition has carried since.
|
|
33
|
+
* The cost is not in the count itself but in the instance buffer being sized once and bound for
|
|
34
|
+
* every cascade's dispatch, so it is a cap rather than a growth.
|
|
35
|
+
*/
|
|
36
|
+
export declare const MAX_DISTANCE_FIELDS = 32;
|
|
37
|
+
/**
|
|
38
|
+
* How many samples the frame's distinct sources may add up to, across all of them.
|
|
39
|
+
*
|
|
40
|
+
* Four million floats is sixteen megabytes on the device, which is a 160-cubed field or two
|
|
41
|
+
* hundred 27-cubed ones. **The budget is over the distinct sources rather than the instances**,
|
|
42
|
+
* because that is what is actually uploaded.
|
|
43
|
+
*/
|
|
44
|
+
export declare const MAX_DISTANCE_FIELD_SAMPLES: number;
|
|
45
|
+
/** One placed field. `transform` is this queue's own copy; `source` is the consumer's object. */
|
|
46
|
+
export interface DistanceFieldInstance {
|
|
47
|
+
/** The baked field. Shared by reference: it does not move and it is the large thing. */
|
|
48
|
+
source: FieldSource;
|
|
49
|
+
/** World from object, copied at the moment it was declared. Uniform scale only. */
|
|
50
|
+
readonly transform: Float32Array;
|
|
51
|
+
/**
|
|
52
|
+
* What colour this placement's surface is, copied like the transform. White where none was given.
|
|
53
|
+
*
|
|
54
|
+
* **A ray that lands on a wall needs to know what colour it is**, because what leaves a surface
|
|
55
|
+
* is the light arriving times its albedo — and a composed distance field is a union that has
|
|
56
|
+
* already forgotten which instance won. `demo/dev/bounce.html` is what made it necessary: a
|
|
57
|
+
* trace without it returned the same answer for a red wall and a white one.
|
|
58
|
+
*
|
|
59
|
+
* On the placement rather than the field, because the same baked shape is placed many times and
|
|
60
|
+
* two pillars cut from one mould may be painted differently.
|
|
61
|
+
*/
|
|
62
|
+
readonly albedo: Float32Array;
|
|
63
|
+
}
|
|
64
|
+
export declare class DistanceFieldScene {
|
|
65
|
+
private readonly pool;
|
|
66
|
+
private count;
|
|
67
|
+
/** The distinct sources of this frame, in the order they were first declared. */
|
|
68
|
+
private readonly distinct;
|
|
69
|
+
/** Where each distinct source's samples start in the packed upload, in floats. */
|
|
70
|
+
private readonly offsets;
|
|
71
|
+
private samples;
|
|
72
|
+
/** The distinct sources of the frame that `reset` last ended, for `packingChanged`. */
|
|
73
|
+
private packed;
|
|
74
|
+
/** Set once when a budget is first reached, so a scene over it says so and then draws. */
|
|
75
|
+
private warnedInstances;
|
|
76
|
+
private warnedSamples;
|
|
77
|
+
/** How many placed fields this frame has recorded. */
|
|
78
|
+
get length(): number;
|
|
79
|
+
/** How many records are pooled, which a test uses to assert a steady scene stops allocating. */
|
|
80
|
+
get capacity(): number;
|
|
81
|
+
/** This frame's distinct sources, in the order their samples are packed. */
|
|
82
|
+
get sources(): readonly FieldSource[];
|
|
83
|
+
/** Floats the packed upload holds — the sum of the distinct sources' fields. */
|
|
84
|
+
get sampleCount(): number;
|
|
85
|
+
/**
|
|
86
|
+
* Whether the samples have to be uploaded again, or the placements alone will do.
|
|
87
|
+
*
|
|
88
|
+
* True when this frame's distinct sources are not the last frame's in the same order, since the
|
|
89
|
+
* offsets are positional. A scene whose fields never change answers false from its second frame
|
|
90
|
+
* on, which is what keeps a sixteen-megabyte upload out of the frame loop.
|
|
91
|
+
*/
|
|
92
|
+
get packingChanged(): boolean;
|
|
93
|
+
/** Where a source's samples start in the packed upload, or -1 if it is not in this frame. */
|
|
94
|
+
offsetOf(source: FieldSource): number;
|
|
95
|
+
/**
|
|
96
|
+
* Declare a field, placed.
|
|
97
|
+
*
|
|
98
|
+
* **A source whose samples do not fit is refused whole rather than clipped.** Half a field on the
|
|
99
|
+
* device is not a coarser field; it is a field whose far half reads whatever the buffer held, and
|
|
100
|
+
* that composes as surfaces that are not there — the failure Wave 4A already paid for once, where
|
|
101
|
+
* a phantom surface passed 111,907 parity samples and was found by looking at a picture.
|
|
102
|
+
*/
|
|
103
|
+
record(source: FieldSource, model: ReadonlyMat4, albedo?: ArrayLike<number>): void;
|
|
104
|
+
replay(each: (instance: DistanceFieldInstance) => void): void;
|
|
105
|
+
/** Empty it for the next frame, keeping the storage and remembering what was packed. */
|
|
106
|
+
reset(): void;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* The world extent of everything a frame declared, into vectors the caller owns.
|
|
110
|
+
*
|
|
111
|
+
* **This is what a probe grid is fitted to when a scene declared none.** A renderer asked for
|
|
112
|
+
* indirect light has to put its probes somewhere, and the only description of the world it holds is
|
|
113
|
+
* the set of fields a consumer said its light may be traced against. That is also the *right*
|
|
114
|
+
* description: a grid sized to include everything drawn would spend its probes on the inside of the
|
|
115
|
+
* sky sphere, while a grid sized to the traceable geometry is a grid around the room.
|
|
116
|
+
*
|
|
117
|
+
* **Every corner is transformed, not the box.** A rotated box's axis-aligned extent is decided by
|
|
118
|
+
* where its corners went; transforming the two extremes and taking their min and max gives a box
|
|
119
|
+
* that is too small wherever a rotation is not a multiple of a quarter turn, and too small is a
|
|
120
|
+
* probe grid that stops short of the wall it is meant to surround.
|
|
121
|
+
*
|
|
122
|
+
* False when the frame declared nothing, rather than an empty box at the origin — which
|
|
123
|
+
* `fitProbeGrid` would answer with a plausible grid of one standing in a place no scene chose.
|
|
124
|
+
*/
|
|
125
|
+
export declare function distanceFieldBounds(scene: DistanceFieldScene, min: Float32Array, max: Float32Array): boolean;
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The frame's declared distance fields — what the renderer's indirect light may be traced against.
|
|
3
|
+
*
|
|
4
|
+
* **A consumer declares this, and the seam is `addOccluder`'s for `addOccluder`'s reason.** A
|
|
5
|
+
* distance field is the shape of a thing it is *safe* to be traced against, and only the consumer
|
|
6
|
+
* knows which of its objects that is true of: the terrain and the walls, not the leaves, not the
|
|
7
|
+
* water, not the thing that is about to be deleted. `quality.occlusionCulling` already makes that
|
|
8
|
+
* sentence about occluders and culls nothing until somebody declares one. This makes it about
|
|
9
|
+
* light, and lights nothing until somebody declares a field.
|
|
10
|
+
*
|
|
11
|
+
* **A field and its placement are separate arguments on purpose.** Forty identical pillars are one
|
|
12
|
+
* baked field and forty matrices: the samples are megabytes and the matrices are bytes. So a source
|
|
13
|
+
* declared twice in a frame is two instances sharing one packed copy of its samples, and the queue
|
|
14
|
+
* is what knows the difference.
|
|
15
|
+
*
|
|
16
|
+
* **The transform is copied and the samples are not.** A consumer holds a matrix and moves it —
|
|
17
|
+
* that is what a matrix is for — so a queue that kept the reference would replay every field
|
|
18
|
+
* wearing the last pose written, which is `DecalQueue`'s recorded mistake and is the dangerous
|
|
19
|
+
* kind: a plausible picture rather than a broken one. A `FieldSource`, by contrast, is baked and
|
|
20
|
+
* does not move, and copying its samples once a frame would move the whole field across the bus to
|
|
21
|
+
* say the same thing.
|
|
22
|
+
*
|
|
23
|
+
* **Records are pooled and never released**, per `AGENTS.md`: `reset` returns the count to zero
|
|
24
|
+
* and keeps the storage, so a steady scene allocates on its first frame and never again.
|
|
25
|
+
*/
|
|
26
|
+
import { assertCubicVoxels } from './globalField.js';
|
|
27
|
+
/**
|
|
28
|
+
* How many placed fields one frame may declare.
|
|
29
|
+
*
|
|
30
|
+
* **Thirty-two, which is what `composeGlobalField` was measured against** — 707 ms on the CPU for
|
|
31
|
+
* thirty-two instances over four cascades, and the number the device composition has carried since.
|
|
32
|
+
* The cost is not in the count itself but in the instance buffer being sized once and bound for
|
|
33
|
+
* every cascade's dispatch, so it is a cap rather than a growth.
|
|
34
|
+
*/
|
|
35
|
+
export const MAX_DISTANCE_FIELDS = 32;
|
|
36
|
+
/**
|
|
37
|
+
* How many samples the frame's distinct sources may add up to, across all of them.
|
|
38
|
+
*
|
|
39
|
+
* Four million floats is sixteen megabytes on the device, which is a 160-cubed field or two
|
|
40
|
+
* hundred 27-cubed ones. **The budget is over the distinct sources rather than the instances**,
|
|
41
|
+
* because that is what is actually uploaded.
|
|
42
|
+
*/
|
|
43
|
+
export const MAX_DISTANCE_FIELD_SAMPLES = 1 << 22;
|
|
44
|
+
function newRecord() {
|
|
45
|
+
return {
|
|
46
|
+
source: EMPTY_SOURCE,
|
|
47
|
+
transform: new Float32Array(16),
|
|
48
|
+
albedo: Float32Array.from([1, 1, 1]),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/** A placeholder a pooled record holds before its first use, so `source` is never null. */
|
|
52
|
+
const EMPTY_SOURCE = {
|
|
53
|
+
field: new Float32Array(0),
|
|
54
|
+
dims: [0, 0, 0],
|
|
55
|
+
bounds: new Float32Array(6),
|
|
56
|
+
};
|
|
57
|
+
export class DistanceFieldScene {
|
|
58
|
+
pool = [];
|
|
59
|
+
count = 0;
|
|
60
|
+
/** The distinct sources of this frame, in the order they were first declared. */
|
|
61
|
+
distinct = [];
|
|
62
|
+
/** Where each distinct source's samples start in the packed upload, in floats. */
|
|
63
|
+
offsets = new Map();
|
|
64
|
+
samples = 0;
|
|
65
|
+
/** The distinct sources of the frame that `reset` last ended, for `packingChanged`. */
|
|
66
|
+
packed = [];
|
|
67
|
+
/** Set once when a budget is first reached, so a scene over it says so and then draws. */
|
|
68
|
+
warnedInstances = false;
|
|
69
|
+
warnedSamples = false;
|
|
70
|
+
/** How many placed fields this frame has recorded. */
|
|
71
|
+
get length() {
|
|
72
|
+
return this.count;
|
|
73
|
+
}
|
|
74
|
+
/** How many records are pooled, which a test uses to assert a steady scene stops allocating. */
|
|
75
|
+
get capacity() {
|
|
76
|
+
return this.pool.length;
|
|
77
|
+
}
|
|
78
|
+
/** This frame's distinct sources, in the order their samples are packed. */
|
|
79
|
+
get sources() {
|
|
80
|
+
return this.distinct;
|
|
81
|
+
}
|
|
82
|
+
/** Floats the packed upload holds — the sum of the distinct sources' fields. */
|
|
83
|
+
get sampleCount() {
|
|
84
|
+
return this.samples;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Whether the samples have to be uploaded again, or the placements alone will do.
|
|
88
|
+
*
|
|
89
|
+
* True when this frame's distinct sources are not the last frame's in the same order, since the
|
|
90
|
+
* offsets are positional. A scene whose fields never change answers false from its second frame
|
|
91
|
+
* on, which is what keeps a sixteen-megabyte upload out of the frame loop.
|
|
92
|
+
*/
|
|
93
|
+
get packingChanged() {
|
|
94
|
+
if (this.distinct.length !== this.packed.length)
|
|
95
|
+
return true;
|
|
96
|
+
for (let i = 0; i < this.distinct.length; i += 1) {
|
|
97
|
+
if (this.distinct[i] !== this.packed[i])
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
/** Where a source's samples start in the packed upload, or -1 if it is not in this frame. */
|
|
103
|
+
offsetOf(source) {
|
|
104
|
+
return this.offsets.get(source) ?? -1;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Declare a field, placed.
|
|
108
|
+
*
|
|
109
|
+
* **A source whose samples do not fit is refused whole rather than clipped.** Half a field on the
|
|
110
|
+
* device is not a coarser field; it is a field whose far half reads whatever the buffer held, and
|
|
111
|
+
* that composes as surfaces that are not there — the failure Wave 4A already paid for once, where
|
|
112
|
+
* a phantom surface passed 111,907 parity samples and was found by looking at a picture.
|
|
113
|
+
*/
|
|
114
|
+
record(source, model, albedo) {
|
|
115
|
+
/*
|
|
116
|
+
* **Before anything else, because a malformed source is not a budget.** The two warnings below
|
|
117
|
+
* are a frame asking for more than it can carry and dropping the surplus is the honest answer;
|
|
118
|
+
* there is none to a source the composer cannot read. The renderer composes on the device and
|
|
119
|
+
* never reaches `composeGlobalField`, so its own check protects only the reference path.
|
|
120
|
+
*/
|
|
121
|
+
assertCubicVoxels(source);
|
|
122
|
+
if (this.count >= MAX_DISTANCE_FIELDS) {
|
|
123
|
+
if (!this.warnedInstances) {
|
|
124
|
+
this.warnedInstances = true;
|
|
125
|
+
console.warn(`driftengine: more than ${MAX_DISTANCE_FIELDS} distance fields were declared in one ` +
|
|
126
|
+
'frame, so the rest are not traced against. Fields that never move are cheaper baked ' +
|
|
127
|
+
'into one source, which is composed as a single instance.');
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const known = this.offsets.get(source);
|
|
132
|
+
if (known === undefined) {
|
|
133
|
+
if (this.samples + source.field.length > MAX_DISTANCE_FIELD_SAMPLES) {
|
|
134
|
+
if (!this.warnedSamples) {
|
|
135
|
+
this.warnedSamples = true;
|
|
136
|
+
console.warn(`driftengine: the frame's distance fields exceed ${MAX_DISTANCE_FIELD_SAMPLES} ` +
|
|
137
|
+
'samples, so this one is not traced against at all. A field composed in part reads ' +
|
|
138
|
+
'whatever the buffer held beyond it, which is surfaces that are not there.');
|
|
139
|
+
}
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
this.offsets.set(source, this.samples);
|
|
143
|
+
this.distinct.push(source);
|
|
144
|
+
this.samples += source.field.length;
|
|
145
|
+
}
|
|
146
|
+
let record = this.pool[this.count];
|
|
147
|
+
if (record === undefined) {
|
|
148
|
+
record = newRecord();
|
|
149
|
+
this.pool.push(record);
|
|
150
|
+
}
|
|
151
|
+
record.source = source;
|
|
152
|
+
for (let at = 0; at < 16; at += 1)
|
|
153
|
+
record.transform[at] = model[at] ?? 0;
|
|
154
|
+
/* Copied like the transform, and white where none was declared. */
|
|
155
|
+
for (let at = 0; at < 3; at += 1)
|
|
156
|
+
record.albedo[at] = albedo?.[at] ?? 1;
|
|
157
|
+
this.count += 1;
|
|
158
|
+
}
|
|
159
|
+
replay(each) {
|
|
160
|
+
for (let i = 0; i < this.count; i += 1) {
|
|
161
|
+
const record = this.pool[i];
|
|
162
|
+
if (record !== undefined)
|
|
163
|
+
each(record);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/** Empty it for the next frame, keeping the storage and remembering what was packed. */
|
|
167
|
+
reset() {
|
|
168
|
+
this.packed = this.distinct.slice();
|
|
169
|
+
this.count = 0;
|
|
170
|
+
this.distinct.length = 0;
|
|
171
|
+
this.offsets.clear();
|
|
172
|
+
this.samples = 0;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* The world extent of everything a frame declared, into vectors the caller owns.
|
|
177
|
+
*
|
|
178
|
+
* **This is what a probe grid is fitted to when a scene declared none.** A renderer asked for
|
|
179
|
+
* indirect light has to put its probes somewhere, and the only description of the world it holds is
|
|
180
|
+
* the set of fields a consumer said its light may be traced against. That is also the *right*
|
|
181
|
+
* description: a grid sized to include everything drawn would spend its probes on the inside of the
|
|
182
|
+
* sky sphere, while a grid sized to the traceable geometry is a grid around the room.
|
|
183
|
+
*
|
|
184
|
+
* **Every corner is transformed, not the box.** A rotated box's axis-aligned extent is decided by
|
|
185
|
+
* where its corners went; transforming the two extremes and taking their min and max gives a box
|
|
186
|
+
* that is too small wherever a rotation is not a multiple of a quarter turn, and too small is a
|
|
187
|
+
* probe grid that stops short of the wall it is meant to surround.
|
|
188
|
+
*
|
|
189
|
+
* False when the frame declared nothing, rather than an empty box at the origin — which
|
|
190
|
+
* `fitProbeGrid` would answer with a plausible grid of one standing in a place no scene chose.
|
|
191
|
+
*/
|
|
192
|
+
export function distanceFieldBounds(scene, min, max) {
|
|
193
|
+
if (scene.length === 0)
|
|
194
|
+
return false;
|
|
195
|
+
for (let axis = 0; axis < 3; axis += 1) {
|
|
196
|
+
min[axis] = Infinity;
|
|
197
|
+
max[axis] = -Infinity;
|
|
198
|
+
}
|
|
199
|
+
scene.replay((instance) => {
|
|
200
|
+
const box = instance.source.bounds;
|
|
201
|
+
const m = instance.transform;
|
|
202
|
+
for (let corner = 0; corner < 8; corner += 1) {
|
|
203
|
+
const x = box[(corner & 1) === 0 ? 0 : 3];
|
|
204
|
+
const y = box[(corner & 2) === 0 ? 1 : 4];
|
|
205
|
+
const z = box[(corner & 4) === 0 ? 2 : 5];
|
|
206
|
+
for (let axis = 0; axis < 3; axis += 1) {
|
|
207
|
+
const value = m[axis] * x +
|
|
208
|
+
m[4 + axis] * y +
|
|
209
|
+
m[8 + axis] * z +
|
|
210
|
+
m[12 + axis];
|
|
211
|
+
if (value < min[axis])
|
|
212
|
+
min[axis] = value;
|
|
213
|
+
if (value > max[axis])
|
|
214
|
+
max[axis] = value;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/** The world near the camera as one distance field, assembled from the objects standing in it. */
|
|
2
|
+
import type { ReadonlyMat4, ReadonlyVec3 } from 'gl-matrix';
|
|
3
|
+
import type { Vec3 } from '../../math/color.ts';
|
|
4
|
+
/**
|
|
5
|
+
* A baked object field, as this package can describe one.
|
|
6
|
+
*
|
|
7
|
+
* **Structural rather than imported**, because `@driftengine/core` does not depend on
|
|
8
|
+
* `@driftengine/assets` and must not start: the baker is a build step and this is a frame. What
|
|
9
|
+
* `bakeObjectSdf` returns satisfies this shape, and so does what `readSdfv` hands back from a
|
|
10
|
+
* container. That is the arrangement `Terrain` already has with `Heightfield` across the physics
|
|
11
|
+
* boundary and `DTEX` has with `@driftengine/texture` across the container one.
|
|
12
|
+
*/
|
|
13
|
+
export interface FieldSource {
|
|
14
|
+
/** Distance in metres, negative inside. `x` fastest, then `y`, then `z`. */
|
|
15
|
+
readonly field: Float32Array;
|
|
16
|
+
readonly dims: readonly [number, number, number];
|
|
17
|
+
/** The field's own extent in **object** space, `[minX, minY, minZ, maxX, maxY, maxZ]`. */
|
|
18
|
+
readonly bounds: Float32Array;
|
|
19
|
+
}
|
|
20
|
+
export interface GlobalFieldInstance {
|
|
21
|
+
readonly source: FieldSource;
|
|
22
|
+
/**
|
|
23
|
+
* What colour this instance's surface is, or white where it did not say.
|
|
24
|
+
*
|
|
25
|
+
* On the instance rather than the source, because the same baked field is placed many times and
|
|
26
|
+
* two pillars cut from one mould may be painted differently. Optional so that every caller
|
|
27
|
+
* written before the trace needed a colour is unchanged.
|
|
28
|
+
*/
|
|
29
|
+
readonly albedo?: Vec3;
|
|
30
|
+
/**
|
|
31
|
+
* World from object.
|
|
32
|
+
*
|
|
33
|
+
* **Uniform scale only, and `composeGlobalField` refuses the rest rather than approximating.**
|
|
34
|
+
* A distance is not preserved by a non-uniform scale: the factor to correct by depends on the
|
|
35
|
+
* direction to the nearest surface, which is exactly what a distance field does not record. An
|
|
36
|
+
* instance squashed on one axis and sampled anyway produces something that is a distance in no
|
|
37
|
+
* direction and looks like one in every slice.
|
|
38
|
+
*/
|
|
39
|
+
readonly transform: ReadonlyMat4;
|
|
40
|
+
}
|
|
41
|
+
/** One level of the clipmap: a grid the composer fills and `sampleGlobalField` reads. */
|
|
42
|
+
export interface GlobalFieldCascade {
|
|
43
|
+
readonly field: Float32Array;
|
|
44
|
+
/**
|
|
45
|
+
* The colour of whatever surface won the union at each sample, three floats a sample.
|
|
46
|
+
*
|
|
47
|
+
* **A union loses which instance won, and a ray that lands on a wall needs to know what colour
|
|
48
|
+
* it is.** `demo/dev/bounce.html` is what made this necessary: a trace whose radiance came from
|
|
49
|
+
* a rasterised capture could not follow a wall that changed colour, and one that read the probe
|
|
50
|
+
* volume's irradiance returned the same answer for a red wall and a white one — irradiance is
|
|
51
|
+
* light arriving, and what leaves a surface is that times its albedo.
|
|
52
|
+
*
|
|
53
|
+
* **White where no instance reached**, rather than black: the colour of a sample no ray can hit
|
|
54
|
+
* is never read, and black would turn a read that did happen into light quietly vanishing
|
|
55
|
+
* instead of a picture that is obviously wrong.
|
|
56
|
+
*/
|
|
57
|
+
readonly albedo: Float32Array;
|
|
58
|
+
readonly dims: readonly [number, number, number];
|
|
59
|
+
/** Where this cascade stands, written by `composeGlobalField`. Six floats. */
|
|
60
|
+
readonly bounds: Float32Array;
|
|
61
|
+
/** Metres between samples, the same on all three axes. Written by `composeGlobalField`. */
|
|
62
|
+
step: number;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The composed world, as nested cascades centred on the camera.
|
|
66
|
+
*
|
|
67
|
+
* **A clipmap and not a sparse brick tree, which is a smaller claim than the plan made and is
|
|
68
|
+
* what is here.** Fine near the viewer and coarse further out is the requirement, and nesting
|
|
69
|
+
* grids of equal sample count at doubling extents meets it exactly: cascade 0 resolves a room and
|
|
70
|
+
* cascade 3 resolves a district, at one allocation each and no indirection to traverse. What a
|
|
71
|
+
* brick tree buys on top is *memory* — skipping the volume no surface is near — and nothing yet
|
|
72
|
+
* measured says this needs it. `traceField.ts` will say, because it is the thing that pays.
|
|
73
|
+
*/
|
|
74
|
+
export interface GlobalField {
|
|
75
|
+
/** Finest first. Each is twice the reach and twice the step of the one before it. */
|
|
76
|
+
readonly cascades: readonly GlobalFieldCascade[];
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* How much of a cascade's extent, at each face, fades into the cascade outside it.
|
|
80
|
+
*
|
|
81
|
+
* **A hard switch between cascades is worse than either cascade's own error.** An error that is
|
|
82
|
+
* wrong everywhere reads as a coarse solution; one that changes abruptly along a surface reads as
|
|
83
|
+
* a crack, and the crack moves with the camera because the boundary does. A tenth of the extent
|
|
84
|
+
* is wide enough that the fade is below a voxel per step and narrow enough that the inner
|
|
85
|
+
* cascade's resolution is what most of the inner cascade actually delivers.
|
|
86
|
+
*/
|
|
87
|
+
export declare const GLOBAL_FIELD_BLEND = 0.1;
|
|
88
|
+
/** Allocate a field. Called once; `composeGlobalField` fills it and allocates nothing. */
|
|
89
|
+
export declare function createGlobalField(resolution: number, cascades: number): GlobalField;
|
|
90
|
+
/**
|
|
91
|
+
* Put one cascade where the camera is, and answer its half-extent.
|
|
92
|
+
*
|
|
93
|
+
* **Exported because the compute path needs the same placement and two spellings drift.** A pass
|
|
94
|
+
* filling these on a device has to agree with this about where every cascade stands, to the last
|
|
95
|
+
* bit: disagree by a fraction of a step and the fade between cascades lands somewhere different on
|
|
96
|
+
* each side, which is a seam that appears only when both paths are compared.
|
|
97
|
+
*
|
|
98
|
+
* The snapping is the decision. See `composeGlobalField` for why a grid centred on the exact camera
|
|
99
|
+
* position makes the whole field shimmer.
|
|
100
|
+
*/
|
|
101
|
+
export declare function placeCascade(cascade: GlobalFieldCascade, level: number, radius: number, cameraPos: ReadonlyVec3): number;
|
|
102
|
+
/**
|
|
103
|
+
* Fill every cascade from the instances standing near the camera.
|
|
104
|
+
*
|
|
105
|
+
* `radius` is the innermost cascade's half-extent in metres; each cascade outside it reaches twice
|
|
106
|
+
* as far at the same sample count.
|
|
107
|
+
*
|
|
108
|
+
* **Every cascade's centre is snapped to a multiple of its own step, and that is the decision this
|
|
109
|
+
* function exists to make.** A grid centred on the exact camera position resamples the entire
|
|
110
|
+
* world at a new sub-pixel offset every frame, so every value changes slightly every frame and the
|
|
111
|
+
* indirect light shimmers — worst on a slow camera, which is exactly when a viewer is looking at
|
|
112
|
+
* it. Snapped, a camera move is either nothing at all or a whole voxel, and a whole voxel leaves
|
|
113
|
+
* every sample the two grids share bit-identical.
|
|
114
|
+
*
|
|
115
|
+
* **A point no instance is near takes the cascade's own reach rather than infinity.** That is an
|
|
116
|
+
* underestimate of the true distance, which is the safe direction: a march that steps by it takes
|
|
117
|
+
* a shorter step than it could and converges anyway, where an overestimate steps through the wall
|
|
118
|
+
* it was meant to stop at.
|
|
119
|
+
*
|
|
120
|
+
* **This is the reference, and it visits every sample of every cascade for every instance that is
|
|
121
|
+
* not culled outright.** `scripts/gpu-parity.mjs` is the pattern the WGSL will be checked against,
|
|
122
|
+
* and the WGSL is where the brick culling belongs — a CPU implementation that was already
|
|
123
|
+
* accelerated would be a second thing to be wrong rather than the thing the first is checked by.
|
|
124
|
+
*/
|
|
125
|
+
export declare function composeGlobalField(instances: readonly GlobalFieldInstance[], cameraPos: ReadonlyVec3, radius: number, out: GlobalField): void;
|
|
126
|
+
/**
|
|
127
|
+
* The field at a world point, from the finest cascade that holds it.
|
|
128
|
+
*
|
|
129
|
+
* A point past every cascade reads the outermost one clamped to its edge, rather than a sentinel:
|
|
130
|
+
* a caller marching outward wants a number it can step by, and the outermost cascade's own reach
|
|
131
|
+
* is the honest one.
|
|
132
|
+
*/
|
|
133
|
+
export declare function sampleGlobalField(field: GlobalField, x: number, y: number, z: number): number;
|
|
134
|
+
/**
|
|
135
|
+
* Refuse a source whose voxels are not cubic, which `sourceAt` and the shader both assume.
|
|
136
|
+
*
|
|
137
|
+
* **One step for all three axes is the contract, and it was unwritten until it cost a feature.**
|
|
138
|
+
* `bakeObjectSdf` picks its step from the object's longest axis and gives each axis whatever count
|
|
139
|
+
* it needs at that step, so `dims` differ per axis and the spacing does not. Both readers rely on
|
|
140
|
+
* that: `sourceAt` here derives the step from x alone, and `composeField.wgsl.ts` says so in a
|
|
141
|
+
* comment. A source sampled with the same count on every axis of a box that is not a cube has
|
|
142
|
+
* oblong voxels, and every sample of it is taken at the wrong place along y and z — far enough
|
|
143
|
+
* wrong that a point at the centre of a slab reads the slab's corner.
|
|
144
|
+
*
|
|
145
|
+
* **Measured on `demo/dev/bounce.html`**, whose room is five such slabs: the composed field held
|
|
146
|
+
* almost no room, 98.9% of every probe's rays left a closed room, and the traced bounce delivered a
|
|
147
|
+
* thirty-fifth of the light. Nothing failed; the field was simply empty. Refusing is the only
|
|
148
|
+
* honest answer, because a distance field carries no record of the axis it was sampled along and
|
|
149
|
+
* there is nothing here to correct it with — the same reason a non-uniform scale is refused above.
|
|
150
|
+
*
|
|
151
|
+
* **An axis of one sample is refused here too, and by its own sentence**, because `read` addresses
|
|
152
|
+
* `index` and `index + 1` on every axis and cannot be given an axis with no pair. `bakeObjectSdf`
|
|
153
|
+
* never produces one — its counts are a ceiling plus one over a span that includes two voxels of
|
|
154
|
+
* padding — but `FieldSource` is a shape a consumer may build by hand, which is the whole reason
|
|
155
|
+
* this function exists.
|
|
156
|
+
*/
|
|
157
|
+
export declare function assertCubicVoxels(source: FieldSource): void;
|