@driftengine/core 4.10.0 → 4.11.1
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 +107 -0
- package/README.md +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.js +2 -2
- package/dist/render/ambientOcclusionPass.d.ts +3 -2
- package/dist/render/ambientOcclusionPass.js +4 -2
- package/dist/render/backend/api.d.ts +21 -1
- package/dist/render/backend/webgl2/renderer.d.ts +141 -3
- package/dist/render/backend/webgl2/renderer.js +454 -42
- package/dist/render/backend/webgpu/buffers.d.ts +5 -0
- package/dist/render/backend/webgpu/buffers.js +98 -48
- 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/drawCommand.d.ts +2 -0
- package/dist/render/backend/webgpu/drawCommand.js +1 -0
- package/dist/render/backend/webgpu/flatPass.d.ts +1 -1
- package/dist/render/backend/webgpu/flatPass.js +2 -1
- package/dist/render/backend/webgpu/gpuClothSet.d.ts +80 -0
- package/dist/render/backend/webgpu/gpuClothSet.js +462 -0
- package/dist/render/backend/webgpu/latePass.js +1 -0
- package/dist/render/backend/webgpu/motionPass.js +15 -6
- package/dist/render/backend/webgpu/pipelineCache.d.ts +22 -1
- package/dist/render/backend/webgpu/pipelineCache.js +39 -1
- package/dist/render/backend/webgpu/postPass.d.ts +1 -1
- package/dist/render/backend/webgpu/renderer.d.ts +100 -1
- package/dist/render/backend/webgpu/renderer.js +423 -95
- 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/staticBundles.js +2 -1
- package/dist/render/backend/webgpu/vertexFormats.d.ts +22 -0
- package/dist/render/backend/webgpu/vertexFormats.js +97 -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 +13 -1
- package/dist/render/instances.js +10 -0
- package/dist/render/lightMatrix.d.ts +11 -0
- package/dist/render/lightMatrix.js +14 -0
- package/dist/render/mesh.d.ts +9 -0
- package/dist/render/mesh.js +38 -14
- package/dist/render/occlusionFade.d.ts +21 -0
- package/dist/render/occlusionFade.js +27 -0
- 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/sceneTarget.d.ts +2 -0
- package/dist/render/sceneTarget.js +1 -1
- package/dist/render/shader.d.ts +15 -0
- package/dist/render/shader.js +41 -0
- package/dist/render/shaders/ambientOcclusion.js +12 -1
- 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 +15 -0
- package/dist/render/shaders/flat/index.js +24 -4
- package/dist/render/shaders/flat/layered.d.ts +27 -0
- package/dist/render/shaders/flat/layered.js +198 -0
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +50 -16
- package/dist/render/shaders/flat/mapReads.d.ts +10 -0
- package/dist/render/shaders/flat/mapReads.js +38 -0
- 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 +23 -3
- package/dist/render/shaders/flat/overlay.d.ts +3 -2
- package/dist/render/shaders/flat/overlay.js +41 -19
- package/dist/render/shaders/flat/preamble.js +23 -5
- package/dist/render/shaders/flat/worldUv.d.ts +22 -0
- package/dist/render/shaders/flat/worldUv.js +113 -0
- package/dist/render/shaders/generated/ambientOcclusion.wgsl.d.ts +6 -1
- package/dist/render/shaders/generated/ambientOcclusion.wgsl.js +6 -1
- package/dist/render/shaders/generated/flat.wgsl.d.ts +768 -368
- package/dist/render/shaders/generated/flat.wgsl.js +791 -391
- 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/tangentFrame.d.ts +1 -1
- package/dist/render/shaders/tangentFrame.js +16 -3
- package/dist/render/skinnedClothRun.d.ts +28 -0
- package/dist/render/skinnedClothRun.js +103 -35
- package/dist/render/surfaceLayers.d.ts +107 -0
- package/dist/render/surfaceLayers.js +82 -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 +39 -0
- package/dist/render/transmission.d.ts +21 -0
- package/dist/render/transmission.js +21 -0
- package/dist/render/vertexPacking.d.ts +48 -0
- package/dist/render/vertexPacking.js +68 -0
- package/dist/ui/splash.d.ts +42 -2
- package/dist/ui/splash.js +78 -13
- package/package.json +6 -6
- package/src/index.ts +6 -2
- package/src/render/ambientOcclusionPass.ts +4 -1
- package/src/render/backend/api.ts +31 -0
- package/src/render/backend/webgl2/renderer.ts +519 -56
- package/src/render/backend/webgpu/buffers.ts +153 -54
- 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/drawCommand.ts +3 -0
- package/src/render/backend/webgpu/flatPass.ts +26 -28
- package/src/render/backend/webgpu/gpuClothSet.ts +564 -0
- package/src/render/backend/webgpu/latePass.ts +1 -0
- package/src/render/backend/webgpu/motionPass.ts +18 -7
- package/src/render/backend/webgpu/pipelineCache.ts +46 -7
- package/src/render/backend/webgpu/renderer.ts +622 -164
- package/src/render/backend/webgpu/shaders/clothSolve.wgsl.ts +121 -47
- package/src/render/backend/webgpu/staticBundles.ts +2 -1
- package/src/render/backend/webgpu/vertexFormats.ts +113 -0
- package/src/render/globalMediumPass.ts +7 -0
- package/src/render/indexWidth.ts +27 -0
- package/src/render/instances.ts +18 -1
- package/src/render/lightMatrix.ts +15 -0
- package/src/render/mesh.ts +66 -14
- package/src/render/occlusionFade.ts +29 -0
- package/src/render/renderQuality.ts +38 -0
- package/src/render/rendererHarness.ts +15 -0
- package/src/render/sceneTarget.ts +3 -0
- package/src/render/shader.ts +59 -0
- package/src/render/shaders/ambientOcclusion.ts +12 -1
- 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 +39 -4
- package/src/render/shaders/flat/layered.ts +198 -0
- package/src/render/shaders/flat/main.ts +50 -16
- package/src/render/shaders/flat/mapReads.ts +38 -0
- package/src/render/shaders/flat/movingSun.ts +46 -0
- package/src/render/shaders/flat/normalMap.ts +23 -3
- package/src/render/shaders/flat/overlay.ts +41 -19
- package/src/render/shaders/flat/preamble.ts +23 -5
- package/src/render/shaders/flat/worldUv.ts +113 -0
- package/src/render/shaders/generated/ambientOcclusion.wgsl.ts +6 -1
- package/src/render/shaders/generated/flat.wgsl.ts +791 -391
- 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/tangentFrame.ts +16 -3
- package/src/render/skinnedClothRun.ts +138 -38
- package/src/render/surfaceLayers.ts +176 -0
- package/src/render/surfaceOverlay.ts +24 -7
- package/src/render/surfaceProjection.ts +57 -0
- package/src/render/surfaceTexture.ts +39 -0
- package/src/render/transmission.ts +27 -0
- package/src/render/vertexPacking.ts +109 -0
- package/src/ui/splash.ts +83 -14
- 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
|
@@ -29,13 +29,56 @@ export const DIRECTIONALSHADOW_GLSL = `vec2 receiverPlaneDepthGradient(vec3 p) {
|
|
|
29
29
|
);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
/* One stored depth against a receiver, in a map spanning \`span\` metres of depth. */
|
|
33
|
+
float directionalVisibilityAt(float receiverDepth, float compareDepth, float storedDepth, float span) {
|
|
33
34
|
if (compareDepth <= storedDepth) return 1.0;
|
|
34
|
-
float rayDistance = max(receiverDepth - storedDepth, 0.0) *
|
|
35
|
+
float rayDistance = max(receiverDepth - storedDepth, 0.0) * span;
|
|
35
36
|
float groundDistance = rayDistance * length(uDirectionalDir.xz);
|
|
36
37
|
return 1.0 - shadowReach(groundDistance, uShadowMaxDistance);
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
float directionalVisibility(float receiverDepth, float compareDepth, float storedDepth) {
|
|
41
|
+
return directionalVisibilityAt(receiverDepth, compareDepth, storedDepth, uShadowDepthSpan);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* How far inside its square a receiver stands in a layer's map: 1 well inside, 0 at its border. */
|
|
45
|
+
float sunMapFade(vec3 p) {
|
|
46
|
+
if (p.z > 1.0 || p.x < 0.0 || p.x > 1.0 || p.y < 0.0 || p.y > 1.0) return 0.0;
|
|
47
|
+
vec2 fromCentre = abs(p.xy - 0.5) * 2.0;
|
|
48
|
+
return (1.0 - smoothstep(0.72, 0.98, max(fromCentre.x, fromCentre.y))) *
|
|
49
|
+
(1.0 - smoothstep(0.90, 1.0, p.z));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/*
|
|
53
|
+
* The moving layer at its own place, where it has a matrix of its own (movingSun.ts): the coarse
|
|
54
|
+
* test of shadowFactor, and its filter, each against the moving map's own depth span, and lit by
|
|
55
|
+
* how far inside the moving map's square the receiver stands rather than cut at its border.
|
|
56
|
+
*/
|
|
57
|
+
float movingSunCoarse(vec3 pm, float span) {
|
|
58
|
+
float inside = sunMapFade(pm);
|
|
59
|
+
if (inside <= 0.0) return 1.0;
|
|
60
|
+
float stored = textureLod(uSunShadows, vec3(pm.xy, ${SUN_DYNAMIC_LAYER}.0), 0.0).r;
|
|
61
|
+
return mix(1.0, directionalVisibilityAt(pm.z, pm.z - 1.0 / span, stored, span), inside);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
float movingSunFiltered(vec3 pm, vec2 gradient, float span) {
|
|
65
|
+
float inside = sunMapFade(pm);
|
|
66
|
+
if (inside <= 0.0) return 1.0;
|
|
67
|
+
float texel = 1.35 / uShadowMapSize;
|
|
68
|
+
float slopeLimit = 9.1 / uShadowMapSize;
|
|
69
|
+
float lit = 0.0;
|
|
70
|
+
for (int i = 0; i < ${MAX_SHADOW_FILTER_TAPS}; i++) {
|
|
71
|
+
if (i >= uShadowFilterTaps) break;
|
|
72
|
+
vec2 sampleUv = pm.xy + DIRECTIONAL_PCF_OFFSETS[i] * texel;
|
|
73
|
+
vec2 texelCentre = (floor(sampleUv * uShadowMapSize) + 0.5) / uShadowMapSize;
|
|
74
|
+
float slopeOffset = clamp(dot(gradient, texelCentre - pm.xy), -slopeLimit, slopeLimit);
|
|
75
|
+
float tapReceiverDepth = pm.z + slopeOffset;
|
|
76
|
+
float stored = textureLod(uSunShadows, vec3(sampleUv, ${SUN_DYNAMIC_LAYER}.0), 0.0).r;
|
|
77
|
+
lit += directionalVisibilityAt(tapReceiverDepth, tapReceiverDepth - 0.14 / span, stored, span);
|
|
78
|
+
}
|
|
79
|
+
return mix(1.0, lit / float(max(uShadowFilterTaps, 1)), inside);
|
|
80
|
+
}
|
|
81
|
+
|
|
39
82
|
float shadowFactor(float directionalNdl) {
|
|
40
83
|
if (uShadowStrength <= 0.0) return 1.0;
|
|
41
84
|
|
|
@@ -65,6 +108,14 @@ float shadowFactor(float directionalNdl) {
|
|
|
65
108
|
vec3 p = vLightPos.xyz / vLightPos.w;
|
|
66
109
|
p = p * 0.5 + 0.5;
|
|
67
110
|
vec2 depthGradient = receiverPlaneDepthGradient(p);
|
|
111
|
+
/* The moving layer's own place and plane where it has a matrix of its own (movingSun.ts), taken
|
|
112
|
+
here beside the static layer's for the same reason: above every varying-dependent return. */
|
|
113
|
+
vec3 pm = p;
|
|
114
|
+
vec2 movingGradient = depthGradient;
|
|
115
|
+
if (MOVING_SUN) {
|
|
116
|
+
pm = movingSunAt();
|
|
117
|
+
movingGradient = receiverPlaneDepthGradient(pm);
|
|
118
|
+
}
|
|
68
119
|
|
|
69
120
|
// Near a surface's own light terminator its direct-light term is already
|
|
70
121
|
// approaching zero, while the orthographic projection of that surface
|
|
@@ -129,7 +180,8 @@ float shadowFactor(float directionalNdl) {
|
|
|
129
180
|
float coarseCompare = p.z - 1.0 / uShadowDepthSpan;
|
|
130
181
|
float coarse =
|
|
131
182
|
directionalVisibility(p.z, coarseCompare, textureLod(uSunShadows, vec3(p.xy, ${SUN_STATIC_LAYER}.0), 0.0).r) *
|
|
132
|
-
|
|
183
|
+
(MOVING_SUN ? movingSunCoarse(pm, movingSunSpan()) :
|
|
184
|
+
directionalVisibility(p.z, coarseCompare, textureLod(uSunShadows, vec3(p.xy, ${SUN_DYNAMIC_LAYER}.0), 0.0).r));
|
|
133
185
|
if (uPeeledShadowEnabled != 0) {
|
|
134
186
|
coarse *= directionalVisibility(p.z, coarseCompare, textureLod(uSunShadows, vec3(p.xy, ${SUN_PEELED_LAYER}.0), 0.0).r);
|
|
135
187
|
}
|
|
@@ -193,7 +245,6 @@ float shadowFactor(float directionalNdl) {
|
|
|
193
245
|
* there is no mip to select and level zero is the only thing \`texture\` could have read.
|
|
194
246
|
*/
|
|
195
247
|
float staticDepth = textureLod(uSunShadows, vec3(sampleUv, ${SUN_STATIC_LAYER}.0), 0.0).r;
|
|
196
|
-
float dynamicDepth = textureLod(uSunShadows, vec3(sampleUv, ${SUN_DYNAMIC_LAYER}.0), 0.0).r;
|
|
197
248
|
staticLit += directionalVisibility(tapReceiverDepth, compare, staticDepth);
|
|
198
249
|
if (uPeeledShadowEnabled != 0) {
|
|
199
250
|
float peeledDepth = textureLod(uSunShadows, vec3(sampleUv, ${SUN_PEELED_LAYER}.0), 0.0).r;
|
|
@@ -201,12 +252,18 @@ float shadowFactor(float directionalNdl) {
|
|
|
201
252
|
} else {
|
|
202
253
|
peeledLit += 1.0;
|
|
203
254
|
}
|
|
204
|
-
|
|
255
|
+
/* The moving layer here where it shares the static one's place; at its own below otherwise.
|
|
256
|
+
Branched on the switch itself, never its negation: see overridableConstants in transform.mjs. */
|
|
257
|
+
if (MOVING_SUN) {
|
|
258
|
+
} else {
|
|
259
|
+
float dynamicDepth = textureLod(uSunShadows, vec3(sampleUv, ${SUN_DYNAMIC_LAYER}.0), 0.0).r;
|
|
260
|
+
dynamicLit += directionalVisibility(tapReceiverDepth, compare, dynamicDepth);
|
|
261
|
+
}
|
|
205
262
|
}
|
|
206
263
|
float tapCount = float(max(uShadowFilterTaps, 1));
|
|
207
264
|
staticLit /= tapCount;
|
|
208
265
|
peeledLit /= tapCount;
|
|
209
|
-
dynamicLit
|
|
266
|
+
dynamicLit = MOVING_SUN ? movingSunFiltered(pm, movingGradient, movingSunSpan()) : dynamicLit / tapCount;
|
|
210
267
|
|
|
211
268
|
// Independent layers preserve every recorded fact at an overlap: each
|
|
212
269
|
// caster keeps its own distance fade, and their transmissions compose
|
|
@@ -227,11 +284,11 @@ float shadowFactor(float directionalNdl) {
|
|
|
227
284
|
* behind the receiver lending it colour within the spread, which only a receiver between two panes
|
|
228
285
|
* meets. One function for both layers, because two copies were most of the lit shaders' growth.
|
|
229
286
|
*/
|
|
230
|
-
vec3 sunGlassLayer(vec3 p, float paneLayer, float tintLayer, float bias, float texel, float uvPerMetre) {
|
|
287
|
+
vec3 sunGlassLayer(vec3 p, float paneLayer, float tintLayer, float bias, float texel, float uvPerMetre, float span) {
|
|
231
288
|
float tintSize = float(textureSize(uSunGlassTints, 0).x);
|
|
232
289
|
float clarity = textureLod(uSunGlassTints, vec3(p.xy, tintLayer), 0.0).a;
|
|
233
290
|
float behind =
|
|
234
|
-
max(p.z - textureLod(uSunShadows, vec3(p.xy, paneLayer), 0.0).r, 0.0) *
|
|
291
|
+
max(p.z - textureLod(uSunShadows, vec3(p.xy, paneLayer), 0.0).r, 0.0) * span;
|
|
235
292
|
float radius = clamp(
|
|
236
293
|
(1.0 - clarity) * ${FROST_SPREAD.toFixed(6)} * behind * uvPerMetre,
|
|
237
294
|
texel,
|
|
@@ -271,6 +328,13 @@ vec3 sunGlassLookup(float directionalNdl) {
|
|
|
271
328
|
/* Map units per metre along the receiver, from derivatives taken first in uniform control flow,
|
|
272
329
|
so a frost spread stated in metres lands the same whatever size of light matrix a consumer built. */
|
|
273
330
|
float uvPerMetre = length(dFdx(p.xy)) / max(length(dFdx(vWorldPos)), 1e-6);
|
|
331
|
+
/* The moving glass at its own place where its layer has a matrix of its own: movingSun.ts. */
|
|
332
|
+
vec3 pm = p;
|
|
333
|
+
float movingPerMetre = uvPerMetre;
|
|
334
|
+
if (MOVING_SUN) {
|
|
335
|
+
pm = movingSunAt();
|
|
336
|
+
movingPerMetre = length(dFdx(pm.xy)) / max(length(dFdx(vWorldPos)), 1e-6);
|
|
337
|
+
}
|
|
274
338
|
if (uShadowStrength <= 0.0 || textureSize(uSunGlassTints, 0).x <= 1) return vec3(1.0);
|
|
275
339
|
if (directionalNdl <= 0.0) return vec3(1.0);
|
|
276
340
|
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);
|
|
@@ -288,8 +352,18 @@ vec3 sunGlassLookup(float directionalNdl) {
|
|
|
288
352
|
|
|
289
353
|
float bias = 0.14 / uShadowDepthSpan;
|
|
290
354
|
float texel = 1.35 / uShadowMapSize;
|
|
291
|
-
vec3 staticTint = sunGlassLayer(p, ${SUN_GLASS_STATIC_LAYER}.0, 0.0, bias, texel, uvPerMetre);
|
|
292
|
-
vec3 movingTint =
|
|
355
|
+
vec3 staticTint = sunGlassLayer(p, ${SUN_GLASS_STATIC_LAYER}.0, 0.0, bias, texel, uvPerMetre, uShadowDepthSpan);
|
|
356
|
+
vec3 movingTint = MOVING_SUN && sunMapFade(pm) <= 0.0
|
|
357
|
+
? vec3(1.0)
|
|
358
|
+
: sunGlassLayer(
|
|
359
|
+
pm,
|
|
360
|
+
${SUN_GLASS_MOVING_LAYER}.0,
|
|
361
|
+
1.0,
|
|
362
|
+
MOVING_SUN ? 0.14 / movingSunSpan() : bias,
|
|
363
|
+
texel,
|
|
364
|
+
movingPerMetre,
|
|
365
|
+
MOVING_SUN ? movingSunSpan() : uShadowDepthSpan
|
|
366
|
+
);
|
|
293
367
|
return mix(vec3(1.0), staticTint * movingTint, strength);
|
|
294
368
|
}
|
|
295
369
|
|
|
@@ -8,7 +8,7 @@ export declare function glassTintGlsl(glassShadows: boolean): string;
|
|
|
8
8
|
* The lit stage's switches, by the name of the constant each is: the decision both backends make
|
|
9
9
|
* about which optional code a lit program carries. See `litSwitchesGlsl`.
|
|
10
10
|
*/
|
|
11
|
-
export type LitSwitch = 'GLASS_SHADOWS' | 'CLUSTERED_LIGHTS' | 'LIGHT_FIXTURES' | 'SURFACE_EFFECTS' | 'DRIFT_LIGHT' | 'PHYSICAL_SPECULAR' | 'SURFACE_OVERLAY' | 'REFLECTION_MAPS';
|
|
11
|
+
export type LitSwitch = 'GLASS_SHADOWS' | 'CLUSTERED_LIGHTS' | 'LIGHT_FIXTURES' | 'SURFACE_EFFECTS' | 'DRIFT_LIGHT' | 'PHYSICAL_SPECULAR' | 'SURFACE_OVERLAY' | 'REFLECTION_MAPS' | 'WORLD_UVS' | 'MOVING_SUN' | 'LAYERED';
|
|
12
12
|
/** Every switch off: what a renderer starts from before its profile and its content say otherwise. */
|
|
13
13
|
export declare function noLitSwitches(): Record<LitSwitch, boolean>;
|
|
14
14
|
/** Which of the lit stage's optional features a build reads. See `litSwitchesGlsl`. */
|
|
@@ -125,6 +125,21 @@ export interface FlatShaderOptions {
|
|
|
125
125
|
* the frame's materials' reflections. True when absent. See `reflectionSurfaceGlsl`.
|
|
126
126
|
*/
|
|
127
127
|
readonly reflectionMaps?: boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Whether it places a material's maps by the world, `uWorldUv` and all (`SurfaceMaterial
|
|
130
|
+
* .projection`). True when absent. See `worldUvGlsl`.
|
|
131
|
+
*/
|
|
132
|
+
readonly worldUvs?: boolean;
|
|
133
|
+
/**
|
|
134
|
+
* Whether it reads the sun's moving layer through a matrix of its own, \`uMovingLightViewProj\`
|
|
135
|
+
* and all. True when absent. See \`movingSunGlsl\`.
|
|
136
|
+
*/
|
|
137
|
+
readonly movingSun?: boolean;
|
|
138
|
+
/**
|
|
139
|
+
* Whether it blends a material's layers by a mask, \`uLayers\` and all (\`SurfaceMaterial.layers\`).
|
|
140
|
+
* True when absent. See \`layeredGlsl\`.
|
|
141
|
+
*/
|
|
142
|
+
readonly layered?: boolean;
|
|
128
143
|
/**
|
|
129
144
|
* How many point lights this build declares room for. `MAX_POINT_LIGHTS` when absent.
|
|
130
145
|
*
|
|
@@ -18,9 +18,14 @@ import { modelsGlsl } from './models.js';
|
|
|
18
18
|
import { AREA_LIGHT_GLSL } from './areaLight.js';
|
|
19
19
|
import { NORMAL_MAP_GLSL } from './normalMap.js';
|
|
20
20
|
import { DRIFT_LIGHT_GLSL } from './driftLight.js';
|
|
21
|
+
import { LIGHTMAP_OPACITY_STEPS } from '../../instances.js';
|
|
21
22
|
import { SURFACE_EFFECTS_GLSL } from './surfaceEffects.js';
|
|
22
23
|
import { overlayGlsl } from './overlay.js';
|
|
23
24
|
import { reflectionSurfaceGlsl } from './reflectionSurface.js';
|
|
25
|
+
import { worldUvGlsl } from './worldUv.js';
|
|
26
|
+
import { movingSunGlsl } from './movingSun.js';
|
|
27
|
+
import { layeredGlsl } from './layered.js';
|
|
28
|
+
import { MAP_READS_GLSL } from './mapReads.js';
|
|
24
29
|
import { CHANNEL_BEND, LIT_CHANNEL_ATTRIBUTE } from '../vertexChannel.js';
|
|
25
30
|
import { BONE_ANIMATION_GLSL } from '../boneAnimation.js';
|
|
26
31
|
import { FULL_LIGHT_BUDGET } from '../../uniformVectorBudget.js';
|
|
@@ -337,10 +342,16 @@ void main() {
|
|
|
337
342
|
vAlpha = aInstanceTint.w;
|
|
338
343
|
vThickness = 1.0;
|
|
339
344
|
if (LIGHTMAP_REGIONS) {
|
|
345
|
+
/*
|
|
346
|
+
* The opacity's transparency rides the U offset as whole steps of 2, the offset itself under 1
|
|
347
|
+
* (\`MeshInstances.lightmapRegions\`): taken off, and none where the instance is opaque.
|
|
348
|
+
*/
|
|
349
|
+
float steps = floor(aInstanceTint.z * 0.5);
|
|
350
|
+
float offsetU = aInstanceTint.z - 2.0 * steps;
|
|
340
351
|
/* Out of the lane's \`-1 - uv\`, through the region, and back into it. See lightmap.ts. */
|
|
341
|
-
vGrain = -1.0 - ((-1.0 - aGrain) * aInstanceTint.x +
|
|
352
|
+
vGrain = -1.0 - ((-1.0 - aGrain) * aInstanceTint.x + offsetU);
|
|
342
353
|
vRelief = -1.0 - ((-1.0 - aRelief) * aInstanceTint.y + aInstanceTint.w);
|
|
343
|
-
vAlpha = 1.0;
|
|
354
|
+
vAlpha = 1.0 - steps / ${LIGHTMAP_OPACITY_STEPS.toFixed(1)};
|
|
344
355
|
}
|
|
345
356
|
#else
|
|
346
357
|
vSkyDirect = aChannel.y;
|
|
@@ -461,6 +472,8 @@ export function flatFrag(options) {
|
|
|
461
472
|
surfaceEffects: options.surfaceEffects ?? true,
|
|
462
473
|
driftLight: options.driftLight ?? true,
|
|
463
474
|
}),
|
|
475
|
+
/* Depth zero and ahead of the sun's lookup, which calls it; its switch takes the next id. */
|
|
476
|
+
movingSunGlsl(options.movingSun ?? true),
|
|
464
477
|
LOBES_GLSL,
|
|
465
478
|
POINTSHADOW_GLSL,
|
|
466
479
|
DIRECTIONALSHADOW_GLSL,
|
|
@@ -486,8 +499,6 @@ export function flatFrag(options) {
|
|
|
486
499
|
* and `main` is unconditional by construction.
|
|
487
500
|
*/
|
|
488
501
|
TANGENT_FRAME_GLSL,
|
|
489
|
-
/* After the frame it calls, unconditional for the same reason. See normalMap.ts. */
|
|
490
|
-
NORMAL_MAP_GLSL,
|
|
491
502
|
/* Unconditional for the same reason, and called only where a cutoff was asked for. */
|
|
492
503
|
CUTOUT_COVERAGE_GLSL,
|
|
493
504
|
CUTOUT_DITHER_GLSL,
|
|
@@ -499,6 +510,15 @@ export function flatFrag(options) {
|
|
|
499
510
|
overlayGlsl(options.surfaceOverlay ?? true),
|
|
500
511
|
/* After the overlay, so its switch takes the next id. See reflectionSurface.ts. */
|
|
501
512
|
reflectionSurfaceGlsl(options.reflectionMaps ?? true),
|
|
513
|
+
/* After the reflection maps, so its switch takes the next id; main calls it. worldUv.ts. */
|
|
514
|
+
worldUvGlsl(options.worldUvs ?? true),
|
|
515
|
+
/* After the projection, so its switch takes the next id; the normal map below calls it. */
|
|
516
|
+
layeredGlsl(options.layered ?? true),
|
|
517
|
+
/* Where each map is read, after both of the ways it can be read otherwise. See mapReads.ts. */
|
|
518
|
+
MAP_READS_GLSL,
|
|
519
|
+
/* After the frame and the projection it calls, unconditional for the same reason as the
|
|
520
|
+
frame. See normalMap.ts. */
|
|
521
|
+
NORMAL_MAP_GLSL,
|
|
502
522
|
/* After the models, whose answer to a rectangle it asks for. See areaLight.ts. */
|
|
503
523
|
AREA_LIGHT_GLSL,
|
|
504
524
|
MAIN_GLSL,
|
|
@@ -0,0 +1,27 @@
|
|
|
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 its own repeat of where the
|
|
6
|
+
* layers are laid: the mesh's coordinates, or the world's horizontal plane where the material has a
|
|
7
|
+
* projection (`worldUv.ts`). The mask's channels weigh layers 1 to 4 — red, green, blue, alpha — and
|
|
8
|
+
* come from a map read where a shading model's would be, from the ORM array's layer past the layers,
|
|
9
|
+
* or from the vertex colour; an added mask placed as the layers are raises one layer's weight, and
|
|
10
|
+
* one layer may be weighed by how much the surface faces up instead of by its channel.
|
|
11
|
+
*
|
|
12
|
+
* **Two ways to combine them.** Laid over, each layer covers everything before it, so a layer's
|
|
13
|
+
* share is its weight times one less each later weight, and the base keeps what none took. Summed,
|
|
14
|
+
* the base is mixed toward each layer times its weight by the weights' sum held to 1, so each
|
|
15
|
+
* layer's share is its weight times that and the base keeps the rest. The shares are worked out once
|
|
16
|
+
* a pixel, in `layeredBegin`; colour, roughness and occlusion are blended by them, and the normal
|
|
17
|
+
* map's tangent-space texels too, before the frame turns them into the world — around a normal read
|
|
18
|
+
* at the mesh's own coordinates, where the material lays one under the layers.
|
|
19
|
+
*
|
|
20
|
+
* **Every layer present is read, whatever its share.** A read whose level is implicit is legal only
|
|
21
|
+
* in uniform control flow (AGENTS.md 2026-08-07), so the loop is bounded by the layer count, a
|
|
22
|
+
* uniform, and never skips on a share, which is the pixel's own.
|
|
23
|
+
*
|
|
24
|
+
* **A lit switch, `LAYERED`**, off until a material first asks, declared last so every switch before
|
|
25
|
+
* it keeps its id. Compiled out, its four vectors are not declared and its calls are constants.
|
|
26
|
+
*/
|
|
27
|
+
export declare function layeredGlsl(on: boolean): string;
|
|
@@ -0,0 +1,198 @@
|
|
|
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 its own repeat of where the
|
|
6
|
+
* layers are laid: the mesh's coordinates, or the world's horizontal plane where the material has a
|
|
7
|
+
* projection (`worldUv.ts`). The mask's channels weigh layers 1 to 4 — red, green, blue, alpha — and
|
|
8
|
+
* come from a map read where a shading model's would be, from the ORM array's layer past the layers,
|
|
9
|
+
* or from the vertex colour; an added mask placed as the layers are raises one layer's weight, and
|
|
10
|
+
* one layer may be weighed by how much the surface faces up instead of by its channel.
|
|
11
|
+
*
|
|
12
|
+
* **Two ways to combine them.** Laid over, each layer covers everything before it, so a layer's
|
|
13
|
+
* share is its weight times one less each later weight, and the base keeps what none took. Summed,
|
|
14
|
+
* the base is mixed toward each layer times its weight by the weights' sum held to 1, so each
|
|
15
|
+
* layer's share is its weight times that and the base keeps the rest. The shares are worked out once
|
|
16
|
+
* a pixel, in `layeredBegin`; colour, roughness and occlusion are blended by them, and the normal
|
|
17
|
+
* map's tangent-space texels too, before the frame turns them into the world — around a normal read
|
|
18
|
+
* at the mesh's own coordinates, where the material lays one under the layers.
|
|
19
|
+
*
|
|
20
|
+
* **Every layer present is read, whatever its share.** A read whose level is implicit is legal only
|
|
21
|
+
* in uniform control flow (AGENTS.md 2026-08-07), so the loop is bounded by the layer count, a
|
|
22
|
+
* uniform, and never skips on a share, which is the pixel's own.
|
|
23
|
+
*
|
|
24
|
+
* **A lit switch, `LAYERED`**, off until a material first asks, declared last so every switch before
|
|
25
|
+
* it keeps its id. Compiled out, its four vectors are not declared and its calls are constants.
|
|
26
|
+
*/
|
|
27
|
+
export function layeredGlsl(on) {
|
|
28
|
+
if (!on) {
|
|
29
|
+
return /* glsl */ `
|
|
30
|
+
const bool LAYERED = false; // wgsl:override
|
|
31
|
+
bool layered() { return false; }
|
|
32
|
+
void layeredBegin() {}
|
|
33
|
+
vec3 layeredColor(vec3 color) { return color; }
|
|
34
|
+
vec4 layeredAlbedo() { return vec4(1.0); }
|
|
35
|
+
vec3 layeredOrm() { return vec3(1.0); }
|
|
36
|
+
vec3 layeredTexel() { return vec3(0.0, 0.0, 1.0); }
|
|
37
|
+
bool layeredMeshNormal() { return false; }
|
|
38
|
+
vec3 layeredMeshTexel() { return vec3(0.0, 0.0, 1.0); }
|
|
39
|
+
bool layeredGlows() { return false; }
|
|
40
|
+
vec3 layeredEmissive() { return vec3(0.0); }
|
|
41
|
+
`;
|
|
42
|
+
}
|
|
43
|
+
return /* glsl */ `
|
|
44
|
+
/* Whether this program blends a material's layers. Off but where one has asked: layered.ts. */
|
|
45
|
+
const bool LAYERED = true; // wgsl:override
|
|
46
|
+
/*
|
|
47
|
+
* Four vectors, as surfaceLayers.ts packs them: the five repeats, base first (x y z w, then x); the
|
|
48
|
+
* count (y), the glowing layer or -1 (z) and where the mask is, 0 a map, 1 the ORM array, 2 the
|
|
49
|
+
* vertex colour (w); the blend, 0 over and 1 summed (x), whether a mesh normal is laid under the
|
|
50
|
+
* layers (y), the added mask's layer or -1 (z) and its repeat (w); its intensity (x), the facing
|
|
51
|
+
* layer or -1 (y), its bias (z) and its sharpness (w).
|
|
52
|
+
*/
|
|
53
|
+
uniform vec4 uLayers[4]; // wgsl:material
|
|
54
|
+
|
|
55
|
+
/* Each layer's share of this point, the base's and layers 1 to 4's: worked out once, in layeredBegin. */
|
|
56
|
+
float lShareBase;
|
|
57
|
+
vec4 lShare;
|
|
58
|
+
|
|
59
|
+
bool layered() {
|
|
60
|
+
return uLayers[1].y > 0.5;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
float layerRepeat(int i) {
|
|
64
|
+
return i < 4 ? uLayers[0][i] : uLayers[1].x;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* Where the layers are laid: the world's horizontal plane under a projection, the mesh's otherwise. */
|
|
68
|
+
vec2 layerPlace() {
|
|
69
|
+
if (WORLD_UVS && projected()) return planarAt();
|
|
70
|
+
return vUv.xy;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
vec3 layerAt(int i) {
|
|
74
|
+
return vec3(layerPlace() * layerRepeat(i), float(i));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* The weights as the material keeps them: the mask's channels, at the mesh's own coordinates. */
|
|
78
|
+
vec4 layerWeights() {
|
|
79
|
+
float source = uLayers[1].w;
|
|
80
|
+
if (source > 1.5) return vec4(vColor, 0.0);
|
|
81
|
+
if (source > 0.5) return texture(uOrmMap, vec3(vUv.xy, uLayers[1].y));
|
|
82
|
+
return texture(uModelMap, vec3(vUv.xy, 0.0));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* Which of layers 1 to 4 a packed layer number names, as a vector of one 1: none for -1. */
|
|
86
|
+
vec4 layerPick(float layer) {
|
|
87
|
+
return vec4(equal(vec4(layer), vec4(1.0, 2.0, 3.0, 4.0)));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/*
|
|
91
|
+
* The weights, changed where the material asks and zero past the count, and every share from them.
|
|
92
|
+
* Under a branch on uniforms, as its reads are.
|
|
93
|
+
*/
|
|
94
|
+
void layeredBegin() {
|
|
95
|
+
vec4 m = layerWeights();
|
|
96
|
+
if (uLayers[2].z > 0.5) {
|
|
97
|
+
/* The ORM array's layer past the layers, and past the mask where the array carries that too. */
|
|
98
|
+
float at = uLayers[1].y + (abs(uLayers[1].w - 1.0) < 0.5 ? 1.0 : 0.0);
|
|
99
|
+
float added = texture(uOrmMap, vec3(layerPlace() * uLayers[2].w, at)).r * uLayers[3].x;
|
|
100
|
+
m = mix(m, clamp(m + added, 0.0, 1.0), layerPick(uLayers[2].z));
|
|
101
|
+
}
|
|
102
|
+
if (uLayers[3].y > 0.5) {
|
|
103
|
+
float up = normalize(vNormal).y * 0.5 + 0.5;
|
|
104
|
+
float facing = clamp(uLayers[3].z + uLayers[3].w * up, 0.0, 1.0);
|
|
105
|
+
m = mix(m, vec4(facing), layerPick(uLayers[3].y));
|
|
106
|
+
}
|
|
107
|
+
float n = uLayers[1].y;
|
|
108
|
+
m *= vec4(n > 1.5 ? 1.0 : 0.0, n > 2.5 ? 1.0 : 0.0, n > 3.5 ? 1.0 : 0.0, n > 4.5 ? 1.0 : 0.0);
|
|
109
|
+
if (uLayers[2].x > 0.5) {
|
|
110
|
+
float taken = min(m.x + m.y + m.z + m.w, 1.0);
|
|
111
|
+
lShare = m * taken;
|
|
112
|
+
lShareBase = 1.0 - taken;
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
/* Each over everything before it: a layer keeps its weight of what every later one leaves. */
|
|
116
|
+
float keep = 1.0;
|
|
117
|
+
lShare.w = m.w * keep;
|
|
118
|
+
keep *= 1.0 - m.w;
|
|
119
|
+
lShare.z = m.z * keep;
|
|
120
|
+
keep *= 1.0 - m.z;
|
|
121
|
+
lShare.y = m.y * keep;
|
|
122
|
+
keep *= 1.0 - m.y;
|
|
123
|
+
lShare.x = m.x * keep;
|
|
124
|
+
keep *= 1.0 - m.x;
|
|
125
|
+
lShareBase = keep;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
float layerShare(int i) {
|
|
129
|
+
if (i == 0) return lShareBase;
|
|
130
|
+
return lShare[i - 1];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* The vertex colour's part in the albedo: none where it is the mask instead. */
|
|
134
|
+
vec3 layeredColor(vec3 color) {
|
|
135
|
+
return uLayers[1].w > 1.5 ? vec3(1.0) : color;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* Colour by the shares, and the base layer's coverage, which a cutout reads. */
|
|
139
|
+
vec4 layeredAlbedo() {
|
|
140
|
+
vec4 sum = vec4(0.0);
|
|
141
|
+
for (int i = 0; i < 5; i++) {
|
|
142
|
+
if (float(i) >= uLayers[1].y) break;
|
|
143
|
+
vec4 t = texture(uAlbedo, layerAt(i));
|
|
144
|
+
sum.rgb += t.rgb * layerShare(i);
|
|
145
|
+
if (i == 0) sum.a = t.a;
|
|
146
|
+
}
|
|
147
|
+
return sum;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
vec3 layeredOrm() {
|
|
151
|
+
vec3 sum = vec3(0.0);
|
|
152
|
+
for (int i = 0; i < 5; i++) {
|
|
153
|
+
if (float(i) >= uLayers[1].y) break;
|
|
154
|
+
sum += texture(uOrmMap, layerAt(i)).rgb * layerShare(i);
|
|
155
|
+
}
|
|
156
|
+
return sum;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/* A tangent-space texel off the normal array, its z rebuilt where a two-channel map stored none. */
|
|
160
|
+
vec3 layerNormalTexel(vec3 at) {
|
|
161
|
+
vec3 t = texture(uNormalMap, at).xyz * 2.0 - 1.0;
|
|
162
|
+
t.z = t.z > 0.0 ? t.z : sqrt(max(0.0, 1.0 - dot(t.xy, t.xy)));
|
|
163
|
+
return t;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/* The tangent-space texels blended by the shares. */
|
|
167
|
+
vec3 layeredTexel() {
|
|
168
|
+
vec3 sum = vec3(0.0);
|
|
169
|
+
for (int i = 0; i < 5; i++) {
|
|
170
|
+
if (float(i) >= uLayers[1].y) break;
|
|
171
|
+
sum += layerNormalTexel(layerAt(i)) * layerShare(i);
|
|
172
|
+
}
|
|
173
|
+
float len = length(sum);
|
|
174
|
+
return len > 1e-5 ? sum / len : vec3(0.0, 0.0, 1.0);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/* Whether a normal read at the mesh's own coordinates lies under the layers. */
|
|
178
|
+
bool layeredMeshNormal() {
|
|
179
|
+
return uLayers[2].y > 0.5;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* That normal's texel: the normal array's layer past the layers, at the mesh's coordinates. */
|
|
183
|
+
vec3 layeredMeshTexel() {
|
|
184
|
+
return layerNormalTexel(vec3(vUv.xy, uLayers[1].y));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* Whether the emissive map belongs to one layer rather than to the whole surface. */
|
|
188
|
+
bool layeredGlows() {
|
|
189
|
+
return uLayers[1].z > -0.5;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/* The emissive map at its layer's place, glowing by that layer's share. */
|
|
193
|
+
vec3 layeredEmissive() {
|
|
194
|
+
int glow = int(uLayers[1].z + 0.5);
|
|
195
|
+
return texture(uEmissiveMap, layerAt(glow)).rgb * layerShare(glow);
|
|
196
|
+
}
|
|
197
|
+
`;
|
|
198
|
+
}
|