@driftengine/core 4.4.2 → 4.5.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.
Files changed (193) hide show
  1. package/CHANGELOG.json +46 -0
  2. package/README.md +1 -1
  3. package/dist/index.d.ts +5 -1
  4. package/dist/index.js +7 -0
  5. package/dist/render/backend/api.d.ts +17 -11
  6. package/dist/render/backend/webgl2/glassTintPrograms.d.ts +13 -0
  7. package/dist/render/backend/webgl2/glassTintPrograms.js +38 -0
  8. package/dist/render/backend/webgl2/renderer.d.ts +79 -8
  9. package/dist/render/backend/webgl2/renderer.js +360 -105
  10. package/dist/render/backend/webgpu/buffers.d.ts +19 -1
  11. package/dist/render/backend/webgpu/buffers.js +27 -1
  12. package/dist/render/backend/webgpu/depthPass.d.ts +1 -1
  13. package/dist/render/backend/webgpu/depthPass.js +2 -1
  14. package/dist/render/backend/webgpu/flatPass.d.ts +2 -0
  15. package/dist/render/backend/webgpu/flatPass.js +20 -1
  16. package/dist/render/backend/webgpu/glassTintPass.d.ts +28 -0
  17. package/dist/render/backend/webgpu/glassTintPass.js +205 -0
  18. package/dist/render/backend/webgpu/gpuDrivenPass.d.ts +60 -1
  19. package/dist/render/backend/webgpu/gpuDrivenPass.js +305 -5
  20. package/dist/render/backend/webgpu/instanced.d.ts +12 -1
  21. package/dist/render/backend/webgpu/instanced.js +20 -1
  22. package/dist/render/backend/webgpu/latePass.d.ts +26 -0
  23. package/dist/render/backend/webgpu/latePass.js +133 -0
  24. package/dist/render/backend/webgpu/lightVolumePass.js +5 -1
  25. package/dist/render/backend/webgpu/motionPass.d.ts +95 -0
  26. package/dist/render/backend/webgpu/motionPass.js +491 -0
  27. package/dist/render/backend/webgpu/pipelineCache.d.ts +10 -1
  28. package/dist/render/backend/webgpu/pipelineCache.js +11 -1
  29. package/dist/render/backend/webgpu/pointGlassTintPass.d.ts +32 -0
  30. package/dist/render/backend/webgpu/pointGlassTintPass.js +193 -0
  31. package/dist/render/backend/webgpu/pointShadowPass.d.ts +35 -6
  32. package/dist/render/backend/webgpu/pointShadowPass.js +105 -20
  33. package/dist/render/backend/webgpu/postPass.d.ts +2 -12
  34. package/dist/render/backend/webgpu/renderer.d.ts +171 -54
  35. package/dist/render/backend/webgpu/renderer.js +944 -390
  36. package/dist/render/backend/webgpu/scatterPass.js +2 -1
  37. package/dist/render/backend/webgpu/skinPaletteRing.d.ts +4 -0
  38. package/dist/render/backend/webgpu/skinPaletteRing.js +4 -0
  39. package/dist/render/backend/webgpu/snapshotMips.d.ts +29 -0
  40. package/dist/render/backend/webgpu/snapshotMips.js +52 -0
  41. package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +5 -0
  42. package/dist/render/backend/webgpu/surfaceTexturePass.js +1 -1
  43. package/dist/render/glass.d.ts +45 -0
  44. package/dist/render/glass.js +39 -0
  45. package/dist/render/glassCasters.d.ts +40 -0
  46. package/dist/render/glassCasters.js +84 -0
  47. package/dist/render/glassShadow.d.ts +78 -0
  48. package/dist/render/glassShadow.js +125 -0
  49. package/dist/render/globalMediumPass.d.ts +2 -3
  50. package/dist/render/globalMediumPass.js +2 -8
  51. package/dist/render/gpudriven/lit.d.ts +12 -0
  52. package/dist/render/gpudriven/lit.js +23 -0
  53. package/dist/render/gpudriven/materialTable.d.ts +15 -5
  54. package/dist/render/gpudriven/materialTable.js +26 -6
  55. package/dist/render/gpudriven/pipeline.d.ts +1 -1
  56. package/dist/render/gpudriven/pipeline.js +4 -3
  57. package/dist/render/gpudriven/sunGlass.d.ts +35 -0
  58. package/dist/render/gpudriven/sunGlass.js +89 -0
  59. package/dist/render/lightBudget.d.ts +17 -5
  60. package/dist/render/lightBudget.js +17 -5
  61. package/dist/render/pointGlassTint.d.ts +59 -0
  62. package/dist/render/pointGlassTint.js +198 -0
  63. package/dist/render/pointShadowArray.d.ts +56 -3
  64. package/dist/render/pointShadowArray.js +127 -16
  65. package/dist/render/pointShadowMap.d.ts +23 -1
  66. package/dist/render/pointShadowMap.js +27 -1
  67. package/dist/render/recon/changeFrames.d.ts +15 -0
  68. package/dist/render/recon/changeFrames.js +21 -0
  69. package/dist/render/recon/lateRouting.d.ts +14 -0
  70. package/dist/render/recon/lateRouting.js +18 -0
  71. package/dist/render/recon/mover.d.ts +57 -0
  72. package/dist/render/recon/mover.js +86 -0
  73. package/dist/render/renderQuality.d.ts +35 -0
  74. package/dist/render/renderQuality.js +7 -0
  75. package/dist/render/rendererHarness.js +12 -0
  76. package/dist/render/sceneTarget.d.ts +7 -1
  77. package/dist/render/sceneTarget.js +58 -12
  78. package/dist/render/shaders/depth.d.ts +16 -1
  79. package/dist/render/shaders/depth.js +85 -9
  80. package/dist/render/shaders/flat/directionalShadow.js +91 -6
  81. package/dist/render/shaders/flat/glassTint.d.ts +6 -0
  82. package/dist/render/shaders/flat/glassTint.js +25 -0
  83. package/dist/render/shaders/flat/index.d.ts +13 -0
  84. package/dist/render/shaders/flat/index.js +4 -0
  85. package/dist/render/shaders/flat/lampGlass.d.ts +1 -0
  86. package/dist/render/shaders/flat/lampGlass.js +183 -0
  87. package/dist/render/shaders/flat/main.d.ts +1 -1
  88. package/dist/render/shaders/flat/main.js +115 -15
  89. package/dist/render/shaders/flat/pointShadow.d.ts +1 -1
  90. package/dist/render/shaders/flat/pointShadow.js +2 -0
  91. package/dist/render/shaders/flat/preamble.js +21 -6
  92. package/dist/render/shaders/generated/depth.wgsl.d.ts +227 -4
  93. package/dist/render/shaders/generated/depth.wgsl.js +227 -4
  94. package/dist/render/shaders/generated/flat.wgsl.d.ts +360 -312
  95. package/dist/render/shaders/generated/flat.wgsl.js +376 -328
  96. package/dist/render/shaders/generated/globalMedium.wgsl.d.ts +3 -13
  97. package/dist/render/shaders/generated/globalMedium.wgsl.js +3 -13
  98. package/dist/render/shaders/generated/lightVolume.wgsl.d.ts +3 -13
  99. package/dist/render/shaders/generated/lightVolume.wgsl.js +4 -14
  100. package/dist/render/shaders/generated/octahedralResolve.wgsl.d.ts +29 -0
  101. package/dist/render/shaders/generated/octahedralResolve.wgsl.js +29 -0
  102. package/dist/render/shaders/globalMedium.js +6 -6
  103. package/dist/render/shaders/gpudriven/blendRaster.wgsl.d.ts +7 -0
  104. package/dist/render/shaders/gpudriven/blendRaster.wgsl.js +86 -1
  105. package/dist/render/shaders/gpudriven/cull.wgsl.d.ts +1 -1
  106. package/dist/render/shaders/gpudriven/cull.wgsl.js +11 -2
  107. package/dist/render/shaders/gpudriven/materialTable.wgsl.d.ts +1 -1
  108. package/dist/render/shaders/gpudriven/materialTable.wgsl.js +3 -1
  109. package/dist/render/shaders/gpudriven/shade.wgsl.js +50 -1
  110. package/dist/render/shaders/gpudriven/sunGlass.wgsl.d.ts +9 -0
  111. package/dist/render/shaders/gpudriven/sunGlass.wgsl.js +133 -0
  112. package/dist/render/shaders/lightVolume.js +6 -6
  113. package/dist/render/shaders/octahedralResolve.d.ts +8 -0
  114. package/dist/render/shaders/octahedralResolve.js +48 -17
  115. package/dist/render/shaders/recon/lateDepth.wgsl.d.ts +1 -0
  116. package/dist/render/shaders/recon/lateDepth.wgsl.js +44 -0
  117. package/dist/render/shaders/recon/motion.wgsl.d.ts +1 -1
  118. package/dist/render/shaders/recon/motion.wgsl.js +84 -0
  119. package/dist/render/shadowCasters.d.ts +20 -1
  120. package/dist/render/shadowCasters.js +15 -1
  121. package/dist/render/shadowMap.d.ts +54 -5
  122. package/dist/render/shadowMap.js +190 -36
  123. package/dist/render/sunGlassTint.d.ts +25 -0
  124. package/dist/render/sunGlassTint.js +79 -0
  125. package/dist/render/translucentQueue.d.ts +6 -0
  126. package/dist/render/translucentQueue.js +15 -1
  127. package/package.json +6 -6
  128. package/src/index.ts +10 -0
  129. package/src/render/backend/api.ts +15 -9
  130. package/src/render/backend/webgl2/glassTintPrograms.ts +58 -0
  131. package/src/render/backend/webgl2/renderer.ts +401 -108
  132. package/src/render/backend/webgpu/buffers.ts +50 -2
  133. package/src/render/backend/webgpu/depthPass.ts +2 -1
  134. package/src/render/backend/webgpu/flatPass.ts +22 -1
  135. package/src/render/backend/webgpu/glassTintPass.ts +244 -0
  136. package/src/render/backend/webgpu/gpuDrivenPass.ts +349 -5
  137. package/src/render/backend/webgpu/instanced.ts +21 -1
  138. package/src/render/backend/webgpu/latePass.ts +152 -0
  139. package/src/render/backend/webgpu/lightVolumePass.ts +5 -1
  140. package/src/render/backend/webgpu/motionPass.ts +535 -0
  141. package/src/render/backend/webgpu/pipelineCache.ts +12 -1
  142. package/src/render/backend/webgpu/pointGlassTintPass.ts +233 -0
  143. package/src/render/backend/webgpu/pointShadowPass.ts +158 -26
  144. package/src/render/backend/webgpu/renderer.ts +1108 -421
  145. package/src/render/backend/webgpu/scatterPass.ts +2 -1
  146. package/src/render/backend/webgpu/skinPaletteRing.ts +4 -0
  147. package/src/render/backend/webgpu/snapshotMips.ts +80 -0
  148. package/src/render/backend/webgpu/surfaceTexturePass.ts +1 -1
  149. package/src/render/glass.ts +74 -0
  150. package/src/render/glassCasters.ts +164 -0
  151. package/src/render/glassShadow.ts +153 -0
  152. package/src/render/globalMediumPass.ts +4 -11
  153. package/src/render/gpudriven/lit.ts +31 -0
  154. package/src/render/gpudriven/materialTable.ts +28 -6
  155. package/src/render/gpudriven/pipeline.ts +4 -3
  156. package/src/render/gpudriven/sunGlass.ts +117 -0
  157. package/src/render/lightBudget.ts +19 -5
  158. package/src/render/pointGlassTint.ts +250 -0
  159. package/src/render/pointShadowArray.ts +188 -22
  160. package/src/render/pointShadowMap.ts +41 -0
  161. package/src/render/recon/changeFrames.ts +24 -0
  162. package/src/render/recon/lateRouting.ts +29 -0
  163. package/src/render/recon/mover.ts +118 -0
  164. package/src/render/renderQuality.ts +47 -0
  165. package/src/render/rendererHarness.ts +12 -0
  166. package/src/render/sceneTarget.ts +58 -11
  167. package/src/render/shaders/depth.ts +109 -9
  168. package/src/render/shaders/flat/directionalShadow.ts +97 -6
  169. package/src/render/shaders/flat/glassTint.ts +25 -0
  170. package/src/render/shaders/flat/index.ts +17 -0
  171. package/src/render/shaders/flat/lampGlass.ts +185 -0
  172. package/src/render/shaders/flat/main.ts +115 -15
  173. package/src/render/shaders/flat/pointShadow.ts +2 -0
  174. package/src/render/shaders/flat/preamble.ts +21 -6
  175. package/src/render/shaders/generated/depth.wgsl.ts +234 -4
  176. package/src/render/shaders/generated/flat.wgsl.ts +376 -328
  177. package/src/render/shaders/generated/globalMedium.wgsl.ts +3 -13
  178. package/src/render/shaders/generated/lightVolume.wgsl.ts +4 -14
  179. package/src/render/shaders/generated/octahedralResolve.wgsl.ts +30 -0
  180. package/src/render/shaders/globalMedium.ts +6 -6
  181. package/src/render/shaders/gpudriven/blendRaster.wgsl.ts +88 -1
  182. package/src/render/shaders/gpudriven/cull.wgsl.ts +11 -2
  183. package/src/render/shaders/gpudriven/materialTable.wgsl.ts +3 -1
  184. package/src/render/shaders/gpudriven/shade.wgsl.ts +50 -1
  185. package/src/render/shaders/gpudriven/sunGlass.wgsl.ts +139 -0
  186. package/src/render/shaders/lightVolume.ts +6 -6
  187. package/src/render/shaders/octahedralResolve.ts +50 -17
  188. package/src/render/shaders/recon/lateDepth.wgsl.ts +47 -0
  189. package/src/render/shaders/recon/motion.wgsl.ts +84 -0
  190. package/src/render/shadowCasters.ts +28 -1
  191. package/src/render/shadowMap.ts +231 -37
  192. package/src/render/sunGlassTint.ts +86 -0
  193. package/src/render/translucentQueue.ts +20 -1
