@driftengine/core 3.63.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.json +111 -0
- package/NOTICE +20 -0
- package/README.md +1 -1
- package/dist/core/loop.js +18 -2
- package/dist/core/storage.js +5 -0
- package/dist/geometry/meshBuilder.d.ts +1 -1
- package/dist/geometry/meshBuilder.js +6 -2
- package/dist/index.d.ts +69 -4
- package/dist/index.js +112 -2
- package/dist/input/input.js +6 -1
- package/dist/math/exact.d.ts +13 -3
- package/dist/math/exact.js +68 -3
- package/dist/math/frustum.d.ts +17 -2
- package/dist/math/frustum.js +24 -0
- package/dist/render/backend/api.d.ts +34 -1
- package/dist/render/backend/api.js +22 -1
- package/dist/render/backend/budget.d.ts +7 -6
- package/dist/render/backend/budget.js +7 -6
- package/dist/render/backend/createRenderer.d.ts +57 -0
- package/dist/render/backend/createRenderer.js +51 -2
- package/dist/render/backend/materialChanges.d.ts +41 -0
- package/dist/render/backend/materialChanges.js +38 -0
- package/dist/render/backend/select.js +8 -0
- package/dist/render/backend/webgl2/renderer.d.ts +80 -1
- package/dist/render/backend/webgl2/renderer.js +208 -10
- package/dist/render/backend/webgpu/buffers.d.ts +20 -1
- package/dist/render/backend/webgpu/buffers.js +116 -58
- package/dist/render/backend/webgpu/fieldCompose.d.ts +156 -0
- package/dist/render/backend/webgpu/fieldCompose.js +396 -0
- package/dist/render/backend/webgpu/giFieldPass.d.ts +66 -0
- package/dist/render/backend/webgpu/giFieldPass.js +137 -0
- package/dist/render/backend/webgpu/gpuDrivenPass.d.ts +728 -0
- package/dist/render/backend/webgpu/gpuDrivenPass.js +2473 -0
- package/dist/render/backend/webgpu/linePass.d.ts +4 -14
- package/dist/render/backend/webgpu/linePass.js +4 -33
- package/dist/render/backend/webgpu/probeBake.d.ts +148 -0
- package/dist/render/backend/webgpu/probeBake.js +493 -0
- package/dist/render/backend/webgpu/renderer.d.ts +379 -24
- package/dist/render/backend/webgpu/renderer.js +1369 -76
- package/dist/render/backend/webgpu/scatterPass.js +15 -1
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +19 -9
- package/dist/render/backend/webgpu/surfaceTexturePass.js +58 -32
- package/dist/render/causticsRenderer.d.ts +3 -1
- package/dist/render/causticsRenderer.js +4 -1
- package/dist/render/depthConvention.d.ts +12 -0
- package/dist/render/depthConvention.js +14 -0
- package/dist/render/deviceFeatures.d.ts +32 -0
- package/dist/render/deviceFeatures.js +41 -0
- package/dist/render/fog.d.ts +79 -0
- package/dist/render/fog.js +138 -0
- package/dist/render/frame/alias.d.ts +30 -0
- package/dist/render/frame/alias.js +61 -0
- package/dist/render/frame/deps.d.ts +37 -0
- package/dist/render/frame/deps.js +122 -0
- package/dist/render/frame/flushGraph.d.ts +43 -0
- package/dist/render/frame/flushGraph.js +84 -0
- package/dist/render/frame/graphSchedule.d.ts +49 -0
- package/dist/render/frame/graphSchedule.js +120 -0
- package/dist/render/frame/index.d.ts +15 -0
- package/dist/render/frame/index.js +8 -0
- package/dist/render/frame/lifetime.d.ts +24 -0
- package/dist/render/frame/lifetime.js +36 -0
- package/dist/render/frame/passTimings.d.ts +42 -0
- package/dist/render/frame/passTimings.js +38 -0
- package/dist/render/frame/validate.d.ts +15 -0
- package/dist/render/frame/validate.js +22 -0
- package/dist/render/frame/virtual.d.ts +44 -0
- package/dist/render/frame/virtual.js +61 -0
- package/dist/render/gi/chain.d.ts +82 -0
- package/dist/render/gi/chain.js +135 -0
- package/dist/render/gi/denoise.d.ts +114 -0
- package/dist/render/gi/denoise.js +169 -0
- package/dist/render/gi/fieldScene.d.ts +125 -0
- package/dist/render/gi/fieldScene.js +219 -0
- package/dist/render/gi/globalField.d.ts +157 -0
- package/dist/render/gi/globalField.js +373 -0
- package/dist/render/gi/probeGridFit.d.ts +24 -0
- package/dist/render/gi/probeGridFit.js +71 -0
- package/dist/render/gi/probeTrace.d.ts +82 -0
- package/dist/render/gi/probeTrace.js +128 -0
- package/dist/render/gi/probeVolume.d.ts +136 -0
- package/dist/render/gi/probeVolume.js +191 -0
- package/dist/render/gi/reflection.d.ts +73 -0
- package/dist/render/gi/reflection.js +109 -0
- package/dist/render/gi/traceField.d.ts +91 -0
- package/dist/render/gi/traceField.js +129 -0
- package/dist/render/gi/traceScreen.d.ts +68 -0
- package/dist/render/gi/traceScreen.js +82 -0
- package/dist/render/gpuScene.d.ts +38 -0
- package/dist/render/gpuScene.js +41 -0
- package/dist/render/gpudriven/alphaTest.d.ts +28 -0
- package/dist/render/gpudriven/alphaTest.js +32 -0
- package/dist/render/gpudriven/clusterUpload.d.ts +59 -0
- package/dist/render/gpudriven/clusterUpload.js +75 -0
- package/dist/render/gpudriven/clusterWorld.d.ts +57 -0
- package/dist/render/gpudriven/clusterWorld.js +155 -0
- package/dist/render/gpudriven/compact.d.ts +51 -0
- package/dist/render/gpudriven/compact.js +68 -0
- package/dist/render/gpudriven/cullClusters.d.ts +129 -0
- package/dist/render/gpudriven/cullClusters.js +237 -0
- package/dist/render/gpudriven/decodeTables.d.ts +64 -0
- package/dist/render/gpudriven/decodeTables.js +219 -0
- package/dist/render/gpudriven/frustum.d.ts +25 -0
- package/dist/render/gpudriven/frustum.js +59 -0
- package/dist/render/gpudriven/geometryHarness.d.ts +15 -0
- package/dist/render/gpudriven/geometryHarness.js +31 -0
- package/dist/render/gpudriven/hzb.d.ts +60 -0
- package/dist/render/gpudriven/hzb.js +97 -0
- package/dist/render/gpudriven/ibl.d.ts +85 -0
- package/dist/render/gpudriven/ibl.js +129 -0
- package/dist/render/gpudriven/indirect.d.ts +67 -0
- package/dist/render/gpudriven/indirect.js +77 -0
- package/dist/render/gpudriven/instances.d.ts +43 -0
- package/dist/render/gpudriven/instances.js +172 -0
- package/dist/render/gpudriven/lit.d.ts +165 -0
- package/dist/render/gpudriven/lit.js +165 -0
- package/dist/render/gpudriven/lodCut.d.ts +28 -0
- package/dist/render/gpudriven/lodCut.js +38 -0
- package/dist/render/gpudriven/materialBin.d.ts +60 -0
- package/dist/render/gpudriven/materialBin.js +108 -0
- package/dist/render/gpudriven/materialTable.d.ts +88 -0
- package/dist/render/gpudriven/materialTable.js +115 -0
- package/dist/render/gpudriven/pipeline.d.ts +135 -0
- package/dist/render/gpudriven/pipeline.js +237 -0
- package/dist/render/gpudriven/rangeAlloc.d.ts +50 -0
- package/dist/render/gpudriven/rangeAlloc.js +112 -0
- package/dist/render/gpudriven/sceneUpload.d.ts +96 -0
- package/dist/render/gpudriven/sceneUpload.js +115 -0
- package/dist/render/gpudriven/shadeBins.d.ts +92 -0
- package/dist/render/gpudriven/shadeBins.js +182 -0
- package/dist/render/gpudriven/shadow.d.ts +136 -0
- package/dist/render/gpudriven/shadow.js +224 -0
- package/dist/render/gpudriven/shadowCamera.d.ts +87 -0
- package/dist/render/gpudriven/shadowCamera.js +345 -0
- package/dist/render/gpudriven/streamScene.d.ts +194 -0
- package/dist/render/gpudriven/streamScene.js +459 -0
- package/dist/render/gpudriven/surfaceFrame.d.ts +25 -0
- package/dist/render/gpudriven/surfaceFrame.js +51 -0
- package/dist/render/gpudriven/twoPhase.d.ts +122 -0
- package/dist/render/gpudriven/twoPhase.js +160 -0
- package/dist/render/gpudriven/visbuffer.d.ts +25 -0
- package/dist/render/gpudriven/visbuffer.js +37 -0
- package/dist/render/halfFloat.d.ts +10 -0
- package/dist/render/halfFloat.js +23 -4
- package/dist/render/inference/attentionTiled.d.ts +27 -0
- package/dist/render/inference/attentionTiled.js +117 -0
- package/dist/render/inference/denseKernels.d.ts +29 -0
- package/dist/render/inference/denseKernels.js +312 -0
- package/dist/render/inference/device.d.ts +21 -0
- package/dist/render/inference/device.js +32 -0
- package/dist/render/inference/deviceGraph.d.ts +34 -0
- package/dist/render/inference/deviceGraph.js +17 -0
- package/dist/render/inference/fuse.d.ts +15 -0
- package/dist/render/inference/fuse.js +37 -0
- package/dist/render/inference/kernelKit.d.ts +52 -0
- package/dist/render/inference/kernelKit.js +70 -0
- package/dist/render/inference/kernels.d.ts +3 -0
- package/dist/render/inference/kernels.js +11 -0
- package/dist/render/inference/plan.d.ts +13 -0
- package/dist/render/inference/plan.js +18 -0
- package/dist/render/inference/reuse.d.ts +39 -0
- package/dist/render/inference/reuse.js +65 -0
- package/dist/render/inference/runner.d.ts +31 -0
- package/dist/render/inference/runner.js +175 -0
- package/dist/render/inference/schedule.d.ts +39 -0
- package/dist/render/inference/schedule.js +59 -0
- package/dist/render/inference/shapeKernels.d.ts +10 -0
- package/dist/render/inference/shapeKernels.js +80 -0
- package/dist/render/inference/spatialKernels.d.ts +21 -0
- package/dist/render/inference/spatialKernels.js +179 -0
- package/dist/render/inference/splatRaster.wgsl.d.ts +75 -0
- package/dist/render/inference/splatRaster.wgsl.js +337 -0
- package/dist/render/inference/splatTiles.d.ts +79 -0
- package/dist/render/inference/splatTiles.js +161 -0
- package/dist/render/lineBatch.d.ts +2 -5
- package/dist/render/lineBatch.js +4 -24
- package/dist/render/mesh.d.ts +4 -0
- package/dist/render/mesh.js +45 -1
- package/dist/render/pass.d.ts +145 -8
- package/dist/render/prefilterEnvMap.d.ts +22 -0
- package/dist/render/prefilterEnvMap.js +31 -0
- package/dist/render/readback.d.ts +30 -0
- package/dist/render/readback.js +47 -0
- package/dist/render/recon/clamp.d.ts +36 -0
- package/dist/render/recon/clamp.js +92 -0
- package/dist/render/recon/disocclusion.d.ts +60 -0
- package/dist/render/recon/disocclusion.js +109 -0
- package/dist/render/recon/frameGen.d.ts +81 -0
- package/dist/render/recon/frameGen.js +150 -0
- package/dist/render/recon/frameSizes.d.ts +34 -0
- package/dist/render/recon/frameSizes.js +58 -0
- package/dist/render/recon/jitter.d.ts +60 -0
- package/dist/render/recon/jitter.js +118 -0
- package/dist/render/recon/motionVectors.d.ts +55 -0
- package/dist/render/recon/motionVectors.js +107 -0
- package/dist/render/recon/reproject.d.ts +41 -0
- package/dist/render/recon/reproject.js +131 -0
- package/dist/render/recon/resolve.d.ts +90 -0
- package/dist/render/recon/resolve.js +313 -0
- package/dist/render/recon/tier.d.ts +24 -0
- package/dist/render/recon/tier.js +8 -0
- package/dist/render/reflectionProbe.d.ts +26 -0
- package/dist/render/reflectionProbe.js +33 -0
- package/dist/render/renderQuality.d.ts +69 -0
- package/dist/render/renderQuality.js +22 -0
- package/dist/render/screenSpaceReflection.d.ts +23 -0
- package/dist/render/screenSpaceReflection.js +22 -3
- package/dist/render/sdfFont.js +2 -0
- package/dist/render/segmentQuads.d.ts +13 -0
- package/dist/render/segmentQuads.js +32 -0
- package/dist/render/shaders/flat/lobes.d.ts +27 -0
- package/dist/render/shaders/flat/lobes.js +33 -3
- package/dist/render/shaders/flat/preamble.d.ts +7 -0
- package/dist/render/shaders/flat/preamble.js +24 -1
- package/dist/render/shaders/flat/probeGrid.d.ts +1 -1
- package/dist/render/shaders/flat/probeGrid.js +71 -2
- package/dist/render/shaders/generated/flat.wgsl.d.ts +48 -8
- package/dist/render/shaders/generated/flat.wgsl.js +64 -24
- package/dist/render/shaders/gi/composeField.wgsl.d.ts +56 -0
- package/dist/render/shaders/gi/composeField.wgsl.js +231 -0
- package/dist/render/shaders/gi/marchField.wgsl.d.ts +30 -0
- package/dist/render/shaders/gi/marchField.wgsl.js +141 -0
- package/dist/render/shaders/gi/probeBake.wgsl.d.ts +113 -0
- package/dist/render/shaders/gi/probeBake.wgsl.js +635 -0
- package/dist/render/shaders/gi/probeTrace.wgsl.d.ts +72 -0
- package/dist/render/shaders/gi/probeTrace.wgsl.js +261 -0
- package/dist/render/shaders/gi/probeVolume.wgsl.d.ts +49 -0
- package/dist/render/shaders/gi/probeVolume.wgsl.js +242 -0
- package/dist/render/shaders/gi/sampleField.wgsl.d.ts +32 -0
- package/dist/render/shaders/gi/sampleField.wgsl.js +134 -0
- package/dist/render/shaders/gpudriven/blendRaster.wgsl.d.ts +26 -0
- package/dist/render/shaders/gpudriven/blendRaster.wgsl.js +339 -0
- package/dist/render/shaders/gpudriven/compact.wgsl.d.ts +29 -0
- package/dist/render/shaders/gpudriven/compact.wgsl.js +58 -0
- package/dist/render/shaders/gpudriven/cull.wgsl.d.ts +90 -0
- package/dist/render/shaders/gpudriven/cull.wgsl.js +423 -0
- package/dist/render/shaders/gpudriven/decode.wgsl.d.ts +72 -0
- package/dist/render/shaders/gpudriven/decode.wgsl.js +349 -0
- package/dist/render/shaders/gpudriven/environment.wgsl.d.ts +44 -0
- package/dist/render/shaders/gpudriven/environment.wgsl.js +137 -0
- package/dist/render/shaders/gpudriven/fog.wgsl.d.ts +28 -0
- package/dist/render/shaders/gpudriven/fog.wgsl.js +107 -0
- package/dist/render/shaders/gpudriven/frameBlock.wgsl.d.ts +13 -0
- package/dist/render/shaders/gpudriven/frameBlock.wgsl.js +62 -0
- package/dist/render/shaders/gpudriven/materialBin.wgsl.d.ts +51 -0
- package/dist/render/shaders/gpudriven/materialBin.wgsl.js +112 -0
- package/dist/render/shaders/gpudriven/materialTable.wgsl.d.ts +25 -0
- package/dist/render/shaders/gpudriven/materialTable.wgsl.js +43 -0
- package/dist/render/shaders/gpudriven/meshTransform.wgsl.d.ts +18 -0
- package/dist/render/shaders/gpudriven/meshTransform.wgsl.js +37 -0
- package/dist/render/shaders/gpudriven/present.wgsl.d.ts +43 -0
- package/dist/render/shaders/gpudriven/present.wgsl.js +127 -0
- package/dist/render/shaders/gpudriven/shade.wgsl.d.ts +127 -0
- package/dist/render/shaders/gpudriven/shade.wgsl.js +874 -0
- package/dist/render/shaders/gpudriven/shadow.wgsl.d.ts +44 -0
- package/dist/render/shaders/gpudriven/shadow.wgsl.js +308 -0
- package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.d.ts +28 -0
- package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.js +245 -0
- package/dist/render/shaders/network.wgsl.d.ts +101 -0
- package/dist/render/shaders/network.wgsl.js +206 -0
- package/dist/render/shaders/recon/motion.wgsl.d.ts +44 -0
- package/dist/render/shaders/recon/motion.wgsl.js +96 -0
- package/dist/render/shaders/recon/resolve.wgsl.d.ts +51 -0
- package/dist/render/shaders/recon/resolve.wgsl.js +733 -0
- package/dist/render/temporalAa.js +8 -33
- package/dist/render/textLayout.js +12 -6
- package/dist/render/textRenderer.d.ts +3 -1
- package/dist/render/textRenderer.js +4 -1
- package/dist/render/translucentQueue.d.ts +6 -0
- package/dist/render/translucentQueue.js +11 -0
- package/dist/render/windStreakRenderer.d.ts +2 -1
- package/dist/render/windStreakRenderer.js +3 -1
- package/dist/world/cell.d.ts +63 -0
- package/dist/world/cell.js +181 -0
- package/dist/world/cellStream.d.ts +103 -0
- package/dist/world/cellStream.js +198 -0
- package/dist/world/freeze.d.ts +81 -0
- package/dist/world/freeze.js +114 -0
- package/dist/world/rebase.d.ts +29 -0
- package/dist/world/rebase.js +41 -0
- package/package.json +8 -3
- package/scripts/frames.mjs +92 -0
- package/scripts/gpuCompute.mjs +323 -0
- package/scripts/png.d.mts +16 -0
- package/scripts/png.mjs +25 -0
- package/src/core/loop.ts +19 -2
- package/src/core/storage.ts +5 -0
- package/src/geometry/meshBuilder.ts +5 -1
- package/src/index.ts +246 -4
- package/src/input/input.ts +6 -1
- package/src/math/exact.ts +69 -3
- package/src/math/frustum.ts +52 -3
- package/src/render/backend/api.ts +42 -1
- package/src/render/backend/budget.ts +7 -6
- package/src/render/backend/createRenderer.ts +92 -3
- package/src/render/backend/materialChanges.ts +54 -0
- package/src/render/backend/select.ts +8 -0
- package/src/render/backend/webgl2/renderer.ts +211 -9
- package/src/render/backend/webgpu/buffers.ts +182 -69
- package/src/render/backend/webgpu/fieldCompose.ts +482 -0
- package/src/render/backend/webgpu/giFieldPass.ts +200 -0
- package/src/render/backend/webgpu/gpuDrivenPass.ts +2993 -0
- package/src/render/backend/webgpu/linePass.ts +4 -39
- package/src/render/backend/webgpu/probeBake.ts +588 -0
- package/src/render/backend/webgpu/renderer.ts +1481 -76
- package/src/render/backend/webgpu/scatterPass.ts +15 -1
- package/src/render/backend/webgpu/surfaceTexturePass.ts +63 -36
- package/src/render/causticsRenderer.ts +5 -2
- package/src/render/depthConvention.ts +15 -0
- package/src/render/deviceFeatures.ts +77 -0
- package/src/render/fog.ts +185 -0
- package/src/render/frame/alias.ts +97 -0
- package/src/render/frame/deps.ts +155 -0
- package/src/render/frame/flushGraph.ts +135 -0
- package/src/render/frame/graphSchedule.ts +170 -0
- package/src/render/frame/index.ts +52 -0
- package/src/render/frame/lifetime.ts +61 -0
- package/src/render/frame/passTimings.ts +69 -0
- package/src/render/frame/validate.ts +40 -0
- package/src/render/frame/virtual.ts +91 -0
- package/src/render/gi/chain.ts +225 -0
- package/src/render/gi/denoise.ts +276 -0
- package/src/render/gi/fieldScene.ts +264 -0
- package/src/render/gi/globalField.ts +543 -0
- package/src/render/gi/probeGridFit.ts +89 -0
- package/src/render/gi/probeTrace.ts +143 -0
- package/src/render/gi/probeVolume.ts +288 -0
- package/src/render/gi/reflection.ts +163 -0
- package/src/render/gi/traceField.ts +223 -0
- package/src/render/gi/traceScreen.ts +106 -0
- package/src/render/gpuScene.ts +80 -0
- package/src/render/gpudriven/alphaTest.ts +33 -0
- package/src/render/gpudriven/clusterUpload.ts +123 -0
- package/src/render/gpudriven/clusterWorld.ts +239 -0
- package/src/render/gpudriven/compact.ts +80 -0
- package/src/render/gpudriven/cullClusters.ts +333 -0
- package/src/render/gpudriven/decodeTables.ts +332 -0
- package/src/render/gpudriven/frustum.ts +70 -0
- package/src/render/gpudriven/geometryHarness.ts +51 -0
- package/src/render/gpudriven/hzb.ts +113 -0
- package/src/render/gpudriven/ibl.ts +147 -0
- package/src/render/gpudriven/indirect.ts +89 -0
- package/src/render/gpudriven/instances.ts +234 -0
- package/src/render/gpudriven/lit.ts +329 -0
- package/src/render/gpudriven/lodCut.ts +62 -0
- package/src/render/gpudriven/materialBin.ts +131 -0
- package/src/render/gpudriven/materialTable.ts +164 -0
- package/src/render/gpudriven/pipeline.ts +336 -0
- package/src/render/gpudriven/rangeAlloc.ts +122 -0
- package/src/render/gpudriven/sceneUpload.ts +173 -0
- package/src/render/gpudriven/shadeBins.ts +222 -0
- package/src/render/gpudriven/shadow.ts +285 -0
- package/src/render/gpudriven/shadowCamera.ts +406 -0
- package/src/render/gpudriven/streamScene.ts +575 -0
- package/src/render/gpudriven/surfaceFrame.ts +68 -0
- package/src/render/gpudriven/twoPhase.ts +214 -0
- package/src/render/gpudriven/visbuffer.ts +41 -0
- package/src/render/halfFloat.ts +22 -4
- package/src/render/inference/attentionTiled.ts +132 -0
- package/src/render/inference/denseKernels.ts +369 -0
- package/src/render/inference/device.ts +44 -0
- package/src/render/inference/deviceGraph.ts +39 -0
- package/src/render/inference/fuse.ts +47 -0
- package/src/render/inference/kernelKit.ts +120 -0
- package/src/render/inference/kernels.ts +18 -0
- package/src/render/inference/plan.ts +26 -0
- package/src/render/inference/reuse.ts +79 -0
- package/src/render/inference/runner.ts +204 -0
- package/src/render/inference/schedule.ts +80 -0
- package/src/render/inference/shapeKernels.ts +112 -0
- package/src/render/inference/spatialKernels.ts +220 -0
- package/src/render/inference/splatRaster.wgsl.ts +348 -0
- package/src/render/inference/splatTiles.ts +186 -0
- package/src/render/lineBatch.ts +4 -24
- package/src/render/mesh.ts +46 -1
- package/src/render/pass.ts +155 -10
- package/src/render/prefilterEnvMap.ts +33 -0
- package/src/render/readback.ts +73 -0
- package/src/render/recon/clamp.ts +113 -0
- package/src/render/recon/disocclusion.ts +143 -0
- package/src/render/recon/frameGen.ts +191 -0
- package/src/render/recon/frameSizes.ts +64 -0
- package/src/render/recon/jitter.ts +126 -0
- package/src/render/recon/motionVectors.ts +138 -0
- package/src/render/recon/reproject.ts +179 -0
- package/src/render/recon/resolve.ts +444 -0
- package/src/render/recon/tier.ts +31 -0
- package/src/render/reflectionProbe.ts +34 -0
- package/src/render/renderQuality.ts +87 -0
- package/src/render/screenSpaceReflection.ts +37 -3
- package/src/render/sdfFont.ts +2 -0
- package/src/render/segmentQuads.ts +39 -0
- package/src/render/shaders/flat/lobes.ts +35 -3
- package/src/render/shaders/flat/preamble.ts +25 -1
- package/src/render/shaders/flat/probeGrid.ts +77 -2
- package/src/render/shaders/generated/flat.wgsl.ts +64 -24
- package/src/render/shaders/gi/composeField.wgsl.ts +239 -0
- package/src/render/shaders/gi/marchField.wgsl.ts +146 -0
- package/src/render/shaders/gi/probeBake.wgsl.ts +649 -0
- package/src/render/shaders/gi/probeTrace.wgsl.ts +269 -0
- package/src/render/shaders/gi/probeVolume.wgsl.ts +246 -0
- package/src/render/shaders/gi/sampleField.wgsl.ts +140 -0
- package/src/render/shaders/gpudriven/blendRaster.wgsl.ts +343 -0
- package/src/render/shaders/gpudriven/compact.wgsl.ts +59 -0
- package/src/render/shaders/gpudriven/cull.wgsl.ts +433 -0
- package/src/render/shaders/gpudriven/decode.wgsl.ts +373 -0
- package/src/render/shaders/gpudriven/environment.wgsl.ts +141 -0
- package/src/render/shaders/gpudriven/fog.wgsl.ts +110 -0
- package/src/render/shaders/gpudriven/frameBlock.wgsl.ts +62 -0
- package/src/render/shaders/gpudriven/materialBin.wgsl.ts +116 -0
- package/src/render/shaders/gpudriven/materialTable.wgsl.ts +46 -0
- package/src/render/shaders/gpudriven/meshTransform.wgsl.ts +37 -0
- package/src/render/shaders/gpudriven/present.wgsl.ts +129 -0
- package/src/render/shaders/gpudriven/shade.wgsl.ts +884 -0
- package/src/render/shaders/gpudriven/shadow.wgsl.ts +322 -0
- package/src/render/shaders/gpudriven/visbufferRaster.wgsl.ts +251 -0
- package/src/render/shaders/network.wgsl.ts +238 -0
- package/src/render/shaders/recon/motion.wgsl.ts +101 -0
- package/src/render/shaders/recon/resolve.wgsl.ts +741 -0
- package/src/render/temporalAa.ts +9 -33
- package/src/render/textLayout.ts +12 -6
- package/src/render/textRenderer.ts +5 -2
- package/src/render/translucentQueue.ts +11 -0
- package/src/render/windStreakRenderer.ts +4 -2
- package/src/world/cell.ts +231 -0
- package/src/world/cellStream.ts +278 -0
- package/src/world/freeze.ts +157 -0
- package/src/world/rebase.ts +56 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Putting the GPU-driven pipeline's colour into the frame it was invited to draw in.
|
|
3
|
+
*
|
|
4
|
+
* **A contributed pass cannot write into the frame's attachment directly and should not want
|
|
5
|
+
* to.** `pass.ts` refuses a declared write-set for a reason it states at length; what a pass gets
|
|
6
|
+
* instead is a target of its own and a draw call at the point the caller put it. So the pipeline
|
|
7
|
+
* fills a storage texture in `prepare` and this puts it on the screen in `draw` — one triangle, no
|
|
8
|
+
* sampler, and `textureLoad` at the fragment's own pixel.
|
|
9
|
+
*
|
|
10
|
+
* **`textureLoad` rather than a sampler, because there is nothing to filter.** The target is the
|
|
11
|
+
* frame's own size and the mapping is one texel to one pixel; a sampler would add a filtering mode
|
|
12
|
+
* to choose and a half-pixel offset to get wrong, in exchange for nothing. It also means the
|
|
13
|
+
* texture needs no `filterable` sample type, which `rgba16float` has but which a future `r32float`
|
|
14
|
+
* would not.
|
|
15
|
+
*
|
|
16
|
+
* **It writes linear light.** The output transform is the composite's, for the reason
|
|
17
|
+
* `PassContext` gives: with a composite the resolve grades and a pass that also graded would apply
|
|
18
|
+
* the curve twice. A frame without one is refused in words rather than papered over — see the
|
|
19
|
+
* pass's `draw`.
|
|
20
|
+
*/
|
|
21
|
+
export const GPU_DRIVEN_BLIT_WGSL = `
|
|
22
|
+
struct Varying {
|
|
23
|
+
@builtin(position) position: vec4<f32>,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@group(0) @binding(0) var source: texture_2d<f32>;
|
|
27
|
+
|
|
28
|
+
@vertex
|
|
29
|
+
fn vertexMain(@builtin(vertex_index) vertex: u32) -> Varying {
|
|
30
|
+
/* One triangle covering the frame rather than two: no shared edge, no diagonal seam, and the
|
|
31
|
+
third of the area outside the viewport costs nothing. */
|
|
32
|
+
var corners = array<vec2<f32>, 3>(
|
|
33
|
+
vec2<f32>(-1.0, -1.0),
|
|
34
|
+
vec2<f32>(3.0, -1.0),
|
|
35
|
+
vec2<f32>(-1.0, 3.0),
|
|
36
|
+
);
|
|
37
|
+
var out: Varying;
|
|
38
|
+
out.position = vec4<f32>(corners[vertex], 0.0, 1.0);
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@fragment
|
|
43
|
+
fn fragmentMain(in: Varying) -> @location(0) vec4<f32> {
|
|
44
|
+
/* The fragment's own pixel. Clamped rather than trusted: a frame larger than the pass's target
|
|
45
|
+
is a caller that did not resize, and reading out of bounds is a zero this would draw as a
|
|
46
|
+
black band nobody could place. */
|
|
47
|
+
let size = textureDimensions(source);
|
|
48
|
+
let at = min(vec2<u32>(in.position.xy), size - vec2<u32>(1u, 1u));
|
|
49
|
+
let texel = textureLoad(source, at, 0);
|
|
50
|
+
/*
|
|
51
|
+
* **Nothing was drawn here, so nothing is written — and that is a cost, not a correctness
|
|
52
|
+
* guard.** The target is cleared to zero and a zero laid over anything premultiplied is that
|
|
53
|
+
* thing, so the pixel would come out the same either way; this spares every pixel the pipeline
|
|
54
|
+
* did not cover a blend. Removing it left "depth-share-check.mjs" green on 2026-09-18, which is
|
|
55
|
+
* what says so. Alpha is the coverage: the shading writes one and a blended surface adds its own.
|
|
56
|
+
*
|
|
57
|
+
* **Returned as it is, premultiplied, and the pipeline blends it over.** Where the visibility
|
|
58
|
+
* buffer covered the pixel the coverage is one and over is a plain write. Where only a pane did,
|
|
59
|
+
* it is the pane's coverage, and the frame behind it — the sky — has to show through by the rest.
|
|
60
|
+
* This used to discard below a half and write the colour as opaque, which painted a heavy pane
|
|
61
|
+
* over black and dropped a light one altogether.
|
|
62
|
+
*/
|
|
63
|
+
if (texel.a <= 0.0) { discard; }
|
|
64
|
+
return texel;
|
|
65
|
+
}
|
|
66
|
+
`;
|
|
67
|
+
/**
|
|
68
|
+
* The same blit, handing the frame the pass's depth as well as its colour.
|
|
69
|
+
*
|
|
70
|
+
* **Two pipelines in one frame need one depth buffer.** The blit above presents colour and nothing
|
|
71
|
+
* else, which is right for a consumer whose whole scene is this pipeline — and wrong for one that
|
|
72
|
+
* draws its own geometry beside it, because then a forward-path mesh behind GPU-driven geometry has
|
|
73
|
+
* nothing to be hidden by. No ordering fixes that: drawn before the blit it is painted over, drawn
|
|
74
|
+
* after it floats in front of a hill.
|
|
75
|
+
*
|
|
76
|
+
* **The formats already agree.** The pass's depth texture and the frame's both take `DEPTH_FORMAT`
|
|
77
|
+
* and `DEPTH_COMPARE` from `depthConvention.ts`, so this is the same format and the same reversed
|
|
78
|
+
* convention and there is no conversion to get wrong. `gpuDrivenPass.ts` asserts that rather than
|
|
79
|
+
* trusting it, because a backend that ever differed would produce a depth test that is silently
|
|
80
|
+
* inverted instead of one that fails.
|
|
81
|
+
*
|
|
82
|
+
* **A pixel the pipeline did not cover is discarded before it writes**, exactly as above, so the
|
|
83
|
+
* sky keeps the depth the frame already had rather than being pushed to the near plane. A pixel
|
|
84
|
+
* only a pane covered writes the depth the pass cleared to, which is the frame's own clear — the
|
|
85
|
+
* pipeline compares "or equal" so that it passes over the sky, and what it writes there is the
|
|
86
|
+
* value already there.
|
|
87
|
+
*/
|
|
88
|
+
export const GPU_DRIVEN_BLIT_DEPTH_WGSL = /* wgsl */ `
|
|
89
|
+
struct Varying {
|
|
90
|
+
@builtin(position) position: vec4<f32>,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
struct Presented {
|
|
94
|
+
@location(0) colour: vec4<f32>,
|
|
95
|
+
@builtin(frag_depth) depth: f32,
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@group(0) @binding(0) var source: texture_2d<f32>;
|
|
99
|
+
@group(0) @binding(1) var sourceDepth: texture_depth_2d;
|
|
100
|
+
|
|
101
|
+
@vertex
|
|
102
|
+
fn vertexMain(@builtin(vertex_index) vertex: u32) -> Varying {
|
|
103
|
+
var corners = array<vec2<f32>, 3>(
|
|
104
|
+
vec2<f32>(-1.0, -1.0),
|
|
105
|
+
vec2<f32>(3.0, -1.0),
|
|
106
|
+
vec2<f32>(-1.0, 3.0),
|
|
107
|
+
);
|
|
108
|
+
var out: Varying;
|
|
109
|
+
out.position = vec4<f32>(corners[vertex], 0.0, 1.0);
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
@fragment
|
|
114
|
+
fn fragmentMain(in: Varying) -> Presented {
|
|
115
|
+
let size = textureDimensions(source);
|
|
116
|
+
let at = min(vec2<u32>(in.position.xy), size - vec2<u32>(1u, 1u));
|
|
117
|
+
let texel = textureLoad(source, at, 0);
|
|
118
|
+
/* A cost here too: the depth the pass cleared to passes only where the frame holds that same
|
|
119
|
+
clear, so the write would change nothing. The colour blit above says how that was measured. */
|
|
120
|
+
if (texel.a <= 0.0) { discard; }
|
|
121
|
+
var out: Presented;
|
|
122
|
+
out.colour = texel;
|
|
123
|
+
/* The depth this pixel was rastered at, so the frame tests against the surface it can see. */
|
|
124
|
+
out.depth = textureLoad(sourceDepth, at, 0);
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
`;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rebuilding a pixel's surface from the triangle it recorded, on the device.
|
|
3
|
+
*
|
|
4
|
+
* **The same arithmetic as `gpudriven/shadeBins.ts`, term for term**, and
|
|
5
|
+
* `scripts/gpu-parity.mjs` runs both over generated triangles until they agree. That is the whole
|
|
6
|
+
* safety property of this file: a visibility buffer's interpolation is the only part of the
|
|
7
|
+
* shading that differs between the two pipelines, so it is the only part that can drift, and the
|
|
8
|
+
* check is on the part that can drift.
|
|
9
|
+
*
|
|
10
|
+
* **The lighting is not here and must not arrive here.** What this pass produces is a surface — a
|
|
11
|
+
* position, a normal, texture coordinates and their screen gradients — and the lit expression that
|
|
12
|
+
* consumes it is the forward path's own, wired in when the pipeline is assembled. A second copy of
|
|
13
|
+
* the lighting would drift from the first and the parity gate would be the only thing that
|
|
14
|
+
* noticed; the plan says so in those words and it is right.
|
|
15
|
+
*
|
|
16
|
+
* **Gradients are derived, not differenced.** A fragment shader gets `dpdx` and `dpdy` because it
|
|
17
|
+
* runs in quads of four; a compute invocation has no neighbours and no derivative at all. Without
|
|
18
|
+
* an analytic one a texture read picks a fixed level, which is blurry everywhere, or level zero,
|
|
19
|
+
* which sparkles — and neither reads as a missing derivative.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* The interpolation itself, with no bindings and no layout in it.
|
|
23
|
+
*
|
|
24
|
+
* **Its own string because two shaders do this and only one of them can be checked.** The
|
|
25
|
+
* reconstruction below is run against `shadeBins.ts` over generated triangles by
|
|
26
|
+
* `gpu-parity.mjs`; the pipeline's real shading pass reads different buffers and writes a colour,
|
|
27
|
+
* so nothing can compare it to a reference. Sharing the *text* is what makes the checked one
|
|
28
|
+
* speak for both — a second copy of the quotient rule would be the half nobody is watching, which
|
|
29
|
+
* is precisely the arrangement this wave keeps finding and refusing.
|
|
30
|
+
*
|
|
31
|
+
* Takes clip positions and pixel centres and answers weights and their two gradients. `valid` is
|
|
32
|
+
* false for a degenerate triangle or a perspective denominator at zero, which is a visibility
|
|
33
|
+
* identifier for a triangle the rasteriser never drew.
|
|
34
|
+
*/
|
|
35
|
+
export declare const SHADE_SURFACE_WGSL = "\nstruct Screen {\n x: f32,\n y: f32,\n invW: f32,\n}\n\nstruct Weights {\n /** The perspective-correct barycentric weights. */\n c: vec3<f32>,\n /** How each of them changes with screen x, and with screen y. */\n gx: vec3<f32>,\n gy: vec3<f32>,\n valid: bool,\n}\n\nfn screenOf(clip: vec4<f32>, width: f32, height: f32) -> Screen {\n let inv = 1.0 / clip.w;\n var out: Screen;\n out.x = (clip.x * inv * 0.5 + 0.5) * width;\n out.y = (clip.y * inv * 0.5 + 0.5) * height;\n out.invW = inv;\n return out;\n}\n\nfn surfaceWeights(s0: Screen, s1: Screen, s2: Screen, px: f32, py: f32) -> Weights {\n var out: Weights;\n out.valid = false;\n\n let area = (s1.x - s0.x) * (s2.y - s0.y) - (s2.x - s0.x) * (s1.y - s0.y);\n if (abs(area) < 1e-12) { return out; }\n let inverse = 1.0 / area;\n\n /* Screen-space weights. A point outside the triangle gets a negative one rather than a clamp:\n clamping shades a pixel beyond an edge as though it were on the triangle. */\n let l1 = ((px - s0.x) * (s2.y - s0.y) - (s2.x - s0.x) * (py - s0.y)) * inverse;\n let l2 = ((s1.x - s0.x) * (py - s0.y) - (px - s0.x) * (s1.y - s0.y)) * inverse;\n let l0 = 1.0 - l1 - l2;\n\n let n0 = l0 * s0.invW;\n let n1 = l1 * s1.invW;\n let n2 = l2 * s2.invW;\n let sum = n0 + n1 + n2;\n if (abs(sum) < 1e-20) { return out; }\n let invSum = 1.0 / sum;\n out.c = vec3<f32>(n0 * invSum, n1 * invSum, n2 * invSum);\n\n /* The screen weights' gradients, constant over the triangle. */\n let d1dx = (s2.y - s0.y) * inverse;\n let d1dy = (s0.x - s2.x) * inverse;\n let d2dx = (s0.y - s1.y) * inverse;\n let d2dy = (s1.x - s0.x) * inverse;\n let d0dx = -d1dx - d2dx;\n let d0dy = -d1dy - d2dy;\n\n let dSumDx = d0dx * s0.invW + d1dx * s1.invW + d2dx * s2.invW;\n let dSumDy = d0dy * s0.invW + d1dy * s1.invW + d2dy * s2.invW;\n\n /* Quotient rule, per weight, with the shared denominator factored out. */\n out.gx = vec3<f32>(\n (d0dx * s0.invW - n0 * invSum * dSumDx) * invSum,\n (d1dx * s1.invW - n1 * invSum * dSumDx) * invSum,\n (d2dx * s2.invW - n2 * invSum * dSumDx) * invSum,\n );\n out.gy = vec3<f32>(\n (d0dy * s0.invW - n0 * invSum * dSumDy) * invSum,\n (d1dy * s1.invW - n1 * invSum * dSumDy) * invSum,\n (d2dy * s2.invW - n2 * invSum * dSumDy) * invSum,\n );\n out.valid = true;\n return out;\n}\n\n/* surfaceFrame.ts surfaceLod: the longer screen-axis UV step, in texels, as a power of two. */\nfn surfaceLod(gradX: vec2<f32>, gradY: vec2<f32>, size: f32) -> f32 {\n let footprint = max(length(gradX), length(gradY)) * size;\n return max(log2(max(footprint, 1e-30)), 0.0);\n}\n\nstruct TangentFrame {\n t: vec3<f32>,\n b: vec3<f32>,\n}\n\n/* surfaceFrame.ts derivedTangentFrame, which is tangentFrame.ts's derived branch term for term. */\nfn derivedTangentFrame(\n n: vec3<f32>,\n dp1: vec3<f32>,\n dp2: vec3<f32>,\n duv1: vec2<f32>,\n duv2: vec2<f32>,\n) -> TangentFrame {\n let dp2perp = cross(dp2, n);\n let dp1perp = cross(n, dp1);\n let dt = dp2perp * duv1.x + dp1perp * duv2.x;\n let db = dp2perp * duv1.y + dp1perp * duv2.y;\n let det = duv1.x * duv2.y - duv2.x * duv1.y;\n let handed = select(1.0, -1.0, det < 0.0);\n let scale = inverseSqrt(max(max(dot(dt, dt), dot(db, db)), 1e-12)) * handed;\n var frame: TangentFrame;\n frame.t = dt * scale;\n frame.b = db * scale;\n return frame;\n}\n";
|
|
36
|
+
/**
|
|
37
|
+
* One invocation a pixel of one material's bin.
|
|
38
|
+
*
|
|
39
|
+
* `settings` is the bin's offset, its length, the screen width and the screen height. A dispatch
|
|
40
|
+
* is issued per material from the blocks `materialBin.wgsl.ts` wrote, so the bin this reads is
|
|
41
|
+
* whichever one the caller bound — the shader does not choose.
|
|
42
|
+
*
|
|
43
|
+
* The outputs here are the reconstruction rather than a colour, because that is what can be
|
|
44
|
+
* compared against a reference. In the assembled pipeline the same values feed the lit expression
|
|
45
|
+
* and the colour is what lands.
|
|
46
|
+
*/
|
|
47
|
+
export declare const SHADE_RECONSTRUCT_WGSL = "\nstruct Screen {\n x: f32,\n y: f32,\n invW: f32,\n}\n\nstruct Weights {\n /** The perspective-correct barycentric weights. */\n c: vec3<f32>,\n /** How each of them changes with screen x, and with screen y. */\n gx: vec3<f32>,\n gy: vec3<f32>,\n valid: bool,\n}\n\nfn screenOf(clip: vec4<f32>, width: f32, height: f32) -> Screen {\n let inv = 1.0 / clip.w;\n var out: Screen;\n out.x = (clip.x * inv * 0.5 + 0.5) * width;\n out.y = (clip.y * inv * 0.5 + 0.5) * height;\n out.invW = inv;\n return out;\n}\n\nfn surfaceWeights(s0: Screen, s1: Screen, s2: Screen, px: f32, py: f32) -> Weights {\n var out: Weights;\n out.valid = false;\n\n let area = (s1.x - s0.x) * (s2.y - s0.y) - (s2.x - s0.x) * (s1.y - s0.y);\n if (abs(area) < 1e-12) { return out; }\n let inverse = 1.0 / area;\n\n /* Screen-space weights. A point outside the triangle gets a negative one rather than a clamp:\n clamping shades a pixel beyond an edge as though it were on the triangle. */\n let l1 = ((px - s0.x) * (s2.y - s0.y) - (s2.x - s0.x) * (py - s0.y)) * inverse;\n let l2 = ((s1.x - s0.x) * (py - s0.y) - (px - s0.x) * (s1.y - s0.y)) * inverse;\n let l0 = 1.0 - l1 - l2;\n\n let n0 = l0 * s0.invW;\n let n1 = l1 * s1.invW;\n let n2 = l2 * s2.invW;\n let sum = n0 + n1 + n2;\n if (abs(sum) < 1e-20) { return out; }\n let invSum = 1.0 / sum;\n out.c = vec3<f32>(n0 * invSum, n1 * invSum, n2 * invSum);\n\n /* The screen weights' gradients, constant over the triangle. */\n let d1dx = (s2.y - s0.y) * inverse;\n let d1dy = (s0.x - s2.x) * inverse;\n let d2dx = (s0.y - s1.y) * inverse;\n let d2dy = (s1.x - s0.x) * inverse;\n let d0dx = -d1dx - d2dx;\n let d0dy = -d1dy - d2dy;\n\n let dSumDx = d0dx * s0.invW + d1dx * s1.invW + d2dx * s2.invW;\n let dSumDy = d0dy * s0.invW + d1dy * s1.invW + d2dy * s2.invW;\n\n /* Quotient rule, per weight, with the shared denominator factored out. */\n out.gx = vec3<f32>(\n (d0dx * s0.invW - n0 * invSum * dSumDx) * invSum,\n (d1dx * s1.invW - n1 * invSum * dSumDx) * invSum,\n (d2dx * s2.invW - n2 * invSum * dSumDx) * invSum,\n );\n out.gy = vec3<f32>(\n (d0dy * s0.invW - n0 * invSum * dSumDy) * invSum,\n (d1dy * s1.invW - n1 * invSum * dSumDy) * invSum,\n (d2dy * s2.invW - n2 * invSum * dSumDy) * invSum,\n );\n out.valid = true;\n return out;\n}\n\n/* surfaceFrame.ts surfaceLod: the longer screen-axis UV step, in texels, as a power of two. */\nfn surfaceLod(gradX: vec2<f32>, gradY: vec2<f32>, size: f32) -> f32 {\n let footprint = max(length(gradX), length(gradY)) * size;\n return max(log2(max(footprint, 1e-30)), 0.0);\n}\n\nstruct TangentFrame {\n t: vec3<f32>,\n b: vec3<f32>,\n}\n\n/* surfaceFrame.ts derivedTangentFrame, which is tangentFrame.ts's derived branch term for term. */\nfn derivedTangentFrame(\n n: vec3<f32>,\n dp1: vec3<f32>,\n dp2: vec3<f32>,\n duv1: vec2<f32>,\n duv2: vec2<f32>,\n) -> TangentFrame {\n let dp2perp = cross(dp2, n);\n let dp1perp = cross(n, dp1);\n let dt = dp2perp * duv1.x + dp1perp * duv2.x;\n let db = dp2perp * duv1.y + dp1perp * duv2.y;\n let det = duv1.x * duv2.y - duv2.x * duv1.y;\n let handed = select(1.0, -1.0, det < 0.0);\n let scale = inverseSqrt(max(max(dot(dt, dt), dot(db, db)), 1e-12)) * handed;\n var frame: TangentFrame;\n frame.t = dt * scale;\n frame.b = db * scale;\n return frame;\n}\n\nconst VIS_TRIANGLE_BITS: u32 = 7u;\nconst VIS_EMPTY: u32 = 0xffffffffu;\n\n/** binOffset, binCount, width, height. */\n@group(0) @binding(0) var<storage, read> settings: array<u32, 4>;\n@group(0) @binding(1) var<storage, read> binPixels: array<u32>;\n@group(0) @binding(2) var<storage, read> visibility: array<u32>;\n/** Four words a cluster: index offset, index count, cluster identifier, mesh. */\n@group(0) @binding(3) var<storage, read> clusterMeta: array<u32>;\n@group(0) @binding(4) var<storage, read> indices: array<u32>;\n/** Four floats a vertex: clip x, clip y, clip w, and one attribute to interpolate. */\n@group(0) @binding(5) var<storage, read> vertices: array<f32>;\n/** Six floats a pixel: the interpolated attribute, its two gradients, and the three weights. */\n@group(0) @binding(6) var<storage, read_write> surface: array<f32>;\n\nfn clipOf(vertex: u32) -> vec4<f32> {\n let at = vertex * 4u;\n return vec4<f32>(vertices[at], vertices[at + 1u], 0.0, vertices[at + 2u]);\n}\n\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) id: vec3<u32>) {\n /* Past the end of the bin, which is the ordinary case: a bin is not a multiple of the group\n size, and the spare lanes must do nothing rather than shade the last pixel again. */\n if (id.x >= settings[1]) { return; }\n let pixel = binPixels[settings[0] + id.x];\n let packed = visibility[pixel];\n if (packed == VIS_EMPTY) { return; }\n\n let cluster = packed >> VIS_TRIANGLE_BITS;\n let triangle = packed & 127u;\n let metaAt = cluster * 4u;\n let indexOffset = clusterMeta[metaAt];\n\n let i0 = indices[indexOffset + triangle * 3u];\n let i1 = indices[indexOffset + triangle * 3u + 1u];\n let i2 = indices[indexOffset + triangle * 3u + 2u];\n\n let width = f32(settings[2]);\n let height = f32(settings[3]);\n let s0 = screenOf(clipOf(i0), width, height);\n let s1 = screenOf(clipOf(i1), width, height);\n let s2 = screenOf(clipOf(i2), width, height);\n\n /* Pixel centres, which is where a rasteriser decided this pixel was covered. */\n let px = f32(pixel % settings[2]) + 0.5;\n let py = f32(pixel / settings[2]) + 0.5;\n\n let w = surfaceWeights(s0, s1, s2, px, py);\n if (!w.valid) { return; }\n\n let a = vec3<f32>(\n vertices[i0 * 4u + 3u],\n vertices[i1 * 4u + 3u],\n vertices[i2 * 4u + 3u],\n );\n\n let out = id.x * 6u;\n surface[out] = dot(a, w.c);\n surface[out + 1u] = dot(a, w.gx);\n surface[out + 2u] = dot(a, w.gy);\n surface[out + 3u] = w.c.x;\n surface[out + 4u] = w.c.y;\n surface[out + 5u] = w.c.z;\n}\n";
|
|
48
|
+
/**
|
|
49
|
+
* One dispatch a material: every pixel in that material's bin, lit and written to the frame.
|
|
50
|
+
*
|
|
51
|
+
* **This is the assembled half, and it is the one nothing can compare against a reference.** The
|
|
52
|
+
* interpolation it rests on is `SHADE_SURFACE_WGSL` above, which *is* compared — that sharing is
|
|
53
|
+
* the point of the split, because the quotient rule is what drifts and a second copy of it would
|
|
54
|
+
* be the copy nobody watches.
|
|
55
|
+
*
|
|
56
|
+
* **Which material a dispatch is shading arrives in a uniform with a per-material bind group**, not
|
|
57
|
+
* in the shader. There is no builtin for "which dispatch am I", and a shader per material would be
|
|
58
|
+
* a pipeline per material — so one pipeline is bound once and group one moves per dispatch. The
|
|
59
|
+
* bin's own offset and length are read from the buffers `materialBin.wgsl.ts` wrote rather than
|
|
60
|
+
* passed in, so there is one statement of where a bin is.
|
|
61
|
+
*
|
|
62
|
+
* **Eight storage buffers is the budget and this pass spends all of it.**
|
|
63
|
+
* `maxStorageBuffersPerShaderStage` is 8 by default; this adapter offers 16 and most do, and a
|
|
64
|
+
* pipeline that asked for the higher limit would refuse to create a device on the ones that do
|
|
65
|
+
* not. So the vertex attributes are interleaved into one buffer rather than three, and the frame
|
|
66
|
+
* block and the material table are uniforms. Found at pipeline creation with twelve.
|
|
67
|
+
*
|
|
68
|
+
* **What the lighting here is, and what it is not.** Vertex colour times the material's tint, one
|
|
69
|
+
* directional term with its highlight and its shadow, the same hemispheric ambient the forward path
|
|
70
|
+
* uses — `mix(ground, sky, n.y * 0.5 + 0.5)` — and the material's emissive. It is not the forward
|
|
71
|
+
* path's standard material: no image-based term and no texture. Those belong to a pipeline that
|
|
72
|
+
* draws the published scenes, and this one draws drafts. **Stating the gap is the point**;
|
|
73
|
+
* discovering it from a capture is what this repository keeps refusing.
|
|
74
|
+
*
|
|
75
|
+
* **The pixel centre is the flipped row.** WebGPU's framebuffer origin is the top-left, so row zero
|
|
76
|
+
* is where clip y is +1, and `screenOf` maps clip y of +1 to `height`. The flip belongs to the
|
|
77
|
+
* pixel rather than to the shared function, which is checked against a reference and must not
|
|
78
|
+
* learn about a framebuffer; flipping all four points of a barycentric evaluation leaves the
|
|
79
|
+
* weights unchanged, which is why this is the whole of it.
|
|
80
|
+
*
|
|
81
|
+
* **The normal is transformed by the upper three-by-three and renormalised**, which is right for
|
|
82
|
+
* the rigid transforms with uniform scale the draft scenes use and wrong for a non-uniform one —
|
|
83
|
+
* that wants the inverse transpose. A scene that needs it will find this comment rather than a
|
|
84
|
+
* lighting error nobody can place.
|
|
85
|
+
*/
|
|
86
|
+
/**
|
|
87
|
+
* The lit expression, as text, with no bindings in it.
|
|
88
|
+
*
|
|
89
|
+
* **Its own string for the reason the reconstruction above has one, arrived at the same way.** The
|
|
90
|
+
* shading pass writes a colour into a storage texture, so nothing could compare it to a reference —
|
|
91
|
+
* and for as long as the expression was four lines, the shading pass's header argued that nothing
|
|
92
|
+
* needed to. That argument stops the moment the expression grows a term. `gpudriven/lit.ts` is the
|
|
93
|
+
* twin now, `SHADE_LIGHTING_PARITY_WGSL` below is an entry point that does nothing but call this,
|
|
94
|
+
* and `scripts/gpu-parity.mjs` runs both over generated surfaces.
|
|
95
|
+
*
|
|
96
|
+
* Every term matches `flat/main.ts` at the one configuration this pipeline supports — metalness
|
|
97
|
+
* zero, which collapses the Fresnel blend on the highlight to the surface's own reflectance.
|
|
98
|
+
*/
|
|
99
|
+
export declare const SHADE_LIGHTING_WGSL = "\n/* The normalised GGX distribution, transcribed from flat/lobes.ts. See lit.ts about its peak. */\n/*\n * **Two floors, and only one of them can be seen in the output.** The denominator's is what keeps\n * a near-mirror's peak finite, and deleting it is worth 256 times the highlight at roughness 0.05 \u2014\n * gpu-parity.mjs catches that, now that a fifth of its corpus sits on the peak. The floor on a\n * cannot be caught, because the denominator's already prevents the division by zero it guards\n * against: at roughness zero the whole term is a ten-thousandth with it and zero without, and both\n * round to nothing beside a lit surface. It is kept because it is what flat/lobes.ts has, and two\n * spellings of one lobe differing by a guard is the drift this pair exists to prevent.\n */\nfn specularLobe(ndh: f32, roughness: f32) -> f32 {\n let a = max(roughness * roughness, 0.01);\n let a2 = a * a;\n let d = ndh * ndh * (a2 - 1.0) + 1.0;\n return (a2 * a2) / max(d * d, 1e-8);\n}\n\n/* A dielectric's normal-incidence reflectance, which every material in this pipeline is. */\nconst DIELECTRIC_F0: f32 = 0.04;\n\n/*\n * The split sum's second half, as Karis's analytic fit: dfg.x scales f0 and dfg.y adds on top.\n * gpudriven/ibl.ts is the twin and carries what the tabulated integral would cost instead.\n */\nfn envBrdfApprox(ndv: f32, roughness: f32) -> vec2<f32> {\n let c0 = vec4<f32>(-1.0, -0.0275, -0.572, 0.022);\n let c1 = vec4<f32>(1.0, 0.0425, 1.04, -0.04);\n let r = roughness * c0 + c1;\n let a004 = min(r.x * r.x, exp2(-9.28 * ndv)) * r.x + r.y;\n return vec2<f32>(-1.04, 1.04) * a004 + r.zw;\n}\n\n/*\n * The same with the multiply-scattered light put back. At f0 of one this returns exactly 1.0 at\n * every roughness, which is the invariant ibl.test.ts asserts across the range: roughness\n * scatters light and does not absorb it, and the bare split sum claims otherwise by up to 44%.\n */\nfn envSpecularEnergy(f0: f32, dfg: vec2<f32>) -> f32 {\n let single = f0 * dfg.x + dfg.y;\n let energy = dfg.x + dfg.y;\n let missing = 1.0 - energy;\n let average = f0 + (1.0 - f0) / 21.0;\n let multi = single * average / (1.0 - missing * average);\n return single + multi * missing;\n}\n\n/*\n * How much of what the surface sees in the mirror direction reaches the frame.\n *\n * Two expressions and a selector that is exactly 0 or exactly 1: the split sum is the correct\n * share of a prefiltered environment and asserts an integral that was never performed over\n * anything else. See ibl.ts for what ignoring that costs on the other pipeline.\n */\nfn surfaceF0(metalness: f32) -> f32 {\n return mix(DIELECTRIC_F0, 1.0, metalness);\n}\n\nfn environmentWeight(\n ndv: f32,\n roughness: f32,\n reflectivity: f32,\n prefiltered: f32,\n metalness: f32,\n) -> f32 {\n let f0 = surfaceF0(metalness);\n let facing = 1.0 - ndv;\n let fresnel = mix(f0, 1.0, facing * facing * facing * facing * facing);\n /*\n * max(reflectivity * (1 - roughness), metal), and both halves are repairs flat/main.ts records:\n * a metal reflects whether or not the pass asked for reflections, and the one-minus-roughness\n * thinning belongs to the dielectric alone. See ibl.ts.\n */\n let amount = max(reflectivity * (1.0 - roughness), metalness);\n let sweep = clamp(fresnel * amount, 0.0, 1.0);\n let dfg = envBrdfApprox(ndv, roughness);\n let integrated = clamp(envSpecularEnergy(f0, dfg) * max(reflectivity, metalness), 0.0, 1.0);\n return mix(sweep, integrated, prefiltered);\n}\n\nstruct LitSurface {\n albedo: vec3<f32>,\n normal: vec3<f32>,\n toEye: vec3<f32>,\n roughness: f32,\n specular: f32,\n emissive: f32,\n /*\n * What the glow is coloured by: the albedo times an emissive map, flat/main.ts's\n * emissiveTint * emissiveMapped. The albedo itself where the material binds no map. See lit.ts.\n */\n emissiveColour: vec3<f32>,\n /* How much of the sun reaches here: 0 in full shadow, 1 in full light. See lit.ts. */\n shade: f32,\n reflectivity: f32,\n /* 0 is a dielectric and 1 is a metal. One number a material here, an ORM texel there. */\n metalness: f32,\n /* A crevice's share of everything below, as flat/main.ts applies ormOcclusion. See lit.ts. */\n occlusion: f32,\n}\n\nstruct LitEnvironment {\n lightDir: vec3<f32>,\n lightColour: vec3<f32>,\n sky: vec3<f32>,\n ground: vec3<f32>,\n irradiance: vec3<f32>,\n radiance: vec3<f32>,\n irradianceAmount: f32,\n /* How much of the mirror-direction gradient the radiance replaces. Zero keeps the gradient. */\n radianceAmount: f32,\n prefiltered: f32,\n /* The frame's emissive gain times its night factor, which scales every glow. See lit.ts. */\n emission: f32,\n}\n\nfn litColour(surface: LitSurface, env: LitEnvironment) -> vec3<f32> {\n let n = surface.normal;\n let albedo = surface.albedo;\n let lightColour = env.lightColour;\n let roughness = surface.roughness;\n let shade = surface.shade;\n let ndl = max(dot(n, env.lightDir), 0.0);\n /*\n * The forward path's own hemispheric term \u2014 flat/main.ts, mix(ground, sky, n.y * 0.5 + 0.5) \u2014\n * replaced by the room's irradiance where a caller supplies one rather than lifted toward it.\n */\n let hemispheric = mix(env.ground, env.sky, n.y * 0.5 + 0.5);\n let ambient = mix(hemispheric, env.irradiance, env.irradianceAmount);\n\n /*\n * The half vector between the light and the eye, and the highlight at its peak. Computed whether\n * or not the surface is shiny and multiplied by zero where it is not: a branch on a value read\n * from the material table is not provably uniform control flow, which is the rule this pass and\n * the forward one both keep.\n */\n let halfway = env.lightDir + surface.toEye;\n let length2 = dot(halfway, halfway);\n var ndh = 0.0;\n if (length2 > 0.0) {\n ndh = max(dot(n, halfway * inverseSqrt(length2)), 0.0);\n }\n let lobe = specularLobe(ndh, roughness);\n let metal = surface.metalness;\n /*\n * A metal's highlight takes its own colour and goes white at the edge; a dielectric's takes the\n * light's and does neither. mix(specular, albedo, 0) is specular exactly, and the Schlick term is\n * scaled by metalness, so at metalness 0 both lines are the expression that shipped before them.\n */\n let specColour = mix(vec3<f32>(surface.specular), albedo, metal);\n let voh = select(0.0, max(dot(surface.toEye, halfway * inverseSqrt(length2)), 0.0), length2 > 0.0);\n let facing = 1.0 - voh;\n let sunSpec = mix(specColour, vec3<f32>(1.0), facing * facing * facing * facing * facing * metal);\n let highlight = lightColour * lobe * sunSpec * shade;\n\n /*\n * Shade multiplies the two direct terms and not the ambient, which is where flat/main.ts puts\n * its own sunShade and what its comment says the wider version costs. A metal keeps its ambient\n * and loses its direct diffuse; the honest two terms cancel into this one.\n */\n let base = albedo * (ambient + lightColour * ndl * shade * (1.0 - metal)) + highlight * (1.0 - metal);\n /*\n * **The dielectric's highlight goes underneath the blend and the metal's goes on top**, which is\n * flat/main.ts's two lines with the mix between them. Underneath, the blend dims it by the\n * surface's own reflectance \u2014 taking it out puts a lobe that was correctly dimmed back at full\n * strength, which on a near-black glass lens carrying specular 1 is a white blob where there was\n * none. On top, it survives a weight of one, which is what a metal always has.\n */\n let nv = dot(n, surface.toEye);\n let ndv = max(nv, 0.0);\n /*\n * What the surface sees along the mirror direction, and with no probe it is the same two-colour\n * gradient the ambient comes from \u2014 which is what flat/main.ts reflects where a scene has no\n * environment. Unclamped nv, because reflect takes the facing term as it is: clamping folds the\n * direction back on a silhouette and reflects the sky where the ground belongs.\n */\n let mirrored = 2.0 * nv * n - surface.toEye;\n let gradient = mix(env.ground, env.sky, mirrored.y * 0.5 + 0.5);\n let seen = mix(gradient, env.radiance, env.radianceAmount);\n let weight = environmentWeight(ndv, roughness, surface.reflectivity, env.prefiltered, metal);\n /* A metal reflects the room through itself; mix(1, albedo, 0) is exactly 1 for a dielectric. */\n let reflected = seen * mix(vec3<f32>(1.0), albedo, metal);\n /* Scaled by the frame and dimmed by a share of the sun's shadow, as flat/main.ts's glow is. */\n let glow = surface.emissive * env.emission\n * mix(1.0, surface.shade, 0.55);\n return (mix(base, reflected, weight) + highlight * metal + surface.emissiveColour * glow)\n * surface.occlusion;\n}\n";
|
|
100
|
+
export declare const SHADE_MATERIAL_WGSL: string;
|
|
101
|
+
/**
|
|
102
|
+
* An entry point that does nothing but light a surface, so `gpu-parity.mjs` can compare the
|
|
103
|
+
* expression above with `gpudriven/lit.ts` directly rather than through a shading pass that writes
|
|
104
|
+
* to a texture and reads eleven buffers.
|
|
105
|
+
*
|
|
106
|
+
* `LIT_SURFACE_FLOATS` a surface: albedo, normal, the direction to the eye, then roughness,
|
|
107
|
+
* specular, emissive, shade, reflectivity, metalness, occlusion and the emitted colour; and
|
|
108
|
+
* `LIT_ENVIRONMENT_FLOATS` a room. The prose said fourteen and twenty for two waves after both had
|
|
109
|
+
* grown, which is why it names the constants now rather than counting them.
|
|
110
|
+
*/
|
|
111
|
+
/**
|
|
112
|
+
* Floats a surface of the lighting check. Matches `LitSurface`: occlusion at fifteen, then the
|
|
113
|
+
* emitted colour, which a surface with no emissive map sets to its albedo.
|
|
114
|
+
*/
|
|
115
|
+
export declare const LIT_SURFACE_FLOATS = 19;
|
|
116
|
+
/**
|
|
117
|
+
* Floats an environment of the lighting check. Matches `LitEnvironment`.
|
|
118
|
+
*
|
|
119
|
+
* **One block a surface rather than one for the corpus**, which it was until the image-based term
|
|
120
|
+
* arrived: the room, the chain and the two selectors are per-pixel quantities in the shading pass,
|
|
121
|
+
* and a check that held them constant would run the split sum at exactly one configuration.
|
|
122
|
+
*/
|
|
123
|
+
export declare const LIT_ENVIRONMENT_FLOATS = 22;
|
|
124
|
+
export declare const SHADE_LIGHTING_PARITY_WGSL = "\n@group(0) @binding(0) var<storage, read> params: array<f32>;\n@group(0) @binding(1) var<storage, read> environment: array<f32>;\n@group(0) @binding(2) var<storage, read> surfaces: array<f32>;\n@group(0) @binding(3) var<storage, read_write> colours: array<f32>;\n\n\n/* The normalised GGX distribution, transcribed from flat/lobes.ts. See lit.ts about its peak. */\n/*\n * **Two floors, and only one of them can be seen in the output.** The denominator's is what keeps\n * a near-mirror's peak finite, and deleting it is worth 256 times the highlight at roughness 0.05 \u2014\n * gpu-parity.mjs catches that, now that a fifth of its corpus sits on the peak. The floor on a\n * cannot be caught, because the denominator's already prevents the division by zero it guards\n * against: at roughness zero the whole term is a ten-thousandth with it and zero without, and both\n * round to nothing beside a lit surface. It is kept because it is what flat/lobes.ts has, and two\n * spellings of one lobe differing by a guard is the drift this pair exists to prevent.\n */\nfn specularLobe(ndh: f32, roughness: f32) -> f32 {\n let a = max(roughness * roughness, 0.01);\n let a2 = a * a;\n let d = ndh * ndh * (a2 - 1.0) + 1.0;\n return (a2 * a2) / max(d * d, 1e-8);\n}\n\n/* A dielectric's normal-incidence reflectance, which every material in this pipeline is. */\nconst DIELECTRIC_F0: f32 = 0.04;\n\n/*\n * The split sum's second half, as Karis's analytic fit: dfg.x scales f0 and dfg.y adds on top.\n * gpudriven/ibl.ts is the twin and carries what the tabulated integral would cost instead.\n */\nfn envBrdfApprox(ndv: f32, roughness: f32) -> vec2<f32> {\n let c0 = vec4<f32>(-1.0, -0.0275, -0.572, 0.022);\n let c1 = vec4<f32>(1.0, 0.0425, 1.04, -0.04);\n let r = roughness * c0 + c1;\n let a004 = min(r.x * r.x, exp2(-9.28 * ndv)) * r.x + r.y;\n return vec2<f32>(-1.04, 1.04) * a004 + r.zw;\n}\n\n/*\n * The same with the multiply-scattered light put back. At f0 of one this returns exactly 1.0 at\n * every roughness, which is the invariant ibl.test.ts asserts across the range: roughness\n * scatters light and does not absorb it, and the bare split sum claims otherwise by up to 44%.\n */\nfn envSpecularEnergy(f0: f32, dfg: vec2<f32>) -> f32 {\n let single = f0 * dfg.x + dfg.y;\n let energy = dfg.x + dfg.y;\n let missing = 1.0 - energy;\n let average = f0 + (1.0 - f0) / 21.0;\n let multi = single * average / (1.0 - missing * average);\n return single + multi * missing;\n}\n\n/*\n * How much of what the surface sees in the mirror direction reaches the frame.\n *\n * Two expressions and a selector that is exactly 0 or exactly 1: the split sum is the correct\n * share of a prefiltered environment and asserts an integral that was never performed over\n * anything else. See ibl.ts for what ignoring that costs on the other pipeline.\n */\nfn surfaceF0(metalness: f32) -> f32 {\n return mix(DIELECTRIC_F0, 1.0, metalness);\n}\n\nfn environmentWeight(\n ndv: f32,\n roughness: f32,\n reflectivity: f32,\n prefiltered: f32,\n metalness: f32,\n) -> f32 {\n let f0 = surfaceF0(metalness);\n let facing = 1.0 - ndv;\n let fresnel = mix(f0, 1.0, facing * facing * facing * facing * facing);\n /*\n * max(reflectivity * (1 - roughness), metal), and both halves are repairs flat/main.ts records:\n * a metal reflects whether or not the pass asked for reflections, and the one-minus-roughness\n * thinning belongs to the dielectric alone. See ibl.ts.\n */\n let amount = max(reflectivity * (1.0 - roughness), metalness);\n let sweep = clamp(fresnel * amount, 0.0, 1.0);\n let dfg = envBrdfApprox(ndv, roughness);\n let integrated = clamp(envSpecularEnergy(f0, dfg) * max(reflectivity, metalness), 0.0, 1.0);\n return mix(sweep, integrated, prefiltered);\n}\n\nstruct LitSurface {\n albedo: vec3<f32>,\n normal: vec3<f32>,\n toEye: vec3<f32>,\n roughness: f32,\n specular: f32,\n emissive: f32,\n /*\n * What the glow is coloured by: the albedo times an emissive map, flat/main.ts's\n * emissiveTint * emissiveMapped. The albedo itself where the material binds no map. See lit.ts.\n */\n emissiveColour: vec3<f32>,\n /* How much of the sun reaches here: 0 in full shadow, 1 in full light. See lit.ts. */\n shade: f32,\n reflectivity: f32,\n /* 0 is a dielectric and 1 is a metal. One number a material here, an ORM texel there. */\n metalness: f32,\n /* A crevice's share of everything below, as flat/main.ts applies ormOcclusion. See lit.ts. */\n occlusion: f32,\n}\n\nstruct LitEnvironment {\n lightDir: vec3<f32>,\n lightColour: vec3<f32>,\n sky: vec3<f32>,\n ground: vec3<f32>,\n irradiance: vec3<f32>,\n radiance: vec3<f32>,\n irradianceAmount: f32,\n /* How much of the mirror-direction gradient the radiance replaces. Zero keeps the gradient. */\n radianceAmount: f32,\n prefiltered: f32,\n /* The frame's emissive gain times its night factor, which scales every glow. See lit.ts. */\n emission: f32,\n}\n\nfn litColour(surface: LitSurface, env: LitEnvironment) -> vec3<f32> {\n let n = surface.normal;\n let albedo = surface.albedo;\n let lightColour = env.lightColour;\n let roughness = surface.roughness;\n let shade = surface.shade;\n let ndl = max(dot(n, env.lightDir), 0.0);\n /*\n * The forward path's own hemispheric term \u2014 flat/main.ts, mix(ground, sky, n.y * 0.5 + 0.5) \u2014\n * replaced by the room's irradiance where a caller supplies one rather than lifted toward it.\n */\n let hemispheric = mix(env.ground, env.sky, n.y * 0.5 + 0.5);\n let ambient = mix(hemispheric, env.irradiance, env.irradianceAmount);\n\n /*\n * The half vector between the light and the eye, and the highlight at its peak. Computed whether\n * or not the surface is shiny and multiplied by zero where it is not: a branch on a value read\n * from the material table is not provably uniform control flow, which is the rule this pass and\n * the forward one both keep.\n */\n let halfway = env.lightDir + surface.toEye;\n let length2 = dot(halfway, halfway);\n var ndh = 0.0;\n if (length2 > 0.0) {\n ndh = max(dot(n, halfway * inverseSqrt(length2)), 0.0);\n }\n let lobe = specularLobe(ndh, roughness);\n let metal = surface.metalness;\n /*\n * A metal's highlight takes its own colour and goes white at the edge; a dielectric's takes the\n * light's and does neither. mix(specular, albedo, 0) is specular exactly, and the Schlick term is\n * scaled by metalness, so at metalness 0 both lines are the expression that shipped before them.\n */\n let specColour = mix(vec3<f32>(surface.specular), albedo, metal);\n let voh = select(0.0, max(dot(surface.toEye, halfway * inverseSqrt(length2)), 0.0), length2 > 0.0);\n let facing = 1.0 - voh;\n let sunSpec = mix(specColour, vec3<f32>(1.0), facing * facing * facing * facing * facing * metal);\n let highlight = lightColour * lobe * sunSpec * shade;\n\n /*\n * Shade multiplies the two direct terms and not the ambient, which is where flat/main.ts puts\n * its own sunShade and what its comment says the wider version costs. A metal keeps its ambient\n * and loses its direct diffuse; the honest two terms cancel into this one.\n */\n let base = albedo * (ambient + lightColour * ndl * shade * (1.0 - metal)) + highlight * (1.0 - metal);\n /*\n * **The dielectric's highlight goes underneath the blend and the metal's goes on top**, which is\n * flat/main.ts's two lines with the mix between them. Underneath, the blend dims it by the\n * surface's own reflectance \u2014 taking it out puts a lobe that was correctly dimmed back at full\n * strength, which on a near-black glass lens carrying specular 1 is a white blob where there was\n * none. On top, it survives a weight of one, which is what a metal always has.\n */\n let nv = dot(n, surface.toEye);\n let ndv = max(nv, 0.0);\n /*\n * What the surface sees along the mirror direction, and with no probe it is the same two-colour\n * gradient the ambient comes from \u2014 which is what flat/main.ts reflects where a scene has no\n * environment. Unclamped nv, because reflect takes the facing term as it is: clamping folds the\n * direction back on a silhouette and reflects the sky where the ground belongs.\n */\n let mirrored = 2.0 * nv * n - surface.toEye;\n let gradient = mix(env.ground, env.sky, mirrored.y * 0.5 + 0.5);\n let seen = mix(gradient, env.radiance, env.radianceAmount);\n let weight = environmentWeight(ndv, roughness, surface.reflectivity, env.prefiltered, metal);\n /* A metal reflects the room through itself; mix(1, albedo, 0) is exactly 1 for a dielectric. */\n let reflected = seen * mix(vec3<f32>(1.0), albedo, metal);\n /* Scaled by the frame and dimmed by a share of the sun's shadow, as flat/main.ts's glow is. */\n let glow = surface.emissive * env.emission\n * mix(1.0, surface.shade, 0.55);\n return (mix(base, reflected, weight) + highlight * metal + surface.emissiveColour * glow)\n * surface.occlusion;\n}\n\n\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) id: vec3<u32>) {\n let count = u32(params[0]);\n if (id.x >= count) {\n return;\n }\n let at = id.x * 19u;\n let env = id.x * 22u;\n var surface: LitSurface;\n surface.albedo = vec3<f32>(surfaces[at], surfaces[at + 1u], surfaces[at + 2u]);\n surface.normal = vec3<f32>(surfaces[at + 3u], surfaces[at + 4u], surfaces[at + 5u]);\n surface.toEye = vec3<f32>(surfaces[at + 6u], surfaces[at + 7u], surfaces[at + 8u]);\n surface.roughness = surfaces[at + 9u];\n surface.specular = surfaces[at + 10u];\n surface.emissive = surfaces[at + 11u];\n surface.shade = surfaces[at + 12u];\n surface.reflectivity = surfaces[at + 13u];\n surface.metalness = surfaces[at + 14u];\n surface.occlusion = surfaces[at + 15u];\n surface.emissiveColour = vec3<f32>(surfaces[at + 16u], surfaces[at + 17u], surfaces[at + 18u]);\n\n var room: LitEnvironment;\n room.lightDir = vec3<f32>(environment[env], environment[env + 1u], environment[env + 2u]);\n room.lightColour = vec3<f32>(environment[env + 3u], environment[env + 4u], environment[env + 5u]);\n room.sky = vec3<f32>(environment[env + 6u], environment[env + 7u], environment[env + 8u]);\n room.ground = vec3<f32>(environment[env + 9u], environment[env + 10u], environment[env + 11u]);\n room.irradiance =\n vec3<f32>(environment[env + 12u], environment[env + 13u], environment[env + 14u]);\n room.radiance = vec3<f32>(environment[env + 15u], environment[env + 16u], environment[env + 17u]);\n room.irradianceAmount = environment[env + 18u];\n room.radianceAmount = environment[env + 19u];\n room.prefiltered = environment[env + 20u];\n room.emission = environment[env + 21u];\n\n let colour = litColour(surface, room);\n colours[id.x * 3u] = colour.x;\n colours[id.x * 3u + 1u] = colour.y;\n colours[id.x * 3u + 2u] = colour.z;\n}\n";
|
|
125
|
+
/** Floats a case of the surface-frame check: n, dp1, dp2, duv1, duv2, size. */
|
|
126
|
+
export declare const SURFACE_FRAME_CASE_FLOATS = 14;
|
|
127
|
+
export declare const SURFACE_FRAME_PARITY_WGSL = "\nstruct Screen {\n x: f32,\n y: f32,\n invW: f32,\n}\n\nstruct Weights {\n /** The perspective-correct barycentric weights. */\n c: vec3<f32>,\n /** How each of them changes with screen x, and with screen y. */\n gx: vec3<f32>,\n gy: vec3<f32>,\n valid: bool,\n}\n\nfn screenOf(clip: vec4<f32>, width: f32, height: f32) -> Screen {\n let inv = 1.0 / clip.w;\n var out: Screen;\n out.x = (clip.x * inv * 0.5 + 0.5) * width;\n out.y = (clip.y * inv * 0.5 + 0.5) * height;\n out.invW = inv;\n return out;\n}\n\nfn surfaceWeights(s0: Screen, s1: Screen, s2: Screen, px: f32, py: f32) -> Weights {\n var out: Weights;\n out.valid = false;\n\n let area = (s1.x - s0.x) * (s2.y - s0.y) - (s2.x - s0.x) * (s1.y - s0.y);\n if (abs(area) < 1e-12) { return out; }\n let inverse = 1.0 / area;\n\n /* Screen-space weights. A point outside the triangle gets a negative one rather than a clamp:\n clamping shades a pixel beyond an edge as though it were on the triangle. */\n let l1 = ((px - s0.x) * (s2.y - s0.y) - (s2.x - s0.x) * (py - s0.y)) * inverse;\n let l2 = ((s1.x - s0.x) * (py - s0.y) - (px - s0.x) * (s1.y - s0.y)) * inverse;\n let l0 = 1.0 - l1 - l2;\n\n let n0 = l0 * s0.invW;\n let n1 = l1 * s1.invW;\n let n2 = l2 * s2.invW;\n let sum = n0 + n1 + n2;\n if (abs(sum) < 1e-20) { return out; }\n let invSum = 1.0 / sum;\n out.c = vec3<f32>(n0 * invSum, n1 * invSum, n2 * invSum);\n\n /* The screen weights' gradients, constant over the triangle. */\n let d1dx = (s2.y - s0.y) * inverse;\n let d1dy = (s0.x - s2.x) * inverse;\n let d2dx = (s0.y - s1.y) * inverse;\n let d2dy = (s1.x - s0.x) * inverse;\n let d0dx = -d1dx - d2dx;\n let d0dy = -d1dy - d2dy;\n\n let dSumDx = d0dx * s0.invW + d1dx * s1.invW + d2dx * s2.invW;\n let dSumDy = d0dy * s0.invW + d1dy * s1.invW + d2dy * s2.invW;\n\n /* Quotient rule, per weight, with the shared denominator factored out. */\n out.gx = vec3<f32>(\n (d0dx * s0.invW - n0 * invSum * dSumDx) * invSum,\n (d1dx * s1.invW - n1 * invSum * dSumDx) * invSum,\n (d2dx * s2.invW - n2 * invSum * dSumDx) * invSum,\n );\n out.gy = vec3<f32>(\n (d0dy * s0.invW - n0 * invSum * dSumDy) * invSum,\n (d1dy * s1.invW - n1 * invSum * dSumDy) * invSum,\n (d2dy * s2.invW - n2 * invSum * dSumDy) * invSum,\n );\n out.valid = true;\n return out;\n}\n\n/* surfaceFrame.ts surfaceLod: the longer screen-axis UV step, in texels, as a power of two. */\nfn surfaceLod(gradX: vec2<f32>, gradY: vec2<f32>, size: f32) -> f32 {\n let footprint = max(length(gradX), length(gradY)) * size;\n return max(log2(max(footprint, 1e-30)), 0.0);\n}\n\nstruct TangentFrame {\n t: vec3<f32>,\n b: vec3<f32>,\n}\n\n/* surfaceFrame.ts derivedTangentFrame, which is tangentFrame.ts's derived branch term for term. */\nfn derivedTangentFrame(\n n: vec3<f32>,\n dp1: vec3<f32>,\n dp2: vec3<f32>,\n duv1: vec2<f32>,\n duv2: vec2<f32>,\n) -> TangentFrame {\n let dp2perp = cross(dp2, n);\n let dp1perp = cross(n, dp1);\n let dt = dp2perp * duv1.x + dp1perp * duv2.x;\n let db = dp2perp * duv1.y + dp1perp * duv2.y;\n let det = duv1.x * duv2.y - duv2.x * duv1.y;\n let handed = select(1.0, -1.0, det < 0.0);\n let scale = inverseSqrt(max(max(dot(dt, dt), dot(db, db)), 1e-12)) * handed;\n var frame: TangentFrame;\n frame.t = dt * scale;\n frame.b = db * scale;\n return frame;\n}\n\n@group(0) @binding(0) var<storage, read> params: array<f32>;\n@group(0) @binding(1) var<storage, read> cases: array<f32>;\n@group(0) @binding(2) var<storage, read_write> results: array<f32>;\n\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) id: vec3<u32>) {\n if (id.x >= u32(params[0])) {\n return;\n }\n let at = id.x * 14u;\n let n = vec3<f32>(cases[at], cases[at + 1u], cases[at + 2u]);\n let dp1 = vec3<f32>(cases[at + 3u], cases[at + 4u], cases[at + 5u]);\n let dp2 = vec3<f32>(cases[at + 6u], cases[at + 7u], cases[at + 8u]);\n let duv1 = vec2<f32>(cases[at + 9u], cases[at + 10u]);\n let duv2 = vec2<f32>(cases[at + 11u], cases[at + 12u]);\n let frame = derivedTangentFrame(n, dp1, dp2, duv1, duv2);\n let dst = id.x * 7u;\n results[dst] = surfaceLod(duv1, duv2, cases[at + 13u]);\n results[dst + 1u] = frame.t.x;\n results[dst + 2u] = frame.t.y;\n results[dst + 3u] = frame.t.z;\n results[dst + 4u] = frame.b.x;\n results[dst + 5u] = frame.b.y;\n results[dst + 6u] = frame.b.z;\n}\n";
|