@driftengine/core 4.4.2 → 4.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.json +97 -0
- package/README.md +1 -1
- package/dist/geometry/boxCsg.d.ts +28 -0
- package/dist/geometry/boxCsg.js +139 -0
- package/dist/geometry/csg.d.ts +36 -0
- package/dist/geometry/csg.js +329 -0
- package/dist/geometry/meshBuilder.js +13 -0
- package/dist/geometry/meshConcat.js +1 -0
- package/dist/geometry/pixelFont.js +2 -0
- package/dist/geometry/solid.d.ts +61 -0
- package/dist/geometry/solid.js +152 -0
- package/dist/geometry/solidFlat.d.ts +35 -0
- package/dist/geometry/solidFlat.js +428 -0
- package/dist/geometry/solidHarness.d.ts +19 -0
- package/dist/geometry/solidHarness.js +66 -0
- package/dist/geometry/solidNormals.d.ts +17 -0
- package/dist/geometry/solidNormals.js +86 -0
- package/dist/geometry/solidRound.d.ts +41 -0
- package/dist/geometry/solidRound.js +323 -0
- package/dist/geometry/solidWriter.d.ts +16 -0
- package/dist/geometry/solidWriter.js +42 -0
- package/dist/index.d.ts +28 -3
- package/dist/index.js +20 -1
- package/dist/render/backend/api.d.ts +34 -14
- package/dist/render/backend/drawFog.d.ts +26 -0
- package/dist/render/backend/drawFog.js +30 -0
- package/dist/render/backend/materialChanges.d.ts +2 -1
- package/dist/render/backend/materialChanges.js +6 -1
- package/dist/render/backend/webgl2/glassTintPrograms.d.ts +13 -0
- package/dist/render/backend/webgl2/glassTintPrograms.js +38 -0
- package/dist/render/backend/webgl2/instanced.d.ts +24 -5
- package/dist/render/backend/webgl2/instanced.js +72 -15
- package/dist/render/backend/webgl2/renderer.d.ts +201 -16
- package/dist/render/backend/webgl2/renderer.js +596 -224
- package/dist/render/backend/webgpu/buffers.d.ts +29 -1
- package/dist/render/backend/webgpu/buffers.js +61 -4
- package/dist/render/backend/webgpu/depthPass.d.ts +1 -1
- package/dist/render/backend/webgpu/depthPass.js +7 -2
- package/dist/render/backend/webgpu/drawCommand.d.ts +5 -0
- package/dist/render/backend/webgpu/drawCommand.js +1 -0
- package/dist/render/backend/webgpu/flatPass.d.ts +6 -1
- package/dist/render/backend/webgpu/flatPass.js +57 -9
- package/dist/render/backend/webgpu/glassTintPass.d.ts +28 -0
- package/dist/render/backend/webgpu/glassTintPass.js +206 -0
- package/dist/render/backend/webgpu/gpuDrivenPass.d.ts +60 -1
- package/dist/render/backend/webgpu/gpuDrivenPass.js +305 -5
- package/dist/render/backend/webgpu/instanceCullPass.d.ts +34 -0
- package/dist/render/backend/webgpu/instanceCullPass.js +144 -0
- package/dist/render/backend/webgpu/instanced.d.ts +23 -2
- package/dist/render/backend/webgpu/instanced.js +41 -4
- package/dist/render/backend/webgpu/latePass.d.ts +30 -0
- package/dist/render/backend/webgpu/latePass.js +140 -0
- package/dist/render/backend/webgpu/lightVolumePass.js +5 -1
- package/dist/render/backend/webgpu/motionPass.d.ts +95 -0
- package/dist/render/backend/webgpu/motionPass.js +491 -0
- package/dist/render/backend/webgpu/pipelineCache.d.ts +10 -1
- package/dist/render/backend/webgpu/pipelineCache.js +11 -1
- package/dist/render/backend/webgpu/pointGlassTintPass.d.ts +32 -0
- package/dist/render/backend/webgpu/pointGlassTintPass.js +193 -0
- package/dist/render/backend/webgpu/pointShadowPass.d.ts +35 -6
- package/dist/render/backend/webgpu/pointShadowPass.js +105 -20
- package/dist/render/backend/webgpu/postPass.d.ts +3 -13
- package/dist/render/backend/webgpu/renderer.d.ts +271 -64
- package/dist/render/backend/webgpu/renderer.js +1395 -580
- package/dist/render/backend/webgpu/scatterPass.js +2 -1
- package/dist/render/backend/webgpu/sdfTextPass.js +6 -1
- package/dist/render/backend/webgpu/shaders/instanceCull.wgsl.d.ts +18 -0
- package/dist/render/backend/webgpu/shaders/instanceCull.wgsl.js +59 -0
- package/dist/render/backend/webgpu/skinPaletteRing.d.ts +4 -0
- package/dist/render/backend/webgpu/skinPaletteRing.js +4 -0
- package/dist/render/backend/webgpu/snapshotMips.d.ts +29 -0
- package/dist/render/backend/webgpu/snapshotMips.js +52 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +22 -3
- package/dist/render/backend/webgpu/surfaceTexturePass.js +78 -39
- package/dist/render/driftLight/bake.d.ts +7 -0
- package/dist/render/driftLight/bake.js +61 -56
- package/dist/render/driftLight/denseField.d.ts +46 -0
- package/dist/render/driftLight/denseField.js +109 -0
- package/dist/render/driftLight/lightField.d.ts +10 -5
- package/dist/render/driftLight/lightField.js +25 -19
- package/dist/render/driftLight/presence.d.ts +50 -0
- package/dist/render/driftLight/presence.js +43 -0
- package/dist/render/driftLight/uniforms.d.ts +3 -3
- package/dist/render/driftLight/uniforms.js +5 -6
- package/dist/render/driftLight/worldLightField.d.ts +41 -0
- package/dist/render/driftLight/worldLightField.js +66 -0
- package/dist/render/fixtureAtlas.d.ts +20 -0
- package/dist/render/fixtureAtlas.js +49 -0
- package/dist/render/glass.d.ts +45 -0
- package/dist/render/glass.js +39 -0
- package/dist/render/glassCasters.d.ts +40 -0
- package/dist/render/glassCasters.js +84 -0
- package/dist/render/glassShadow.d.ts +78 -0
- package/dist/render/glassShadow.js +125 -0
- package/dist/render/globalMediumPass.d.ts +2 -3
- package/dist/render/globalMediumPass.js +2 -8
- package/dist/render/gpudriven/lit.d.ts +12 -0
- package/dist/render/gpudriven/lit.js +23 -0
- package/dist/render/gpudriven/materialTable.d.ts +15 -5
- package/dist/render/gpudriven/materialTable.js +26 -6
- package/dist/render/gpudriven/pipeline.d.ts +1 -1
- package/dist/render/gpudriven/pipeline.js +4 -3
- package/dist/render/gpudriven/sunGlass.d.ts +35 -0
- package/dist/render/gpudriven/sunGlass.js +89 -0
- package/dist/render/instanceCull.d.ts +63 -0
- package/dist/render/instanceCull.js +160 -0
- package/dist/render/instances.d.ts +15 -0
- package/dist/render/lightBudget.d.ts +36 -35
- package/dist/render/lightBudget.js +38 -37
- package/dist/render/lightGrid.d.ts +42 -0
- package/dist/render/lightGrid.js +87 -0
- package/dist/render/mesh.d.ts +18 -24
- package/dist/render/mesh.js +51 -68
- package/dist/render/occlusion.d.ts +10 -0
- package/dist/render/occlusion.js +36 -7
- package/dist/render/pointGlassTint.d.ts +59 -0
- package/dist/render/pointGlassTint.js +198 -0
- package/dist/render/pointLightSelection.d.ts +7 -0
- package/dist/render/pointLightSelection.js +29 -4
- package/dist/render/pointShadowArray.d.ts +56 -3
- package/dist/render/pointShadowArray.js +127 -16
- package/dist/render/pointShadowMap.d.ts +23 -1
- package/dist/render/pointShadowMap.js +27 -1
- package/dist/render/rainField.d.ts +13 -0
- package/dist/render/rainField.js +27 -5
- package/dist/render/recon/changeFrames.d.ts +15 -0
- package/dist/render/recon/changeFrames.js +21 -0
- package/dist/render/recon/lateDepth.d.ts +11 -0
- package/dist/render/recon/lateDepth.js +70 -0
- package/dist/render/recon/lateRouting.d.ts +14 -0
- package/dist/render/recon/lateRouting.js +18 -0
- package/dist/render/recon/mover.d.ts +57 -0
- package/dist/render/recon/mover.js +86 -0
- package/dist/render/recon/resolve.d.ts +34 -4
- package/dist/render/recon/resolve.js +161 -20
- package/dist/render/renderQuality.d.ts +35 -0
- package/dist/render/renderQuality.js +7 -0
- package/dist/render/rendererHarness.js +16 -0
- package/dist/render/sceneTarget.d.ts +9 -1
- package/dist/render/sceneTarget.js +60 -12
- package/dist/render/shaders/depth.d.ts +16 -1
- package/dist/render/shaders/depth.js +94 -15
- package/dist/render/shaders/flat/directionalShadow.js +91 -6
- package/dist/render/shaders/flat/driftLight.d.ts +1 -1
- package/dist/render/shaders/flat/driftLight.js +32 -7
- package/dist/render/shaders/flat/glassTint.d.ts +6 -0
- package/dist/render/shaders/flat/glassTint.js +25 -0
- package/dist/render/shaders/flat/index.d.ts +13 -0
- package/dist/render/shaders/flat/index.js +9 -3
- package/dist/render/shaders/flat/lampGlass.d.ts +1 -0
- package/dist/render/shaders/flat/lampGlass.js +183 -0
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +190 -38
- package/dist/render/shaders/flat/pointShadow.d.ts +1 -1
- package/dist/render/shaders/flat/pointShadow.js +2 -0
- package/dist/render/shaders/flat/preamble.js +96 -26
- package/dist/render/shaders/flat/surfaceEffects.d.ts +29 -0
- package/dist/render/shaders/flat/surfaceEffects.js +152 -0
- package/dist/render/shaders/generated/depth.wgsl.d.ts +230 -7
- package/dist/render/shaders/generated/depth.wgsl.js +230 -7
- package/dist/render/shaders/generated/flat.wgsl.d.ts +1644 -1596
- package/dist/render/shaders/generated/flat.wgsl.js +1683 -1621
- package/dist/render/shaders/generated/globalMedium.wgsl.d.ts +3 -13
- package/dist/render/shaders/generated/globalMedium.wgsl.js +3 -13
- package/dist/render/shaders/generated/lightVolume.wgsl.d.ts +3 -13
- package/dist/render/shaders/generated/lightVolume.wgsl.js +21 -17
- package/dist/render/shaders/generated/octahedralResolve.wgsl.d.ts +29 -0
- package/dist/render/shaders/generated/octahedralResolve.wgsl.js +29 -0
- package/dist/render/shaders/generated/rush.wgsl.d.ts +21 -16
- package/dist/render/shaders/generated/rush.wgsl.js +21 -16
- package/dist/render/shaders/generated/sdfText.wgsl.d.ts +2 -2
- package/dist/render/shaders/generated/sdfText.wgsl.js +2 -2
- package/dist/render/shaders/generated/text.wgsl.d.ts +1 -1
- package/dist/render/shaders/generated/text.wgsl.js +1 -1
- package/dist/render/shaders/gi/driftLight.wgsl.d.ts +1 -1
- package/dist/render/shaders/gi/driftLight.wgsl.js +27 -4
- package/dist/render/shaders/globalMedium.js +6 -6
- package/dist/render/shaders/gpudriven/blendRaster.wgsl.d.ts +7 -0
- package/dist/render/shaders/gpudriven/blendRaster.wgsl.js +86 -1
- package/dist/render/shaders/gpudriven/cull.wgsl.d.ts +1 -1
- package/dist/render/shaders/gpudriven/cull.wgsl.js +11 -2
- package/dist/render/shaders/gpudriven/materialTable.wgsl.d.ts +1 -1
- package/dist/render/shaders/gpudriven/materialTable.wgsl.js +3 -1
- package/dist/render/shaders/gpudriven/shade.wgsl.js +50 -1
- package/dist/render/shaders/gpudriven/sunGlass.wgsl.d.ts +9 -0
- package/dist/render/shaders/gpudriven/sunGlass.wgsl.js +133 -0
- package/dist/render/shaders/lightVolume.js +6 -6
- package/dist/render/shaders/octahedralResolve.d.ts +8 -0
- package/dist/render/shaders/octahedralResolve.js +48 -17
- package/dist/render/shaders/recon/lateDepth.wgsl.d.ts +1 -0
- package/dist/render/shaders/recon/lateDepth.wgsl.js +69 -0
- package/dist/render/shaders/recon/motion.wgsl.d.ts +1 -1
- package/dist/render/shaders/recon/motion.wgsl.js +84 -0
- package/dist/render/shaders/recon/resolve.wgsl.d.ts +8 -7
- package/dist/render/shaders/recon/resolve.wgsl.js +161 -27
- package/dist/render/shaders/rush.js +11 -1
- package/dist/render/shaders/sdfText.d.ts +1 -1
- package/dist/render/shaders/sdfText.js +3 -2
- package/dist/render/shaders/text.d.ts +1 -1
- package/dist/render/shaders/text.js +18 -9
- package/dist/render/shadowCasters.d.ts +20 -1
- package/dist/render/shadowCasters.js +15 -1
- package/dist/render/shadowMap.d.ts +54 -5
- package/dist/render/shadowMap.js +190 -36
- package/dist/render/sunGlassTint.d.ts +25 -0
- package/dist/render/sunGlassTint.js +79 -0
- package/dist/render/surfaceEffects.d.ts +83 -0
- package/dist/render/surfaceEffects.js +75 -0
- package/dist/render/surfaceScene.d.ts +8 -0
- package/dist/render/surfaceScene.js +8 -0
- package/dist/render/surfaceTexture.d.ts +18 -2
- package/dist/render/surfaceTexture.js +68 -15
- package/dist/render/temporalAa.d.ts +25 -6
- package/dist/render/temporalAa.js +45 -15
- package/dist/render/textureSource.d.ts +30 -0
- package/dist/render/textureSource.js +53 -0
- package/dist/render/translucentQueue.d.ts +6 -0
- package/dist/render/translucentQueue.js +15 -1
- package/dist/render/vertexDefaults.js +2 -0
- package/dist/world/hlod.d.ts +104 -0
- package/dist/world/hlod.js +214 -0
- package/package.json +6 -6
- package/src/geometry/boxCsg.ts +186 -0
- package/src/geometry/csg.ts +384 -0
- package/src/geometry/meshBuilder.ts +15 -0
- package/src/geometry/meshConcat.ts +1 -0
- package/src/geometry/pixelFont.ts +2 -0
- package/src/geometry/solid.ts +189 -0
- package/src/geometry/solidFlat.ts +522 -0
- package/src/geometry/solidHarness.ts +83 -0
- package/src/geometry/solidNormals.ts +103 -0
- package/src/geometry/solidRound.ts +443 -0
- package/src/geometry/solidWriter.ts +58 -0
- package/src/index.ts +61 -1
- package/src/render/backend/api.ts +36 -11
- package/src/render/backend/drawFog.ts +31 -0
- package/src/render/backend/materialChanges.ts +9 -2
- package/src/render/backend/webgl2/glassTintPrograms.ts +58 -0
- package/src/render/backend/webgl2/instanced.ts +80 -14
- package/src/render/backend/webgl2/renderer.ts +700 -250
- package/src/render/backend/webgpu/buffers.ts +92 -5
- package/src/render/backend/webgpu/depthPass.ts +7 -2
- package/src/render/backend/webgpu/drawCommand.ts +6 -0
- package/src/render/backend/webgpu/flatPass.ts +62 -11
- package/src/render/backend/webgpu/glassTintPass.ts +245 -0
- package/src/render/backend/webgpu/gpuDrivenPass.ts +349 -5
- package/src/render/backend/webgpu/instanceCullPass.ts +165 -0
- package/src/render/backend/webgpu/instanced.ts +42 -4
- package/src/render/backend/webgpu/latePass.ts +161 -0
- package/src/render/backend/webgpu/lightVolumePass.ts +5 -1
- package/src/render/backend/webgpu/motionPass.ts +535 -0
- package/src/render/backend/webgpu/pipelineCache.ts +12 -1
- package/src/render/backend/webgpu/pointGlassTintPass.ts +233 -0
- package/src/render/backend/webgpu/pointShadowPass.ts +158 -26
- package/src/render/backend/webgpu/renderer.ts +1671 -670
- package/src/render/backend/webgpu/scatterPass.ts +2 -1
- package/src/render/backend/webgpu/sdfTextPass.ts +6 -1
- package/src/render/backend/webgpu/shaders/instanceCull.wgsl.ts +61 -0
- package/src/render/backend/webgpu/skinPaletteRing.ts +4 -0
- package/src/render/backend/webgpu/snapshotMips.ts +80 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +91 -51
- package/src/render/driftLight/bake.ts +85 -62
- package/src/render/driftLight/denseField.ts +149 -0
- package/src/render/driftLight/lightField.ts +33 -18
- package/src/render/driftLight/presence.ts +69 -0
- package/src/render/driftLight/uniforms.ts +8 -9
- package/src/render/driftLight/worldLightField.ts +83 -0
- package/src/render/fixtureAtlas.ts +63 -0
- package/src/render/glass.ts +74 -0
- package/src/render/glassCasters.ts +164 -0
- package/src/render/glassShadow.ts +153 -0
- package/src/render/globalMediumPass.ts +4 -11
- package/src/render/gpudriven/lit.ts +31 -0
- package/src/render/gpudriven/materialTable.ts +28 -6
- package/src/render/gpudriven/pipeline.ts +4 -3
- package/src/render/gpudriven/sunGlass.ts +117 -0
- package/src/render/instanceCull.ts +205 -0
- package/src/render/instances.ts +16 -0
- package/src/render/lightBudget.ts +41 -38
- package/src/render/lightGrid.ts +130 -0
- package/src/render/mesh.ts +59 -72
- package/src/render/occlusion.ts +51 -7
- package/src/render/pointGlassTint.ts +250 -0
- package/src/render/pointLightSelection.ts +72 -4
- package/src/render/pointShadowArray.ts +188 -22
- package/src/render/pointShadowMap.ts +41 -0
- package/src/render/rainField.ts +39 -5
- package/src/render/recon/changeFrames.ts +24 -0
- package/src/render/recon/lateDepth.ts +83 -0
- package/src/render/recon/lateRouting.ts +29 -0
- package/src/render/recon/mover.ts +118 -0
- package/src/render/recon/resolve.ts +206 -20
- package/src/render/renderQuality.ts +47 -0
- package/src/render/rendererHarness.ts +16 -0
- package/src/render/sceneTarget.ts +62 -11
- package/src/render/shaders/depth.ts +118 -15
- package/src/render/shaders/flat/directionalShadow.ts +97 -6
- package/src/render/shaders/flat/driftLight.ts +32 -7
- package/src/render/shaders/flat/glassTint.ts +25 -0
- package/src/render/shaders/flat/index.ts +22 -3
- package/src/render/shaders/flat/lampGlass.ts +185 -0
- package/src/render/shaders/flat/main.ts +190 -38
- package/src/render/shaders/flat/pointShadow.ts +2 -0
- package/src/render/shaders/flat/preamble.ts +96 -26
- package/src/render/shaders/flat/surfaceEffects.ts +152 -0
- package/src/render/shaders/generated/depth.wgsl.ts +237 -7
- package/src/render/shaders/generated/flat.wgsl.ts +1685 -1621
- package/src/render/shaders/generated/globalMedium.wgsl.ts +3 -13
- package/src/render/shaders/generated/lightVolume.wgsl.ts +23 -17
- package/src/render/shaders/generated/octahedralResolve.wgsl.ts +30 -0
- package/src/render/shaders/generated/rush.wgsl.ts +21 -16
- package/src/render/shaders/generated/sdfText.wgsl.ts +2 -2
- package/src/render/shaders/generated/text.wgsl.ts +1 -1
- package/src/render/shaders/gi/driftLight.wgsl.ts +27 -4
- package/src/render/shaders/globalMedium.ts +6 -6
- package/src/render/shaders/gpudriven/blendRaster.wgsl.ts +88 -1
- package/src/render/shaders/gpudriven/cull.wgsl.ts +11 -2
- package/src/render/shaders/gpudriven/materialTable.wgsl.ts +3 -1
- package/src/render/shaders/gpudriven/shade.wgsl.ts +50 -1
- package/src/render/shaders/gpudriven/sunGlass.wgsl.ts +139 -0
- package/src/render/shaders/lightVolume.ts +6 -6
- package/src/render/shaders/octahedralResolve.ts +50 -17
- package/src/render/shaders/recon/lateDepth.wgsl.ts +72 -0
- package/src/render/shaders/recon/motion.wgsl.ts +84 -0
- package/src/render/shaders/recon/resolve.wgsl.ts +161 -27
- package/src/render/shaders/rush.ts +11 -1
- package/src/render/shaders/sdfText.ts +3 -2
- package/src/render/shaders/text.ts +18 -9
- package/src/render/shadowCasters.ts +28 -1
- package/src/render/shadowMap.ts +231 -37
- package/src/render/sunGlassTint.ts +86 -0
- package/src/render/surfaceEffects.ts +132 -0
- package/src/render/surfaceScene.ts +16 -0
- package/src/render/surfaceTexture.ts +110 -19
- package/src/render/temporalAa.ts +78 -14
- package/src/render/textureSource.ts +72 -0
- package/src/render/translucentQueue.ts +20 -1
- package/src/render/vertexDefaults.ts +2 -0
- package/src/world/hlod.ts +290 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a lamp's light keeps through glass at this receiver: `glassShadow.ts`'s rules over the point
|
|
3
|
+
* filter's own taps, for a round lamp and for a rectangle.
|
|
4
|
+
*
|
|
5
|
+
* **The sun's lookup, in the lamp's geometry** (`directionalShadow.ts`'s `sunGlassLookup`). The
|
|
6
|
+
* outline comes from depth-tested taps on the opaque filter's own disk — a tap counts a pane only
|
|
7
|
+
* where the receiver is farther from the lamp than that pane, by the opaque bias, and fades with
|
|
8
|
+
* the opaque shadow's own reach and penumbra — and the colour from spread taps, read coarse,
|
|
9
|
+
* unmixed from what lies around the pane and weighed by its share, so a frosted patch mixes its
|
|
10
|
+
* panes and keeps the light a clear one passes.
|
|
11
|
+
*
|
|
12
|
+
* **Where a lamp differs is the frost.** Light scattered at a pane spreads in a cone behind it, so
|
|
13
|
+
* at a receiver `behind` metres past the pane the colour comes from pane points within
|
|
14
|
+
* `(1 − clarity) × tan 30° × behind` of the ray. Seen from the lamp that is an angle over the pane's
|
|
15
|
+
* distance, and in the filter's units — offsets added to the ray at the receiver's distance — it is
|
|
16
|
+
* that spread times `dist / pane`. Capped at `FROST_RADIUS_CAP` base radii, the tap count unchanged.
|
|
17
|
+
*
|
|
18
|
+
* **What it costs** is one coarse tint fetch per shadowed lamp on a receiver with no glass near its
|
|
19
|
+
* filter — the tint is white there, and the lookup stops — and two tap loops where there is. **What
|
|
20
|
+
* it gives up** is what the sun's gives up, plus the octahedral map's fold: a coarse level averages
|
|
21
|
+
* across the map's outer edges, so a wide frost spread lying across one borrows a little colour from
|
|
22
|
+
* the other side of the sphere.
|
|
23
|
+
*/
|
|
24
|
+
import { FROST_RADIUS_CAP, FROST_SPREAD } from '../../glassShadow.ts';
|
|
25
|
+
import { MAX_SHADOW_FILTER_TAPS } from '../../renderQuality.ts';
|
|
26
|
+
|
|
27
|
+
/** Octahedral texels per radian at edge 1: the sphere's 4π over `edge²` texels, as a side. */
|
|
28
|
+
const TEXELS_PER_RADIAN = 1 / Math.sqrt(4 * Math.PI);
|
|
29
|
+
|
|
30
|
+
export const LAMP_GLASS_GLSL = `
|
|
31
|
+
/*
|
|
32
|
+
* Whether any glass lies within \`radius\` of this ray: the tint is exactly white wherever no pane
|
|
33
|
+
* was drawn, and any pane passes at most 0.96 of itself, so one coarse fetch answers for the disk.
|
|
34
|
+
*/
|
|
35
|
+
bool lampGlassNear(float tintLayer, vec3 toFrag, float dist, float radius) {
|
|
36
|
+
float texels = radius / dist * float(textureSize(uPointGlassTints, 0).x) * ${TEXELS_PER_RADIAN.toFixed(6)};
|
|
37
|
+
vec4 around = textureLod(uPointGlassTints, vec3(octEncode(toFrag), tintLayer), log2(max(texels, 1.0)) + 1.0);
|
|
38
|
+
return min(min(around.r, around.g), min(around.b, around.a)) < 0.998;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/*
|
|
42
|
+
* The spread half: the colour of the glass the frost disk finds, read at \`lod\`, unmixed from what
|
|
43
|
+
* lies around it and weighed by its share (glassShadow.ts's spreadTint). Clear when it finds none.
|
|
44
|
+
*/
|
|
45
|
+
vec3 lampGlassColour(
|
|
46
|
+
float tintLayer,
|
|
47
|
+
vec3 toFrag,
|
|
48
|
+
vec3 tu,
|
|
49
|
+
vec3 tv,
|
|
50
|
+
float offset,
|
|
51
|
+
float lod,
|
|
52
|
+
float clarity,
|
|
53
|
+
float ca,
|
|
54
|
+
float sa
|
|
55
|
+
) {
|
|
56
|
+
vec3 colour = vec3(0.0);
|
|
57
|
+
float found = 0.0;
|
|
58
|
+
for (int i = 0; i < ${MAX_SHADOW_FILTER_TAPS}; i++) {
|
|
59
|
+
if (i >= uShadowFilterTaps) break;
|
|
60
|
+
vec2 disk = PCF_OFFSETS[i];
|
|
61
|
+
vec2 turned = vec2(disk.x * ca - disk.y * sa, disk.x * sa + disk.y * ca);
|
|
62
|
+
vec3 spreadDir = toFrag + (tu * turned.x + tv * turned.y) * offset;
|
|
63
|
+
vec4 pane = glassUnmix(textureLod(uPointGlassTints, vec3(octEncode(spreadDir), tintLayer), lod), clarity);
|
|
64
|
+
colour += pane.rgb * pane.a;
|
|
65
|
+
found += pane.a;
|
|
66
|
+
}
|
|
67
|
+
return found > 0.0 ? colour / found : vec3(1.0);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* One outline tap: how much of the pane on this ray lies between the lamp and the receiver. */
|
|
71
|
+
float lampGlassTap(vec3 sampleDir, float glassLayer, float dist, float bias, float far, float size) {
|
|
72
|
+
float stored = textureLod(uPointShadows, vec3(octEncode(sampleDir), glassLayer), 0.0).r;
|
|
73
|
+
if (stored >= 0.9999) return 0.0;
|
|
74
|
+
float storedDistance = stored * far;
|
|
75
|
+
if (dist - bias <= storedDistance) return 0.0;
|
|
76
|
+
return shadowReach(max(dist - storedDistance, 0.0), far) * tapStrength(dist, storedDistance, size);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/*
|
|
80
|
+
* The frost's offset in the filter's units, the tint level that reads it as an average, and the
|
|
81
|
+
* pane's clarity: the cone behind the nearest pane on the ray, as the lamp sees it, capped at
|
|
82
|
+
* \`base\` radii.
|
|
83
|
+
*/
|
|
84
|
+
vec3 lampGlassFrost(float tintLayer, vec2 centreUv, float dist, float paneDistance, float base) {
|
|
85
|
+
float clarity = textureLod(uPointGlassTints, vec3(centreUv, tintLayer), 0.0).a;
|
|
86
|
+
float behind = max(dist - paneDistance, 0.0);
|
|
87
|
+
float frost = (1.0 - clarity) * ${FROST_SPREAD.toFixed(6)} * behind * dist / max(paneDistance, 0.05);
|
|
88
|
+
float offset = clamp(frost, base, base * ${FROST_RADIUS_CAP.toFixed(1)});
|
|
89
|
+
float texels = offset / dist * float(textureSize(uPointGlassTints, 0).x) * ${TEXELS_PER_RADIAN.toFixed(6)};
|
|
90
|
+
return vec3(offset, log2(max(texels, 1.0)), clarity);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/*
|
|
94
|
+
* **One lookup for both kinds of lamp**, a rectangle's being the round one with its outline taps on
|
|
95
|
+
* areaShadow's ellipse: a zero \`halfSize\` is a round lamp of \`sourceRadius\`. One function rather
|
|
96
|
+
* than two because every lit permutation carries it, and two copies of the same eighty lines were
|
|
97
|
+
* thirty-five kilobytes of the generated shaders.
|
|
98
|
+
*/
|
|
99
|
+
vec3 lampGlassTint(
|
|
100
|
+
float layer,
|
|
101
|
+
vec3 toFrag,
|
|
102
|
+
float far,
|
|
103
|
+
float near,
|
|
104
|
+
vec3 right,
|
|
105
|
+
vec3 up,
|
|
106
|
+
vec2 halfSize,
|
|
107
|
+
float sourceRadius
|
|
108
|
+
) {
|
|
109
|
+
if (textureSize(uPointGlassTints, 0).x <= 1) return vec3(1.0);
|
|
110
|
+
float dist = length(toFrag);
|
|
111
|
+
if (dist <= near) return vec3(1.0);
|
|
112
|
+
float glassLayer = layer + 1.0;
|
|
113
|
+
float tintLayer = layer * 0.5;
|
|
114
|
+
/* The opaque filter's own widest disk, so the check covers every tap it could take. */
|
|
115
|
+
if (!lampGlassNear(tintLayer, toFrag, dist, MAX_FILTER_RADIUS + 0.012)) return vec3(1.0);
|
|
116
|
+
|
|
117
|
+
float bias = 0.04 + dist * 0.015;
|
|
118
|
+
vec2 centreUv = octEncode(toFrag);
|
|
119
|
+
float paneStored = textureLod(uPointShadows, vec3(centreUv, glassLayer), 0.0).r;
|
|
120
|
+
float paneDistance = paneStored >= 0.9999 ? dist : paneStored * far;
|
|
121
|
+
float spread = max(dist - paneDistance, 0.0) / max(paneDistance, 0.05);
|
|
122
|
+
|
|
123
|
+
vec3 fwd = toFrag / dist;
|
|
124
|
+
vec3 lift = abs(fwd.y) < 0.9 ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0);
|
|
125
|
+
vec3 tu = normalize(cross(lift, fwd));
|
|
126
|
+
vec3 tv = cross(fwd, tu);
|
|
127
|
+
/* A round lamp: the opaque filter's radius, from the pane as its occluder, so a large lamp's
|
|
128
|
+
tint is soft too. A rectangle: its two axes across the ray, each with its own width. */
|
|
129
|
+
vec3 alongX = tu;
|
|
130
|
+
vec3 alongY = tv;
|
|
131
|
+
float radius = clamp(sourceRadius * spread, 0.01, MAX_FILTER_RADIUS) + 0.012;
|
|
132
|
+
float widthX = radius;
|
|
133
|
+
float widthY = radius;
|
|
134
|
+
float penumbra = sourceRadius;
|
|
135
|
+
if (halfSize.x > 0.0) {
|
|
136
|
+
vec3 acrossX = right - fwd * dot(right, fwd);
|
|
137
|
+
vec3 acrossY = up - fwd * dot(up, fwd);
|
|
138
|
+
float seenX = length(acrossX);
|
|
139
|
+
float seenY = length(acrossY);
|
|
140
|
+
alongX = seenX > 1e-5 ? acrossX / seenX : vec3(0.0);
|
|
141
|
+
alongY = seenY > 1e-5 ? acrossY / seenY : vec3(0.0);
|
|
142
|
+
float halfX = halfSize.x * seenX;
|
|
143
|
+
float halfY = halfSize.y * seenY;
|
|
144
|
+
widthX = min(halfX * spread, MAX_FILTER_RADIUS) + 0.012;
|
|
145
|
+
widthY = min(halfY * spread, MAX_FILTER_RADIUS) + 0.012;
|
|
146
|
+
penumbra = min(halfX, halfY);
|
|
147
|
+
}
|
|
148
|
+
/* pointShadow's column-pair turn, so main's pair resolve averages these taps as it does those. */
|
|
149
|
+
float angle = float(int(gl_FragCoord.x) & 1) * PCF_PAIR_TURN;
|
|
150
|
+
float ca = cos(angle);
|
|
151
|
+
float sa = sin(angle);
|
|
152
|
+
|
|
153
|
+
float cover = 0.0;
|
|
154
|
+
for (int i = 0; i < ${MAX_SHADOW_FILTER_TAPS}; i++) {
|
|
155
|
+
if (i >= uShadowFilterTaps) break;
|
|
156
|
+
vec2 disk = PCF_OFFSETS[i];
|
|
157
|
+
vec2 turned = vec2(disk.x * ca - disk.y * sa, disk.x * sa + disk.y * ca);
|
|
158
|
+
vec3 sampleDir = toFrag + alongX * (turned.x * widthX) + alongY * (turned.y * widthY);
|
|
159
|
+
cover += lampGlassTap(sampleDir, glassLayer, dist, bias, far, penumbra);
|
|
160
|
+
}
|
|
161
|
+
if (cover <= 0.0) return vec3(1.0);
|
|
162
|
+
vec3 frost = lampGlassFrost(tintLayer, centreUv, dist, paneDistance, max(widthX, widthY));
|
|
163
|
+
vec3 colour = lampGlassColour(tintLayer, toFrag, tu, tv, frost.x, frost.y, frost.z, ca, sa);
|
|
164
|
+
return mix(vec3(1.0), colour, cover / float(max(uShadowFilterTaps, 1)));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* The two ways in, each asking first whether this build reads glass at all (GLASS_SHADOWS). */
|
|
168
|
+
vec3 pointGlassTint(float layer, vec3 toFrag, float far, float near, float sourceRadius) {
|
|
169
|
+
if (GLASS_SHADOWS) return lampGlassTint(layer, toFrag, far, near, vec3(0.0), vec3(0.0), vec2(0.0), sourceRadius);
|
|
170
|
+
return vec3(1.0);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
vec3 areaGlassTint(
|
|
174
|
+
float layer,
|
|
175
|
+
vec3 toFrag,
|
|
176
|
+
float far,
|
|
177
|
+
float near,
|
|
178
|
+
vec3 right,
|
|
179
|
+
vec3 up,
|
|
180
|
+
vec2 halfSize
|
|
181
|
+
) {
|
|
182
|
+
if (GLASS_SHADOWS) return lampGlassTint(layer, toFrag, far, near, right, up, halfSize, 0.0);
|
|
183
|
+
return vec3(1.0);
|
|
184
|
+
}
|
|
185
|
+
`;
|
|
@@ -1,7 +1,24 @@
|
|
|
1
1
|
/** The fragment entry point, which assembles everything above into a colour. */
|
|
2
2
|
|
|
3
3
|
export const MAIN_GLSL = `void main() {
|
|
4
|
-
|
|
4
|
+
/*
|
|
5
|
+
* Where every material map is read: the texture coordinate, and the layer of the array the face
|
|
6
|
+
* wears, rounded here so an interpolated 2.9999 lands on layer 3 however the generated WGSL
|
|
7
|
+
* turns a float into an array index. A plain texture is an array of one, so layer 0.
|
|
8
|
+
*/
|
|
9
|
+
vec3 surfaceAt = vec3(vUv.xy, floor(vUv.z + 0.5));
|
|
10
|
+
if (dot(vec4(vWorldPos, 1.0), uClipPlane) < 0.0) discard;
|
|
11
|
+
/*
|
|
12
|
+
* The layer's surface effects, zeros where the albedo array carries no table — see
|
|
13
|
+
* surfaceEffects.ts. Scroll and flipbook move surfaceAt before any map reads it, so every map of
|
|
14
|
+
* the material moves together. The gradients are taken here, outside every branch, for the room
|
|
15
|
+
* a window samples through textureGrad further down.
|
|
16
|
+
*/
|
|
17
|
+
fxLoad(int(surfaceAt.z));
|
|
18
|
+
surfaceAt = fxAnimate(surfaceAt);
|
|
19
|
+
vec2 fxDx = dFdx(surfaceAt.xy);
|
|
20
|
+
vec2 fxDy = dFdy(surfaceAt.xy);
|
|
21
|
+
if (uWriteMode.y != 0.0 && !ditherKeeps(uWriteMode.y)) discard;
|
|
5
22
|
|
|
6
23
|
/*
|
|
7
24
|
* Surface colour and its coverage, resolved before anything else — including before it is
|
|
@@ -35,13 +52,13 @@ export const MAIN_GLSL = `void main() {
|
|
|
35
52
|
*/
|
|
36
53
|
float coverage = 1.0;
|
|
37
54
|
if (uAlbedoEnabled != 0) {
|
|
38
|
-
vec4 texel = texture(uAlbedo,
|
|
55
|
+
vec4 texel = texture(uAlbedo, surfaceAt);
|
|
39
56
|
// Discarded before anything else is computed: a thrown-away fragment should not pay
|
|
40
57
|
// for the lighting it will never contribute to. The test credits alpha for the mip level
|
|
41
58
|
// it samples, so a needle averaged thin down the chain is not lost; see cutoutCoverage.ts.
|
|
42
59
|
// Behind a branch on a uniform, so a material with no cutoff pays nothing for it.
|
|
43
60
|
if (uAlbedoCutout > 0.0) {
|
|
44
|
-
float tested = cutoutAlpha(texel.a, vUv * vec2(textureSize(uAlbedo, 0)));
|
|
61
|
+
float tested = cutoutAlpha(texel.a, vUv.xy * vec2(textureSize(uAlbedo, 0).xy));
|
|
45
62
|
if (tested < uAlbedoCutout) discard;
|
|
46
63
|
}
|
|
47
64
|
albedo *= texel.rgb;
|
|
@@ -70,7 +87,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
70
87
|
float ormRoughness = vRoughness;
|
|
71
88
|
float metal = 0.0;
|
|
72
89
|
if (uOrmEnabled != 0) {
|
|
73
|
-
vec3 t = texture(uOrmMap,
|
|
90
|
+
vec3 t = texture(uOrmMap, surfaceAt).rgb;
|
|
74
91
|
ormOcclusion = mix(1.0, t.r, uOrmScale.r);
|
|
75
92
|
ormRoughness = clamp(t.g * uOrmScale.g, 0.0, 1.0);
|
|
76
93
|
metal = clamp(t.b * uOrmScale.b, 0.0, 1.0);
|
|
@@ -82,7 +99,28 @@ export const MAIN_GLSL = `void main() {
|
|
|
82
99
|
* \`uLightingEnabled\` at 0 keeps to the end, and the value every lit draw starts from before
|
|
83
100
|
* the branch below builds a shaded result on top of it.
|
|
84
101
|
*/
|
|
102
|
+
/*
|
|
103
|
+
* Wear, rain and windows, on the colour and roughness everything below shades. Each is exactly
|
|
104
|
+
* nothing where its numbers are zero, which is every surface of a scene that names no effect.
|
|
105
|
+
*/
|
|
106
|
+
fxNormal = normalize(vNormal);
|
|
107
|
+
fxDistance = distance(vWorldPos, uCameraPos);
|
|
108
|
+
vec4 weathered = fxWeather(vWorldPos, albedo, ormRoughness);
|
|
109
|
+
albedo = weathered.rgb;
|
|
110
|
+
ormRoughness = weathered.w;
|
|
111
|
+
vec3 windowGlow = vec3(0.0);
|
|
112
|
+
if (fx0.x > 0.0) {
|
|
113
|
+
vec4 window = fxWindows(surfaceAt, fxDx, fxDy, coverage);
|
|
114
|
+
albedo = mix(albedo, fxRoom * 0.35, window.w);
|
|
115
|
+
windowGlow = window.rgb;
|
|
116
|
+
}
|
|
117
|
+
|
|
85
118
|
vec3 lit = albedo;
|
|
119
|
+
/* The light arriving at a glass pane from behind it, gathered in each light loop below and added
|
|
120
|
+
over the see-through at the end. Stays zero for anything that is not glass. See glass.ts. */
|
|
121
|
+
vec3 glassGlow = vec3(0.0);
|
|
122
|
+
float glassTransmission = uSeeThrough.z;
|
|
123
|
+
float glassFrost = uSeeThrough.w;
|
|
86
124
|
|
|
87
125
|
if (uLightingEnabled != 0) {
|
|
88
126
|
vec3 n = normalize(vNormal);
|
|
@@ -94,7 +132,13 @@ export const MAIN_GLSL = `void main() {
|
|
|
94
132
|
* back of the sheet; what that gives up is a sliver at a curved silhouette, where a front face's
|
|
95
133
|
* interpolated normal already leans away and is lit as the back.
|
|
96
134
|
*/
|
|
97
|
-
|
|
135
|
+
/*
|
|
136
|
+
* **Glass is two-sided whatever its material says**: a pane is symmetric, so from behind the
|
|
137
|
+
* side the eye sees is the one lit, and the light on its far side is what glows through it.
|
|
138
|
+
* Taken from the stored normal alone, a pane seen from behind glowed with the light on the
|
|
139
|
+
* eye's side and not with the sun shining through it.
|
|
140
|
+
*/
|
|
141
|
+
bool backFace = (uDoubleSided != 0 || glassTransmission > 0.0) && dot(n, uCameraPos - vWorldPos) < 0.0;
|
|
98
142
|
/*
|
|
99
143
|
* The authored normal, before either relief perturbs it.
|
|
100
144
|
*
|
|
@@ -112,8 +156,8 @@ export const MAIN_GLSL = `void main() {
|
|
|
112
156
|
* below. The rule is about non-uniform branches, and this branch is on a uniform.
|
|
113
157
|
*/
|
|
114
158
|
if (uNormalStrength > 0.0) {
|
|
115
|
-
vec3 mapped = texture(uNormalMap,
|
|
116
|
-
mat3 tbn = tangentFrame(n, vWorldPos, vUv, vTangent, vHasTangents);
|
|
159
|
+
vec3 mapped = texture(uNormalMap, surfaceAt).xyz * 2.0 - 1.0;
|
|
160
|
+
mat3 tbn = tangentFrame(n, vWorldPos, vUv.xy, vTangent, vHasTangents);
|
|
117
161
|
n = normalize(mix(n, normalize(tbn * mapped), uNormalStrength));
|
|
118
162
|
}
|
|
119
163
|
|
|
@@ -233,14 +277,14 @@ export const MAIN_GLSL = `void main() {
|
|
|
233
277
|
* antisymmetric, the cancellation holds, and it is the more accurate estimate besides: a
|
|
234
278
|
* forward difference reports the slope half a step away from the point it is used at.
|
|
235
279
|
*/
|
|
236
|
-
vec2 duvdx = dFdx(vUv);
|
|
237
|
-
vec2 duvdy = dFdy(vUv);
|
|
280
|
+
vec2 duvdx = dFdx(vUv.xy);
|
|
281
|
+
vec2 duvdy = dFdy(vUv.xy);
|
|
238
282
|
/* Half, because a centred difference spans two steps and three.js's number means one. */
|
|
239
283
|
float reliefPerStep = uTextureRelief * 0.5;
|
|
240
|
-
float dHdx = (surfaceHeight(texture(uAlbedo, vUv + duvdx).rgb)
|
|
241
|
-
- surfaceHeight(texture(uAlbedo, vUv - duvdx).rgb)) * reliefPerStep;
|
|
242
|
-
float dHdy = (surfaceHeight(texture(uAlbedo, vUv + duvdy).rgb)
|
|
243
|
-
- surfaceHeight(texture(uAlbedo, vUv - duvdy).rgb)) * reliefPerStep;
|
|
284
|
+
float dHdx = (surfaceHeight(texture(uAlbedo, vec3(vUv.xy + duvdx, surfaceAt.z)).rgb)
|
|
285
|
+
- surfaceHeight(texture(uAlbedo, vec3(vUv.xy - duvdx, surfaceAt.z)).rgb)) * reliefPerStep;
|
|
286
|
+
float dHdy = (surfaceHeight(texture(uAlbedo, vec3(vUv.xy + duvdy, surfaceAt.z)).rgb)
|
|
287
|
+
- surfaceHeight(texture(uAlbedo, vec3(vUv.xy - duvdy, surfaceAt.z)).rgb)) * reliefPerStep;
|
|
244
288
|
|
|
245
289
|
vec3 dpdx = dFdx(vWorldPos);
|
|
246
290
|
vec3 dpdy = dFdy(vWorldPos);
|
|
@@ -313,9 +357,13 @@ export const MAIN_GLSL = `void main() {
|
|
|
313
357
|
float surfaceNdl = dot(surfaceN, uDirectionalDir);
|
|
314
358
|
#if DIRECTIONAL_SHADOWS
|
|
315
359
|
float sunShade = shadowFactor(max(surfaceNdl, 0.0));
|
|
360
|
+
/* What the sun keeps through any glass on its way here: see sunGlassLookup. */
|
|
361
|
+
vec3 sunGlass = sunGlassTint(max(surfaceNdl, 0.0));
|
|
316
362
|
#else
|
|
317
363
|
float sunShade = 1.0;
|
|
364
|
+
vec3 sunGlass = vec3(1.0);
|
|
318
365
|
#endif
|
|
366
|
+
vec3 sunColor = uDirectionalColor * sunGlass;
|
|
319
367
|
/*
|
|
320
368
|
* **How much sky this vertex can see, applied to the sun and to nothing else.**
|
|
321
369
|
*
|
|
@@ -343,6 +391,8 @@ export const MAIN_GLSL = `void main() {
|
|
|
343
391
|
*/
|
|
344
392
|
float sunFacing = smoothstep(0.0, 0.02, surfaceNdl);
|
|
345
393
|
float direct = ndl * sunShade * sunFacing;
|
|
394
|
+
/* Glass: the sun on the far side of a pane lights it through the glass. See glassGlow. */
|
|
395
|
+
if (glassTransmission > 0.0) glassGlow += sunColor * max(-surfaceNdl, 0.0) * sunShade;
|
|
346
396
|
|
|
347
397
|
/*
|
|
348
398
|
* Hemispheric: sky above, ground below, mixed by which way the surface looks. An
|
|
@@ -389,7 +439,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
389
439
|
* smallest change available in a shader with a documented history of shared subexpressions
|
|
390
440
|
* rounding differently when their neighbours move. See the night-emissive term.
|
|
391
441
|
*/
|
|
392
|
-
lit = albedo * (ambient +
|
|
442
|
+
lit = albedo * (ambient + sunColor * direct * (1.0 - metal));
|
|
393
443
|
|
|
394
444
|
/*
|
|
395
445
|
* A sun highlight, for the few surfaces that ask for one.
|
|
@@ -451,7 +501,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
451
501
|
* before: \`sunSpec\` is \`vec3(vSpecular)\` at metal 0 and \`vSpecular\` defaults to 0.
|
|
452
502
|
*/
|
|
453
503
|
vec3 sunHighlight =
|
|
454
|
-
|
|
504
|
+
sunColor * specularLobe(max(dot(n, halfway), 0.0), surfaceRoughness) * sunSpec * sunShade * sunFacing;
|
|
455
505
|
/*
|
|
456
506
|
* The dielectric's share goes in here, where it has always gone, so the environment blend
|
|
457
507
|
* below dims it by exactly the reflectance it always dimmed it by. The metal's share is added
|
|
@@ -891,6 +941,9 @@ export const MAIN_GLSL = `void main() {
|
|
|
891
941
|
* CLUSTER_TEXELS;
|
|
892
942
|
int clusterLights = uClustered == 1 ? int(clusterTexel(clusterBaseTexel).x) : 0;
|
|
893
943
|
|
|
944
|
+
/* The fixture atlas's size, asked once: its cookie band and its profile rows are found from it. */
|
|
945
|
+
vec2 fixtureSize = vec2(textureSize(uFixtureAtlas, 0));
|
|
946
|
+
|
|
894
947
|
// Point lights. Inverse-square-ish falloff clipped to a finite radius, so a
|
|
895
948
|
// light can be culled without a visible seam.
|
|
896
949
|
for (int slot = 0; slot < LIGHT_LOOP_MAX; slot++) {
|
|
@@ -1023,7 +1076,14 @@ export const MAIN_GLSL = `void main() {
|
|
|
1023
1076
|
}
|
|
1024
1077
|
if (falloff <= 0.0) continue;
|
|
1025
1078
|
float ndl = max(dot(n, toLight / max(dist, 1e-4)), 0.0);
|
|
1026
|
-
|
|
1079
|
+
/*
|
|
1080
|
+
* **A lamp behind a pane is not skipped when the surface is glass**: it lights the pane through
|
|
1081
|
+
* the glass, shaped and shadowed like any lamp, and is added to glassGlow once its shape is
|
|
1082
|
+
* known below. For everything else a lamp behind the surface is skipped here, as always.
|
|
1083
|
+
*/
|
|
1084
|
+
float backNdl =
|
|
1085
|
+
glassTransmission > 0.0 ? max(-dot(n, toLight / max(dist, 1e-4)), 0.0) : 0.0;
|
|
1086
|
+
if (ndl <= 0.0 && backNdl <= 0.0) continue;
|
|
1027
1087
|
|
|
1028
1088
|
/*
|
|
1029
1089
|
* Normal-offset bias.
|
|
@@ -1076,7 +1136,17 @@ export const MAIN_GLSL = `void main() {
|
|
|
1076
1136
|
*/
|
|
1077
1137
|
int shadowRead = clamp(shadowSlot, 0, MAX_LIGHTS - 1);
|
|
1078
1138
|
int layer = shadowSlot >= 0 && shadowSlot < MAX_LIGHTS ? uPointShadowLayer[shadowRead] : -1;
|
|
1139
|
+
/* What the panes between this receiver and the lamp let through, per map: lampGlass.ts. */
|
|
1140
|
+
vec3 staticGlass = vec3(1.0);
|
|
1141
|
+
vec3 liveGlass = vec3(1.0);
|
|
1079
1142
|
if (layer >= 0 && shadowWeight > POINT_SHADOW_MIN_WEIGHT) {
|
|
1143
|
+
staticGlass = pointGlassTint(
|
|
1144
|
+
float(layer),
|
|
1145
|
+
receiverPoint - uPointShadowProjection[shadowRead].xyz,
|
|
1146
|
+
uPointShadowProjection[shadowRead].w,
|
|
1147
|
+
uPointShadowNear[shadowRead],
|
|
1148
|
+
uPointShadowSize[shadowRead]
|
|
1149
|
+
);
|
|
1080
1150
|
occl = mix(
|
|
1081
1151
|
1.0,
|
|
1082
1152
|
pointShadow(
|
|
@@ -1116,10 +1186,20 @@ export const MAIN_GLSL = `void main() {
|
|
|
1116
1186
|
uLivePointShadowSize[shadowRead]
|
|
1117
1187
|
);
|
|
1118
1188
|
liveWeight = uLivePointShadowWeight[shadowRead];
|
|
1189
|
+
liveGlass = pointGlassTint(
|
|
1190
|
+
float(liveLayer),
|
|
1191
|
+
receiverPoint - uLivePointShadowProjection[shadowRead].xyz,
|
|
1192
|
+
uLivePointShadowProjection[shadowRead].w,
|
|
1193
|
+
uLivePointShadowNear[shadowRead],
|
|
1194
|
+
uLivePointShadowSize[shadowRead]
|
|
1195
|
+
);
|
|
1119
1196
|
}
|
|
1120
1197
|
occl *= mix(1.0, liveOccl, liveWeight);
|
|
1198
|
+
/* Each map's glass faded in by that map's presence, as its occlusion is. */
|
|
1199
|
+
vec3 lampGlass = mix(vec3(1.0), staticGlass, uPointShadowWeight[shadowRead]) * mix(vec3(1.0), liveGlass, liveWeight);
|
|
1121
1200
|
#else
|
|
1122
1201
|
float occl = 1.0;
|
|
1202
|
+
vec3 lampGlass = vec3(1.0);
|
|
1123
1203
|
#endif
|
|
1124
1204
|
|
|
1125
1205
|
// Blend occlusion out as the light fades, so a shadow can never be more
|
|
@@ -1158,7 +1238,9 @@ export const MAIN_GLSL = `void main() {
|
|
|
1158
1238
|
vec3 iesRay = -toLight / max(dist, 1e-4);
|
|
1159
1239
|
/* The angle from the fixture's axis, as a fraction of the 0-to-180 arc every row spans. */
|
|
1160
1240
|
float iesAngle = acos(clamp(dot(iesRay, lightDir), -1.0, 1.0));
|
|
1161
|
-
|
|
1241
|
+
/* Clamped to the row's first and last texel centres, which is what the edge clamp of a
|
|
1242
|
+
texture exactly one row wide gave; the atlas can be wider now, with cookies beside it. */
|
|
1243
|
+
float iesU = clamp(iesAngle / PI_IES * IES_WIDTH, 0.5, IES_WIDTH - 0.5) / fixtureSize.x;
|
|
1162
1244
|
/*
|
|
1163
1245
|
* **The azimuth about the fixture's own axis**, behind a branch on a uniform so a scene whose
|
|
1164
1246
|
* profiles are all axially symmetric pays none of this arithmetic. A \`uniform float\` is
|
|
@@ -1212,13 +1294,14 @@ export const MAIN_GLSL = `void main() {
|
|
|
1212
1294
|
*/
|
|
1213
1295
|
float cookieTile = max(cookie, 0.0);
|
|
1214
1296
|
vec2 cookieAt = vec2(
|
|
1215
|
-
(cookieTile + clamp(cookieUv.x, COOKIE_INSET, 1.0 - COOKIE_INSET))
|
|
1216
|
-
|
|
1297
|
+
(cookieTile + clamp(cookieUv.x, COOKIE_INSET, 1.0 - COOKIE_INSET)) * COOKIE_SPAN
|
|
1298
|
+
/ fixtureSize.x,
|
|
1299
|
+
clamp(cookieUv.y, COOKIE_INSET, 1.0 - COOKIE_INSET) * COOKIE_SPAN / fixtureSize.y);
|
|
1217
1300
|
/* Unconditional and \`textureLod\`, and selected with a \`mix\` afterwards: the light
|
|
1218
1301
|
loop is not provably uniform control flow — the clustered arm reads its index from a
|
|
1219
1302
|
texture — so a branch on the *light's* cookie index around a sample is what the
|
|
1220
1303
|
2026-08-07 rule forbids. */
|
|
1221
|
-
vec3 cookieSample = textureLod(
|
|
1304
|
+
vec3 cookieSample = textureLod(uFixtureAtlas, cookieAt, 0.0).rgb;
|
|
1222
1305
|
cookieTint = mix(vec3(1.0), cookieSample, step(0.0, cookie));
|
|
1223
1306
|
}
|
|
1224
1307
|
}
|
|
@@ -1233,13 +1316,17 @@ export const MAIN_GLSL = `void main() {
|
|
|
1233
1316
|
* bytes on \`core-only\`** across the sixteen fragment permutations.
|
|
1234
1317
|
*/
|
|
1235
1318
|
float iesBase = iesRow * (uIesPlaneCount + 1.0);
|
|
1319
|
+
/* The profiles are the atlas's bottom rows, below any cookies. */
|
|
1320
|
+
float iesTop = fixtureSize.y - uIesAtlasRows;
|
|
1236
1321
|
float iesV = uIesAtlasRows <= 1.0
|
|
1237
|
-
? 0.5
|
|
1238
|
-
: (iesBase + iesTurns * uIesPlaneCount + 0.5) /
|
|
1239
|
-
float iesGain = textureLod(
|
|
1322
|
+
? (iesTop + 0.5) / fixtureSize.y
|
|
1323
|
+
: (iesTop + iesBase + iesTurns * uIesPlaneCount + 0.5) / fixtureSize.y;
|
|
1324
|
+
float iesGain = textureLod(uFixtureAtlas, vec2(iesU, iesV), 0.0).r;
|
|
1240
1325
|
float photometric = mix(1.0, iesGain, step(0.0, iesProfile));
|
|
1241
1326
|
|
|
1242
1327
|
float shape = (uLightFalloff == 1 ? falloff : falloff * falloff) * coneFalloff * photometric;
|
|
1328
|
+
glassGlow += lightColor * backNdl * shape * lightWeight * shaded * lampGlass;
|
|
1329
|
+
if (ndl <= 0.0) continue;
|
|
1243
1330
|
/*
|
|
1244
1331
|
* The cookie tints rather than scales, because a mask may be coloured — a stained window is
|
|
1245
1332
|
* the case that makes the difference — and a greyscale one is exactly a scale. Applied to
|
|
@@ -1278,7 +1365,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
1278
1365
|
*/
|
|
1279
1366
|
vec3 lampDiffuse = albedo * lightColor * ndl * shape * lightWeight * (1.0 - metal);
|
|
1280
1367
|
lampOpen += lampDiffuse;
|
|
1281
|
-
lampShadowed += lampDiffuse * shaded;
|
|
1368
|
+
lampShadowed += lampDiffuse * shaded * lampGlass;
|
|
1282
1369
|
|
|
1283
1370
|
/*
|
|
1284
1371
|
* A lamp reflected in a polished surface, which point lights could not do at all.
|
|
@@ -1313,7 +1400,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
1313
1400
|
lightColor * sphereLobe(max(dot(n, lampHalfway), 0.0), surfaceRoughness, lightSourceRadius, dist)
|
|
1314
1401
|
* lampSpec * shape * lightWeight;
|
|
1315
1402
|
lampOpen += lampHighlight;
|
|
1316
|
-
lampShadowed += lampHighlight * shaded;
|
|
1403
|
+
lampShadowed += lampHighlight * shaded * lampGlass;
|
|
1317
1404
|
}
|
|
1318
1405
|
/*
|
|
1319
1406
|
* The most any light in range shadows this point, for the emissive term below —
|
|
@@ -1374,7 +1461,11 @@ export const MAIN_GLSL = `void main() {
|
|
|
1374
1461
|
*/
|
|
1375
1462
|
float signedForm = quadFormFactor(n, vWorldPos, c0, c1, c2, c3);
|
|
1376
1463
|
float form = mix(max(0.0, signedForm), abs(signedForm), uAreaLightTwoSided[a]);
|
|
1377
|
-
|
|
1464
|
+
/* The form factor is linear in the normal, so the one seen from behind is its negation. */
|
|
1465
|
+
float backForm = glassTransmission > 0.0
|
|
1466
|
+
? mix(max(0.0, -signedForm), abs(signedForm), uAreaLightTwoSided[a])
|
|
1467
|
+
: 0.0;
|
|
1468
|
+
if (form <= 0.0 && backForm <= 0.0) continue;
|
|
1378
1469
|
|
|
1379
1470
|
/*
|
|
1380
1471
|
* **How much of the rectangle this fragment can actually see past whatever is in the way.**
|
|
@@ -1401,6 +1492,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
1401
1492
|
* is a constant nobody has measured. \`docs/IMPROVEMENTS.md\` carries the row.
|
|
1402
1493
|
*/
|
|
1403
1494
|
float areaOccl = 1.0;
|
|
1495
|
+
vec3 areaGlass = vec3(1.0);
|
|
1404
1496
|
#if POINT_SHADOWS
|
|
1405
1497
|
/*
|
|
1406
1498
|
* The sample position is pushed along the surface normal before the lookup, and it scales
|
|
@@ -1441,6 +1533,12 @@ export const MAIN_GLSL = `void main() {
|
|
|
1441
1533
|
),
|
|
1442
1534
|
uAreaShadowWeight[a]
|
|
1443
1535
|
);
|
|
1536
|
+
/* What the panes before the rectangle let through, faded in as its occlusion is. */
|
|
1537
|
+
areaGlass = mix(
|
|
1538
|
+
vec3(1.0),
|
|
1539
|
+
areaGlassTint(float(areaLayer), areaShadowFrom, uAreaShadowFar[a], uAreaShadowNear[a], right, up, halfSize),
|
|
1540
|
+
uAreaShadowWeight[a]
|
|
1541
|
+
);
|
|
1444
1542
|
}
|
|
1445
1543
|
int areaLiveLayer = uLiveAreaShadowLayer[a];
|
|
1446
1544
|
if (areaLiveLayer >= 0) {
|
|
@@ -1458,13 +1556,28 @@ export const MAIN_GLSL = `void main() {
|
|
|
1458
1556
|
),
|
|
1459
1557
|
uLiveAreaShadowWeight[a]
|
|
1460
1558
|
);
|
|
1559
|
+
areaGlass *= mix(
|
|
1560
|
+
vec3(1.0),
|
|
1561
|
+
areaGlassTint(
|
|
1562
|
+
float(areaLiveLayer),
|
|
1563
|
+
areaShadowFrom,
|
|
1564
|
+
uLiveAreaShadowFar[a],
|
|
1565
|
+
uLiveAreaShadowNear[a],
|
|
1566
|
+
right,
|
|
1567
|
+
up,
|
|
1568
|
+
halfSize
|
|
1569
|
+
),
|
|
1570
|
+
uLiveAreaShadowWeight[a]
|
|
1571
|
+
);
|
|
1461
1572
|
}
|
|
1462
1573
|
#endif
|
|
1463
1574
|
|
|
1575
|
+
glassGlow += uAreaLightColor[a] * backForm * areaOccl * areaGlass;
|
|
1576
|
+
if (form <= 0.0) continue;
|
|
1464
1577
|
/* Held back with the point lamps', and shadowed by the same term it always was. */
|
|
1465
1578
|
vec3 areaDiffuse = albedo * uAreaLightColor[a] * form * (1.0 - metal);
|
|
1466
1579
|
lampOpen += areaDiffuse;
|
|
1467
|
-
lampShadowed += areaDiffuse * areaOccl;
|
|
1580
|
+
lampShadowed += areaDiffuse * areaOccl * areaGlass;
|
|
1468
1581
|
|
|
1469
1582
|
if (vSpecular > 0.0 || metal > 0.0) {
|
|
1470
1583
|
/*
|
|
@@ -1491,7 +1604,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
1491
1604
|
vec3 areaSpec = specColor * areaDfg.x + vec3(areaDfg.y);
|
|
1492
1605
|
vec3 areaHighlight = areaSpec * uAreaLightColor[a] * coverage;
|
|
1493
1606
|
lampOpen += areaHighlight;
|
|
1494
|
-
lampShadowed += areaHighlight * areaOccl;
|
|
1607
|
+
lampShadowed += areaHighlight * areaOccl * areaGlass;
|
|
1495
1608
|
}
|
|
1496
1609
|
}
|
|
1497
1610
|
}
|
|
@@ -1551,6 +1664,9 @@ export const MAIN_GLSL = `void main() {
|
|
|
1551
1664
|
* \`driftLight.ts\`.
|
|
1552
1665
|
*/
|
|
1553
1666
|
if (driftShare > 0.0) {
|
|
1667
|
+
if (glassTransmission > 0.0) {
|
|
1668
|
+
glassGlow += driftLightIrradiance(vWorldPos, -n) * (driftShare * uDriftLight.w);
|
|
1669
|
+
}
|
|
1554
1670
|
lit += albedo * driftLightIrradiance(vWorldPos, n) * (driftShare * uDriftLight.w) * (1.0 - metal);
|
|
1555
1671
|
}
|
|
1556
1672
|
|
|
@@ -1622,10 +1738,14 @@ export const MAIN_GLSL = `void main() {
|
|
|
1622
1738
|
*/
|
|
1623
1739
|
vec3 emissiveMapped = vec3(1.0);
|
|
1624
1740
|
if (uEmissiveMapEnabled != 0) {
|
|
1625
|
-
emissiveMapped = texture(uEmissiveMap,
|
|
1741
|
+
emissiveMapped = texture(uEmissiveMap, surfaceAt).rgb * uEmissiveScale;
|
|
1626
1742
|
}
|
|
1743
|
+
/* Pulse, flicker and fade from the layer's effects; exactly 1 where it names none. */
|
|
1744
|
+
float fxGlow = fx4.z + fx5.x + fx5.w > 0.0 ? fxEmission(surfaceAt.z) : 1.0;
|
|
1627
1745
|
lit += emissiveTint * emissiveMapped * uEmissiveGain * vEmissive * uNightFactor
|
|
1628
|
-
* mix(1.0, min(lightShade, sunShade), EMISSIVE_SHADOW_SHARE);
|
|
1746
|
+
* mix(1.0, min(lightShade, sunShade), EMISSIVE_SHADOW_SHARE) * fxGlow;
|
|
1747
|
+
/* And a lit window's glow, which the scene's lit share already times: not gated on night. */
|
|
1748
|
+
lit += windowGlow * uEmissiveGain;
|
|
1629
1749
|
|
|
1630
1750
|
#if NIGHT_EMISSIVE
|
|
1631
1751
|
/*
|
|
@@ -1699,7 +1819,14 @@ export const MAIN_GLSL = `void main() {
|
|
|
1699
1819
|
fog = mediumFog(distance(vWorldPos, uCameraPos), vWorldPos.y);
|
|
1700
1820
|
}
|
|
1701
1821
|
|
|
1702
|
-
|
|
1822
|
+
/*
|
|
1823
|
+
* A surface recedes into the medium; light added to the frame fades in it (\`uFogEnabled\` at 2,
|
|
1824
|
+
* \`drawFog.ts\`). What is behind an additive draw is already fogged, so mixing the draw toward
|
|
1825
|
+
* the medium's colour and adding it puts the haze into the frame twice — a glow by day drew as
|
|
1826
|
+
* a grey shape of itself, greyer the thicker the air.
|
|
1827
|
+
*/
|
|
1828
|
+
vec3 shaded = applyOutputTransform(
|
|
1829
|
+
uFogEnabled == 2 ? lit * (1.0 - fog) : mix(lit, mediumColor(), fog));
|
|
1703
1830
|
|
|
1704
1831
|
/*
|
|
1705
1832
|
* **Refraction: the scene behind this surface, bent and absorbed.**
|
|
@@ -1738,14 +1865,39 @@ export const MAIN_GLSL = `void main() {
|
|
|
1738
1865
|
* splitting the specular out before the fog, which is a larger change to the one shader every
|
|
1739
1866
|
* draw uses.
|
|
1740
1867
|
*/
|
|
1741
|
-
if (
|
|
1868
|
+
if (uSeeThrough.x > 0.0 || glassTransmission > 0.0) {
|
|
1742
1869
|
vec3 refractN = normalize(vNormal);
|
|
1743
1870
|
vec3 refractV = normalize(uCameraPos - vWorldPos);
|
|
1744
|
-
|
|
1745
|
-
vec2
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1871
|
+
ivec2 snapSize = textureSize(uRefractScene, 0);
|
|
1872
|
+
vec2 screenUv = gl_FragCoord.xy / vec2(snapSize);
|
|
1873
|
+
vec2 bent = clamp(screenUv + refractN.xy * uSeeThrough.x, vec2(0.0), vec2(1.0));
|
|
1874
|
+
/*
|
|
1875
|
+
* **An explicit level, and frost is what picks it** — the 2026-08-07 rule, and the reason a
|
|
1876
|
+
* frosted pane blurs rather than fades: the copy carries a mip chain on a frame that draws glass,
|
|
1877
|
+
* so a milkier pane reads a coarser level of it. Level 0 on a copy with one level is exactly what
|
|
1878
|
+
* \`texture()\` read here before, so plain refraction draws as it did.
|
|
1879
|
+
*/
|
|
1880
|
+
/* Two short of the last level: a fully frosted pane still shows the large light and dark of what
|
|
1881
|
+
is behind it, where the chain's last level is one texel, the whole frame's average. */
|
|
1882
|
+
float levels = max(floor(log2(float(max(snapSize.x, snapSize.y)))) - 2.0, 0.0);
|
|
1883
|
+
vec3 behind = textureLod(uRefractScene, bent, glassFrost * levels).rgb;
|
|
1884
|
+
/* Unsigned: a pane passes the same light seen from either side, and so does its Fresnel. */
|
|
1885
|
+
float cosView = max(abs(dot(refractN, refractV)), 0.05);
|
|
1886
|
+
float pathLength = uSeeThrough.y * vThickness / cosView;
|
|
1887
|
+
vec3 seen = behind * pow(uRefractTint, vec3(pathLength));
|
|
1888
|
+
if (glassTransmission > 0.0) {
|
|
1889
|
+
/*
|
|
1890
|
+
* **Glass keeps its own surface and shows what is behind over it**, by how much it lets
|
|
1891
|
+
* through and how little it reflects at this angle — Schlick's Fresnel at 0.04, the arithmetic
|
|
1892
|
+
* \`glass.ts\` states. So a clear pane keeps its highlight, and a pane seen edge-on is a mirror.
|
|
1893
|
+
* The glow of the lights behind it is added last, by how frosted it is.
|
|
1894
|
+
*/
|
|
1895
|
+
float fresnel = 0.04 + 0.96 * pow(1.0 - cosView, 5.0);
|
|
1896
|
+
shaded = mix(shaded, seen * uGlassTint, glassTransmission * (1.0 - fresnel)) +
|
|
1897
|
+
applyOutputTransform(glassGlow * uGlassTint * (glassFrost * glassTransmission) * (1.0 - fog));
|
|
1898
|
+
} else {
|
|
1899
|
+
shaded = seen;
|
|
1900
|
+
}
|
|
1749
1901
|
}
|
|
1750
1902
|
/*
|
|
1751
1903
|
* The per-vertex alpha lane, folded in here rather than given an expression of its own so the
|
|
@@ -1771,7 +1923,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
1771
1923
|
* up holding the weighted sum of colour and the weighted sum of alpha. The revealage pass draws
|
|
1772
1924
|
* the same geometry with this at 0, where the branch below is not taken at all.
|
|
1773
1925
|
*/
|
|
1774
|
-
if (
|
|
1926
|
+
if (uWriteMode.x != 0.0) {
|
|
1775
1927
|
float z = distance(vWorldPos, uCameraPos) / 200.0;
|
|
1776
1928
|
float falloff = 0.03 / (1e-5 + z * z * z * z);
|
|
1777
1929
|
float w = alpha * clamp(falloff, 1e-2, 3e3);
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { MAX_SHADOW_FILTER_TAPS } from '../../renderQuality.ts';
|
|
16
16
|
import { OUTPUT_TRANSFORM_GLSL } from '../outputTransform.ts';
|
|
17
|
+
import { LAMP_GLASS_GLSL } from './lampGlass.ts';
|
|
17
18
|
|
|
18
19
|
export const POINTSHADOW_GLSL = `/**
|
|
19
20
|
* How much of one tap's occlusion survives its own penumbra.
|
|
@@ -338,6 +339,7 @@ float areaShadow(
|
|
|
338
339
|
}
|
|
339
340
|
return lit / float(max(uShadowFilterTaps, 1));
|
|
340
341
|
}
|
|
342
|
+
${LAMP_GLASS_GLSL}
|
|
341
343
|
#endif
|
|
342
344
|
|
|
343
345
|
out vec4 outColor;
|