@@ -0,0 +1,139 @@
1
+ /**
2
+ * The sun through glass for the device: `gpudriven/sunGlass.ts`'s `glassTintAt`, term for term, as
3
+ * the text the shading pass and the parity entry point both include.
4
+ *
5
+ * **The reads are the including module's**, as `SHADE_SHADOW_WGSL`'s are: `glassDepthAt(u, v)` the
6
+ * nearest pane's depth, `glassTintTexel(u, v)` the tint at the finest level, and
7
+ * `glassTintAround(u, v, radius)` the tint averaged over a spread of that radius. The shading pass
8
+ * answers the last from the tint's mip chain; the parity entry answers it with the reference's own
9
+ * box average, so everything around the reads is checked as it stands. Every constant is the
10
+ * reference's, interpolated rather than restated.
11
+ */
12
+ import { FROST_RADIUS_CAP, FROST_SPREAD } from '../../glassShadow.ts';
13
+ import {
14
+ SHADOW_DEPTH_BIAS,
15
+ SHADOW_PCF_OFFSETS,
16
+ SHADOW_TEXEL_SCALE,
17
+ } from '../../gpudriven/shadow.ts';
18
+ import { SHADE_SHADOW_WGSL } from './shadow.wgsl.ts';
19
+
20
+ export const SUN_GLASS_WGSL = /* wgsl */ `
21
+ struct SunGlassSettings {
22
+ mapSize: f32,
23
+ depthSpan: f32,
24
+ uvPerMetre: f32,
25
+ taps: f32,
26
+ }
27
+
28
+ /*
29
+ * A coarse texel with the open ground's white taken back out, by how milky the pane at the
30
+ * receiver is: the pane's colour and its share of the texel. glassShadow.ts's unmixTint.
31
+ */
32
+ fn sunGlassUnmix(mip: vec4<f32>, centreClarity: f32) -> vec4<f32> {
33
+ if (centreClarity >= 1.0) { return vec4<f32>(mip.xyz, 1.0); }
34
+ let share = clamp((1.0 - mip.w) / (1.0 - centreClarity), 0.0, 1.0);
35
+ if (share <= 0.0) { return vec4<f32>(1.0, 1.0, 1.0, 0.0); }
36
+ let colour = clamp((mip.xyz - vec3<f32>(1.0 - share)) / share, vec3<f32>(0.0), vec3<f32>(1.0));
37
+ return vec4<f32>(colour, share);
38
+ }
39
+
40
+ /* What the sun keeps through glass at map position (u, v) and light depth "depth". */
41
+ fn sunGlass(u: f32, v: f32, depth: f32, settings: SunGlassSettings) -> vec3<f32> {
42
+ if (u < 0.0 || u > 1.0 || v < 0.0 || v > 1.0) { return vec3<f32>(1.0); }
43
+ let texel = ${SHADOW_TEXEL_SCALE.toFixed(4)} / settings.mapSize;
44
+ let bias = ${SHADOW_DEPTH_BIAS.toFixed(4)} / settings.depthSpan;
45
+ let taps = min(u32(settings.taps), ${SHADOW_PCF_OFFSETS.length}u);
46
+ let clarity = glassTintTexel(u, v).w;
47
+ let behind = max(depth - glassDepthAt(u, v), 0.0) * settings.depthSpan;
48
+ let spread = (1.0 - clarity) * ${FROST_SPREAD.toFixed(6)} * behind * settings.uvPerMetre;
49
+ let radius = min(max(texel, spread), texel * ${FROST_RADIUS_CAP.toFixed(1)});
50
+
51
+ var cover = 0.0;
52
+ var colour = vec3<f32>(0.0);
53
+ var found = 0.0;
54
+ for (var i = 0u; i < taps; i = i + 1u) {
55
+ let offset = SHADOW_TAPS[i];
56
+ /* The outline: a tap counts the pane only where the receiver is behind it, by the bias. */
57
+ if (depth > glassDepthAt(u + offset.x * texel, v + offset.y * texel) + bias) {
58
+ cover = cover + 1.0;
59
+ }
60
+ /* The colour: the spread's taps, each an average, unmixed and weighed by the pane's share. */
61
+ let pane = sunGlassUnmix(glassTintAround(u + offset.x * radius, v + offset.y * radius, radius), clarity);
62
+ colour = colour + pane.xyz * pane.w;
63
+ found = found + pane.w;
64
+ }
65
+ let mixed = select(vec3<f32>(1.0), colour / max(found, 1e-6), found > 0.0);
66
+ return mix(vec3<f32>(1.0), mixed, cover / f32(max(taps, 1u)));
67
+ }
68
+ `;
69
+
70
+ /**
71
+ * `sunGlass` over maps held in storage buffers, read as the reference reads them, so the whole
72
+ * lookup can be compared with `glassTintAt` rather than only the arithmetic between the reads.
73
+ *
74
+ * Four floats a case in — u, v, the receiver's depth, one spare — and four out, the tint and one
75
+ * spare. The config is the map's size, the depth span, map units a metre, and the tap count.
76
+ */
77
+ export const SUN_GLASS_PARITY_WGSL = /* wgsl */ `
78
+ @group(0) @binding(0) var<storage, read> depths: array<f32>;
79
+ @group(0) @binding(1) var<storage, read> tints: array<f32>;
80
+ @group(0) @binding(2) var<storage, read> cases: array<f32>;
81
+ @group(0) @binding(3) var<storage, read> config: array<f32>;
82
+ @group(0) @binding(4) var<storage, read_write> results: array<f32>;
83
+
84
+ /* The opaque lookup's own read, which this entry never calls: included only for its tap table. */
85
+ fn shadowDepthAt(u: f32, v: f32) -> f32 { return 1.0; }
86
+ ${SHADE_SHADOW_WGSL}
87
+
88
+ fn glassIndex(u: f32, v: f32) -> u32 {
89
+ let size = config[0];
90
+ let x = u32(clamp(floor(u * size), 0.0, size - 1.0));
91
+ let y = u32(clamp(floor(v * size), 0.0, size - 1.0));
92
+ return y * u32(size) + x;
93
+ }
94
+
95
+ fn glassDepthAt(u: f32, v: f32) -> f32 {
96
+ return depths[glassIndex(u, v)];
97
+ }
98
+
99
+ fn glassTintTexel(u: f32, v: f32) -> vec4<f32> {
100
+ let at = glassIndex(u, v) * 4u;
101
+ return vec4<f32>(tints[at], tints[at + 1u], tints[at + 2u], tints[at + 3u]);
102
+ }
103
+
104
+ /* The reference's coarse read: the average of every texel within the radius. */
105
+ fn glassTintAround(u: f32, v: f32, radius: f32) -> vec4<f32> {
106
+ let size = config[0];
107
+ let reach = radius * size;
108
+ let x0 = u32(max(0.0, floor(u * size - reach)));
109
+ let x1 = u32(min(size - 1.0, floor(u * size + reach)));
110
+ let y0 = u32(max(0.0, floor(v * size - reach)));
111
+ let y1 = u32(min(size - 1.0, floor(v * size + reach)));
112
+ var sum = vec4<f32>(0.0);
113
+ var n = 0.0;
114
+ for (var y = y0; y <= y1; y = y + 1u) {
115
+ for (var x = x0; x <= x1; x = x + 1u) {
116
+ let at = (y * u32(size) + x) * 4u;
117
+ sum = sum + vec4<f32>(tints[at], tints[at + 1u], tints[at + 2u], tints[at + 3u]);
118
+ n = n + 1.0;
119
+ }
120
+ }
121
+ return sum / max(n, 1.0);
122
+ }
123
+ ${SUN_GLASS_WGSL}
124
+
125
+ @compute @workgroup_size(64)
126
+ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
127
+ if (id.x * 4u + 3u >= arrayLength(&cases)) { return; }
128
+ var settings: SunGlassSettings;
129
+ settings.mapSize = config[0];
130
+ settings.depthSpan = config[1];
131
+ settings.uvPerMetre = config[2];
132
+ settings.taps = config[3];
133
+ let at = id.x * 4u;
134
+ let tint = sunGlass(cases[at], cases[at + 1u], cases[at + 2u], settings);
135
+ results[at] = tint.x;
136
+ results[at + 1u] = tint.y;
137
+ results[at + 2u] = tint.z;
138
+ }
139
+ `;
@@ -47,6 +47,7 @@
47
47
  * halves of one frame disagreeing about the same light.
