@driftengine/core 4.11.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.
Files changed (56) hide show
  1. package/CHANGELOG.json +36 -0
  2. package/README.md +1 -1
  3. package/dist/index.d.ts +2 -2
  4. package/dist/index.js +1 -1
  5. package/dist/render/ambientOcclusionPass.d.ts +3 -2
  6. package/dist/render/ambientOcclusionPass.js +4 -2
  7. package/dist/render/backend/api.d.ts +21 -1
  8. package/dist/render/backend/webgl2/renderer.d.ts +45 -4
  9. package/dist/render/backend/webgl2/renderer.js +112 -19
  10. package/dist/render/backend/webgpu/renderer.d.ts +52 -1
  11. package/dist/render/backend/webgpu/renderer.js +237 -89
  12. package/dist/render/instances.d.ts +13 -1
  13. package/dist/render/instances.js +10 -0
  14. package/dist/render/occlusionFade.d.ts +21 -0
  15. package/dist/render/occlusionFade.js +27 -0
  16. package/dist/render/sceneTarget.d.ts +2 -0
  17. package/dist/render/sceneTarget.js +1 -1
  18. package/dist/render/shaders/ambientOcclusion.js +12 -1
  19. package/dist/render/shaders/flat/index.js +9 -2
  20. package/dist/render/shaders/flat/layered.d.ts +15 -7
  21. package/dist/render/shaders/flat/layered.js +114 -31
  22. package/dist/render/shaders/flat/main.d.ts +1 -1
  23. package/dist/render/shaders/flat/main.js +10 -3
  24. package/dist/render/shaders/flat/normalMap.d.ts +1 -1
  25. package/dist/render/shaders/flat/normalMap.js +11 -2
  26. package/dist/render/shaders/generated/ambientOcclusion.wgsl.d.ts +6 -1
  27. package/dist/render/shaders/generated/ambientOcclusion.wgsl.js +6 -1
  28. package/dist/render/shaders/generated/flat.wgsl.d.ts +48 -48
  29. package/dist/render/shaders/generated/flat.wgsl.js +71 -71
  30. package/dist/render/surfaceLayers.d.ts +83 -15
  31. package/dist/render/surfaceLayers.js +69 -13
  32. package/dist/render/surfaceTexture.d.ts +13 -4
  33. package/dist/render/transmission.d.ts +21 -0
  34. package/dist/render/transmission.js +21 -0
  35. package/dist/ui/splash.d.ts +42 -2
  36. package/dist/ui/splash.js +78 -13
  37. package/package.json +6 -6
  38. package/src/index.ts +2 -0
  39. package/src/render/ambientOcclusionPass.ts +4 -1
  40. package/src/render/backend/api.ts +31 -0
  41. package/src/render/backend/webgl2/renderer.ts +130 -20
  42. package/src/render/backend/webgpu/renderer.ts +371 -139
  43. package/src/render/instances.ts +18 -1
  44. package/src/render/occlusionFade.ts +29 -0
  45. package/src/render/sceneTarget.ts +3 -0
  46. package/src/render/shaders/ambientOcclusion.ts +12 -1
  47. package/src/render/shaders/flat/index.ts +9 -2
  48. package/src/render/shaders/flat/layered.ts +114 -31
  49. package/src/render/shaders/flat/main.ts +10 -3
  50. package/src/render/shaders/flat/normalMap.ts +11 -2
  51. package/src/render/shaders/generated/ambientOcclusion.wgsl.ts +6 -1
  52. package/src/render/shaders/generated/flat.wgsl.ts +71 -71
  53. package/src/render/surfaceLayers.ts +138 -19
  54. package/src/render/surfaceTexture.ts +13 -4
  55. package/src/render/transmission.ts +27 -0
  56. package/src/ui/splash.ts +83 -14
@@ -46,9 +46,16 @@ export interface MeshInstances {
46
46
  * read by a draw whose material is a `lightmapModel` — instances of one mesh in one material sit
47
47
  * in different regions of one page, each applied before the material's own region. **They ride
48
48
  * the tint and the opacity**, which a lightmapped instance gives up (see `lightmap.ts`): where
49
- * these are given, those are not read. **A lightmapped batch needs them**, since a bake is
49
+ * these are given, the tints are not read. **A lightmapped batch needs them**, since a bake is
50
50
  * different light at each instance; one drawn without them reads its tints as its regions, and
51
51
  * either mismatch is said once on the console.
52
+ *
53
+ * **The opacity survives, in sixty-fourths**, so a lightmapped instance fades by
54
+ * `setDitherOpacity` as any other does: its transparency travels as whole steps of 2 added to the
55
+ * region's U offset, which is a place in a page and so under 1, and the vertex stage takes them
56
+ * back off (`LIGHTMAP_OPACITY_STEPS`). Sixty-four is the screen door's own resolution, an 8x8
57
+ * pattern. **What it gives up**: a fading instance's offset keeps 16 bits of fraction rather than
58
+ * 23, a sixteenth of a texel on a 4,096 page; an opaque one is written to the bit as before.
52
59
  */
53
60
  readonly lightmapRegions?: Float32Array;
54
61
  /**
@@ -109,6 +116,12 @@ export function createMeshInstances(capacity: number): MeshInstances {
109
116
  */
110
117
  export const INSTANCE_FLOATS = 20;
111
118
 
119
+ /**
120
+ * How finely a lightmapped instance's opacity travels in its region: transparency in steps of
121
+ * 1/64, each 2 on the U offset. See `MeshInstances.lightmapRegions`; the vertex stage reads it back.
122
+ */
123
+ export const LIGHTMAP_OPACITY_STEPS = 64;
124
+
112
125
  /** Bytes one instance occupies. See `INSTANCE_FLOATS`. */
113
126
  export const INSTANCE_STRIDE = INSTANCE_FLOATS * 4;
114
127
 
@@ -138,6 +151,10 @@ export function packInstances(instances: MeshInstances, out: Float32Array): void
138
151
  if (lightmapRegions !== undefined) {
139
152
  /* The region in the tint's and the opacity's place. See `lightmapRegions`. */
140
153
  for (let c = 0; c < 4; c += 1) out[at + 16 + c] = lightmapRegions[i * 4 + c] as number;
154
+ /* And the opacity's transparency in whole steps of 2 on the U offset, none where opaque. */
155
+ const alpha = Math.min(Math.max(alphas?.[i] ?? 1, 0), 1);
156
+ const steps = Math.round((1 - alpha) * LIGHTMAP_OPACITY_STEPS);
157
+ if (steps > 0) out[at + 18] = (out[at + 18] as number) + 2 * steps;
141
158
  continue;
142
159
  }
143
160
  const t = i * 3;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Where ambient occlusion fades out with distance: `setAmbientOcclusionFade`'s two numbers as the
3
+ * estimate reads them, `uFade` in `shaders/ambientOcclusion.ts`.
4
+ *
5
+ * **Occlusion is whole up to `distance` metres from the eye and gone `radius` metres past it.** A
6
+ * screen-space estimate reaches as far as the depth does, and far off the depth buffer's steps are
7
+ * metres apart: a sky dome or a mountain range kilometres away is shaded in rings that follow
8
+ * those steps, and nothing there is in contact with anything the estimate could be right about.
9
+ * A pixel past the fade skips the estimate's walk, as the sky does, so it costs less as well.
10
+ *
11
+ * **What it gives up**: distant contact shade that is real — a doorway a hundred metres off — fades
12
+ * with the rest, and the distance is the eye's and not the subject's, so a long lens fades what it
13
+ * frames close up. What would make it wrong is a scene whose far depth is precise enough to shade,
14
+ * which is a reason to fade further, not to leave the fade off.
15
+ */
16
+
17
+ /** `uFade` for no fade: a distance below zero, which the estimate reads as "never". */
18
+ const NO_FADE = -1;
19
+
20
+ /**
21
+ * `distance` and `radius` into `out`, `[distance, radius]`. A distance that is not a finite number
22
+ * at or above zero is no fade, `[−1, 0]`, so `Infinity` reads as "never fades". A radius below zero
23
+ * or not a number is 0, which is a cut at `distance` rather than a ramp.
24
+ */
25
+ export function resolveOcclusionFade(distance: number, radius: number, out: Float32Array): void {
26
+ const fades = Number.isFinite(distance) && distance >= 0;
27
+ out[0] = fades ? distance : NO_FADE;
28
+ out[1] = fades && Number.isFinite(radius) ? Math.max(radius, 0) : 0;
29
+ }
@@ -800,6 +800,8 @@ export class SceneTarget {
800
800
  /** The projection's x and y scales, which turn a world radius into a screen one. */
801
801
  readonly projScale: Float32Array;
802
802
  readonly invProjection: Float32Array;
803
+ /** Where it fades out with distance, `uFade`: see `occlusionFade.ts`. */
804
+ readonly fade: Float32Array;
803
805
  /** Where the frame's jitter put the occlusion, in uv: see the shader's `uAoOffset`. */
804
806
  readonly offset?: Float32Array;
805
807
  },
@@ -959,6 +961,7 @@ export class SceneTarget {
959
961
  ao.radius,
960
962
  ao.projScale,
961
963
  ao.invProjection,
964
+ ao.fade,
962
965
  );
963
966
  }
