@driftengine/core 4.9.0 → 4.11.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 +133 -11
- package/README.md +43 -38
- package/dist/index.d.ts +7 -4
- package/dist/index.js +5 -4
- package/dist/render/ambientHarmonics.d.ts +1 -1
- package/dist/render/ambientHarmonics.js +1 -1
- package/dist/render/areaLights.d.ts +4 -4
- package/dist/render/areaLights.js +1 -1
- package/dist/render/backend/api.d.ts +10 -3
- package/dist/render/backend/select.js +11 -5
- package/dist/render/backend/webgl2/boneAnimations.d.ts +29 -0
- package/dist/render/backend/webgl2/boneAnimations.js +80 -0
- package/dist/render/backend/webgl2/instanced.d.ts +5 -1
- package/dist/render/backend/webgl2/instanced.js +13 -1
- package/dist/render/backend/webgl2/renderer.d.ts +189 -4
- package/dist/render/backend/webgl2/renderer.js +614 -66
- package/dist/render/backend/webgl2/skinPaletteTexture.d.ts +5 -3
- package/dist/render/backend/webgl2/skinPaletteTexture.js +6 -4
- package/dist/render/backend/webgpu/boneAnimations.d.ts +34 -0
- package/dist/render/backend/webgpu/boneAnimations.js +76 -0
- package/dist/render/backend/webgpu/buffers.d.ts +24 -4
- package/dist/render/backend/webgpu/buffers.js +116 -59
- package/dist/render/backend/webgpu/clothKernels.d.ts +8 -0
- package/dist/render/backend/webgpu/clothKernels.js +76 -0
- package/dist/render/backend/webgpu/clothOps.d.ts +39 -0
- package/dist/render/backend/webgpu/clothOps.js +78 -0
- package/dist/render/backend/webgpu/clothSetLayout.d.ts +25 -0
- package/dist/render/backend/webgpu/clothSetLayout.js +84 -0
- package/dist/render/backend/webgpu/clothSetPack.d.ts +33 -0
- package/dist/render/backend/webgpu/clothSetPack.js +144 -0
- package/dist/render/backend/webgpu/compressedUpload.d.ts +0 -1
- package/dist/render/backend/webgpu/compressedUpload.js +53 -8
- package/dist/render/backend/webgpu/depthPass.d.ts +59 -3
- package/dist/render/backend/webgpu/depthPass.js +49 -14
- package/dist/render/backend/webgpu/drawCommand.d.ts +20 -4
- package/dist/render/backend/webgpu/drawCommand.js +11 -3
- package/dist/render/backend/webgpu/flatPass.d.ts +85 -42
- package/dist/render/backend/webgpu/flatPass.js +137 -20
- package/dist/render/backend/webgpu/gpuClothSet.d.ts +80 -0
- package/dist/render/backend/webgpu/gpuClothSet.js +462 -0
- package/dist/render/backend/webgpu/instanced.d.ts +14 -1
- package/dist/render/backend/webgpu/instanced.js +24 -1
- package/dist/render/backend/webgpu/latePass.js +1 -0
- package/dist/render/backend/webgpu/materialStore.d.ts +101 -0
- package/dist/render/backend/webgpu/materialStore.js +265 -0
- package/dist/render/backend/webgpu/motionPass.js +15 -6
- package/dist/render/backend/webgpu/pipelineCache.d.ts +28 -1
- package/dist/render/backend/webgpu/pipelineCache.js +46 -1
- package/dist/render/backend/webgpu/postPass.d.ts +1 -1
- package/dist/render/backend/webgpu/renderer.d.ts +200 -35
- package/dist/render/backend/webgpu/renderer.js +993 -168
- package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.d.ts +9 -2
- package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.js +121 -47
- package/dist/render/backend/webgpu/skinPaletteRing.d.ts +7 -3
- package/dist/render/backend/webgpu/skinPaletteRing.js +13 -7
- package/dist/render/backend/webgpu/skinPaletteTexture.d.ts +6 -0
- package/dist/render/backend/webgpu/skinPaletteTexture.js +7 -1
- package/dist/render/backend/webgpu/staticBundles.d.ts +125 -0
- package/dist/render/backend/webgpu/staticBundles.js +242 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +10 -3
- package/dist/render/backend/webgpu/surfaceTexturePass.js +20 -2
- package/dist/render/backend/webgpu/uniformRing.d.ts +21 -0
- package/dist/render/backend/webgpu/uniformRing.js +9 -1
- package/dist/render/backend/webgpu/vertexFormats.d.ts +22 -0
- package/dist/render/backend/webgpu/vertexFormats.js +97 -0
- package/dist/render/bloomChain.d.ts +3 -3
- package/dist/render/boneAnimation.d.ts +92 -0
- package/dist/render/boneAnimation.js +133 -0
- package/dist/render/compressedSource.d.ts +33 -13
- package/dist/render/compressedSource.js +70 -25
- package/dist/render/glCompressed.js +83 -0
- package/dist/render/globalMediumPass.d.ts +2 -0
- package/dist/render/globalMediumPass.js +1 -0
- package/dist/render/indexWidth.d.ts +17 -0
- package/dist/render/indexWidth.js +23 -0
- package/dist/render/instances.d.ts +26 -0
- package/dist/render/lightBudget.d.ts +10 -0
- package/dist/render/lightBudget.js +10 -0
- package/dist/render/lightMatrix.d.ts +11 -0
- package/dist/render/lightMatrix.js +14 -0
- package/dist/render/lightmap.d.ts +1 -1
- package/dist/render/mesh.d.ts +14 -0
- package/dist/render/mesh.js +44 -14
- package/dist/render/pointLightSelection.d.ts +4 -4
- package/dist/render/renderQuality.d.ts +30 -0
- package/dist/render/renderQuality.js +5 -0
- package/dist/render/rendererHarness.d.ts +7 -0
- package/dist/render/rendererHarness.js +12 -0
- package/dist/render/screenSpaceReflection.d.ts +2 -1
- package/dist/render/shader.d.ts +15 -0
- package/dist/render/shader.js +41 -0
- package/dist/render/shaders/boneAnimation.d.ts +19 -0
- package/dist/render/shaders/boneAnimation.js +63 -0
- package/dist/render/shaders/depth.d.ts +7 -0
- package/dist/render/shaders/depth.js +35 -10
- package/dist/render/shaders/flat/directionalShadow.js +84 -10
- package/dist/render/shaders/flat/glassTint.d.ts +1 -1
- package/dist/render/shaders/flat/glassTint.js +3 -0
- package/dist/render/shaders/flat/index.d.ts +21 -0
- package/dist/render/shaders/flat/index.js +42 -7
- package/dist/render/shaders/flat/layered.d.ts +19 -0
- package/dist/render/shaders/flat/layered.js +115 -0
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +58 -21
- package/dist/render/shaders/flat/mapReads.d.ts +10 -0
- package/dist/render/shaders/flat/mapReads.js +38 -0
- package/dist/render/shaders/flat/models.js +1 -1
- package/dist/render/shaders/flat/movingSun.d.ts +21 -0
- package/dist/render/shaders/flat/movingSun.js +46 -0
- package/dist/render/shaders/flat/normalMap.d.ts +6 -2
- package/dist/render/shaders/flat/normalMap.js +14 -3
- package/dist/render/shaders/flat/overlay.d.ts +3 -2
- package/dist/render/shaders/flat/overlay.js +42 -20
- package/dist/render/shaders/flat/pointShadow.js +2 -2
- package/dist/render/shaders/flat/preamble.js +59 -27
- package/dist/render/shaders/flat/worldUv.d.ts +22 -0
- package/dist/render/shaders/flat/worldUv.js +113 -0
- package/dist/render/shaders/generated/depth.wgsl.d.ts +93 -0
- package/dist/render/shaders/generated/depth.wgsl.js +93 -0
- package/dist/render/shaders/generated/flat.wgsl.d.ts +3616 -3042
- package/dist/render/shaders/generated/flat.wgsl.js +3688 -3113
- package/dist/render/shaders/generated/globalMedium.wgsl.d.ts +8 -3
- package/dist/render/shaders/generated/globalMedium.wgsl.js +8 -3
- package/dist/render/shaders/generated/lightVolume.wgsl.d.ts +15 -10
- package/dist/render/shaders/generated/lightVolume.wgsl.js +16 -11
- package/dist/render/shaders/globalMedium.js +9 -1
- package/dist/render/shaders/lightVolume.js +9 -1
- package/dist/render/shaders/outputTransform.d.ts +7 -0
- package/dist/render/shaders/outputTransform.js +28 -14
- package/dist/render/shaders/tangentFrame.d.ts +1 -1
- package/dist/render/shaders/tangentFrame.js +16 -3
- package/dist/render/shaders/vertexChannel.d.ts +7 -0
- package/dist/render/shaders/vertexChannel.js +26 -0
- package/dist/render/skinnedClothRun.d.ts +28 -0
- package/dist/render/skinnedClothRun.js +103 -35
- package/dist/render/staticDraws.d.ts +47 -0
- package/dist/render/staticDraws.js +43 -0
- package/dist/render/surfaceLayers.d.ts +39 -0
- package/dist/render/surfaceLayers.js +26 -0
- package/dist/render/surfaceOverlay.d.ts +16 -3
- package/dist/render/surfaceOverlay.js +11 -6
- package/dist/render/surfaceProjection.d.ts +34 -0
- package/dist/render/surfaceProjection.js +30 -0
- package/dist/render/surfaceTexture.d.ts +68 -7
- package/dist/render/surfaceTexture.js +56 -17
- package/dist/render/vertexPacking.d.ts +48 -0
- package/dist/render/vertexPacking.js +68 -0
- package/package.json +6 -6
- package/src/index.ts +12 -4
- package/src/render/ambientHarmonics.ts +1 -1
- package/src/render/areaLights.ts +4 -4
- package/src/render/backend/api.ts +13 -3
- package/src/render/backend/select.ts +10 -5
- package/src/render/backend/webgl2/boneAnimations.ts +105 -0
- package/src/render/backend/webgl2/instanced.ts +20 -1
- package/src/render/backend/webgl2/renderer.ts +723 -79
- package/src/render/backend/webgl2/skinPaletteTexture.ts +6 -4
- package/src/render/backend/webgpu/boneAnimations.ts +97 -0
- package/src/render/backend/webgpu/buffers.ts +195 -70
- package/src/render/backend/webgpu/clothKernels.ts +86 -0
- package/src/render/backend/webgpu/clothOps.ts +86 -0
- package/src/render/backend/webgpu/clothSetLayout.ts +102 -0
- package/src/render/backend/webgpu/clothSetPack.ts +197 -0
- package/src/render/backend/webgpu/compressedUpload.ts +59 -9
- package/src/render/backend/webgpu/depthPass.ts +51 -10
- package/src/render/backend/webgpu/drawCommand.ts +25 -4
- package/src/render/backend/webgpu/flatPass.ts +212 -41
- package/src/render/backend/webgpu/gpuClothSet.ts +564 -0
- package/src/render/backend/webgpu/instanced.ts +32 -1
- package/src/render/backend/webgpu/latePass.ts +1 -0
- package/src/render/backend/webgpu/materialStore.ts +276 -0
- package/src/render/backend/webgpu/motionPass.ts +18 -7
- package/src/render/backend/webgpu/pipelineCache.ts +53 -7
- package/src/render/backend/webgpu/renderer.ts +1201 -202
- package/src/render/backend/webgpu/shaders/clothSolve.wgsl.ts +121 -47
- package/src/render/backend/webgpu/skinPaletteRing.ts +13 -7
- package/src/render/backend/webgpu/skinPaletteTexture.ts +7 -1
- package/src/render/backend/webgpu/staticBundles.ts +364 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +29 -3
- package/src/render/backend/webgpu/uniformRing.ts +10 -1
- package/src/render/backend/webgpu/vertexFormats.ts +113 -0
- package/src/render/bloomChain.ts +3 -3
- package/src/render/boneAnimation.ts +223 -0
- package/src/render/compressedSource.ts +121 -29
- package/src/render/glCompressed.ts +83 -0
- package/src/render/globalMediumPass.ts +7 -0
- package/src/render/indexWidth.ts +27 -0
- package/src/render/instances.ts +27 -0
- package/src/render/lightBudget.ts +11 -0
- package/src/render/lightMatrix.ts +15 -0
- package/src/render/lightmap.ts +1 -1
- package/src/render/mesh.ts +72 -14
- package/src/render/pointLightSelection.ts +4 -4
- package/src/render/renderQuality.ts +38 -0
- package/src/render/rendererHarness.ts +15 -0
- package/src/render/screenSpaceReflection.ts +2 -1
- package/src/render/shader.ts +59 -0
- package/src/render/shaders/boneAnimation.ts +63 -0
- package/src/render/shaders/depth.ts +45 -10
- package/src/render/shaders/flat/directionalShadow.ts +84 -10
- package/src/render/shaders/flat/glassTint.ts +7 -1
- package/src/render/shaders/flat/index.ts +63 -7
- package/src/render/shaders/flat/layered.ts +115 -0
- package/src/render/shaders/flat/main.ts +58 -21
- package/src/render/shaders/flat/mapReads.ts +38 -0
- package/src/render/shaders/flat/models.ts +1 -1
- package/src/render/shaders/flat/movingSun.ts +46 -0
- package/src/render/shaders/flat/normalMap.ts +14 -3
- package/src/render/shaders/flat/overlay.ts +42 -20
- package/src/render/shaders/flat/pointShadow.ts +2 -2
- package/src/render/shaders/flat/preamble.ts +59 -27
- package/src/render/shaders/flat/worldUv.ts +113 -0
- package/src/render/shaders/generated/depth.wgsl.ts +95 -0
- package/src/render/shaders/generated/flat.wgsl.ts +3688 -3113
- package/src/render/shaders/generated/globalMedium.wgsl.ts +8 -3
- package/src/render/shaders/generated/lightVolume.wgsl.ts +16 -11
- package/src/render/shaders/globalMedium.ts +9 -1
- package/src/render/shaders/lightVolume.ts +9 -1
- package/src/render/shaders/outputTransform.ts +30 -14
- package/src/render/shaders/tangentFrame.ts +16 -3
- package/src/render/shaders/vertexChannel.ts +27 -0
- package/src/render/skinnedClothRun.ts +138 -38
- package/src/render/staticDraws.ts +95 -0
- package/src/render/surfaceLayers.ts +57 -0
- package/src/render/surfaceOverlay.ts +24 -7
- package/src/render/surfaceProjection.ts +57 -0
- package/src/render/surfaceTexture.ts +124 -27
- package/src/render/vertexPacking.ts +109 -0
- package/dist/render/backend/webgpu/gpuSkinnedCloth.d.ts +0 -74
- package/dist/render/backend/webgpu/gpuSkinnedCloth.js +0 -452
- package/src/render/backend/webgpu/gpuSkinnedCloth.ts +0 -502
|
@@ -36,13 +36,56 @@ export const DIRECTIONALSHADOW_GLSL = `vec2 receiverPlaneDepthGradient(vec3 p) {
|
|
|
36
36
|
);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
/* One stored depth against a receiver, in a map spanning \`span\` metres of depth. */
|
|
40
|
+
float directionalVisibilityAt(float receiverDepth, float compareDepth, float storedDepth, float span) {
|
|
40
41
|
if (compareDepth <= storedDepth) return 1.0;
|
|
41
|
-
float rayDistance = max(receiverDepth - storedDepth, 0.0) *
|
|
42
|
+
float rayDistance = max(receiverDepth - storedDepth, 0.0) * span;
|
|
42
43
|
float groundDistance = rayDistance * length(uDirectionalDir.xz);
|
|
43
44
|
return 1.0 - shadowReach(groundDistance, uShadowMaxDistance);
|
|
44
45
|
}
|
|
45
46
|
|
|
47
|
+
float directionalVisibility(float receiverDepth, float compareDepth, float storedDepth) {
|
|
48
|
+
return directionalVisibilityAt(receiverDepth, compareDepth, storedDepth, uShadowDepthSpan);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* How far inside its square a receiver stands in a layer's map: 1 well inside, 0 at its border. */
|
|
52
|
+
float sunMapFade(vec3 p) {
|
|
53
|
+
if (p.z > 1.0 || p.x < 0.0 || p.x > 1.0 || p.y < 0.0 || p.y > 1.0) return 0.0;
|
|
54
|
+
vec2 fromCentre = abs(p.xy - 0.5) * 2.0;
|
|
55
|
+
return (1.0 - smoothstep(0.72, 0.98, max(fromCentre.x, fromCentre.y))) *
|
|
56
|
+
(1.0 - smoothstep(0.90, 1.0, p.z));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/*
|
|
60
|
+
* The moving layer at its own place, where it has a matrix of its own (movingSun.ts): the coarse
|
|
61
|
+
* test of shadowFactor, and its filter, each against the moving map's own depth span, and lit by
|
|
62
|
+
* how far inside the moving map's square the receiver stands rather than cut at its border.
|
|
63
|
+
*/
|
|
64
|
+
float movingSunCoarse(vec3 pm, float span) {
|
|
65
|
+
float inside = sunMapFade(pm);
|
|
66
|
+
if (inside <= 0.0) return 1.0;
|
|
67
|
+
float stored = textureLod(uSunShadows, vec3(pm.xy, ${SUN_DYNAMIC_LAYER}.0), 0.0).r;
|
|
68
|
+
return mix(1.0, directionalVisibilityAt(pm.z, pm.z - 1.0 / span, stored, span), inside);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
float movingSunFiltered(vec3 pm, vec2 gradient, float span) {
|
|
72
|
+
float inside = sunMapFade(pm);
|
|
73
|
+
if (inside <= 0.0) return 1.0;
|
|
74
|
+
float texel = 1.35 / uShadowMapSize;
|
|
75
|
+
float slopeLimit = 9.1 / uShadowMapSize;
|
|
76
|
+
float lit = 0.0;
|
|
77
|
+
for (int i = 0; i < ${MAX_SHADOW_FILTER_TAPS}; i++) {
|
|
78
|
+
if (i >= uShadowFilterTaps) break;
|
|
79
|
+
vec2 sampleUv = pm.xy + DIRECTIONAL_PCF_OFFSETS[i] * texel;
|
|
80
|
+
vec2 texelCentre = (floor(sampleUv * uShadowMapSize) + 0.5) / uShadowMapSize;
|
|
81
|
+
float slopeOffset = clamp(dot(gradient, texelCentre - pm.xy), -slopeLimit, slopeLimit);
|
|
82
|
+
float tapReceiverDepth = pm.z + slopeOffset;
|
|
83
|
+
float stored = textureLod(uSunShadows, vec3(sampleUv, ${SUN_DYNAMIC_LAYER}.0), 0.0).r;
|
|
84
|
+
lit += directionalVisibilityAt(tapReceiverDepth, tapReceiverDepth - 0.14 / span, stored, span);
|
|
85
|
+
}
|
|
86
|
+
return mix(1.0, lit / float(max(uShadowFilterTaps, 1)), inside);
|
|
87
|
+
}
|
|
88
|
+
|
|
46
89
|
float shadowFactor(float directionalNdl) {
|
|
47
90
|
if (uShadowStrength <= 0.0) return 1.0;
|
|
48
91
|
|
|
@@ -72,6 +115,14 @@ float shadowFactor(float directionalNdl) {
|
|
|
72
115
|
vec3 p = vLightPos.xyz / vLightPos.w;
|
|
73
116
|
p = p * 0.5 + 0.5;
|
|
74
117
|
vec2 depthGradient = receiverPlaneDepthGradient(p);
|
|
118
|
+
/* The moving layer's own place and plane where it has a matrix of its own (movingSun.ts), taken
|
|
119
|
+
here beside the static layer's for the same reason: above every varying-dependent return. */
|
|
120
|
+
vec3 pm = p;
|
|
121
|
+
vec2 movingGradient = depthGradient;
|
|
122
|
+
if (MOVING_SUN) {
|
|
123
|
+
pm = movingSunAt();
|
|
124
|
+
movingGradient = receiverPlaneDepthGradient(pm);
|
|
125
|
+
}
|
|
75
126
|
|
|
76
127
|
// Near a surface's own light terminator its direct-light term is already
|
|
77
128
|
// approaching zero, while the orthographic projection of that surface
|
|
@@ -136,7 +187,8 @@ float shadowFactor(float directionalNdl) {
|
|
|
136
187
|
float coarseCompare = p.z - 1.0 / uShadowDepthSpan;
|
|
137
188
|
float coarse =
|
|
138
189
|
directionalVisibility(p.z, coarseCompare, textureLod(uSunShadows, vec3(p.xy, ${SUN_STATIC_LAYER}.0), 0.0).r) *
|
|
139
|
-
|
|
190
|
+
(MOVING_SUN ? movingSunCoarse(pm, movingSunSpan()) :
|
|
191
|
+
directionalVisibility(p.z, coarseCompare, textureLod(uSunShadows, vec3(p.xy, ${SUN_DYNAMIC_LAYER}.0), 0.0).r));
|
|
140
192
|
if (uPeeledShadowEnabled != 0) {
|
|
141
193
|
coarse *= directionalVisibility(p.z, coarseCompare, textureLod(uSunShadows, vec3(p.xy, ${SUN_PEELED_LAYER}.0), 0.0).r);
|
|
142
194
|
}
|
|
@@ -200,7 +252,6 @@ float shadowFactor(float directionalNdl) {
|
|
|
200
252
|
* there is no mip to select and level zero is the only thing \`texture\` could have read.
|
|
201
253
|
*/
|
|
202
254
|
float staticDepth = textureLod(uSunShadows, vec3(sampleUv, ${SUN_STATIC_LAYER}.0), 0.0).r;
|
|
203
|
-
float dynamicDepth = textureLod(uSunShadows, vec3(sampleUv, ${SUN_DYNAMIC_LAYER}.0), 0.0).r;
|
|
204
255
|
staticLit += directionalVisibility(tapReceiverDepth, compare, staticDepth);
|
|
205
256
|
if (uPeeledShadowEnabled != 0) {
|
|
206
257
|
float peeledDepth = textureLod(uSunShadows, vec3(sampleUv, ${SUN_PEELED_LAYER}.0), 0.0).r;
|
|
@@ -208,12 +259,18 @@ float shadowFactor(float directionalNdl) {
|
|
|
208
259
|
} else {
|
|
209
260
|
peeledLit += 1.0;
|
|
210
261
|
}
|
|
211
|
-
|
|
262
|
+
/* The moving layer here where it shares the static one's place; at its own below otherwise.
|
|
263
|
+
Branched on the switch itself, never its negation: see overridableConstants in transform.mjs. */
|
|
264
|
+
if (MOVING_SUN) {
|
|
265
|
+
} else {
|
|
266
|
+
float dynamicDepth = textureLod(uSunShadows, vec3(sampleUv, ${SUN_DYNAMIC_LAYER}.0), 0.0).r;
|
|
267
|
+
dynamicLit += directionalVisibility(tapReceiverDepth, compare, dynamicDepth);
|
|
268
|
+
}
|
|
212
269
|
}
|
|
213
270
|
float tapCount = float(max(uShadowFilterTaps, 1));
|
|
214
271
|
staticLit /= tapCount;
|
|
215
272
|
peeledLit /= tapCount;
|
|
216
|
-
dynamicLit
|
|
273
|
+
dynamicLit = MOVING_SUN ? movingSunFiltered(pm, movingGradient, movingSunSpan()) : dynamicLit / tapCount;
|
|
217
274
|
|
|
218
275
|
// Independent layers preserve every recorded fact at an overlap: each
|
|
219
276
|
// caster keeps its own distance fade, and their transmissions compose
|
|
@@ -234,11 +291,11 @@ float shadowFactor(float directionalNdl) {
|
|
|
234
291
|
* behind the receiver lending it colour within the spread, which only a receiver between two panes
|
|
235
292
|
* meets. One function for both layers, because two copies were most of the lit shaders' growth.
|
|
236
293
|
*/
|
|
237
|
-
vec3 sunGlassLayer(vec3 p, float paneLayer, float tintLayer, float bias, float texel, float uvPerMetre) {
|
|
294
|
+
vec3 sunGlassLayer(vec3 p, float paneLayer, float tintLayer, float bias, float texel, float uvPerMetre, float span) {
|
|
238
295
|
float tintSize = float(textureSize(uSunGlassTints, 0).x);
|
|
239
296
|
float clarity = textureLod(uSunGlassTints, vec3(p.xy, tintLayer), 0.0).a;
|
|
240
297
|
float behind =
|
|
241
|
-
max(p.z - textureLod(uSunShadows, vec3(p.xy, paneLayer), 0.0).r, 0.0) *
|
|
298
|
+
max(p.z - textureLod(uSunShadows, vec3(p.xy, paneLayer), 0.0).r, 0.0) * span;
|
|
242
299
|
float radius = clamp(
|
|
243
300
|
(1.0 - clarity) * ${FROST_SPREAD.toFixed(6)} * behind * uvPerMetre,
|
|
244
301
|
texel,
|
|
@@ -278,6 +335,13 @@ vec3 sunGlassLookup(float directionalNdl) {
|
|
|
278
335
|
/* Map units per metre along the receiver, from derivatives taken first in uniform control flow,
|
|
279
336
|
so a frost spread stated in metres lands the same whatever size of light matrix a consumer built. */
|
|
280
337
|
float uvPerMetre = length(dFdx(p.xy)) / max(length(dFdx(vWorldPos)), 1e-6);
|
|
338
|
+
/* The moving glass at its own place where its layer has a matrix of its own: movingSun.ts. */
|
|
339
|
+
vec3 pm = p;
|
|
340
|
+
float movingPerMetre = uvPerMetre;
|
|
341
|
+
if (MOVING_SUN) {
|
|
342
|
+
pm = movingSunAt();
|
|
343
|
+
movingPerMetre = length(dFdx(pm.xy)) / max(length(dFdx(vWorldPos)), 1e-6);
|
|
344
|
+
}
|
|
281
345
|
if (uShadowStrength <= 0.0 || textureSize(uSunGlassTints, 0).x <= 1) return vec3(1.0);
|
|
282
346
|
if (directionalNdl <= 0.0) return vec3(1.0);
|
|
283
347
|
if (p.z > 1.0 || p.x < 0.0 || p.x > 1.0 || p.y < 0.0 || p.y > 1.0) return vec3(1.0);
|
|
@@ -295,8 +359,18 @@ vec3 sunGlassLookup(float directionalNdl) {
|
|
|
295
359
|
|
|
296
360
|
float bias = 0.14 / uShadowDepthSpan;
|
|
297
361
|
float texel = 1.35 / uShadowMapSize;
|
|
298
|
-
vec3 staticTint = sunGlassLayer(p, ${SUN_GLASS_STATIC_LAYER}.0, 0.0, bias, texel, uvPerMetre);
|
|
299
|
-
vec3 movingTint =
|
|
362
|
+
vec3 staticTint = sunGlassLayer(p, ${SUN_GLASS_STATIC_LAYER}.0, 0.0, bias, texel, uvPerMetre, uShadowDepthSpan);
|
|
363
|
+
vec3 movingTint = MOVING_SUN && sunMapFade(pm) <= 0.0
|
|
364
|
+
? vec3(1.0)
|
|
365
|
+
: sunGlassLayer(
|
|
366
|
+
pm,
|
|
367
|
+
${SUN_GLASS_MOVING_LAYER}.0,
|
|
368
|
+
1.0,
|
|
369
|
+
MOVING_SUN ? 0.14 / movingSunSpan() : bias,
|
|
370
|
+
texel,
|
|
371
|
+
movingPerMetre,
|
|
372
|
+
MOVING_SUN ? movingSunSpan() : uShadowDepthSpan
|
|
373
|
+
);
|
|
300
374
|
return mix(vec3(1.0), staticTint * movingTint, strength);
|
|
301
375
|
}
|
|
302
376
|
|
|
@@ -36,7 +36,10 @@ export type LitSwitch =
|
|
|
36
36
|
| 'DRIFT_LIGHT'
|
|
37
37
|
| 'PHYSICAL_SPECULAR'
|
|
38
38
|
| 'SURFACE_OVERLAY'
|
|
39
|
-
| 'REFLECTION_MAPS'
|
|
39
|
+
| 'REFLECTION_MAPS'
|
|
40
|
+
| 'WORLD_UVS'
|
|
41
|
+
| 'MOVING_SUN'
|
|
42
|
+
| 'LAYERED';
|
|
40
43
|
|
|
41
44
|
/** Every switch off: what a renderer starts from before its profile and its content say otherwise. */
|
|
42
45
|
export function noLitSwitches(): Record<LitSwitch, boolean> {
|
|
@@ -49,6 +52,9 @@ export function noLitSwitches(): Record<LitSwitch, boolean> {
|
|
|
49
52
|
PHYSICAL_SPECULAR: false,
|
|
50
53
|
SURFACE_OVERLAY: false,
|
|
51
54
|
REFLECTION_MAPS: false,
|
|
55
|
+
WORLD_UVS: false,
|
|
56
|
+
MOVING_SUN: false,
|
|
57
|
+
LAYERED: false,
|
|
52
58
|
};
|
|
53
59
|
}
|
|
54
60
|
|
|
@@ -21,7 +21,12 @@ import { DRIFT_LIGHT_GLSL } from './driftLight.ts';
|
|
|
21
21
|
import { SURFACE_EFFECTS_GLSL } from './surfaceEffects.ts';
|
|
22
22
|
import { overlayGlsl } from './overlay.ts';
|
|
23
23
|
import { reflectionSurfaceGlsl } from './reflectionSurface.ts';
|
|
24
|
-
import {
|
|
24
|
+
import { worldUvGlsl } from './worldUv.ts';
|
|
25
|
+
import { movingSunGlsl } from './movingSun.ts';
|
|
26
|
+
import { layeredGlsl } from './layered.ts';
|
|
27
|
+
import { MAP_READS_GLSL } from './mapReads.ts';
|
|
28
|
+
import { CHANNEL_BEND, LIT_CHANNEL_ATTRIBUTE } from '../vertexChannel.ts';
|
|
29
|
+
import { BONE_ANIMATION_GLSL } from '../boneAnimation.ts';
|
|
25
30
|
import { FULL_LIGHT_BUDGET, type LightBudget } from '../../uniformVectorBudget.ts';
|
|
26
31
|
|
|
27
32
|
/**
|
|
@@ -56,10 +61,16 @@ layout(location = 10) in vec4 aTangent;
|
|
|
56
61
|
${SKINNING_GLSL}
|
|
57
62
|
#endif
|
|
58
63
|
|
|
59
|
-
uniform mat4 uViewProj;
|
|
64
|
+
uniform mat4 uViewProj; // wgsl:view
|
|
60
65
|
/** 1 when the mesh carries a tangent frame. See vHasTangents. */
|
|
61
66
|
uniform int uHasTangents;
|
|
62
|
-
uniform mat4 uLightViewProj;
|
|
67
|
+
uniform mat4 uLightViewProj; // wgsl:view
|
|
68
|
+
/**
|
|
69
|
+
* The scene's clock, what \`setAnimationTime\` set: what a bone animation's instances read
|
|
70
|
+
* their moment from. In the view block, before the wind, so every variant's view block is the
|
|
71
|
+
* plain one's or a prefix of it — the instanced variant declares no wind and keeps this.
|
|
72
|
+
*/
|
|
73
|
+
uniform float uSceneTime; // wgsl:view
|
|
63
74
|
/**
|
|
64
75
|
* How often the surface texture repeats across this mesh's UV range, per axis.
|
|
65
76
|
*
|
|
@@ -122,6 +133,9 @@ layout(location = 15) in vec4 aInstanceTint;
|
|
|
122
133
|
* opacity of one. See lightmap.ts.
|
|
123
134
|
*/
|
|
124
135
|
const bool LIGHTMAP_REGIONS = false; // wgsl:override
|
|
136
|
+
#if ANIMATED
|
|
137
|
+
${BONE_ANIMATION_GLSL}
|
|
138
|
+
#endif
|
|
125
139
|
#else
|
|
126
140
|
uniform mat4 uModel;
|
|
127
141
|
/**
|
|
@@ -147,7 +161,7 @@ uniform vec3 uTint;
|
|
|
147
161
|
* agreeing about where the shared uniforms sit: the instanced variant drops these five along with
|
|
148
162
|
* the two above it, and a dropped field moves nothing. flatPass.ts asserts exactly that.
|
|
149
163
|
*/
|
|
150
|
-
${
|
|
164
|
+
${LIT_CHANNEL_ATTRIBUTE}
|
|
151
165
|
${CHANNEL_BEND}
|
|
152
166
|
#endif
|
|
153
167
|
|
|
@@ -245,6 +259,10 @@ void main() {
|
|
|
245
259
|
vec3 localNormal = aNormal;
|
|
246
260
|
vec3 localTangent = aTangent.xyz;
|
|
247
261
|
#endif
|
|
262
|
+
#if ANIMATED
|
|
263
|
+
/* The clip's turn and place, in the mesh's own space, before the instance places it. */
|
|
264
|
+
local = vec4(boneAnimate(local.xyz, localNormal, localTangent), 1.0);
|
|
265
|
+
#endif
|
|
248
266
|
#if INSTANCED
|
|
249
267
|
mat4 model = mat4(aInstanceModel0, aInstanceModel1, aInstanceModel2, aInstanceModel3);
|
|
250
268
|
/*
|
|
@@ -379,6 +397,12 @@ export interface FlatVertexOptions {
|
|
|
379
397
|
* Excludes `skinned`: the joint attributes and the instance matrix want the same locations.
|
|
380
398
|
*/
|
|
381
399
|
readonly instanced: boolean;
|
|
400
|
+
/**
|
|
401
|
+
* Whether an instanced variant follows a bone animation: \`shaders/boneAnimation.ts\`. A
|
|
402
|
+
* variant rather than a pipeline constant, because it declares three textures the other
|
|
403
|
+
* instanced pipelines would otherwise have to bind stand-ins for. Instanced only.
|
|
404
|
+
*/
|
|
405
|
+
readonly animated?: boolean;
|
|
382
406
|
}
|
|
383
407
|
|
|
384
408
|
/**
|
|
@@ -401,9 +425,17 @@ export function flatVert(options: FlatVertexOptions): string {
|
|
|
401
425
|
'so every instance of the batch would wear one expression between them.',
|
|
402
426
|
);
|
|
403
427
|
}
|
|
428
|
+
if (options.animated === true && !options.instanced) {
|
|
429
|
+
throw new Error('flatVert: a bone animation is played by an instanced variant alone.');
|
|
430
|
+
}
|
|
404
431
|
return resolveConditionals(
|
|
405
432
|
FLAT_VERT_SOURCE,
|
|
406
|
-
{
|
|
433
|
+
{
|
|
434
|
+
SKINNED: options.skinned,
|
|
435
|
+
MORPHED: options.morphed,
|
|
436
|
+
INSTANCED: options.instanced,
|
|
437
|
+
ANIMATED: options.animated === true,
|
|
438
|
+
},
|
|
407
439
|
'flatVert',
|
|
408
440
|
);
|
|
409
441
|
}
|
|
@@ -480,6 +512,21 @@ export interface FlatShaderOptions {
|
|
|
480
512
|
* the frame's materials' reflections. True when absent. See `reflectionSurfaceGlsl`.
|
|
481
513
|
*/
|
|
482
514
|
readonly reflectionMaps?: boolean;
|
|
515
|
+
/**
|
|
516
|
+
* Whether it places a material's maps by the world, `uWorldUv` and all (`SurfaceMaterial
|
|
517
|
+
* .projection`). True when absent. See `worldUvGlsl`.
|
|
518
|
+
*/
|
|
519
|
+
readonly worldUvs?: boolean;
|
|
520
|
+
/**
|
|
521
|
+
* Whether it reads the sun's moving layer through a matrix of its own, \`uMovingLightViewProj\`
|
|
522
|
+
* and all. True when absent. See \`movingSunGlsl\`.
|
|
523
|
+
*/
|
|
524
|
+
readonly movingSun?: boolean;
|
|
525
|
+
/**
|
|
526
|
+
* Whether it blends a material's layers by a mask, \`uLayers\` and all (\`SurfaceMaterial.layers\`).
|
|
527
|
+
* True when absent. See \`layeredGlsl\`.
|
|
528
|
+
*/
|
|
529
|
+
readonly layered?: boolean;
|
|
483
530
|
/**
|
|
484
531
|
* How many point lights this build declares room for. `MAX_POINT_LIGHTS` when absent.
|
|
485
532
|
*
|
|
@@ -591,6 +638,8 @@ export function flatFrag(options: FlatShaderOptions): string {
|
|
|
591
638
|
surfaceEffects: options.surfaceEffects ?? true,
|
|
592
639
|
driftLight: options.driftLight ?? true,
|
|
593
640
|
}),
|
|
641
|
+
/* Depth zero and ahead of the sun's lookup, which calls it; its switch takes the next id. */
|
|
642
|
+
movingSunGlsl(options.movingSun ?? true),
|
|
594
643
|
LOBES_GLSL,
|
|
595
644
|
POINTSHADOW_GLSL,
|
|
596
645
|
DIRECTIONALSHADOW_GLSL,
|
|
@@ -616,8 +665,6 @@ export function flatFrag(options: FlatShaderOptions): string {
|
|
|
616
665
|
* and `main` is unconditional by construction.
|
|
617
666
|
*/
|
|
618
667
|
TANGENT_FRAME_GLSL,
|
|
619
|
-
/* After the frame it calls, unconditional for the same reason. See normalMap.ts. */
|
|
620
|
-
NORMAL_MAP_GLSL,
|
|
621
668
|
/* Unconditional for the same reason, and called only where a cutoff was asked for. */
|
|
622
669
|
CUTOUT_COVERAGE_GLSL,
|
|
623
670
|
CUTOUT_DITHER_GLSL,
|
|
@@ -629,6 +676,15 @@ export function flatFrag(options: FlatShaderOptions): string {
|
|
|
629
676
|
overlayGlsl(options.surfaceOverlay ?? true),
|
|
630
677
|
/* After the overlay, so its switch takes the next id. See reflectionSurface.ts. */
|
|
631
678
|
reflectionSurfaceGlsl(options.reflectionMaps ?? true),
|
|
679
|
+
/* After the reflection maps, so its switch takes the next id; main calls it. worldUv.ts. */
|
|
680
|
+
worldUvGlsl(options.worldUvs ?? true),
|
|
681
|
+
/* After the projection, so its switch takes the next id; the normal map below calls it. */
|
|
682
|
+
layeredGlsl(options.layered ?? true),
|
|
683
|
+
/* Where each map is read, after both of the ways it can be read otherwise. See mapReads.ts. */
|
|
684
|
+
MAP_READS_GLSL,
|
|
685
|
+
/* After the frame and the projection it calls, unconditional for the same reason as the
|
|
686
|
+
frame. See normalMap.ts. */
|
|
687
|
+
NORMAL_MAP_GLSL,
|
|
632
688
|
/* After the models, whose answer to a rectangle it asks for. See areaLight.ts. */
|
|
633
689
|
AREA_LIGHT_GLSL,
|
|
634
690
|
MAIN_GLSL,
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A material blended from layers by a mask: `SurfaceMaterial.layers`, packed into `uLayers` by
|
|
3
|
+
* `surfaceLayers.ts`.
|
|
4
|
+
*
|
|
5
|
+
* **Layer `i` is layer `i` of the material's arrays**, each read at the mesh's coordinates times its
|
|
6
|
+
* own repeat; the mask is layer 0 of the map a shading model would read, at the mesh's coordinates.
|
|
7
|
+
* Each channel lays a layer over everything before it — red layer 1, green 2, blue 3, alpha 4 — so a
|
|
8
|
+
* layer's share is its channel times one less each later channel, and the base keeps what none of
|
|
9
|
+
* them took. Colour, roughness and occlusion are blended by those shares, and the normal map's
|
|
10
|
+
* tangent-space texels too, before the frame turns them into the world.
|
|
11
|
+
*
|
|
12
|
+
* **Every layer present is read, whatever its share.** A read whose level is implicit is legal only
|
|
13
|
+
* in uniform control flow (AGENTS.md 2026-08-07), so the loop is bounded by the layer count, a
|
|
14
|
+
* uniform, and never skips on a share, which is the pixel's own.
|
|
15
|
+
*
|
|
16
|
+
* **A lit switch, `LAYERED`**, off until a material first asks, declared last so every switch before
|
|
17
|
+
* it keeps its id. Compiled out, its two vectors are not declared and its calls are constants.
|
|
18
|
+
*/
|
|
19
|
+
export function layeredGlsl(on: boolean): string {
|
|
20
|
+
if (!on) {
|
|
21
|
+
return /* glsl */ `
|
|
22
|
+
const bool LAYERED = false; // wgsl:override
|
|
23
|
+
bool layered() { return false; }
|
|
24
|
+
void layeredBegin() {}
|
|
25
|
+
vec4 layeredAlbedo() { return vec4(1.0); }
|
|
26
|
+
vec3 layeredOrm() { return vec3(1.0); }
|
|
27
|
+
vec3 layeredTexel() { return vec3(0.0, 0.0, 1.0); }
|
|
28
|
+
bool layeredGlows() { return false; }
|
|
29
|
+
vec3 layeredEmissive() { return vec3(0.0); }
|
|
30
|
+
`;
|
|
31
|
+
}
|
|
32
|
+
return /* glsl */ `
|
|
33
|
+
/* Whether this program blends a material's layers. Off but where one has asked: layered.ts. */
|
|
34
|
+
const bool LAYERED = true; // wgsl:override
|
|
35
|
+
/* The five repeats, base first (x y z w, then x), the count (y) and the glowing layer, or -1 (z). */
|
|
36
|
+
uniform vec4 uLayers[2]; // wgsl:material
|
|
37
|
+
|
|
38
|
+
/* The mask's channels that lay a layer, zero past the count: read once, in layeredBegin. */
|
|
39
|
+
vec4 lMask;
|
|
40
|
+
|
|
41
|
+
bool layered() {
|
|
42
|
+
return uLayers[1].y > 0.5;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* The mask, at the mesh's coordinates, where a model's map is. Under a branch on uniforms. */
|
|
46
|
+
void layeredBegin() {
|
|
47
|
+
vec4 m = texture(uModelMap, vec3(vUv.xy, 0.0));
|
|
48
|
+
float n = uLayers[1].y;
|
|
49
|
+
lMask = vec4(n > 1.5 ? m.r : 0.0, n > 2.5 ? m.g : 0.0, n > 3.5 ? m.b : 0.0, n > 4.5 ? m.a : 0.0);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* Layer i's share of this point: its channel, less every later layer's. */
|
|
53
|
+
float layerShare(int i) {
|
|
54
|
+
float keep = 1.0;
|
|
55
|
+
for (int j = 3; j >= 0; j--) {
|
|
56
|
+
if (j + 1 == i) return lMask[j] * keep;
|
|
57
|
+
keep *= 1.0 - lMask[j];
|
|
58
|
+
}
|
|
59
|
+
return keep;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
float layerRepeat(int i) {
|
|
63
|
+
return i < 4 ? uLayers[0][i] : uLayers[1].x;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
vec3 layerAt(int i) {
|
|
67
|
+
return vec3(vUv.xy * layerRepeat(i), float(i));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* Colour by the shares, and the base layer's coverage, which a cutout reads. */
|
|
71
|
+
vec4 layeredAlbedo() {
|
|
72
|
+
vec4 sum = vec4(0.0);
|
|
73
|
+
for (int i = 0; i < 5; i++) {
|
|
74
|
+
if (float(i) >= uLayers[1].y) break;
|
|
75
|
+
vec4 t = texture(uAlbedo, layerAt(i));
|
|
76
|
+
sum.rgb += t.rgb * layerShare(i);
|
|
77
|
+
if (i == 0) sum.a = t.a;
|
|
78
|
+
}
|
|
79
|
+
return sum;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
vec3 layeredOrm() {
|
|
83
|
+
vec3 sum = vec3(0.0);
|
|
84
|
+
for (int i = 0; i < 5; i++) {
|
|
85
|
+
if (float(i) >= uLayers[1].y) break;
|
|
86
|
+
sum += texture(uOrmMap, layerAt(i)).rgb * layerShare(i);
|
|
87
|
+
}
|
|
88
|
+
return sum;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* The tangent-space texels blended, each with its z rebuilt where a two-channel map stored none. */
|
|
92
|
+
vec3 layeredTexel() {
|
|
93
|
+
vec3 sum = vec3(0.0);
|
|
94
|
+
for (int i = 0; i < 5; i++) {
|
|
95
|
+
if (float(i) >= uLayers[1].y) break;
|
|
96
|
+
vec3 t = texture(uNormalMap, layerAt(i)).xyz * 2.0 - 1.0;
|
|
97
|
+
t.z = t.z > 0.0 ? t.z : sqrt(max(0.0, 1.0 - dot(t.xy, t.xy)));
|
|
98
|
+
sum += t * layerShare(i);
|
|
99
|
+
}
|
|
100
|
+
float len = length(sum);
|
|
101
|
+
return len > 1e-5 ? sum / len : vec3(0.0, 0.0, 1.0);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* Whether the emissive map belongs to one layer rather than to the whole surface. */
|
|
105
|
+
bool layeredGlows() {
|
|
106
|
+
return uLayers[1].z > -0.5;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* The emissive map at its layer's place, glowing by that layer's share. */
|
|
110
|
+
vec3 layeredEmissive() {
|
|
111
|
+
int glow = int(uLayers[1].z + 0.5);
|
|
112
|
+
return texture(uEmissiveMap, layerAt(glow)).rgb * layerShare(glow);
|
|
113
|
+
}
|
|
114
|
+
`;
|
|
115
|
+
}
|
|
@@ -7,6 +7,17 @@ export const MAIN_GLSL = `void main() {
|
|
|
7
7
|
* turns a float into an array index. A plain texture is an array of one, so layer 0.
|
|
8
8
|
*/
|
|
9
9
|
vec3 surfaceAt = vec3(vUv.xy, floor(vUv.z + 0.5));
|
|
10
|
+
/*
|
|
11
|
+
* The coordinates a map's frame and its cutout's level are measured along: the mesh's, or the
|
|
12
|
+
* world's where the material projects its maps (worldUv.ts) — in which case the mesh's tangents
|
|
13
|
+
* no longer run along them either. Triplanar reads its own three planes below; every other
|
|
14
|
+
* reader of surfaceAt takes the horizontal plane.
|
|
15
|
+
*/
|
|
16
|
+
vec2 frameAt = vUv.xy;
|
|
17
|
+
int frameTangents = vHasTangents;
|
|
18
|
+
projectSurface(surfaceAt, frameAt, frameTangents);
|
|
19
|
+
/* A layered material's mask, read once for every layer's share below: layered.ts. */
|
|
20
|
+
if (LAYERED && layered()) layeredBegin();
|
|
10
21
|
if (dot(vec4(vWorldPos, 1.0), uClipPlane) < 0.0) discard;
|
|
11
22
|
/*
|
|
12
23
|
* The layer's surface effects, zeros where the albedo array carries no table — see
|
|
@@ -26,6 +37,8 @@ export const MAIN_GLSL = `void main() {
|
|
|
26
37
|
vec2 fxDx = dFdx(surfaceAt.xy);
|
|
27
38
|
vec2 fxDy = dFdy(surfaceAt.xy);
|
|
28
39
|
if (uWriteMode.y != 0.0 && !ditherKeeps(uWriteMode.y)) discard;
|
|
40
|
+
/* An opacity spent on the screen door instead of the blend: preamble.ts. 0 keeps nothing. */
|
|
41
|
+
if (uWriteMode.z != 0.0 && ditherThreshold() >= vAlpha) discard;
|
|
29
42
|
/* A draw's dissolve cuts the surface away before anything is shaded: overlay.ts. */
|
|
30
43
|
if (SURFACE_OVERLAY) {
|
|
31
44
|
if (overlayCuts(surfaceAt.xy)) discard;
|
|
@@ -63,14 +76,14 @@ export const MAIN_GLSL = `void main() {
|
|
|
63
76
|
*/
|
|
64
77
|
float coverage = 1.0;
|
|
65
78
|
if (uMaterialFlags.x != 0) {
|
|
66
|
-
vec4 texel =
|
|
79
|
+
vec4 texel = albedoTexel(surfaceAt);
|
|
67
80
|
// Discarded before anything else is computed: a thrown-away fragment should not pay
|
|
68
81
|
// for the lighting it will never contribute to. The test credits alpha for the mip level
|
|
69
82
|
// it samples, so a needle averaged thin down the chain is not lost; see cutoutCoverage.ts.
|
|
70
83
|
// Behind a branch on a uniform, so a material with no cutoff pays nothing for it.
|
|
71
84
|
float kept = texel.a;
|
|
72
85
|
if (uCutout.x > 0.0) {
|
|
73
|
-
float tested = cutoutAlpha(texel.a,
|
|
86
|
+
float tested = cutoutAlpha(texel.a, frameAt * vec2(textureSize(uAlbedo, 0).xy));
|
|
74
87
|
if (uCutout.y == 0.0) {
|
|
75
88
|
if (tested < uCutout.x) discard;
|
|
76
89
|
} else {
|
|
@@ -82,7 +95,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
82
95
|
*/
|
|
83
96
|
float share = cutoutShare(tested, uCutout.x);
|
|
84
97
|
if (uCutout.y == 1.0) {
|
|
85
|
-
if (!cutoutKeeps(share, gl_FragCoord.xy,
|
|
98
|
+
if (!cutoutKeeps(share, gl_FragCoord.xy, uHighlightMin.w)) discard;
|
|
86
99
|
kept = 1.0;
|
|
87
100
|
} else {
|
|
88
101
|
kept = share;
|
|
@@ -115,7 +128,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
115
128
|
float ormRoughness = vRoughness;
|
|
116
129
|
float metal = 0.0;
|
|
117
130
|
if (uMaterialFlags.y != 0) {
|
|
118
|
-
vec3 t =
|
|
131
|
+
vec3 t = ormTexel(surfaceAt);
|
|
119
132
|
ormOcclusion = mix(1.0, t.r, uOrmScale.r);
|
|
120
133
|
ormRoughness = clamp(t.g * uOrmScale.g, 0.0, 1.0);
|
|
121
134
|
metal = clamp(t.b * uOrmScale.b, 0.0, 1.0);
|
|
@@ -156,6 +169,14 @@ export const MAIN_GLSL = `void main() {
|
|
|
156
169
|
vec3 glassGlow = vec3(0.0);
|
|
157
170
|
float glassTransmission = uSeeThrough.z;
|
|
158
171
|
float glassFrost = uSeeThrough.w;
|
|
172
|
+
/*
|
|
173
|
+
* **A thin surface lets light from behind through, coloured by itself**: a banner, a leaf, a
|
|
174
|
+
* lampshade, an opaque material's \`diffuseTransmission\`, in the glass tint's spare lane. It
|
|
175
|
+
* gathers the light behind it in glassGlow, as a pane does, and adds it through its own colour.
|
|
176
|
+
* None on glass, which has its own way through.
|
|
177
|
+
*/
|
|
178
|
+
float thin = glassTransmission > 0.0 ? 0.0 : uGlassTint.w;
|
|
179
|
+
bool seesBehind = glassTransmission > 0.0 || thin > 0.0;
|
|
159
180
|
|
|
160
181
|
/*
|
|
161
182
|
* The normal a refracting draw bends by: the shading normal, a normal map's tilt and every relief
|
|
@@ -197,7 +218,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
197
218
|
* below. The rule is about non-uniform branches, and this branch is on a uniform.
|
|
198
219
|
*/
|
|
199
220
|
vec3 unmapped = n;
|
|
200
|
-
if (uNormalStrength > 0.0) n = normalMapped(n, surfaceAt);
|
|
221
|
+
if (uNormalStrength > 0.0) n = normalMapped(n, surfaceAt, frameAt, frameTangents);
|
|
201
222
|
/* A draw's wrinkles, by region, over whatever the map made: overlay.ts. */
|
|
202
223
|
if (SURFACE_OVERLAY) n = overlayWrinkle(n, unmapped, surfaceAt.xy);
|
|
203
224
|
|
|
@@ -368,6 +389,8 @@ export const MAIN_GLSL = `void main() {
|
|
|
368
389
|
*/
|
|
369
390
|
shadingNormal = n;
|
|
370
391
|
if (backFace) n = -n;
|
|
392
|
+
/* A blending rim, on the base colour every light below reads: overlay.ts. */
|
|
393
|
+
if (SURFACE_OVERLAY) albedo = overlayRimBase(albedo, surfaceAt.xy, n);
|
|
371
394
|
/*
|
|
372
395
|
* A model's surface, gathered once, on a branch on the switches alone (models.ts) — and whether
|
|
373
396
|
* there is one, asked once: every site below is a branch on these two, which a pipeline with no
|
|
@@ -450,7 +473,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
450
473
|
float sunFacing = smoothstep(0.0, 0.02, surfaceNdl);
|
|
451
474
|
float direct = ndl * sunShade * sunFacing;
|
|
452
475
|
/* Glass: the sun on the far side of a pane lights it through the glass. See glassGlow. */
|
|
453
|
-
if (
|
|
476
|
+
if (seesBehind) glassGlow += sunColor * max(-surfaceNdl, 0.0) * sunShade;
|
|
454
477
|
|
|
455
478
|
/*
|
|
456
479
|
* Hemispheric: sky above, ground below, mixed by which way the surface looks. An
|
|
@@ -1281,7 +1304,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
1281
1304
|
*/
|
|
1282
1305
|
float falloff;
|
|
1283
1306
|
/*
|
|
1284
|
-
* **A light may name its own falloff, and then it is
|
|
1307
|
+
* **A light may name its own falloff, and then it is the usual one for a light without
|
|
1285
1308
|
* inverse-square falloff**: \`(1 - (d/R)^2)^n\` with no distance term, so a fill authored to
|
|
1286
1309
|
* light a whole hall across a hundred metres does, where the inverse square would have it
|
|
1287
1310
|
* gone a few metres out. Zero is the frame's own rule below, which every light was before.
|
|
@@ -1302,8 +1325,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
1302
1325
|
* the glass, shaped and shadowed like any lamp, and is added to glassGlow once its shape is
|
|
1303
1326
|
* known below. For everything else a lamp behind the surface is skipped here, as always.
|
|
1304
1327
|
*/
|
|
1305
|
-
float backNdl =
|
|
1306
|
-
glassTransmission > 0.0 ? max(-dot(n, toLight / max(dist, 1e-4)), 0.0) : 0.0;
|
|
1328
|
+
float backNdl = seesBehind ? max(-dot(n, toLight / max(dist, 1e-4)), 0.0) : 0.0;
|
|
1307
1329
|
/* A model that sees light from behind keeps the lamps behind the surface. See models.ts. */
|
|
1308
1330
|
if (ndl <= 0.0 && backNdl <= 0.0 && !modelledBehind) continue;
|
|
1309
1331
|
|
|
@@ -1323,7 +1345,9 @@ export const MAIN_GLSL = `void main() {
|
|
|
1323
1345
|
#if POINT_SHADOWS
|
|
1324
1346
|
float grazing = 1.0 - ndl;
|
|
1325
1347
|
float normalOffset = (0.06 + dist * 0.05) * grazing * grazing + 0.02;
|
|
1326
|
-
|
|
1348
|
+
/* Toward a lamp behind a thin surface, which is in the map itself: away from it, the surface
|
|
1349
|
+
would stand in its own shadow. A pane is in no map, so glass keeps the usual side. */
|
|
1350
|
+
vec3 receiverPoint = vWorldPos + (thin > 0.0 && ndl <= 0.0 ? -n : n) * normalOffset;
|
|
1327
1351
|
|
|
1328
1352
|
/*
|
|
1329
1353
|
* uPointShadowWeight is how present the cubemap is, and it is the difference
|
|
@@ -1699,7 +1723,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
1699
1723
|
float signedForm = areaSignedForm(n, seenCentre, right, up, seenHalf);
|
|
1700
1724
|
float form = mix(max(0.0, signedForm), abs(signedForm), uAreaLightTwoSided[a]);
|
|
1701
1725
|
/* The form factor is linear in the normal, so the one seen from behind is its negation. */
|
|
1702
|
-
float backForm =
|
|
1726
|
+
float backForm = seesBehind
|
|
1703
1727
|
? mix(max(0.0, -signedForm), abs(signedForm), uAreaLightTwoSided[a])
|
|
1704
1728
|
: 0.0;
|
|
1705
1729
|
if (form <= 0.0 && backForm <= 0.0) continue;
|
|
@@ -1873,7 +1897,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
1873
1897
|
* \`driftLight.ts\`.
|
|
1874
1898
|
*/
|
|
1875
1899
|
if (driftShare > 0.0) {
|
|
1876
|
-
if (
|
|
1900
|
+
if (seesBehind) {
|
|
1877
1901
|
glassGlow += driftLightIrradiance(vWorldPos, -n) * (driftShare * uDriftLight.w);
|
|
1878
1902
|
}
|
|
1879
1903
|
/* The standard expression in its own order — albedo times the field, then the share — so a
|
|
@@ -1933,6 +1957,8 @@ export const MAIN_GLSL = `void main() {
|
|
|
1933
1957
|
* underneath: a ceiling emitting a dull warm haze over a pale panel cannot be expressed
|
|
1934
1958
|
* by scaling the panel, and scaling it gets the brightness right and the hue wrong.
|
|
1935
1959
|
*/
|
|
1960
|
+
/* What a thin surface lets through from behind, through its own colour; a metal lets none. */
|
|
1961
|
+
if (thin > 0.0) lit += albedo * glassGlow * (thin * (1.0 - metal));
|
|
1936
1962
|
diffuseAlone = lit;
|
|
1937
1963
|
vec3 emissiveTint = vEmissiveColor.r < 0.0 ? albedo : vEmissiveColor;
|
|
1938
1964
|
/*
|
|
@@ -1953,7 +1979,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
1953
1979
|
*/
|
|
1954
1980
|
vec3 emissiveMapped = vec3(1.0);
|
|
1955
1981
|
if (uMaterialFlags.z != 0) {
|
|
1956
|
-
emissiveMapped =
|
|
1982
|
+
emissiveMapped = emissiveTexel(surfaceAt) * uEmissiveScale;
|
|
1957
1983
|
}
|
|
1958
1984
|
/* Pulse, flicker and fade from the layer's effects; exactly 1 where it names none. */
|
|
1959
1985
|
float fxGlow = fx4.z + fx5.x + fx5.w > 0.0 ? fxEmission(surfaceAt.z) : 1.0;
|
|
@@ -1997,7 +2023,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
1997
2023
|
// finishing a run reads the same at noon and at midnight. Costs one compare
|
|
1998
2024
|
// per fragment and no extra draw call — the box is a uniform, not geometry.
|
|
1999
2025
|
if (uHighlightMax.w > 0.0) {
|
|
2000
|
-
vec3 inside = step(uHighlightMin, vWorldPos) * step(vWorldPos, uHighlightMax.xyz);
|
|
2026
|
+
vec3 inside = step(uHighlightMin.xyz, vWorldPos) * step(vWorldPos, uHighlightMax.xyz);
|
|
2001
2027
|
lit += albedo * uHighlightMax.w * inside.x * inside.y * inside.z;
|
|
2002
2028
|
}
|
|
2003
2029
|
}
|
|
@@ -2097,11 +2123,21 @@ export const MAIN_GLSL = `void main() {
|
|
|
2097
2123
|
* map here, on a branch on uniforms alone, which keeps its derivatives legal.
|
|
2098
2124
|
*/
|
|
2099
2125
|
vec3 refractN = shadingNormal;
|
|
2100
|
-
if (uLightingEnabled == 0 && uNormalStrength > 0.0) refractN = normalMapped(refractN, surfaceAt);
|
|
2126
|
+
if (uLightingEnabled == 0 && uNormalStrength > 0.0) refractN = normalMapped(refractN, surfaceAt, frameAt, frameTangents);
|
|
2101
2127
|
vec3 refractV = normalize(uCameraPos - vWorldPos);
|
|
2102
2128
|
ivec2 snapSize = textureSize(uRefractScene, 0);
|
|
2103
2129
|
vec2 screenUv = gl_FragCoord.xy / vec2(snapSize);
|
|
2104
|
-
|
|
2130
|
+
/*
|
|
2131
|
+
* **The normal's sideways part as the eye sees it**: the shading normal in view space, its x
|
|
2132
|
+
* and y across the screen, y turned to the way this frame's rows run (w of the tint). It was the
|
|
2133
|
+
* normal's world x and y until 4.10.0, which on a quad facing the camera is the quad's own lean
|
|
2134
|
+
* against the world's axes: one offset over the whole quad, a lens, with a normal map's tilt a
|
|
2135
|
+
* small change on top. In view space a quad facing the eye bends by nothing but its map, which
|
|
2136
|
+
* is a shimmer, and a pane bends by how it leans toward the eye wherever the eye stands.
|
|
2137
|
+
*/
|
|
2138
|
+
vec3 seenN = mat3(uView) * refractN;
|
|
2139
|
+
vec2 bend = vec2(seenN.x, seenN.y * uRefractTint.w);
|
|
2140
|
+
vec2 bent = clamp(screenUv + bend * uSeeThrough.x, vec2(0.0), vec2(1.0));
|
|
2105
2141
|
/*
|
|
2106
2142
|
* **An explicit level, and frost is what picks it** — the 2026-08-07 rule, and the reason a
|
|
2107
2143
|
* frosted pane blurs rather than fades: the copy carries a mip chain on a frame that draws glass,
|
|
@@ -2115,7 +2151,7 @@ export const MAIN_GLSL = `void main() {
|
|
|
2115
2151
|
/* Unsigned: a pane passes the same light seen from either side, and so does its Fresnel. */
|
|
2116
2152
|
float cosView = max(abs(dot(refractN, refractV)), 0.05);
|
|
2117
2153
|
float pathLength = uSeeThrough.y * vThickness / cosView;
|
|
2118
|
-
vec3 seen = behind * pow(uRefractTint, vec3(pathLength));
|
|
2154
|
+
vec3 seen = behind * pow(uRefractTint.rgb, vec3(pathLength));
|
|
2119
2155
|
if (glassTransmission > 0.0) {
|
|
2120
2156
|
/*
|
|
2121
2157
|
* **Glass keeps its own surface and shows what is behind over it**, by how much it lets
|
|
@@ -2124,8 +2160,8 @@ export const MAIN_GLSL = `void main() {
|
|
|
2124
2160
|
* The glow of the lights behind it is added last, by how frosted it is.
|
|
2125
2161
|
*/
|
|
2126
2162
|
float fresnel = 0.04 + 0.96 * pow(1.0 - cosView, 5.0);
|
|
2127
|
-
shaded = mix(shaded, seen * uGlassTint, glassTransmission * (1.0 - fresnel)) +
|
|
2128
|
-
applyOutputTransform(glassGlow * uGlassTint * (glassFrost * glassTransmission) * (1.0 - fog));
|
|
2163
|
+
shaded = mix(shaded, seen * uGlassTint.rgb, glassTransmission * (1.0 - fresnel)) +
|
|
2164
|
+
applyOutputTransform(glassGlow * uGlassTint.rgb * (glassFrost * glassTransmission) * (1.0 - fog));
|
|
2129
2165
|
} else {
|
|
2130
2166
|
shaded = seen;
|
|
2131
2167
|
}
|
|
@@ -2137,9 +2173,10 @@ export const MAIN_GLSL = `void main() {
|
|
|
2137
2173
|
* **It did not need MeshData.colors widened to four floats**, which is how the consumer who
|
|
2138
2174
|
* asked for it had costed the change -- widening colors moves every mesh producer, the
|
|
2139
2175
|
* container layout and every baked asset. Alpha is a factor on the draw's opacity, not a
|
|
2140
|
-
* component of its albedo, so it rides a lane instead and nothing existing moves.
|
|
2176
|
+
* component of its albedo, so it rides a lane instead and nothing existing moves. Where the draw
|
|
2177
|
+
* spends it on the screen door instead (\`uWriteMode.z\`), it was spent at the top and is 1 here.
|
|
2141
2178
|
*/
|
|
2142
|
-
float alpha = uOpacity * coverage * vAlpha;
|
|
2179
|
+
float alpha = uOpacity * coverage * (uWriteMode.z != 0.0 ? 1.0 : vAlpha);
|
|
2143
2180
|
|
|
2144
2181
|
/*
|
|
2145
2182
|
* **Order-independent transparency, when this draw is accumulating into it.**
|