48
48
  */
49
49
 
50
+ import { SUN_DYNAMIC_LAYER, SUN_PEELED_LAYER, SUN_STATIC_LAYER } from '../shadowMap.ts';
50
51
  import { resolveConditionals } from './conditionals.ts';
51
52
 
52
53
  export const LIGHT_VOLUME_VERT = `#version 300 es
@@ -159,9 +160,8 @@ uniform vec3 uDustOffset;
159
160
  uniform float uSunShadow;
160
161
  uniform mat4 uLightViewProj;
161
162
  uniform float uShadowMapSize;
162
- uniform sampler2D uStaticShadowMap;
163
- uniform sampler2D uPeeledShadowMap;
164
- uniform sampler2D uDynamicShadowMap;
163
+ /* The sun's static, moving and peeled maps, as layers of one array: see shadowMap.ts. */
164
+ uniform highp sampler2DArray uSunShadows;
165
165
  uniform int uPeeledShadowEnabled;
166
166
  #endif
167
167
 
@@ -324,11 +324,11 @@ float sunReach(vec3 worldPos) {
324
324
  if (edgeFade <= 0.0) return 1.0;
325
325
 
326
326
  float compare = p.z - BIAS;
327
- float blocked = occlusion(textureLod(uStaticShadowMap, p.xy, 0.0).r, compare);
327
+ float blocked = occlusion(textureLod(uSunShadows, vec3(p.xy, ${SUN_STATIC_LAYER}.0), 0.0).r, compare);
328
328
  if (uPeeledShadowEnabled != 0) {
329
- blocked = max(blocked, occlusion(textureLod(uPeeledShadowMap, p.xy, 0.0).r, compare));
329
+ blocked = max(blocked, occlusion(textureLod(uSunShadows, vec3(p.xy, ${SUN_PEELED_LAYER}.0), 0.0).r, compare));
330
330
  }
331
- blocked = max(blocked, occlusion(textureLod(uDynamicShadowMap, p.xy, 0.0).r, compare));
331
+ blocked = max(blocked, occlusion(textureLod(uSunShadows, vec3(p.xy, ${SUN_DYNAMIC_LAYER}.0), 0.0).r, compare));
332
332
  return mix(1.0, 1.0 - blocked, uSunShadow * edgeFade);
333
333
  }
334
334
  #endif
@@ -12,6 +12,30 @@ void main() {
12
12
  gl_Position = vec4(p * 2.0 - 1.0, 0.0, 1.0);
13
13
  }`;