964
967
 
@@ -76,6 +76,11 @@ uniform vec2 uProjScale;
76
76
  uniform mat4 uInvProjection;
77
77
  /** How far the sampling reaches, in metres of the world. */
78
78
  uniform float uRadius;
79
+ /**
80
+ * Where occlusion fades out: whole up to x metres from the eye, gone y metres past it, and no fade
81
+ * where x is below zero. See occlusionFade.ts.
82
+ */
83
+ uniform vec2 uFade;
79
84
 
80
85
  out float fragColor;
81
86
 
@@ -168,6 +173,12 @@ void main() {
168
173
  fragColor = 1.0;
169
174
  return;
170
175
  }
176
+ /* How far past the caller's fade this pixel is, and open with no walk where it is all the way. */
177
+ float faded = uFade.x < 0.0 ? 0.0 : clamp((length(p) - uFade.x) / max(uFade.y, 1e-4), 0.0, 1.0);
178
+ if (faded >= 1.0) {
179
+ fragColor = AO_STORE;
180
+ return;
181
+ }
171
182
 
172
183
  /*
173
184
  * At a silhouette, the quad derivative above is replaced by the one-sided difference that
@@ -308,7 +319,7 @@ void main() {
308
319
  visible += projectedLength * gtaoArc(h0, h1, normalAngle);
309
320
  }
310
321
 
311
- fragColor = clamp(visible / float(AO_SLICES) * AO_STORE, 0.0, 1.0);
322
+ fragColor = clamp(mix(visible / float(AO_SLICES), 1.0, faded) * AO_STORE, 0.0, 1.0);
312
323
  }
313
324
  `;
314
325
 
@@ -18,6 +18,7 @@ import { modelsGlsl } from './models.ts';
18
18
  import { AREA_LIGHT_GLSL } from './areaLight.ts';
19
19
  import { NORMAL_MAP_GLSL } from './normalMap.ts';
20
20
  import { DRIFT_LIGHT_GLSL } from './driftLight.ts';
21
+ import { LIGHTMAP_OPACITY_STEPS } from '../../instances.ts';
21
22
  import { SURFACE_EFFECTS_GLSL } from './surfaceEffects.ts';
22
23
  import { overlayGlsl } from './overlay.ts';
23
24
  import { reflectionSurfaceGlsl } from './reflectionSurface.ts';
@@ -342,10 +343,16 @@ void main() {
342
343
  vAlpha = aInstanceTint.w;
343
344
  vThickness = 1.0;
344
345
  if (LIGHTMAP_REGIONS) {
346
+ /*
347
+ * The opacity's transparency rides the U offset as whole steps of 2, the offset itself under 1
348
+ * (\`MeshInstances.lightmapRegions\`): taken off, and none where the instance is opaque.
349
+ */
350
+ float steps = floor(aInstanceTint.z * 0.5);
351
+ float offsetU = aInstanceTint.z - 2.0 * steps;
345
352
  /* Out of the lane's \`-1 - uv\`, through the region, and back into it. See lightmap.ts. */
346
- vGrain = -1.0 - ((-1.0 - aGrain) * aInstanceTint.x + aInstanceTint.z);
353
+ vGrain = -1.0 - ((-1.0 - aGrain) * aInstanceTint.x + offsetU);
347
354
  vRelief = -1.0 - ((-1.0 - aRelief) * aInstanceTint.y + aInstanceTint.w);
348
- vAlpha = 1.0;
355
+ vAlpha = 1.0 - steps / ${LIGHTMAP_OPACITY_STEPS.toFixed(1)};
349
356
  }
350
357
  #else
351
358
  vSkyDirect = aChannel.y;
@@ -2,19 +2,27 @@
2
2
  * A material blended from layers by a mask: `SurfaceMaterial.layers`, packed into `uLayers` by
3
3
  * `surfaceLayers.ts`.
4
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.
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.
11
19
  *
12
20
  * **Every layer present is read, whatever its share.** A read whose level is implicit is legal only
13
21
  * in uniform control flow (AGENTS.md 2026-08-07), so the loop is bounded by the layer count, a
14
22
  * uniform, and never skips on a share, which is the pixel's own.
15
23
  *
16
24
  * **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.
25
+ * it keeps its id. Compiled out, its four vectors are not declared and its calls are constants.
18
26
  */
19
27
  export function layeredGlsl(on: boolean): string {
20
28
  if (!on) {
@@ -22,9 +30,12 @@ export function layeredGlsl(on: boolean): string {
22
30
  const bool LAYERED = false; // wgsl:override
23
31
  bool layered() { return false; }
24
32
  void layeredBegin() {}
33
+ vec3 layeredColor(vec3 color) { return color; }
25
34
  vec4 layeredAlbedo() { return vec4(1.0); }
26
35
  vec3 layeredOrm() { return vec3(1.0); }
27
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); }
28
39
  bool layeredGlows() { return false; }
29
40
  vec3 layeredEmissive() { return vec3(0.0); }
30
41
  `;
@@ -32,39 +43,96 @@ vec3 layeredEmissive() { return vec3(0.0); }
32
43
  return /* glsl */ `
33
44
  /* Whether this program blends a material's layers. Off but where one has asked: layered.ts. */
34
45
  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
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
37
54
 
38
- /* The mask's channels that lay a layer, zero past the count: read once, in layeredBegin. */
39
- vec4 lMask;
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;
40
58
 
41
59
  bool layered() {
42
60
  return uLayers[1].y > 0.5;
43
61
  }
44
62
 
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);
63
+ float layerRepeat(int i) {
64
+ return i < 4 ? uLayers[0][i] : uLayers[1].x;
50
65
  }
