@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,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which tile of the frame each splat lands in, so a device can draw a cloud one tile at a time.
|
|
3
|
+
*
|
|
4
|
+
* **A splat cannot be drawn by one invocation per splat**, because splats overlap and compositing
|
|
5
|
+
* is ordered: two invocations writing the same pixel have no way to agree who was in front. The
|
|
6
|
+
* answer every implementation reaches is to turn the problem inside out — one workgroup per tile
|
|
7
|
+
* of the frame, each walking a list of the splats that touch it, each pixel accumulating its own
|
|
8
|
+
* colour in a register. This builds those lists.
|
|
9
|
+
*
|
|
10
|
+
* **The lists keep the order the splats arrive in.** The scatter is stable, so a caller that hands
|
|
11
|
+
* over a cloud already sorted by depth gets tiles whose lists are sorted by depth, and the
|
|
12
|
+
* compositing is correct without a per-tile sort anywhere. That is the whole reason the sort is a
|
|
13
|
+
* separate step rather than part of this one, and it is a *contract*: a caller that presents its
|
|
14
|
+
* splats in an arbitrary order gets tiles that composite in that arbitrary order, which is a
|
|
15
|
+
* picture rather than an error.
|
|
16
|
+
*
|
|
17
|
+
* **The tile range is derived from the same pixel box the rasteriser loops over**, not from the
|
|
18
|
+
* ellipse again. Two derivations of one footprint drift, and the failure is a splat missing from a
|
|
19
|
+
* tile it actually covers — a hole in the picture that moves with the camera and looks like a
|
|
20
|
+
* culling bug.
|
|
21
|
+
*
|
|
22
|
+
* **The lists are built twice over, by tile and by splat, and the second is what makes a backward
|
|
23
|
+
* pass deterministic.** A tile's gradients have to be added into the splats it walked, and many
|
|
24
|
+
* tiles touch one splat — which on a device is a floating-point atomic. WGSL has none, and the
|
|
25
|
+
* usual answer, a compare-and-swap loop over the bits, sums in whatever order the tiles happen to
|
|
26
|
+
* finish: floating-point addition is not associative, so the same cloud gives a different gradient
|
|
27
|
+
* every run and a fit stops reproducing. So every (tile, splat) pair gets a slot of its own,
|
|
28
|
+
* `pairOf` says which, and a splat's slots are **contiguous** — a reduction over them is a loop in
|
|
29
|
+
* a fixed order, which is the same answer every time on every device.
|
|
30
|
+
*/
|
|
31
|
+
/** The side of a tile in pixels; one workgroup covers one tile. */
|
|
32
|
+
export declare const SPLAT_TILE = 16;
|
|
33
|
+
/** Floats a splat's screen footprint occupies: `x`, `y`, `radius`, `depth`. */
|
|
34
|
+
export declare const SPLAT_BIN_FLOATS = 4;
|
|
35
|
+
/** How many tiles a frame of this size is cut into, across and down. */
|
|
36
|
+
export declare function splatTileGrid(width: number, height: number): {
|
|
37
|
+
across: number;
|
|
38
|
+
down: number;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* The pixel box a splat covers, clamped to the frame, into `out` as `left`, `top`, `right`,
|
|
42
|
+
* `bottom` — or `false` where it covers nothing.
|
|
43
|
+
*
|
|
44
|
+
* **This is the rasteriser's own box.** A device that computed its footprint any other way would
|
|
45
|
+
* be a second answer to the same question.
|
|
46
|
+
*/
|
|
47
|
+
export declare function splatPixelBox(bins: ArrayLike<number>, at: number, width: number, height: number, out: Int32Array): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* How many splats each tile holds, and how many entries the lists need in total.
|
|
50
|
+
*
|
|
51
|
+
* `counts` is cleared here rather than by the caller: a count buffer reused across steps without
|
|
52
|
+
* clearing grows without bound, and the lists then run off the end of themselves.
|
|
53
|
+
*/
|
|
54
|
+
export declare function countSplatTiles(bins: ArrayLike<number>, count: number, width: number, height: number, counts: Uint32Array, tilesPerSplat?: Uint32Array): number;
|
|
55
|
+
/**
|
|
56
|
+
* Where each tile's list begins: the exclusive prefix sum of the counts. Answers the total.
|
|
57
|
+
*
|
|
58
|
+
* A tile with no splats gets a zero-length list at a real offset rather than no entry at all,
|
|
59
|
+
* because the offsets are indexed by tile and dropping an empty one moves every later tile's list.
|
|
60
|
+
*/
|
|
61
|
+
export declare function splatTileOffsets(counts: Uint32Array, offsets: Uint32Array): number;
|
|
62
|
+
/**
|
|
63
|
+
* Every splat written into the list of each tile it touches, in the order the splats are given.
|
|
64
|
+
*
|
|
65
|
+
* `cursors` is where each tile has got to and starts as a copy of `offsets` — it cannot be the
|
|
66
|
+
* offsets themselves, or the second splat of a tile overwrites the first's offset and every later
|
|
67
|
+
* tile's list starts in the wrong place.
|
|
68
|
+
*/
|
|
69
|
+
export declare function fillSplatTiles(bins: ArrayLike<number>, count: number, width: number, height: number, offsets: Uint32Array, cursors: Uint32Array, lists: Uint32Array, pairs?: {
|
|
70
|
+
readonly offsets: Uint32Array;
|
|
71
|
+
readonly pairOf: Uint32Array;
|
|
72
|
+
}): void;
|
|
73
|
+
/**
|
|
74
|
+
* Where each splat's own run of partial-gradient slots begins: the prefix sum over `tilesPerSplat`.
|
|
75
|
+
*
|
|
76
|
+
* The same shape as `splatTileOffsets` and for the opposite index. A splat that covers no tile gets
|
|
77
|
+
* an empty run rather than no entry, because a reduction is indexed by splat.
|
|
78
|
+
*/
|
|
79
|
+
export declare function splatPairOffsets(tilesPerSplat: Uint32Array, offsets: Uint32Array): number;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which tile of the frame each splat lands in, so a device can draw a cloud one tile at a time.
|
|
3
|
+
*
|
|
4
|
+
* **A splat cannot be drawn by one invocation per splat**, because splats overlap and compositing
|
|
5
|
+
* is ordered: two invocations writing the same pixel have no way to agree who was in front. The
|
|
6
|
+
* answer every implementation reaches is to turn the problem inside out — one workgroup per tile
|
|
7
|
+
* of the frame, each walking a list of the splats that touch it, each pixel accumulating its own
|
|
8
|
+
* colour in a register. This builds those lists.
|
|
9
|
+
*
|
|
10
|
+
* **The lists keep the order the splats arrive in.** The scatter is stable, so a caller that hands
|
|
11
|
+
* over a cloud already sorted by depth gets tiles whose lists are sorted by depth, and the
|
|
12
|
+
* compositing is correct without a per-tile sort anywhere. That is the whole reason the sort is a
|
|
13
|
+
* separate step rather than part of this one, and it is a *contract*: a caller that presents its
|
|
14
|
+
* splats in an arbitrary order gets tiles that composite in that arbitrary order, which is a
|
|
15
|
+
* picture rather than an error.
|
|
16
|
+
*
|
|
17
|
+
* **The tile range is derived from the same pixel box the rasteriser loops over**, not from the
|
|
18
|
+
* ellipse again. Two derivations of one footprint drift, and the failure is a splat missing from a
|
|
19
|
+
* tile it actually covers — a hole in the picture that moves with the camera and looks like a
|
|
20
|
+
* culling bug.
|
|
21
|
+
*
|
|
22
|
+
* **The lists are built twice over, by tile and by splat, and the second is what makes a backward
|
|
23
|
+
* pass deterministic.** A tile's gradients have to be added into the splats it walked, and many
|
|
24
|
+
* tiles touch one splat — which on a device is a floating-point atomic. WGSL has none, and the
|
|
25
|
+
* usual answer, a compare-and-swap loop over the bits, sums in whatever order the tiles happen to
|
|
26
|
+
* finish: floating-point addition is not associative, so the same cloud gives a different gradient
|
|
27
|
+
* every run and a fit stops reproducing. So every (tile, splat) pair gets a slot of its own,
|
|
28
|
+
* `pairOf` says which, and a splat's slots are **contiguous** — a reduction over them is a loop in
|
|
29
|
+
* a fixed order, which is the same answer every time on every device.
|
|
30
|
+
*/
|
|
31
|
+
/** The side of a tile in pixels; one workgroup covers one tile. */
|
|
32
|
+
export const SPLAT_TILE = 16;
|
|
33
|
+
/** Floats a splat's screen footprint occupies: `x`, `y`, `radius`, `depth`. */
|
|
34
|
+
export const SPLAT_BIN_FLOATS = 4;
|
|
35
|
+
/** How many tiles a frame of this size is cut into, across and down. */
|
|
36
|
+
export function splatTileGrid(width, height) {
|
|
37
|
+
return {
|
|
38
|
+
across: Math.max(0, Math.ceil(width / SPLAT_TILE)),
|
|
39
|
+
down: Math.max(0, Math.ceil(height / SPLAT_TILE)),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The pixel box a splat covers, clamped to the frame, into `out` as `left`, `top`, `right`,
|
|
44
|
+
* `bottom` — or `false` where it covers nothing.
|
|
45
|
+
*
|
|
46
|
+
* **This is the rasteriser's own box.** A device that computed its footprint any other way would
|
|
47
|
+
* be a second answer to the same question.
|
|
48
|
+
*/
|
|
49
|
+
export function splatPixelBox(bins, at, width, height, out) {
|
|
50
|
+
const x = bins[at * SPLAT_BIN_FLOATS];
|
|
51
|
+
const y = bins[at * SPLAT_BIN_FLOATS + 1];
|
|
52
|
+
const radius = bins[at * SPLAT_BIN_FLOATS + 2];
|
|
53
|
+
if (!(radius > 0))
|
|
54
|
+
return false;
|
|
55
|
+
const left = Math.max(0, Math.floor(x - radius));
|
|
56
|
+
const right = Math.min(width - 1, Math.ceil(x + radius));
|
|
57
|
+
const top = Math.max(0, Math.floor(y - radius));
|
|
58
|
+
const bottom = Math.min(height - 1, Math.ceil(y + radius));
|
|
59
|
+
if (right < left || bottom < top)
|
|
60
|
+
return false;
|
|
61
|
+
out[0] = left;
|
|
62
|
+
out[1] = top;
|
|
63
|
+
out[2] = right;
|
|
64
|
+
out[3] = bottom;
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* How many splats each tile holds, and how many entries the lists need in total.
|
|
69
|
+
*
|
|
70
|
+
* `counts` is cleared here rather than by the caller: a count buffer reused across steps without
|
|
71
|
+
* clearing grows without bound, and the lists then run off the end of themselves.
|
|
72
|
+
*/
|
|
73
|
+
export function countSplatTiles(bins, count, width, height, counts, tilesPerSplat) {
|
|
74
|
+
counts.fill(0);
|
|
75
|
+
tilesPerSplat?.fill(0);
|
|
76
|
+
const { across } = splatTileGrid(width, height);
|
|
77
|
+
const box = new Int32Array(4);
|
|
78
|
+
let total = 0;
|
|
79
|
+
for (let at = 0; at < count; at += 1) {
|
|
80
|
+
if (!splatPixelBox(bins, at, width, height, box))
|
|
81
|
+
continue;
|
|
82
|
+
const first = Math.floor(box[0] / SPLAT_TILE);
|
|
83
|
+
const last = Math.floor(box[2] / SPLAT_TILE);
|
|
84
|
+
const top = Math.floor(box[1] / SPLAT_TILE);
|
|
85
|
+
const bottom = Math.floor(box[3] / SPLAT_TILE);
|
|
86
|
+
for (let row = top; row <= bottom; row += 1) {
|
|
87
|
+
for (let column = first; column <= last; column += 1) {
|
|
88
|
+
const tile = row * across + column;
|
|
89
|
+
counts[tile] = counts[tile] + 1;
|
|
90
|
+
total += 1;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (tilesPerSplat !== undefined) {
|
|
94
|
+
tilesPerSplat[at] = (last - first + 1) * (bottom - top + 1);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return total;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Where each tile's list begins: the exclusive prefix sum of the counts. Answers the total.
|
|
101
|
+
*
|
|
102
|
+
* A tile with no splats gets a zero-length list at a real offset rather than no entry at all,
|
|
103
|
+
* because the offsets are indexed by tile and dropping an empty one moves every later tile's list.
|
|
104
|
+
*/
|
|
105
|
+
export function splatTileOffsets(counts, offsets) {
|
|
106
|
+
let running = 0;
|
|
107
|
+
for (let tile = 0; tile < counts.length; tile += 1) {
|
|
108
|
+
offsets[tile] = running;
|
|
109
|
+
running += counts[tile];
|
|
110
|
+
}
|
|
111
|
+
return running;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Every splat written into the list of each tile it touches, in the order the splats are given.
|
|
115
|
+
*
|
|
116
|
+
* `cursors` is where each tile has got to and starts as a copy of `offsets` — it cannot be the
|
|
117
|
+
* offsets themselves, or the second splat of a tile overwrites the first's offset and every later
|
|
118
|
+
* tile's list starts in the wrong place.
|
|
119
|
+
*/
|
|
120
|
+
export function fillSplatTiles(bins, count, width, height, offsets, cursors, lists, pairs) {
|
|
121
|
+
cursors.set(offsets);
|
|
122
|
+
const { across } = splatTileGrid(width, height);
|
|
123
|
+
const box = new Int32Array(4);
|
|
124
|
+
for (let at = 0; at < count; at += 1) {
|
|
125
|
+
if (!splatPixelBox(bins, at, width, height, box))
|
|
126
|
+
continue;
|
|
127
|
+
const first = Math.floor(box[0] / SPLAT_TILE);
|
|
128
|
+
const last = Math.floor(box[2] / SPLAT_TILE);
|
|
129
|
+
const top = Math.floor(box[1] / SPLAT_TILE);
|
|
130
|
+
const bottom = Math.floor(box[3] / SPLAT_TILE);
|
|
131
|
+
let taken = 0;
|
|
132
|
+
for (let row = top; row <= bottom; row += 1) {
|
|
133
|
+
for (let column = first; column <= last; column += 1) {
|
|
134
|
+
const tile = row * across + column;
|
|
135
|
+
const slot = cursors[tile];
|
|
136
|
+
if (slot >= lists.length)
|
|
137
|
+
continue;
|
|
138
|
+
lists[slot] = at;
|
|
139
|
+
cursors[tile] = slot + 1;
|
|
140
|
+
if (pairs !== undefined) {
|
|
141
|
+
pairs.pairOf[slot] = pairs.offsets[at] + taken;
|
|
142
|
+
taken += 1;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Where each splat's own run of partial-gradient slots begins: the prefix sum over `tilesPerSplat`.
|
|
150
|
+
*
|
|
151
|
+
* The same shape as `splatTileOffsets` and for the opposite index. A splat that covers no tile gets
|
|
152
|
+
* an empty run rather than no entry, because a reduction is indexed by splat.
|
|
153
|
+
*/
|
|
154
|
+
export function splatPairOffsets(tilesPerSplat, offsets) {
|
|
155
|
+
let running = 0;
|
|
156
|
+
for (let at = 0; at < tilesPerSplat.length; at += 1) {
|
|
157
|
+
offsets[at] = running;
|
|
158
|
+
running += tilesPerSplat[at];
|
|
159
|
+
}
|
|
160
|
+
return running;
|
|
161
|
+
}
|
|
@@ -32,11 +32,8 @@ export declare class LineBatch {
|
|
|
32
32
|
/**
|
|
33
33
|
* Send this frame's polyline, expanding each segment to its four vertices.
|
|
34
34
|
*
|
|
35
|
-
* The expansion is
|
|
36
|
-
*
|
|
37
|
-
* read a `LineSegments` too would grow a union that couples two renderers sharing only a
|
|
38
|
-
* quad. This walk is the same shape with none of that: two positions in, copied to four
|
|
39
|
-
* vertices each.
|
|
35
|
+
* The expansion is `expandLineSegments`, which the other backend calls too, so the number of
|
|
36
|
+
* segments drawn — and whether anything is — is decided in one place.
|
|
40
37
|
*/
|
|
41
38
|
upload(gl: WebGL2RenderingContext, data: LineSegments): number;
|
|
42
39
|
/**
|
package/dist/render/lineBatch.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { compileProgram, uniformLocations } from './shader.js';
|
|
2
2
|
import { LINE_FRAG, LINE_VERT } from './shaders/line.js';
|
|
3
|
-
import { buildSegmentQuads } from './segmentQuads.js';
|
|
3
|
+
import { buildSegmentQuads, expandLineSegments } from './segmentQuads.js';
|
|
4
4
|
/**
|
|
5
5
|
* One draw call for a polyline with a real width — an audio waveform, a trail, a plotted
|
|
6
6
|
* curve, anything that is a stroke rather than a surface.
|
|
@@ -64,31 +64,11 @@ export class LineBatch {
|
|
|
64
64
|
/**
|
|
65
65
|
* Send this frame's polyline, expanding each segment to its four vertices.
|
|
66
66
|
*
|
|
67
|
-
* The expansion is
|
|
68
|
-
*
|
|
69
|
-
* read a `LineSegments` too would grow a union that couples two renderers sharing only a
|
|
70
|
-
* quad. This walk is the same shape with none of that: two positions in, copied to four
|
|
71
|
-
* vertices each.
|
|
67
|
+
* The expansion is `expandLineSegments`, which the other backend calls too, so the number of
|
|
68
|
+
* segments drawn — and whether anything is — is decided in one place.
|
|
72
69
|
*/
|
|
73
70
|
upload(gl, data) {
|
|
74
|
-
const count =
|
|
75
|
-
for (let s = 0; s < count; s++) {
|
|
76
|
-
const fx = data.from[s * 3];
|
|
77
|
-
const fy = data.from[s * 3 + 1];
|
|
78
|
-
const fz = data.from[s * 3 + 2];
|
|
79
|
-
const tx = data.to[s * 3];
|
|
80
|
-
const ty = data.to[s * 3 + 1];
|
|
81
|
-
const tz = data.to[s * 3 + 2];
|
|
82
|
-
for (let c = 0; c < 4; c++) {
|
|
83
|
-
const v = s * 4 + c;
|
|
84
|
-
this.from[v * 3] = fx;
|
|
85
|
-
this.from[v * 3 + 1] = fy;
|
|
86
|
-
this.from[v * 3 + 2] = fz;
|
|
87
|
-
this.to[v * 3] = tx;
|
|
88
|
-
this.to[v * 3 + 1] = ty;
|
|
89
|
-
this.to[v * 3 + 2] = tz;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
71
|
+
const count = expandLineSegments(data, this.capacity, this.from, this.to);
|
|
92
72
|
if (count === 0)
|
|
93
73
|
return 0;
|
|
94
74
|
const verts = count * 4;
|
package/dist/render/mesh.d.ts
CHANGED
|
@@ -105,6 +105,10 @@ export declare class Mesh {
|
|
|
105
105
|
* which wants a vertex array per batch rather than a flag here.
|
|
106
106
|
*/
|
|
107
107
|
attachInstances(gl: WebGL2RenderingContext, buffer: WebGLBuffer, stride: number): void;
|
|
108
|
+
/** The instance columns, bound. Called at attach and again after a plain draw disabled them. */
|
|
109
|
+
private bindInstanceColumns;
|
|
110
|
+
private instanceBuffer;
|
|
111
|
+
private instanceStride;
|
|
108
112
|
/** Whether `attachInstances` has already claimed locations 11 to 15 on this mesh's array. */
|
|
109
113
|
private instanced;
|
|
110
114
|
/**
|
package/dist/render/mesh.js
CHANGED
|
@@ -49,6 +49,14 @@ const ATTR_JOINTS = 11;
|
|
|
49
49
|
* setting sway, watching nothing move, and having nothing to read about why.
|
|
50
50
|
*/
|
|
51
51
|
const ATTR_CHANNEL = 13;
|
|
52
|
+
/**
|
|
53
|
+
* The locations `attachInstances` claims, which a plain draw through the same array must not read.
|
|
54
|
+
*
|
|
55
|
+
* Eleven to fourteen are the model matrix's columns and fifteen is the tint. **Thirteen is also
|
|
56
|
+
* the channel**, and eleven and twelve are the skinning pair — which cannot collide, because
|
|
57
|
+
* `flatVert` refuses skinning with instancing. See `draw`.
|
|
58
|
+
*/
|
|
59
|
+
const INSTANCE_COLUMNS = [11, 12, 13, 14, 15];
|
|
52
60
|
const ATTR_WEIGHTS = 12;
|
|
53
61
|
/**
|
|
54
62
|
* One absent attribute's constant, at the width the table gave it.
|
|
@@ -295,6 +303,30 @@ export class Mesh {
|
|
|
295
303
|
/** Caller is responsible for having the flat program + uniforms bound. */
|
|
296
304
|
draw(gl) {
|
|
297
305
|
gl.bindVertexArray(this.vao);
|
|
306
|
+
/*
|
|
307
|
+
* **A mesh with a batch has the instance columns enabled on this very array, and location 13
|
|
308
|
+
* is the channel.** `attachInstances` claims 11 to 14 for the model matrix and 15 for the
|
|
309
|
+
* tint; a `Mesh` owns one vertex array, so those bindings are still live for a *plain* draw
|
|
310
|
+
* through it — and `aChannel` then reads the third column of instance zero's matrix instead
|
|
311
|
+
* of the absent-attribute constant. For a placement with no rotation that column is
|
|
312
|
+
* `(0, 0, 1, 0)`, so `aChannel.y` is zero, `vSkyDirect = aChannel.y` scales the directional
|
|
313
|
+
* term to nothing, and the mesh renders lit by the hemispheric ambient alone — correct
|
|
314
|
+
* normals, correct albedo, no error, no warning.
|
|
315
|
+
*
|
|
316
|
+
* Found on `demo/instancing.ts`, which exists to draw two ranks of one box the two ways and
|
|
317
|
+
* compare them: the `drawMesh` rank's top face read (51, 30, 21) against the `drawInstanced`
|
|
318
|
+
* rank's (218, 110, 59), and 51 is exactly the ambient. WebGPU has no array to share and drew
|
|
319
|
+
* them alike, which is what made it visible at all.
|
|
320
|
+
*
|
|
321
|
+
* Disabled here and put back by `drawInstances`, rather than given the batch an array of its
|
|
322
|
+
* own: that is the larger change this class's own note about two batches already contemplates,
|
|
323
|
+
* and it wants one for a different reason. Three calls on a mesh that has a batch and none on
|
|
324
|
+
* a mesh that does not.
|
|
325
|
+
*/
|
|
326
|
+
if (this.instanced) {
|
|
327
|
+
for (const location of INSTANCE_COLUMNS)
|
|
328
|
+
gl.disableVertexAttribArray(location);
|
|
329
|
+
}
|
|
298
330
|
// The VAO restores the arrays; these it cannot. See `constants`.
|
|
299
331
|
for (const constant of this.constants) {
|
|
300
332
|
const value = constant.value;
|
|
@@ -319,7 +351,15 @@ export class Mesh {
|
|
|
319
351
|
'second batch would rebind the attributes the first draws through.');
|
|
320
352
|
}
|
|
321
353
|
this.instanced = true;
|
|
354
|
+
this.instanceBuffer = buffer;
|
|
355
|
+
this.instanceStride = stride;
|
|
322
356
|
gl.bindVertexArray(this.vao);
|
|
357
|
+
this.bindInstanceColumns(gl, buffer);
|
|
358
|
+
gl.bindVertexArray(null);
|
|
359
|
+
}
|
|
360
|
+
/** The instance columns, bound. Called at attach and again after a plain draw disabled them. */
|
|
361
|
+
bindInstanceColumns(gl, buffer) {
|
|
362
|
+
const stride = this.instanceStride;
|
|
323
363
|
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
324
364
|
/* Four vec4 columns then a vec3 tint: a mat4 is not a vertex attribute in GLSL ES 300. */
|
|
325
365
|
for (let column = 0; column < 4; column += 1) {
|
|
@@ -331,8 +371,9 @@ export class Mesh {
|
|
|
331
371
|
gl.enableVertexAttribArray(15);
|
|
332
372
|
gl.vertexAttribPointer(15, 3, gl.FLOAT, false, stride, 64);
|
|
333
373
|
gl.vertexAttribDivisor(15, 1);
|
|
334
|
-
gl.bindVertexArray(null);
|
|
335
374
|
}
|
|
375
|
+
instanceBuffer = null;
|
|
376
|
+
instanceStride = 0;
|
|
336
377
|
/** Whether `attachInstances` has already claimed locations 11 to 15 on this mesh's array. */
|
|
337
378
|
instanced = false;
|
|
338
379
|
/**
|
|
@@ -343,6 +384,9 @@ export class Mesh {
|
|
|
343
384
|
*/
|
|
344
385
|
drawInstances(gl, count) {
|
|
345
386
|
gl.bindVertexArray(this.vao);
|
|
387
|
+
/* Put back what `draw` disabled, if a plain draw of this mesh came first. See its note. */
|
|
388
|
+
if (this.instanceBuffer !== null)
|
|
389
|
+
this.bindInstanceColumns(gl, this.instanceBuffer);
|
|
346
390
|
for (const constant of this.constants) {
|
|
347
391
|
const value = constant.value;
|
|
348
392
|
applyConstant(gl, constant.location, value);
|
package/dist/render/pass.d.ts
CHANGED
|
@@ -34,22 +34,37 @@ import type { FrameResource } from './frame/index.ts';
|
|
|
34
34
|
*/
|
|
35
35
|
export type PassDevice = {
|
|
36
36
|
/**
|
|
37
|
-
* Pre-multiply a projection by this
|
|
38
|
-
*
|
|
39
|
-
* **The two APIs do not agree about which way clip space points, and the engine settles it in
|
|
40
|
-
* the matrix rather than in the shaders** — see `CLIP_CORRECTION`, whose own comment gives the
|
|
41
|
-
* reason: the shaders are generated from the GLSL the WebGL2 path uses, and a Y flip written
|
|
42
|
-
* into them would be a difference no generator could keep honest. WebGPU's framebuffer origin
|
|
43
|
-
* is the top-left and OpenGL's is the bottom-left, and depth clips to [0, 1] rather than
|
|
44
|
-
* [-1, 1].
|
|
37
|
+
* Pre-multiply a projection by this **if your WGSL was generated from GLSL**. Identity on WebGL2.
|
|
45
38
|
*
|
|
46
39
|
* A contributed pass takes its camera from its caller, so it never sees the corrected matrix the
|
|
47
40
|
* renderer built for its own verbs. Without this it would have to copy four numbers — and a
|
|
48
41
|
* copied convention is one that silently disagrees the day the original changes. The splat pass
|
|
49
42
|
* drew the world upside down on WebGPU until this existed, which is the same first frame this
|
|
50
43
|
* backend ever produced: a colonnade hanging from the ceiling.
|
|
44
|
+
*
|
|
45
|
+
* **It does two things and only one of them is about the APIs.** Depth: OpenGL clips z to
|
|
46
|
+
* [-1, 1] and WebGPU to [0, 1], so an uncorrected matrix throws away half the buffer. Y: it is
|
|
47
|
+
* negated — and *not* because the framebuffer origins differ, which is what this comment used to
|
|
48
|
+
* say. Both viewport transforms put clip `y = +1` at the top of the image. What flips it is
|
|
49
|
+
* `naga`, which ends every generated vertex entry point with `gl_Position.y = -gl_Position.y`;
|
|
50
|
+
* the negation here cancels that one. See `CLIP_CORRECTION` in `webgpu/renderer.ts`.
|
|
51
|
+
*
|
|
52
|
+
* So **a pass whose shader went through the engine's generator wants this one**, which is every
|
|
53
|
+
* pass that has ever used it — `@driftengine/splats` writes GLSL and generates its WGSL — and a
|
|
54
|
+
* pass that wrote WGSL by hand wants `depthCorrection` instead.
|
|
51
55
|
*/
|
|
52
56
|
readonly clipCorrection: Float32Array;
|
|
57
|
+
/**
|
|
58
|
+
* Pre-multiply a projection by this **if you wrote your WGSL by hand**. Identity on WebGL2.
|
|
59
|
+
*
|
|
60
|
+
* The same matrix without the Y negation, because there is no generated negation to cancel.
|
|
61
|
+
* Taking `clipCorrection` in a hand-written shader mirrors the picture vertically — and mirrors
|
|
62
|
+
* the triangle winding with it, so it presents as a culling problem and is answered with
|
|
63
|
+
* `frontFace: 'cw'`, which draws the correct faces of a mirrored world. That is exactly what
|
|
64
|
+
* happened to the GPU-driven pipeline's raster, and the capture that settled it was the same
|
|
65
|
+
* scene through `drawMesh`: an exact vertical mirror about the canvas centre.
|
|
66
|
+
*/
|
|
67
|
+
readonly depthCorrection: Float32Array;
|
|
53
68
|
} & ({
|
|
54
69
|
readonly backend: 'webgl2';
|
|
55
70
|
readonly gl: WebGL2RenderingContext;
|
|
@@ -59,6 +74,19 @@ export type PassDevice = {
|
|
|
59
74
|
readonly format: GPUTextureFormat;
|
|
60
75
|
readonly depthFormat: GPUTextureFormat;
|
|
61
76
|
readonly samples: number;
|
|
77
|
+
/**
|
|
78
|
+
* Whether this renderer reconstructs its frames, which is fixed at construction as `samples`
|
|
79
|
+
* is.
|
|
80
|
+
*
|
|
81
|
+
* **A pass drawing the world into a reconstructed frame owes it two things.** It draws with
|
|
82
|
+
* the frame's jitter (`PrepareContext.jitter`), and it leaves its depth in the frame's
|
|
83
|
+
* attachment: the resolve reprojects every pixel's history through that depth, and a pixel
|
|
84
|
+
* still holding the clear reads as infinitely far away — so a turning camera's history is
|
|
85
|
+
* moved correctly, since a turn moves every depth alike, and a sliding camera's is not moved
|
|
86
|
+
* at all and trails the picture. A pass that decides at `init` whether to write depth
|
|
87
|
+
* decides it by this.
|
|
88
|
+
*/
|
|
89
|
+
readonly reconstruction: boolean;
|
|
62
90
|
});
|
|
63
91
|
/**
|
|
64
92
|
* What it is handed each frame, at the point the caller asked for it.
|
|
@@ -90,6 +118,8 @@ export type PassContext = {
|
|
|
90
118
|
} | {
|
|
91
119
|
readonly backend: 'webgpu';
|
|
92
120
|
readonly pass: GPURenderPassEncoder;
|
|
121
|
+
/** The frame's jitter, the same array `PrepareContext.jitter` held. */
|
|
122
|
+
readonly jitter: Float32Array;
|
|
93
123
|
});
|
|
94
124
|
/**
|
|
95
125
|
* What a pass is handed once a frame, before the frame's own target exists.
|
|
@@ -121,7 +151,114 @@ export type PrepareContext = {
|
|
|
121
151
|
} | {
|
|
122
152
|
readonly backend: 'webgpu';
|
|
123
153
|
readonly encoder: GPUCommandEncoder;
|
|
154
|
+
/**
|
|
155
|
+
* The frame's environment probe, or null where the scene has not baked one.
|
|
156
|
+
*
|
|
157
|
+
* **The first thing this seam hands over that a pass could not render for itself**, and it
|
|
158
|
+
* is here rather than on `PassDevice` because a probe is baked by a scene whenever it likes
|
|
159
|
+
* — after registration, and again when the world changes — so a value captured once at
|
|
160
|
+
* `init` would be null for the life of a pass that registered before the bake.
|
|
161
|
+
*
|
|
162
|
+
* It is offered rather than promised: a pass that wants to light what it draws by the room
|
|
163
|
+
* the rest of the frame is lit by has no other way to reach it, and a pass that does not
|
|
164
|
+
* care ignores the field. What it is *not* is an attachment of the frame — the rule that a
|
|
165
|
+
* contributed pass renders its own targets is unchanged, and this is a resource the renderer
|
|
166
|
+
* owns for the whole frame rather than one it is in the middle of writing.
|
|
167
|
+
*/
|
|
168
|
+
readonly environment: PassEnvironment | null;
|
|
169
|
+
/**
|
|
170
|
+
* The world's composed distance field, or null where the frame composed none.
|
|
171
|
+
*
|
|
172
|
+
* **One frame behind, and that is what it is for.** The renderer composes at `endFrame`,
|
|
173
|
+
* because a consumer declares its fields wherever in its own frame the objects live and the
|
|
174
|
+
* set is not complete until the last verb. `prepare` runs at `beginFrame`, so what a pass is
|
|
175
|
+
* handed here is the field the *previous* frame declared — which is right for everything
|
|
176
|
+
* that reads a distance field, since a field is the shape of the world and the world does
|
|
177
|
+
* not usually change between two frames.
|
|
178
|
+
*
|
|
179
|
+
* Offered rather than promised, as `environment` is: a pass that wants to march the world
|
|
180
|
+
* the renderer's own indirect light marches has no other way to reach it, and one that does
|
|
181
|
+
* not care ignores the field. It is null whenever `quality.indirectLight` is off, which is
|
|
182
|
+
* the default, and whenever the frame declared nothing.
|
|
183
|
+
*/
|
|
184
|
+
readonly distanceField: PassDistanceField | null;
|
|
185
|
+
/**
|
|
186
|
+
* The offset the renderer's own verbs are drawn with this frame, as a fraction of the clip
|
|
187
|
+
* square — x rightward and y upward, the camera's own convention — and zero on a frame that
|
|
188
|
+
* is not reconstructed. Two floats, the same array every frame.
|
|
189
|
+
*
|
|
190
|
+
* **A pass that draws the world applies it**, with `jitterClip`, to the camera matrix it was
|
|
191
|
+
* given and before any correction. Reconstruction un-jitters every sample it takes, so
|
|
192
|
+
* geometry drawn without the offset is placed up to half a render pixel from where it was,
|
|
193
|
+
* differently every frame, and the picture shimmers and softens. Settled at `beginFrame`,
|
|
194
|
+
* before this runs, and once a frame however many mesh passes the frame binds.
|
|
195
|
+
*
|
|
196
|
+
* What it gives up: the temporal resolve's jitter is not handed over. That resolve never
|
|
197
|
+
* un-jitters, so a pass drawn without its offset is left unantialiased by it rather than
|
|
198
|
+
* misplaced — a lesser fault, and one a pass can live with. A pass drawing into a mirror
|
|
199
|
+
* should not apply it either; the renderer's own verbs do not jitter a mirror.
|
|
200
|
+
*/
|
|
201
|
+
readonly jitter: Float32Array;
|
|
124
202
|
};
|
|
203
|
+
/**
|
|
204
|
+
* The world's distance field on the device, as the two buffers and four numbers that address it.
|
|
205
|
+
*
|
|
206
|
+
* **Cascades rather than one grid.** A single grid fine enough to resolve a doorway and large
|
|
207
|
+
* enough to hold a street is not a thing that fits on a device; nested cascades each of the same
|
|
208
|
+
* sample count, each reaching twice as far as the one inside it, spend their resolution where the
|
|
209
|
+
* camera is. `gi/globalField.ts` composes them and `shaders/gi/sampleField.wgsl.ts` reads them,
|
|
210
|
+
* and the layout below is what the two agree on.
|
|
211
|
+
*/
|
|
212
|
+
export interface PassDistanceField {
|
|
213
|
+
/** Every cascade's samples, tightly packed, cascade 0 first. */
|
|
214
|
+
readonly samples: GPUBuffer;
|
|
215
|
+
/**
|
|
216
|
+
* The colour of whatever surface won the union at each sample, three floats, same order.
|
|
217
|
+
*
|
|
218
|
+
* **A union loses which instance won**, and a ray that lands on a wall needs to know what colour
|
|
219
|
+
* it is: what leaves a surface is the light arriving times its albedo, and a distance alone
|
|
220
|
+
* cannot say. `GlobalFieldCascade.albedo` is the reference's own copy of this.
|
|
221
|
+
*/
|
|
222
|
+
readonly albedo: GPUBuffer;
|
|
223
|
+
/** Six bounds and a step per cascade, as `sampleField.wgsl.ts` reads them. */
|
|
224
|
+
readonly cascades: GPUBuffer;
|
|
225
|
+
/** How many cascades stand. */
|
|
226
|
+
readonly levels: number;
|
|
227
|
+
/** Samples along one side of every cascade. */
|
|
228
|
+
readonly side: number;
|
|
229
|
+
/** Metres between samples in the innermost cascade — the finest the field resolves. */
|
|
230
|
+
readonly finestStep: number;
|
|
231
|
+
/** The outermost cascade's corners, `[minX, minY, minZ, maxX, maxY, maxZ]`. */
|
|
232
|
+
readonly outerBounds: Float32Array;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* A baked environment probe, as the two things that sample it and the four numbers that address it.
|
|
236
|
+
*
|
|
237
|
+
* **Octahedral rather than a cubemap**, which is the engine's own storage: one 2D array texture,
|
|
238
|
+
* one layer a probe, a one-texel gutter at every level so a bilinear tap at the border reads the
|
|
239
|
+
* folded direction rather than the other side of the map. `shaders/octahedral.ts` carries the
|
|
240
|
+
* mapping in GLSL and in TypeScript, and `octInsetUv` is what turns a direction into a coordinate.
|
|
241
|
+
*
|
|
242
|
+
* **The chain is roughness, not size.** Level 0 is the mirror and `maxLod` is the roughest GGX
|
|
243
|
+
* convolution; `irradianceLevel` sits one beyond it and holds the cosine convolution, which is the
|
|
244
|
+
* diffuse half. `prefilterEnvMap.ts` derives all three from the edge and is the one place that
|
|
245
|
+
* arithmetic lives.
|
|
246
|
+
*/
|
|
247
|
+
export interface PassEnvironment {
|
|
248
|
+
/** The whole array, `2d-array`, as the lit pass binds it. */
|
|
249
|
+
readonly view: GPUTextureView;
|
|
250
|
+
readonly sampler: GPUSampler;
|
|
251
|
+
/** Texels across level 0, gutter included. A level's own edge is this over `exp2(level)`. */
|
|
252
|
+
readonly edge: number;
|
|
253
|
+
/** The coarsest level of the GGX chain. */
|
|
254
|
+
readonly maxLod: number;
|
|
255
|
+
/** Which level holds the cosine convolution. */
|
|
256
|
+
readonly irradianceLevel: number;
|
|
257
|
+
/** Whether the scene asked this grid to supply its ambient. `ProbeBakeOptions.irradiance`. */
|
|
258
|
+
readonly irradiance: boolean;
|
|
259
|
+
/** How many probes stand in the grid. A pass reading layer 0 alone is right only for one. */
|
|
260
|
+
readonly layers: number;
|
|
261
|
+
}
|
|
125
262
|
export interface PassDefinition {
|
|
126
263
|
/** For diagnostics, and for the label a backend gives the work. */
|
|
127
264
|
readonly label: string;
|
|
@@ -122,3 +122,25 @@ export declare function roughnessForLevel(level: number, maxLevel: number): numb
|
|
|
122
122
|
* objects rather than one written in place.
|
|
123
123
|
*/
|
|
124
124
|
export declare function sourceLevelForSample(pdf: number, faceTexels: number, count: number, maxLevel: number): number;
|
|
125
|
+
/**
|
|
126
|
+
* A level's own edge in texels, which is not the map's.
|
|
127
|
+
*
|
|
128
|
+
* **The gutter is one texel at every level**, so its share of the map doubles as the chain
|
|
129
|
+
* coarsens and an inset computed once from the map's own size is wrong everywhere but level zero.
|
|
130
|
+
* `flat/probeGrid.ts` carries the same expression for the forward path and records the slide it
|
|
131
|
+
* prevents: a thirty-second of the map at the top of the chain, visible as a reflection sliding as
|
|
132
|
+
* roughness rises.
|
|
133
|
+
*
|
|
134
|
+
* Floored at four, which is where the chain stops — a level two texels across is all gutter.
|
|
135
|
+
*/
|
|
136
|
+
export declare function probeLevelEdge(edge: number, level: number): number;
|
|
137
|
+
/**
|
|
138
|
+
* The two levels a roughness reads and how far between them it sits, as `lo`, `hi`, `t`.
|
|
139
|
+
*
|
|
140
|
+
* **Mixed by hand rather than by hardware trilinear**, for the reason above: the two levels have
|
|
141
|
+
* different insets and hardware applies one coordinate to both. So the two fetches are issued at
|
|
142
|
+
* their own insets and blended here, and this is the arithmetic that says which two and by how
|
|
143
|
+
* much. Clamped at both ends, so a lod past the chain reads the coarsest level twice rather than
|
|
144
|
+
* sampling a level that does not exist.
|
|
145
|
+
*/
|
|
146
|
+
export declare function probeLevelMix(lod: number, maxLevel: number, out: Float32Array): Float32Array;
|
|
@@ -144,3 +144,34 @@ export function sourceLevelForSample(pdf, faceTexels, count, maxLevel) {
|
|
|
144
144
|
const level = 0.5 * Math.log2(sampleSolidAngle / texelSolidAngle);
|
|
145
145
|
return Math.min(maxLevel, Math.max(0, level));
|
|
146
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* A level's own edge in texels, which is not the map's.
|
|
149
|
+
*
|
|
150
|
+
* **The gutter is one texel at every level**, so its share of the map doubles as the chain
|
|
151
|
+
* coarsens and an inset computed once from the map's own size is wrong everywhere but level zero.
|
|
152
|
+
* `flat/probeGrid.ts` carries the same expression for the forward path and records the slide it
|
|
153
|
+
* prevents: a thirty-second of the map at the top of the chain, visible as a reflection sliding as
|
|
154
|
+
* roughness rises.
|
|
155
|
+
*
|
|
156
|
+
* Floored at four, which is where the chain stops — a level two texels across is all gutter.
|
|
157
|
+
*/
|
|
158
|
+
export function probeLevelEdge(edge, level) {
|
|
159
|
+
return Math.max(edge / 2 ** level, 4);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* The two levels a roughness reads and how far between them it sits, as `lo`, `hi`, `t`.
|
|
163
|
+
*
|
|
164
|
+
* **Mixed by hand rather than by hardware trilinear**, for the reason above: the two levels have
|
|
165
|
+
* different insets and hardware applies one coordinate to both. So the two fetches are issued at
|
|
166
|
+
* their own insets and blended here, and this is the arithmetic that says which two and by how
|
|
167
|
+
* much. Clamped at both ends, so a lod past the chain reads the coarsest level twice rather than
|
|
168
|
+
* sampling a level that does not exist.
|
|
169
|
+
*/
|
|
170
|
+
export function probeLevelMix(lod, maxLevel, out) {
|
|
171
|
+
const lo = Math.min(Math.max(Math.floor(lod), 0), maxLevel);
|
|
172
|
+
const hi = Math.min(lo + 1, maxLevel);
|
|
173
|
+
out[0] = lo;
|
|
174
|
+
out[1] = hi;
|
|
175
|
+
out[2] = Math.min(Math.max(lod - lo, 0), 1);
|
|
176
|
+
return out;
|
|
177
|
+
}
|