14
14
 
15
+ /**
16
+ * Which cube face owns this octahedral texel, and where in that face it looks: `d`, `forward` and
17
+ * `faceUv`, with every texel another face owns discarded. **One text for both resolves**, depth
18
+ * and tint, because the tie-breaking has to agree with itself as well as with the CPU: a sliver
19
+ * of texels given to one face's depth and another face's colour is a tinted seam.
20
+ */
21
+ const FACE_REGION = ` vec3 d = octDecode(gl_FragCoord.xy / uEdge);
22
+
23
+ /*
24
+ * Which face owns this texel. The tie-breaking is x, then y, then z, and it has to match the
25
+ * CPU's exactly or a sliver of texels along a boundary belongs to two faces or to none.
26
+ */
27
+ vec3 a = abs(d);
28
+ int face;
29
+ if (a.x >= a.y && a.x >= a.z) face = d.x >= 0.0 ? 0 : 1;
30
+ else if (a.y >= a.z) face = d.y >= 0.0 ? 2 : 3;
31
+ else face = d.z >= 0.0 ? 4 : 5;
32
+ if (face != uFaceIndex) discard;
33
+
34
+ /* lookAt looks down -z, so a direction inside this face has a negative z here. */
35
+ vec3 v = uFaceRotation * d;
36
+ float forward = -v.z;
37
+ vec2 faceUv = (v.xy / forward) * 0.5 + 0.5;`;
38
+
15
39
  /**
16
40
  * One cube face into its region of an octahedral map, as linear radial distance.
17
41
  *
@@ -46,23 +70,7 @@ uniform float uEdge;
46
70
  ${OCTAHEDRAL_GLSL}
47
71
 
48
72
  void main() {
49
- vec3 d = octDecode(gl_FragCoord.xy / uEdge);
50
-
51
- /*
52
- * Which face owns this texel. The tie-breaking is x, then y, then z, and it has to match the
53
- * CPU's exactly or a sliver of texels along a boundary belongs to two faces or to none.
54
- */
55
- vec3 a = abs(d);
56
- int face;
57
- if (a.x >= a.y && a.x >= a.z) face = d.x >= 0.0 ? 0 : 1;
58
- else if (a.y >= a.z) face = d.y >= 0.0 ? 2 : 3;
59
- else face = d.z >= 0.0 ? 4 : 5;
60
- if (face != uFaceIndex) discard;
61
-
62
- /* lookAt looks down -z, so a direction inside this face has a negative z here. */
63
- vec3 v = uFaceRotation * d;
64
- float forward = -v.z;
65
- vec2 faceUv = (v.xy / forward) * 0.5 + 0.5;
73
+ ${FACE_REGION}
66
74
 