51
66
 
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];
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;
58
114
  }
59
- return keep;
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;
60
126
  }
61
127
 
62
- float layerRepeat(int i) {
63
- return i < 4 ? uLayers[0][i] : uLayers[1].x;
128
+ float layerShare(int i) {
129
+ if (i == 0) return lShareBase;
130
+ return lShare[i - 1];
64
131
  }
65
132
 
66
- vec3 layerAt(int i) {
67
- return vec3(vUv.xy * layerRepeat(i), float(i));
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;
68
136
  }
69
137
 
70
138
  /* Colour by the shares, and the base layer's coverage, which a cutout reads. */
@@ -88,19 +156,34 @@ vec3 layeredOrm() {
88
156
  return sum;
89
157
  }
90
158
 
91
- /* The tangent-space texels blended, each with its z rebuilt where a two-channel map stored none. */
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. */
92
167
  vec3 layeredTexel() {
93
168
  vec3 sum = vec3(0.0);
94
169
  for (int i = 0; i < 5; i++) {
95
170
  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);
171
+ sum += layerNormalTexel(layerAt(i)) * layerShare(i);
99
172
  }
100
173
  float len = length(sum);
101
174
  return len > 1e-5 ? sum / len : vec3(0.0, 0.0, 1.0);
102
175
  }
103
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
+
104
187
  /* Whether the emissive map belongs to one layer rather than to the whole surface. */
105
188
  bool layeredGlows() {
106
189
  return uLayers[1].z > -0.5;
@@ -102,6 +102,8 @@ export const MAIN_GLSL = `void main() {
102
102
  }
103
103
  }
104
104
  }
105
+ /* A layered material whose mask is the vertex colour takes no tint from it: layered.ts. */
106
+ if (LAYERED && layered()) albedo = layeredColor(albedo);
105
107
  albedo *= texel.rgb;
106
108
  coverage = kept;
107
109
  }
@@ -172,7 +174,8 @@ export const MAIN_GLSL = `void main() {
172
174
  /*
173
175
  * **A thin surface lets light from behind through, coloured by itself**: a banner, a leaf, a
174
176
  * 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.
177
+ * gathers the light behind it in glassGlow, as a pane does, and adds it through its own colour,
178
+ * or through the \`transmissionColor\` it names in the tint's colour lanes (transmission.ts).
176
179
  * None on glass, which has its own way through.
177
180
  */
178
181
  float thin = glassTransmission > 0.0 ? 0.0 : uGlassTint.w;
@@ -1957,8 +1960,12 @@ export const MAIN_GLSL = `void main() {
1957
1960
  * underneath: a ceiling emitting a dull warm haze over a pale panel cannot be expressed
1958
1961
  * by scaling the panel, and scaling it gets the brightness right and the hue wrong.
1959
1962
  */
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));
1963
+ /* What a thin surface lets through from behind, in its own colour or the one it names (below
1964
+ zero: none named, transmission.ts); a metal lets none. */
1965
+ if (thin > 0.0) {
1966
+ vec3 through = uGlassTint.r < 0.0 ? albedo : uGlassTint.rgb;
1967
+ lit += through * glassGlow * (thin * (1.0 - metal));
1968
+ }
1962
1969
  diffuseAlone = lit;
1963
1970
  vec3 emissiveTint = vEmissiveColor.r < 0.0 ? albedo : vEmissiveColor;
1964
1971
  /*
@@ -15,9 +15,18 @@ export const NORMAL_MAP_GLSL = /* glsl */ `
15
15
  vec3 normalMapped(vec3 n, vec3 at, vec2 frameAt, int hasTangents) {
16
16
  /* On three planes where the material projects them so: worldUv.ts. */
17
17
  if (WORLD_UVS && triplanar()) return triplanarNormal(n, at.z);
18
- /* A layered material's texels, blended by its mask before the frame turns them: layered.ts. */
18
+ /*
19
+ * A layered material's texels, blended by its mask before the frame turns them, around a normal
20
+ * read at the mesh's own coordinates and tangents where the material lays one under them, which is
21
+ * the surface's large shape the layers' detail rides on: layered.ts.
22
+ */
19
23
  if (LAYERED && layered()) {
20
- mat3 layeredFrame = tangentFrame(n, vWorldPos, frameAt, vTangent, hasTangents);
24
+ vec3 under = n;
25
+ if (layeredMeshNormal()) {
26
+ mat3 meshFrame = tangentFrame(n, vWorldPos, vUv.xy, vTangent, vHasTangents);
27
+ under = normalize(meshFrame * layeredMeshTexel());
28
+ }
29
+ mat3 layeredFrame = tangentFrame(under, vWorldPos, frameAt, vTangent, hasTangents);
21
30
  return normalize(mix(n, normalize(layeredFrame * layeredTexel()), uNormalStrength));
22
31
  }
23
32
  vec3 mapped = texture(uNormalMap, at).xyz * 2.0 - 1.0;
@@ -7,7 +7,7 @@
7
7
 
8
8
  export const AO_BLUR_FRAG_WGSL = "struct Uniforms {\n uStep: vec2<f32>,\n uDepthToViewZ: vec4<f32>,\n uAoScale: f32,\n}\n\n@group(0) @binding(1) \nvar<uniform> unnamed: Uniforms;\n@group(0) @binding(34) \nvar uDepth_t: texture_2d<f32>;\n@group(0) @binding(35) \nvar uDepth_s: sampler;\nvar<private> vUv_1: vec2<f32>;\n@group(0) @binding(32) \nvar uAo_t: texture_2d<f32>;\n@group(0) @binding(33) \nvar uAo_s: sampler;\nvar<private> fragColor: f32;\n\nfn viewZ_u0028_f1_u003b(depth: ptr<function, f32>) -> f32 {\n var ndc: f32;\n\n let _e22 = (*depth);\n ndc = (1f - (_e22 * 2f));\n let _e27 = unnamed.uDepthToViewZ[0u];\n let _e28 = ndc;\n let _e32 = unnamed.uDepthToViewZ[1u];\n let _e36 = unnamed.uDepthToViewZ[2u];\n let _e37 = ndc;\n let _e41 = unnamed.uDepthToViewZ[3u];\n return (((_e27 * _e28) + _e32) / ((_e36 * _e37) + _e41));\n}\n\nfn main_1() {\n var depth_1: f32;\n var own: f32;\n var centre: f32;\n var param: f32;\n var inverse: f32;\n var stepAfter: f32;\n var param_1: f32;\n var stepBefore: f32;\n var param_2: f32;\n var slope: f32;\n var sum: f32;\n var weight: f32;\n var i: i32;\n var uv: vec2<f32>;\n var z: f32;\n var param_3: f32;\n var w: f32;\n var blurred: f32;\n var local: f32;\n\n let _e39 = vUv_1;\n let _e40 = textureSampleLevel(uDepth_t, uDepth_s, _e39, 0f);\n depth_1 = _e40.x;\n let _e42 = vUv_1;\n let _e43 = textureSampleLevel(uAo_t, uAo_s, _e42, 0f);\n own = _e43.x;\n let _e45 = depth_1;\n if (_e45 <= 0f) {\n let _e47 = own;\n let _e49 = unnamed.uAoScale;\n fragColor = min((_e47 * _e49), 1f);\n return;\n }\n let _e52 = depth_1;\n param = _e52;\n let _e53 = viewZ_u0028_f1_u003b((&param));\n centre = _e53;\n let _e54 = centre;\n inverse = (1f / _e54);\n let _e56 = vUv_1;\n let _e58 = unnamed.uStep;\n let _e60 = textureSampleLevel(uDepth_t, uDepth_s, (_e56 + _e58), 0f);\n param_1 = _e60.x;\n let _e62 = viewZ_u0028_f1_u003b((&param_1));\n let _e64 = inverse;\n stepAfter = ((1f / _e62) - _e64);\n let _e66 = inverse;\n let _e67 = vUv_1;\n let _e69 = unnamed.uStep;\n let _e71 = textureSampleLevel(uDepth_t, uDepth_s, (_e67 - _e69), 0f);\n param_2 = _e71.x;\n let _e73 = viewZ_u0028_f1_u003b((&param_2));\n stepBefore = (_e66 - (1f / _e73));\n let _e76 = stepAfter;\n let _e78 = stepBefore;\n let _e81 = stepAfter;\n let _e82 = stepBefore;\n slope = select(_e82, _e81, (abs(_e76) < abs(_e78)));\n sum = 0f;\n weight = 0f;\n i = -3i;\n loop {\n let _e84 = i;\n if (_e84 <= 4i) {\n let _e86 = vUv_1;\n let _e88 = unnamed.uStep;\n let _e89 = i;\n uv = (_e86 + (_e88 * f32(_e89)));\n let _e93 = uv;\n let _e94 = textureSampleLevel(uDepth_t, uDepth_s, _e93, 0f);\n param_3 = _e94.x;\n let _e96 = viewZ_u0028_f1_u003b((&param_3));\n z = _e96;\n let _e97 = z;\n let _e98 = inverse;\n let _e99 = slope;\n let _e100 = i;\n w = max(0f, (1f - (abs(((_e97 * (_e98 + (_e99 * f32(_e100)))) - 1f)) / 0.02f)));\n let _e110 = uv;\n let _e111 = textureSampleLevel(uAo_t, uAo_s, _e110, 0f);\n let _e113 = w;\n let _e115 = sum;\n sum = (_e115 + (_e111.x * _e113));\n let _e117 = w;\n let _e118 = weight;\n weight = (_e118 + _e117);\n continue;\n } else {\n break;\n }\n continuing {\n let _e120 = i;\n i = (_e120 + 1i);\n }\n }\n let _e122 = weight;\n if (_e122 > 0f) {\n let _e124 = sum;\n let _e125 = weight;\n local = (_e124 / _e125);\n } else {\n let _e127 = own;\n local = _e127;\n }\n let _e128 = local;\n blurred = _e128;\n let _e129 = blurred;\n let _e131 = unnamed.uAoScale;\n fragColor = min((_e129 * _e131), 1f);\n return;\n}\n\n@fragment \nfn main(@location(0) vUv: vec2<f32>) -> @location(0) f32 {\n vUv_1 = vUv;\n main_1();\n let _e3 = fragColor;\n return _e3;\n}\n";
9
9
 
10
- export const AO_FRAG_WGSL = "struct Uniforms {\n uProjScale: vec2<f32>,\n uInvProjection: mat4x4<f32>,\n uRadius: f32,\n}\n\n@group(0) @binding(1) \nvar<uniform> unnamed: Uniforms;\nvar<private> vUv_1: vec2<f32>;\n@group(0) @binding(32) \nvar uDepth_t: texture_2d<f32>;\n@group(0) @binding(33) \nvar uDepth_s: sampler;\nvar<private> fragColor: f32;\nvar<private> gl_FragCoord_1: vec4<f32>;\n\nfn gtaoArc_u0028_f1_u003b_f1_u003b_f1_u003b(h0_: ptr<function, f32>, h1_: ptr<function, f32>, normal: ptr<function, f32>) -> f32 {\n var c: f32;\n var s2_: f32;\n\n let _e40 = (*normal);\n c = cos(_e40);\n let _e42 = (*normal);\n s2_ = (2f * sin(_e42));\n let _e45 = (*h0_);\n let _e47 = (*normal);\n let _e51 = c;\n let _e53 = (*h0_);\n let _e54 = s2_;\n let _e58 = (*h1_);\n let _e60 = (*normal);\n let _e64 = c;\n let _e66 = (*h1_);\n let _e67 = s2_;\n return ((0.25f * ((-(cos(((2f * _e45) - _e47))) + _e51) + (_e53 * _e54))) + (0.25f * ((-(cos(((2f * _e58) - _e60))) + _e64) + (_e66 * _e67))));\n}\n\nfn viewPosition_u0028_vf2_u003b_f1_u003b(uv: ptr<function, vec2<f32>>, depth: ptr<function, f32>) -> vec3<f32> {\n var clip: vec4<f32>;\n var view: vec4<f32>;\n\n let _e39 = (*uv);\n let _e42 = ((_e39 * 2f) - vec2(1f));\n let _e43 = (*depth);\n clip = vec4<f32>(_e42.x, _e42.y, (1f - (_e43 * 2f)), 1f);\n let _e50 = unnamed.uInvProjection;\n let _e51 = clip;\n view = (_e50 * _e51);\n let _e53 = view;\n let _e56 = view[3u];\n return (_e53.xyz / vec3(_e56));\n}\n\nfn main_1() {\n var stepX: vec2<f32>;\n var stepY: vec2<f32>;\n var depth_1: f32;\n var p: vec3<f32>;\n var param: vec2<f32>;\n var param_1: f32;\n var dpdx_: vec3<f32>;\n var dpdy_: vec3<f32>;\n var right: vec3<f32>;\n var param_2: vec2<f32>;\n var param_3: f32;\n var left: vec3<f32>;\n var param_4: vec2<f32>;\n var param_5: f32;\n var down: vec3<f32>;\n var param_6: vec2<f32>;\n var param_7: f32;\n var up: vec3<f32>;\n var param_8: vec2<f32>;\n var param_9: f32;\n var here: f32;\n var edgeFloor: f32;\n var rightGap: f32;\n var leftGap: f32;\n var downGap: f32;\n var upGap: f32;\n var n: vec3<f32>;\n var toEye: vec3<f32>;\n var reach: vec2<f32>;\n var tile: f32;\n var turn: f32;\n var stagger: f32;\n var pixel: vec2<f32>;\n var visible: f32;\n var slice: i32;\n var angle: f32;\n var screen: vec2<f32>;\n var along: vec3<f32>;\n var param_10: vec2<f32>;\n var param_11: f32;\n var across: vec3<f32>;\n var axis: vec3<f32>;\n var projected: vec3<f32>;\n var projectedLength: f32;\n var normalAngle: f32;\n var horizon0_: f32;\n var horizon1_: f32;\n var k: i32;\n var t: f32;\n var stride: vec2<f32>;\n var side: i32;\n var uv_1: vec2<f32>;\n var local: vec2<f32>;\n var sampled: f32;\n var v: vec3<f32>;\n var param_12: vec2<f32>;\n var param_13: f32;\n var distance2_: f32;\n var cosine: f32;\n var fade: f32;\n var local_1: f32;\n var local_2: f32;\n var h0_1: f32;\n var h1_1: f32;\n var param_14: f32;\n var param_15: f32;\n var param_16: f32;\n\n let _e102 = vUv_1;\n let _e103 = dpdx(_e102);\n stepX = _e103;\n let _e104 = vUv_1;\n let _e105 = dpdy(_e104);\n stepY = _e105;\n let _e106 = vUv_1;\n let _e107 = textureSampleLevel(uDepth_t, uDepth_s, _e106, 0f);\n depth_1 = _e107.x;\n let _e109 = vUv_1;\n param = _e109;\n let _e110 = depth_1;\n param_1 = _e110;\n let _e111 = viewPosition_u0028_vf2_u003b_f1_u003b((&param), (&param_1));\n p = _e111;\n let _e112 = p;\n let _e113 = dpdx(_e112);\n dpdx_ = _e113;\n let _e114 = p;\n let _e115 = dpdy(_e114);\n dpdy_ = _e115;\n let _e116 = depth_1;\n if (_e116 <= 0f) {\n fragColor = 1f;\n return;\n }\n let _e118 = vUv_1;\n let _e119 = stepX;\n let _e121 = vUv_1;\n let _e122 = stepX;\n let _e124 = textureSampleLevel(uDepth_t, uDepth_s, (_e121 + _e122), 0f);\n param_2 = (_e118 + _e119);\n param_3 = _e124.x;\n let _e126 = viewPosition_u0028_vf2_u003b_f1_u003b((&param_2), (&param_3));\n right = _e126;\n let _e127 = vUv_1;\n let _e128 = stepX;\n let _e130 = vUv_1;\n let _e131 = stepX;\n let _e133 = textureSampleLevel(uDepth_t, uDepth_s, (_e130 - _e131), 0f);\n param_4 = (_e127 - _e128);\n param_5 = _e133.x;\n let _e135 = viewPosition_u0028_vf2_u003b_f1_u003b((&param_4), (&param_5));\n left = _e135;\n let _e136 = vUv_1;\n let _e137 = stepY;\n let _e139 = vUv_1;\n let _e140 = stepY;\n let _e142 = textureSampleLevel(uDepth_t, uDepth_s, (_e139 + _e140), 0f);\n param_6 = (_e136 + _e137);\n param_7 = _e142.x;\n let _e144 = viewPosition_u0028_vf2_u003b_f1_u003b((&param_6), (&param_7));\n down = _e144;\n let _e145 = vUv_1;\n let _e146 = stepY;\n let _e148 = vUv_1;\n let _e149 = stepY;\n let _e151 = textureSampleLevel(uDepth_t, uDepth_s, (_e148 - _e149), 0f);\n param_8 = (_e145 - _e146);\n param_9 = _e151.x;\n let _e153 = viewPosition_u0028_vf2_u003b_f1_u003b((&param_8), (&param_9));\n up = _e153;\n let _e155 = p[2u];\n here = -(_e155);\n let _e157 = here;\n edgeFloor = (0.05f * _e157);\n let _e160 = right[2u];\n let _e162 = here;\n rightGap = abs((-(_e160) - _e162));\n let _e166 = left[2u];\n let _e168 = here;\n leftGap = abs((-(_e166) - _e168));\n let _e172 = down[2u];\n let _e174 = here;\n downGap = abs((-(_e172) - _e174));\n let _e178 = up[2u];\n let _e180 = here;\n upGap = abs((-(_e178) - _e180));\n let _e183 = rightGap;\n let _e184 = leftGap;\n let _e186 = edgeFloor;\n if (_e183 > ((_e184 * 8f) + _e186)) {\n let _e189 = p;\n let _e190 = left;\n dpdx_ = (_e189 - _e190);\n } else {\n let _e192 = leftGap;\n let _e193 = rightGap;\n let _e195 = edgeFloor;\n if (_e192 > ((_e193 * 8f) + _e195)) {\n let _e198 = right;\n let _e199 = p;\n dpdx_ = (_e198 - _e199);\n }\n }\n let _e201 = downGap;\n let _e202 = upGap;\n let _e204 = edgeFloor;\n if (_e201 > ((_e202 * 8f) + _e204)) {\n let _e207 = p;\n let _e208 = up;\n dpdy_ = (_e207 - _e208);\n } else {\n let _e210 = upGap;\n let _e211 = downGap;\n let _e213 = edgeFloor;\n if (_e210 > ((_e211 * 8f) + _e213)) {\n let _e216 = down;\n let _e217 = p;\n dpdy_ = (_e216 - _e217);\n }\n }\n let _e219 = dpdx_;\n let _e220 = dpdy_;\n n = normalize(cross(_e219, _e220));\n let _e223 = n;\n let _e224 = p;\n if (dot(_e223, _e224) > 0f) {\n let _e227 = n;\n n = -(_e227);\n }\n let _e229 = p;\n toEye = normalize(-(_e229));\n let _e233 = unnamed.uRadius;\n let _e235 = unnamed.uProjScale;\n let _e239 = p[2u];\n reach = min((((_e235 * _e233) * 0.5f) / vec2(max(-(_e239), 0.001f))), vec2<f32>(0.06f, 0.06f));\n let _e246 = gl_FragCoord_1[0u];\n let _e252 = gl_FragCoord_1[1u];\n tile = ((_e246 - (floor((_e246 / 4f)) * 4f)) + (4f * (_e252 - (floor((_e252 / 4f)) * 4f))));\n let _e259 = tile;\n turn = (_e259 * 0.098174766f);\n let _e261 = tile;\n stagger = fract((_e261 * 0.618034f));\n let _e264 = stepX;\n let _e266 = stepY;\n pixel = vec2<f32>(length(_e264), length(_e266));\n visible = 0f;\n slice = 0i;\n loop {\n let _e269 = slice;\n if (_e269 < 2i) {\n let _e271 = turn;\n let _e272 = slice;\n angle = (_e271 + (f32(_e272) * 1.5707964f));\n let _e276 = angle;\n let _e279 = pixel[0u];\n let _e282 = pixel[1u];\n let _e284 = angle;\n screen = vec2<f32>(((cos(_e276) * _e279) / _e282), sin(_e284));\n let _e287 = vUv_1;\n let _e288 = screen;\n param_10 = (_e287 + (_e288 * 0.001f));\n let _e291 = depth_1;\n param_11 = _e291;\n let _e292 = viewPosition_u0028_vf2_u003b_f1_u003b((&param_10), (&param_11));\n let _e293 = p;\n along = normalize((_e292 - _e293));\n let _e296 = along;\n let _e297 = toEye;\n let _e298 = along;\n let _e299 = toEye;\n across = normalize((_e296 - (_e297 * dot(_e298, _e299))));\n let _e304 = across;\n let _e305 = toEye;\n axis = cross(_e304, _e305);\n let _e307 = n;\n let _e308 = axis;\n let _e309 = n;\n let _e310 = axis;\n projected = (_e307 - (_e308 * dot(_e309, _e310)));\n let _e314 = projected;\n projectedLength = length(_e314);\n let _e316 = projectedLength;\n if (_e316 < 0.0001f) {\n continue;\n }\n let _e318 = projected;\n let _e319 = across;\n let _e321 = projected;\n let _e322 = toEye;\n normalAngle = atan2(dot(_e318, _e319), dot(_e321, _e322));\n horizon0_ = -1f;\n horizon1_ = -1f;\n k = 0i;\n loop {\n let _e325 = k;\n if (_e325 < 6i) {\n let _e327 = k;\n let _e329 = stagger;\n t = ((f32(_e327) + _e329) / 6f);\n let _e332 = screen;\n let _e334 = reach[1u];\n let _e336 = t;\n let _e337 = t;\n stride = ((_e332 * _e334) * ((_e336 * _e337) + 0.013888889f));\n side = 0i;\n loop {\n let _e341 = side;\n if (_e341 < 2i) {\n let _e343 = side;\n if (_e343 == 0i) {\n let _e345 = vUv_1;\n let _e346 = stride;\n local = (_e345 - _e346);\n } else {\n let _e348 = vUv_1;\n let _e349 = stride;\n local = (_e348 + _e349);\n }\n let _e351 = local;\n uv_1 = _e351;\n let _e352 = uv_1;\n let _e353 = textureSampleLevel(uDepth_t, uDepth_s, _e352, 0f);\n sampled = _e353.x;\n let _e355 = sampled;\n if (_e355 <= 0f) {\n continue;\n }\n let _e357 = uv_1;\n param_12 = _e357;\n let _e358 = sampled;\n param_13 = _e358;\n let _e359 = viewPosition_u0028_vf2_u003b_f1_u003b((&param_12), (&param_13));\n let _e360 = p;\n v = (_e359 - _e360);\n let _e362 = v;\n let _e363 = v;\n distance2_ = dot(_e362, _e363);\n let _e365 = distance2_;\n if (_e365 < 0.00000001f) {\n continue;\n }\n let _e367 = v;\n let _e368 = toEye;\n let _e370 = distance2_;\n cosine = (dot(_e367, _e368) * inverseSqrt(_e370));\n let _e373 = distance2_;\n let _e375 = unnamed.uRadius;\n let _e377 = unnamed.uRadius;\n fade = clamp((1f - (_e373 / (_e375 * _e377))), 0f, 1f);\n let _e382 = cosine;\n let _e383 = fade;\n cosine = mix(-1f, _e382, _e383);\n let _e385 = side;\n if (_e385 == 0i) {\n let _e387 = cosine;\n let _e388 = horizon0_;\n if (_e387 > _e388) {\n let _e390 = cosine;\n local_1 = _e390;\n } else {\n let _e391 = horizon0_;\n let _e392 = cosine;\n local_1 = mix(_e391, _e392, 0.2f);\n }\n let _e394 = local_1;\n horizon0_ = _e394;\n } else {\n let _e395 = cosine;\n let _e396 = horizon1_;\n if (_e395 > _e396) {\n let _e398 = cosine;\n local_2 = _e398;\n } else {\n let _e399 = horizon1_;\n let _e400 = cosine;\n local_2 = mix(_e399, _e400, 0.2f);\n }\n let _e402 = local_2;\n horizon1_ = _e402;\n }\n continue;\n } else {\n break;\n }\n continuing {\n let _e403 = side;\n side = (_e403 + 1i);\n }\n }\n continue;\n } else {\n break;\n }\n continuing {\n let _e405 = k;\n k = (_e405 + 1i);\n }\n }\n let _e407 = horizon0_;\n let _e411 = normalAngle;\n h0_1 = max(-(acos(clamp(_e407, -1f, 1f))), (_e411 - 1.5707963f));\n let _e414 = horizon1_;\n let _e417 = normalAngle;\n h1_1 = min(acos(clamp(_e414, -1f, 1f)), (_e417 + 1.5707963f));\n let _e420 = projectedLength;\n let _e421 = h0_1;\n param_14 = _e421;\n let _e422 = h1_1;\n param_15 = _e422;\n let _e423 = normalAngle;\n param_16 = _e423;\n let _e424 = gtaoArc_u0028_f1_u003b_f1_u003b_f1_u003b((&param_14), (&param_15), (&param_16));\n let _e426 = visible;\n visible = (_e426 + (_e420 * _e424));\n continue;\n } else {\n break;\n }\n continuing {\n let _e428 = slice;\n slice = (_e428 + 1i);\n }\n }\n let _e430 = visible;\n fragColor = clamp(((_e430 / 2f) * 0.5f), 0f, 1f);\n return;\n}\n\n@fragment \nfn main(@location(0) vUv: vec2<f32>, @builtin(position) gl_FragCoord: vec4<f32>) -> @location(0) f32 {\n vUv_1 = vUv;\n gl_FragCoord_1 = gl_FragCoord;\n main_1();\n let _e5 = fragColor;\n return _e5;\n}\n";
10
+ export const AO_FRAG_WGSL = "struct Uniforms {\n uProjScale: vec2<f32>,\n uInvProjection: mat4x4<f32>,\n uRadius: f32,\n uFade: vec2<f32>,\n}\n\n@group(0) @binding(1) \nvar<uniform> unnamed: Uniforms;\nvar<private> vUv_1: vec2<f32>;\n@group(0) @binding(32) \nvar uDepth_t: texture_2d<f32>;\n@group(0) @binding(33) \nvar uDepth_s: sampler;\nvar<private> fragColor: f32;\nvar<private> gl_FragCoord_1: vec4<f32>;\n\nfn gtaoArc_u0028_f1_u003b_f1_u003b_f1_u003b(h0_: ptr<function, f32>, h1_: ptr<function, f32>, normal: ptr<function, f32>) -> f32 {\n var c: f32;\n var s2_: f32;\n\n let _e41 = (*normal);\n c = cos(_e41);\n let _e43 = (*normal);\n s2_ = (2f * sin(_e43));\n let _e46 = (*h0_);\n let _e48 = (*normal);\n let _e52 = c;\n let _e54 = (*h0_);\n let _e55 = s2_;\n let _e59 = (*h1_);\n let _e61 = (*normal);\n let _e65 = c;\n let _e67 = (*h1_);\n let _e68 = s2_;\n return ((0.25f * ((-(cos(((2f * _e46) - _e48))) + _e52) + (_e54 * _e55))) + (0.25f * ((-(cos(((2f * _e59) - _e61))) + _e65) + (_e67 * _e68))));\n}\n\nfn viewPosition_u0028_vf2_u003b_f1_u003b(uv: ptr<function, vec2<f32>>, depth: ptr<function, f32>) -> vec3<f32> {\n var clip: vec4<f32>;\n var view: vec4<f32>;\n\n let _e40 = (*uv);\n let _e43 = ((_e40 * 2f) - vec2(1f));\n let _e44 = (*depth);\n clip = vec4<f32>(_e43.x, _e43.y, (1f - (_e44 * 2f)), 1f);\n let _e51 = unnamed.uInvProjection;\n let _e52 = clip;\n view = (_e51 * _e52);\n let _e54 = view;\n let _e57 = view[3u];\n return (_e54.xyz / vec3(_e57));\n}\n\nfn main_1() {\n var stepX: vec2<f32>;\n var stepY: vec2<f32>;\n var depth_1: f32;\n var p: vec3<f32>;\n var param: vec2<f32>;\n var param_1: f32;\n var dpdx_: vec3<f32>;\n var dpdy_: vec3<f32>;\n var faded: f32;\n var local: f32;\n var right: vec3<f32>;\n var param_2: vec2<f32>;\n var param_3: f32;\n var left: vec3<f32>;\n var param_4: vec2<f32>;\n var param_5: f32;\n var down: vec3<f32>;\n var param_6: vec2<f32>;\n var param_7: f32;\n var up: vec3<f32>;\n var param_8: vec2<f32>;\n var param_9: f32;\n var here: f32;\n var edgeFloor: f32;\n var rightGap: f32;\n var leftGap: f32;\n var downGap: f32;\n var upGap: f32;\n var n: vec3<f32>;\n var toEye: vec3<f32>;\n var reach: vec2<f32>;\n var tile: f32;\n var turn: f32;\n var stagger: f32;\n var pixel: vec2<f32>;\n var visible: f32;\n var slice: i32;\n var angle: f32;\n var screen: vec2<f32>;\n var along: vec3<f32>;\n var param_10: vec2<f32>;\n var param_11: f32;\n var across: vec3<f32>;\n var axis: vec3<f32>;\n var projected: vec3<f32>;\n var projectedLength: f32;\n var normalAngle: f32;\n var horizon0_: f32;\n var horizon1_: f32;\n var k: i32;\n var t: f32;\n var stride: vec2<f32>;\n var side: i32;\n var uv_1: vec2<f32>;\n var local_1: vec2<f32>;\n var sampled: f32;\n var v: vec3<f32>;\n var param_12: vec2<f32>;\n var param_13: f32;\n var distance2_: f32;\n var cosine: f32;\n var fade: f32;\n var local_2: f32;\n var local_3: f32;\n var h0_1: f32;\n var h1_1: f32;\n var param_14: f32;\n var param_15: f32;\n var param_16: f32;\n\n let _e105 = vUv_1;\n let _e106 = dpdx(_e105);\n stepX = _e106;\n let _e107 = vUv_1;\n let _e108 = dpdy(_e107);\n stepY = _e108;\n let _e109 = vUv_1;\n let _e110 = textureSampleLevel(uDepth_t, uDepth_s, _e109, 0f);\n depth_1 = _e110.x;\n let _e112 = vUv_1;\n param = _e112;\n let _e113 = depth_1;\n param_1 = _e113;\n let _e114 = viewPosition_u0028_vf2_u003b_f1_u003b((&param), (&param_1));\n p = _e114;\n let _e115 = p;\n let _e116 = dpdx(_e115);\n dpdx_ = _e116;\n let _e117 = p;\n let _e118 = dpdy(_e117);\n dpdy_ = _e118;\n let _e119 = depth_1;\n if (_e119 <= 0f) {\n fragColor = 1f;\n return;\n }\n let _e123 = unnamed.uFade[0u];\n if (_e123 < 0f) {\n local = 0f;\n } else {\n let _e125 = p;\n let _e129 = unnamed.uFade[0u];\n let _e133 = unnamed.uFade[1u];\n local = clamp(((length(_e125) - _e129) / max(_e133, 0.0001f)), 0f, 1f);\n }\n let _e137 = local;\n faded = _e137;\n let _e138 = faded;\n if (_e138 >= 1f) {\n fragColor = 0.5f;\n return;\n }\n let _e140 = vUv_1;\n let _e141 = stepX;\n let _e143 = vUv_1;\n let _e144 = stepX;\n let _e146 = textureSampleLevel(uDepth_t, uDepth_s, (_e143 + _e144), 0f);\n param_2 = (_e140 + _e141);\n param_3 = _e146.x;\n let _e148 = viewPosition_u0028_vf2_u003b_f1_u003b((&param_2), (&param_3));\n right = _e148;\n let _e149 = vUv_1;\n let _e150 = stepX;\n let _e152 = vUv_1;\n let _e153 = stepX;\n let _e155 = textureSampleLevel(uDepth_t, uDepth_s, (_e152 - _e153), 0f);\n param_4 = (_e149 - _e150);\n param_5 = _e155.x;\n let _e157 = viewPosition_u0028_vf2_u003b_f1_u003b((&param_4), (&param_5));\n left = _e157;\n let _e158 = vUv_1;\n let _e159 = stepY;\n let _e161 = vUv_1;\n let _e162 = stepY;\n let _e164 = textureSampleLevel(uDepth_t, uDepth_s, (_e161 + _e162), 0f);\n param_6 = (_e158 + _e159);\n param_7 = _e164.x;\n let _e166 = viewPosition_u0028_vf2_u003b_f1_u003b((&param_6), (&param_7));\n down = _e166;\n let _e167 = vUv_1;\n let _e168 = stepY;\n let _e170 = vUv_1;\n let _e171 = stepY;\n let _e173 = textureSampleLevel(uDepth_t, uDepth_s, (_e170 - _e171), 0f);\n param_8 = (_e167 - _e168);\n param_9 = _e173.x;\n let _e175 = viewPosition_u0028_vf2_u003b_f1_u003b((&param_8), (&param_9));\n up = _e175;\n let _e177 = p[2u];\n here = -(_e177);\n let _e179 = here;\n edgeFloor = (0.05f * _e179);\n let _e182 = right[2u];\n let _e184 = here;\n rightGap = abs((-(_e182) - _e184));\n let _e188 = left[2u];\n let _e190 = here;\n leftGap = abs((-(_e188) - _e190));\n let _e194 = down[2u];\n let _e196 = here;\n downGap = abs((-(_e194) - _e196));\n let _e200 = up[2u];\n let _e202 = here;\n upGap = abs((-(_e200) - _e202));\n let _e205 = rightGap;\n let _e206 = leftGap;\n let _e208 = edgeFloor;\n if (_e205 > ((_e206 * 8f) + _e208)) {\n let _e211 = p;\n let _e212 = left;\n dpdx_ = (_e211 - _e212);\n } else {\n let _e214 = leftGap;\n let _e215 = rightGap;\n let _e217 = edgeFloor;\n if (_e214 > ((_e215 * 8f) + _e217)) {\n let _e220 = right;\n let _e221 = p;\n dpdx_ = (_e220 - _e221);\n }\n }\n let _e223 = downGap;\n let _e224 = upGap;\n let _e226 = edgeFloor;\n if (_e223 > ((_e224 * 8f) + _e226)) {\n let _e229 = p;\n let _e230 = up;\n dpdy_ = (_e229 - _e230);\n } else {\n let _e232 = upGap;\n let _e233 = downGap;\n let _e235 = edgeFloor;\n if (_e232 > ((_e233 * 8f) + _e235)) {\n let _e238 = down;\n let _e239 = p;\n dpdy_ = (_e238 - _e239);\n }\n }\n let _e241 = dpdx_;\n let _e242 = dpdy_;\n n = normalize(cross(_e241, _e242));\n let _e245 = n;\n let _e246 = p;\n if (dot(_e245, _e246) > 0f) {\n let _e249 = n;\n n = -(_e249);\n }\n let _e251 = p;\n toEye = normalize(-(_e251));\n let _e255 = unnamed.uRadius;\n let _e257 = unnamed.uProjScale;\n let _e261 = p[2u];\n reach = min((((_e257 * _e255) * 0.5f) / vec2(max(-(_e261), 0.001f))), vec2<f32>(0.06f, 0.06f));\n let _e268 = gl_FragCoord_1[0u];\n let _e274 = gl_FragCoord_1[1u];\n tile = ((_e268 - (floor((_e268 / 4f)) * 4f)) + (4f * (_e274 - (floor((_e274 / 4f)) * 4f))));\n let _e281 = tile;\n turn = (_e281 * 0.098174766f);\n let _e283 = tile;\n stagger = fract((_e283 * 0.618034f));\n let _e286 = stepX;\n let _e288 = stepY;\n pixel = vec2<f32>(length(_e286), length(_e288));\n visible = 0f;\n slice = 0i;\n loop {\n let _e291 = slice;\n if (_e291 < 2i) {\n let _e293 = turn;\n let _e294 = slice;\n angle = (_e293 + (f32(_e294) * 1.5707964f));\n let _e298 = angle;\n let _e301 = pixel[0u];\n let _e304 = pixel[1u];\n let _e306 = angle;\n screen = vec2<f32>(((cos(_e298) * _e301) / _e304), sin(_e306));\n let _e309 = vUv_1;\n let _e310 = screen;\n param_10 = (_e309 + (_e310 * 0.001f));\n let _e313 = depth_1;\n param_11 = _e313;\n let _e314 = viewPosition_u0028_vf2_u003b_f1_u003b((&param_10), (&param_11));\n let _e315 = p;\n along = normalize((_e314 - _e315));\n let _e318 = along;\n let _e319 = toEye;\n let _e320 = along;\n let _e321 = toEye;\n across = normalize((_e318 - (_e319 * dot(_e320, _e321))));\n let _e326 = across;\n let _e327 = toEye;\n axis = cross(_e326, _e327);\n let _e329 = n;\n let _e330 = axis;\n let _e331 = n;\n let _e332 = axis;\n projected = (_e329 - (_e330 * dot(_e331, _e332)));\n let _e336 = projected;\n projectedLength = length(_e336);\n let _e338 = projectedLength;\n if (_e338 < 0.0001f) {\n continue;\n }\n let _e340 = projected;\n let _e341 = across;\n let _e343 = projected;\n let _e344 = toEye;\n normalAngle = atan2(dot(_e340, _e341), dot(_e343, _e344));\n horizon0_ = -1f;\n horizon1_ = -1f;\n k = 0i;\n loop {\n let _e347 = k;\n if (_e347 < 6i) {\n let _e349 = k;\n let _e351 = stagger;\n t = ((f32(_e349) + _e351) / 6f);\n let _e354 = screen;\n let _e356 = reach[1u];\n let _e358 = t;\n let _e359 = t;\n stride = ((_e354 * _e356) * ((_e358 * _e359) + 0.013888889f));\n side = 0i;\n loop {\n let _e363 = side;\n if (_e363 < 2i) {\n let _e365 = side;\n if (_e365 == 0i) {\n let _e367 = vUv_1;\n let _e368 = stride;\n local_1 = (_e367 - _e368);\n } else {\n let _e370 = vUv_1;\n let _e371 = stride;\n local_1 = (_e370 + _e371);\n }\n let _e373 = local_1;\n uv_1 = _e373;\n let _e374 = uv_1;\n let _e375 = textureSampleLevel(uDepth_t, uDepth_s, _e374, 0f);\n sampled = _e375.x;\n let _e377 = sampled;\n if (_e377 <= 0f) {\n continue;\n }\n let _e379 = uv_1;\n param_12 = _e379;\n let _e380 = sampled;\n param_13 = _e380;\n let _e381 = viewPosition_u0028_vf2_u003b_f1_u003b((&param_12), (&param_13));\n let _e382 = p;\n v = (_e381 - _e382);\n let _e384 = v;\n let _e385 = v;\n distance2_ = dot(_e384, _e385);\n let _e387 = distance2_;\n if (_e387 < 0.00000001f) {\n continue;\n }\n let _e389 = v;\n let _e390 = toEye;\n let _e392 = distance2_;\n cosine = (dot(_e389, _e390) * inverseSqrt(_e392));\n let _e395 = distance2_;\n let _e397 = unnamed.uRadius;\n let _e399 = unnamed.uRadius;\n fade = clamp((1f - (_e395 / (_e397 * _e399))), 0f, 1f);\n let _e404 = cosine;\n let _e405 = fade;\n cosine = mix(-1f, _e404, _e405);\n let _e407 = side;\n if (_e407 == 0i) {\n let _e409 = cosine;\n let _e410 = horizon0_;\n if (_e409 > _e410) {\n let _e412 = cosine;\n local_2 = _e412;\n } else {\n let _e413 = horizon0_;\n let _e414 = cosine;\n local_2 = mix(_e413, _e414, 0.2f);\n }\n let _e416 = local_2;\n horizon0_ = _e416;\n } else {\n let _e417 = cosine;\n let _e418 = horizon1_;\n if (_e417 > _e418) {\n let _e420 = cosine;\n local_3 = _e420;\n } else {\n let _e421 = horizon1_;\n let _e422 = cosine;\n local_3 = mix(_e421, _e422, 0.2f);\n }\n let _e424 = local_3;\n horizon1_ = _e424;\n }\n continue;\n } else {\n break;\n }\n continuing {\n let _e425 = side;\n side = (_e425 + 1i);\n }\n }\n continue;\n } else {\n break;\n }\n continuing {\n let _e427 = k;\n k = (_e427 + 1i);\n }\n }\n let _e429 = horizon0_;\n let _e433 = normalAngle;\n h0_1 = max(-(acos(clamp(_e429, -1f, 1f))), (_e433 - 1.5707963f));\n let _e436 = horizon1_;\n let _e439 = normalAngle;\n h1_1 = min(acos(clamp(_e436, -1f, 1f)), (_e439 + 1.5707963f));\n let _e442 = projectedLength;\n let _e443 = h0_1;\n param_14 = _e443;\n let _e444 = h1_1;\n param_15 = _e444;\n let _e445 = normalAngle;\n param_16 = _e445;\n let _e446 = gtaoArc_u0028_f1_u003b_f1_u003b_f1_u003b((&param_14), (&param_15), (&param_16));\n let _e448 = visible;\n visible = (_e448 + (_e442 * _e446));\n continue;\n } else {\n break;\n }\n continuing {\n let _e450 = slice;\n slice = (_e450 + 1i);\n }\n }\n let _e452 = visible;\n let _e454 = faded;\n fragColor = clamp((mix((_e452 / 2f), 1f, _e454) * 0.5f), 0f, 1f);\n return;\n}\n\n@fragment \nfn main(@location(0) vUv: vec2<f32>, @builtin(position) gl_FragCoord: vec4<f32>) -> @location(0) f32 {\n vUv_1 = vUv;\n gl_FragCoord_1 = gl_FragCoord;\n main_1();\n let _e5 = fragColor;\n return _e5;\n}\n";
11
11
 
12
12
  /**
13
13
  * What the transform assigned, so the renderer binds the same numbers.
@@ -66,6 +66,11 @@ export const AMBIENTOCCLUSION_BINDINGS = {
66
66
  "offset": 80,
67
67
  "size": 4,
68
68
  "type": "float"
69
+ },
70
+ "uFade": {
71
+ "offset": 88,
72
+ "size": 8,
73
+ "type": "vec2"
69
74
  }
70
75
  },
71
76
  "textures": {