67
75
  /*
68
76
  * textureLod at level zero rather than plain texture. The scratch carries one storage level and
@@ -81,3 +89,28 @@ void main() {
81
89
  /* d is a unit vector, so forward is its cosine to the axis and this is the true radius. */
82
90
  gl_FragDepth = clamp((faceLocalZ / forward) / uFar, 0.0, 1.0);
83
91
  }`;
92
+
93
+ /**
94
+ * One face of a lamp's glass tint into its region of the tint layer: the depth resolve's region,
95
+ * and a colour copied rather than a depth decoded.
96
+ *
97
+ * **Nearest, at level zero**, like the depth beside it: the tint's outline has to land on the
98
+ * glass depth's texels, and a filtered copy would smear a pane's colour a texel past its edge.
99
+ */
100
+ export const OCTAHEDRAL_RESOLVE_TINT_FRAG = `#version 300 es
101
+ precision highp float;
102
+
103
+ /** The face the bake just tinted, as colour. */
104
+ uniform highp sampler2D uFace;
105
+ uniform mat3 uFaceRotation;
106
+ uniform int uFaceIndex;
107
+ uniform float uEdge;
108
+
109
+ out vec4 outTint;
110
+
111
+ ${OCTAHEDRAL_GLSL}
112
+
113
+ void main() {
114
+ ${FACE_REGION}
115
+ outTint = textureLod(uFace, faceUv, 0.0);
116
+ }`;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * The render's depth, taken up to the output size for the draws that land after the upscale.
3
+ *
4
+ * **The nearest of the four render texels around each output pixel**, so a translucent draw is
5
+ * never let through an opaque edge the render did cover. What it gives up: where a translucent
6
+ * surface crosses an opaque silhouette, the edge steps at the render's resolution.
7
+ *
8
+ * Hand-written, and built by concatenation rather than a template literal: the reconstruction's
9
+ * passes have no WebGL2 twin (the AGENTS.md exception for them), and a `${}` inside WGSL source is
10
+ * the thing that rule forbids.
11
+ */
12
+ import { REVERSED_DEPTH } from '../../depthConvention.ts';
13
+
14
+ /** The nearer of two stored depths: the larger in a reversed buffer. */
15
+ const NEARER = REVERSED_DEPTH ? 'max' : 'min';
16
+ /** Where the search for the nearest starts: the far plane. */
17
+ const FARTHEST = REVERSED_DEPTH ? '0.0' : '1.0';
18
+
19
+ export const LATE_DEPTH_WGSL =
20
+ '@group(0) @binding(0) var source: texture_depth_2d;\n' +
21
+ '@group(0) @binding(1) var<uniform> sizes: vec4<f32>;\n' +
22
+ '\n' +
23
+ '@vertex\n' +
24
+ 'fn lateDepthVert(@builtin(vertex_index) index: u32) -> @builtin(position) vec4<f32> {\n' +
25
+ ' let x = f32((index << 1u) & 2u);\n' +
26
+ ' let y = f32(index & 2u);\n' +
27
+ ' return vec4<f32>(x * 2.0 - 1.0, 1.0 - y * 2.0, 0.0, 1.0);\n' +
28
+ '}\n' +
29
+ '\n' +
30
+ '@fragment\n' +
31
+ 'fn lateDepthFrag(@builtin(position) at: vec4<f32>) -> @builtin(frag_depth) f32 {\n' +
32
+ ' let centre = at.xy * sizes.xy / sizes.zw - vec2<f32>(0.5);\n' +
33
+ ' let base = vec2<i32>(floor(centre));\n' +
34
+ ' let top = vec2<i32>(sizes.xy) - vec2<i32>(1);\n' +
35
+ ' var nearest = ' +
36
+ FARTHEST +
37
+ ';\n' +
38
+ ' for (var y = 0; y < 2; y += 1) {\n' +
39
+ ' for (var x = 0; x < 2; x += 1) {\n' +
40
+ ' let texel = clamp(base + vec2<i32>(x, y), vec2<i32>(0), top);\n' +
41
+ ' nearest = ' +
42
+ NEARER +
43
+ '(nearest, textureLoad(source, texel, 0));\n' +
44
+ ' }\n' +
45
+ ' }\n' +
46
+ ' return nearest;\n' +
47
+ '}\n';
@@ -88,6 +88,90 @@ fn motionUv(clip: vec4<f32>) -> vec2<f32> {
88
88
  return clip.xy / clip.w * 0.5 + vec2<f32>(0.5);
89
89
  }
90
90
 
91
+ /*
92
+ * The skinned stage: the joints this frame and last frame, read from two palette textures of the
93
+ * frame's ring. The same four weighted products in the same order as the scene's own skinning, so
94
+ * the depth this stage produces is the depth the scene wrote.
95
+ */
96
+ @group(1) @binding(0) var palette: texture_2d<f32>;
97
+ @group(1) @binding(1) var previousPalette: texture_2d<f32>;
98
+
99
+ fn jointOf(source: texture_2d<f32>, index: i32) -> mat4x4<f32> {
100
+ let x = index * 4;
101
+ return mat4x4<f32>(
102
+ textureLoad(source, vec2<i32>(x, 0), 0),
103
+ textureLoad(source, vec2<i32>(x + 1, 0), 0),
104
+ textureLoad(source, vec2<i32>(x + 2, 0), 0),
105
+ textureLoad(source, vec2<i32>(x + 3, 0), 0));
106
+ }
107
+
108
+ fn skinOf(source: texture_2d<f32>, joints: vec4<f32>, weights: vec4<f32>) -> mat4x4<f32> {
109
+ return jointOf(source, i32(joints.x)) * weights.x
110
+ + jointOf(source, i32(joints.y)) * weights.y
111
+ + jointOf(source, i32(joints.z)) * weights.z
112
+ + jointOf(source, i32(joints.w)) * weights.w;
113
+ }
114
+
115
+ @vertex
116
+ fn motionSkinnedVert(
117
+ @location(0) position: vec3<f32>,
118
+ @location(11) joints: vec4<f32>,
119
+ @location(12) weights: vec4<f32>,
120
+ ) -> Varyings {
121
+ let world = draw.model * (skinOf(palette, joints, weights) * vec4<f32>(position, 1.0));
122
+ let wasWorld =
123
+ draw.previousModel * (skinOf(previousPalette, joints, weights) * vec4<f32>(position, 1.0));
124
+ var out: Varyings;
125
+ out.clip = frame.raster * world;
126
+ out.now = frame.viewProj * world;
127
+ out.was = frame.previousViewProj * wasWorld;
128
+ return out;
129
+ }
130
+
131
+ /*
132
+ * The rewritten stage: a mesh whose vertices were rewritten this frame reads last frame's rows from
133
+ * a second buffer of the same stride, so each vertex carries both of its positions.
134
+ */
135
+ @vertex
136
+ fn motionDynamicVert(
137
+ @location(0) position: vec3<f32>,
138
+ @location(1) previousPosition: vec3<f32>,
139
+ ) -> Varyings {
140
+ let world = draw.model * vec4<f32>(position, 1.0);
141
+ let wasWorld = draw.previousModel * vec4<f32>(previousPosition, 1.0);
142
+ var out: Varyings;
143
+ out.clip = frame.raster * world;
144
+ out.now = frame.viewProj * world;
145
+ out.was = frame.previousViewProj * wasWorld;
146
+ return out;
147
+ }
148
+
149
+ /*
150
+ * The instanced stage: each instance's matrix this frame and last frame, read from the batch's
151
+ * placement and the copy of last frame's it kept. Slot for slot, so an instance is paired with
152
+ * itself; the per-draw matrices are not read.
153
+ */
154
+ @vertex
155
+ fn motionInstancedVert(
156
+ @location(0) position: vec3<f32>,
157
+ @location(2) m0: vec4<f32>,
158
+ @location(3) m1: vec4<f32>,
159
+ @location(4) m2: vec4<f32>,
160
+ @location(5) m3: vec4<f32>,
161
+ @location(6) p0: vec4<f32>,
162
+ @location(7) p1: vec4<f32>,
163
+ @location(8) p2: vec4<f32>,
164
+ @location(9) p3: vec4<f32>,
165
+ ) -> Varyings {
166
+ let world = mat4x4<f32>(m0, m1, m2, m3) * vec4<f32>(position, 1.0);
167
+ let wasWorld = mat4x4<f32>(p0, p1, p2, p3) * vec4<f32>(position, 1.0);
168
+ var out: Varyings;
169
+ out.clip = frame.raster * world;
170
+ out.now = frame.viewProj * world;
171
+ out.was = frame.previousViewProj * wasWorld;
172
+ return out;
173
+ }
174
+
91
175
  @fragment
92
176
  fn motionFrag(in: Varyings) -> @location(0) vec4<f32> {
93
177
  /*
@@ -1,3 +1,4 @@
1
+ import { resolveGlass, type GlassOptions, type ResolvedGlass } from './glass.ts';
1
2
  import type { ReadonlyMat4 } from 'gl-matrix';
2
3
  import type { InstanceData } from './instancedMesh.ts';
3
4
  import type { InstancedHandle, MeshHandle, ScatterHandle } from './backend/api.ts';
@@ -41,7 +42,33 @@ import type { SurfaceTextureHandle } from './backend/api.ts';
41
42
  * outwards, so a required parameter added in a minor stops every consumer's sink and every test
42
43
  * double compiling. A sink that ignores it behaves exactly as it did.
43
44
  */
44
- export type SceneCasterMaterial = SurfaceMaterial<SurfaceTextureHandle> | null;
45
+ export type SceneCasterMaterial =
46
+ | (SurfaceMaterial<SurfaceTextureHandle> & {
47
+ /**
48
+ * The caster is glass: it lets light through, so it writes no depth into any shadow map and a
49
+ * lamp behind it lights what is beyond. The colour replay into a mirror or a probe still draws
50
+ * it. With `RenderQuality.glassShadows` on it casts a coloured shadow instead: see
51
+ * `glassShadow.ts`.
52
+ */
53
+ readonly glass?: GlassOptions;
54
+ })
55
+ | null;
56
+
57
+ /**
58
+ * Whether a caster writes depth into a shadow map: everything but glass. One line in each depth
59
+ * sink, so the answer is the same in every shadow a backend draws.
60
+ */
61
+ export function castsDepth(material: SceneCasterMaterial | undefined): boolean {
62
+ return (material?.glass?.transmission ?? 0) <= 0;
63
+ }
64
+
65
+ /**
66
+ * The caster's glass, resolved into `out`, and whether it is glass at all — the rule `castsDepth`
67
+ * states from the other side, so a caster is exactly one of the two.
68
+ */
69
+ export function glassOf(material: SceneCasterMaterial | undefined, out: ResolvedGlass): boolean {
70
+ return resolveGlass(material?.glass, out);
71
+ }
45
72
 
46
73
  export interface ShadowCasterSink {
47
74
  /** A rigid mesh at a model transform: the world, a prop, a static piece of scenery. */