@aethervtt/shard-render 0.0.1 → 0.2.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 (170) hide show
  1. package/dist/ablation.d.ts +74 -0
  2. package/dist/ablation.js +117 -0
  3. package/dist/assets.d.ts +3 -0
  4. package/dist/assets.js +43 -2
  5. package/dist/atmosphere-nodes.d.ts +9 -0
  6. package/dist/atmosphere-nodes.js +157 -30
  7. package/dist/atmosphere-plugin.js +8 -2
  8. package/dist/atmosphere-shaders.js +17 -5
  9. package/dist/baseline/atmosphere.d.ts +16 -0
  10. package/dist/baseline/atmosphere.js +301 -0
  11. package/dist/baseline/data-texture.d.ts +36 -0
  12. package/dist/baseline/data-texture.js +181 -0
  13. package/dist/baseline/environment.d.ts +14 -0
  14. package/dist/baseline/environment.js +368 -0
  15. package/dist/baseline/exposure.d.ts +25 -0
  16. package/dist/baseline/exposure.js +133 -0
  17. package/dist/baseline/lights.d.ts +33 -0
  18. package/dist/baseline/lights.js +133 -0
  19. package/dist/baseline/textures.d.ts +97 -0
  20. package/dist/baseline/textures.js +203 -0
  21. package/dist/camera.d.ts +2 -0
  22. package/dist/camera.js +4 -0
  23. package/dist/crowd.d.ts +21 -0
  24. package/dist/crowd.js +121 -0
  25. package/dist/culling-shaders.js +21 -2
  26. package/dist/culling.d.ts +11 -2
  27. package/dist/culling.js +44 -18
  28. package/dist/cutaway-plugin.d.ts +48 -0
  29. package/dist/cutaway-plugin.js +364 -0
  30. package/dist/cutaway.d.ts +27 -0
  31. package/dist/cutaway.js +49 -0
  32. package/dist/data-store.d.ts +69 -0
  33. package/dist/data-store.js +105 -0
  34. package/dist/debug-shaders.js +17 -3
  35. package/dist/debug-views.js +4 -2
  36. package/dist/decals-plugin.d.ts +5 -0
  37. package/dist/decals-plugin.js +54 -0
  38. package/dist/decals.d.ts +69 -0
  39. package/dist/decals.js +326 -0
  40. package/dist/deferred-shaders.js +2 -0
  41. package/dist/deferred.js +28 -4
  42. package/dist/deform.d.ts +5 -4
  43. package/dist/deform.js +4 -9
  44. package/dist/describe-plugin.d.ts +4 -2
  45. package/dist/describe-plugin.js +75 -3
  46. package/dist/display-nodes.js +37 -13
  47. package/dist/dynamic-resolution.d.ts +2 -0
  48. package/dist/dynamic-resolution.js +6 -1
  49. package/dist/environment-plugin.js +17 -2
  50. package/dist/environment-state.d.ts +7 -3
  51. package/dist/environment-state.js +20 -9
  52. package/dist/environment.js +7 -1
  53. package/dist/features.d.ts +49 -0
  54. package/dist/features.js +106 -0
  55. package/dist/foliage-budget.d.ts +64 -0
  56. package/dist/foliage-budget.js +108 -0
  57. package/dist/foliage-path.d.ts +19 -0
  58. package/dist/foliage-path.js +7 -0
  59. package/dist/foliage-shaders.d.ts +11 -0
  60. package/dist/foliage-shaders.js +504 -0
  61. package/dist/foliage.d.ts +194 -0
  62. package/dist/foliage.js +1027 -0
  63. package/dist/forward.d.ts +38 -2
  64. package/dist/forward.js +608 -186
  65. package/dist/fxaa.js +12 -2
  66. package/dist/gizmos-plugin.js +7 -0
  67. package/dist/gizmos.d.ts +3 -3
  68. package/dist/gizmos.js +11 -13
  69. package/dist/gpu-assets.d.ts +58 -4
  70. package/dist/gpu-assets.js +191 -8
  71. package/dist/graph.d.ts +45 -0
  72. package/dist/graph.js +169 -19
  73. package/dist/health.d.ts +58 -0
  74. package/dist/health.js +112 -0
  75. package/dist/index.d.ts +40 -11
  76. package/dist/index.js +343 -9
  77. package/dist/instances.d.ts +119 -8
  78. package/dist/instances.js +387 -78
  79. package/dist/interior-plugin.d.ts +121 -0
  80. package/dist/interior-plugin.js +465 -0
  81. package/dist/interior.d.ts +24 -0
  82. package/dist/interior.js +13 -0
  83. package/dist/layers.d.ts +35 -0
  84. package/dist/layers.js +49 -0
  85. package/dist/lens-plugin.d.ts +43 -0
  86. package/dist/lens-plugin.js +304 -0
  87. package/dist/lens.d.ts +41 -0
  88. package/dist/lens.js +99 -0
  89. package/dist/lights.d.ts +50 -5
  90. package/dist/lights.js +101 -22
  91. package/dist/material-pipelines.d.ts +37 -4
  92. package/dist/material-pipelines.js +102 -5
  93. package/dist/materials.d.ts +23 -0
  94. package/dist/materials.js +44 -1
  95. package/dist/outline-plugin.d.ts +48 -0
  96. package/dist/outline-plugin.js +529 -0
  97. package/dist/outline.d.ts +15 -0
  98. package/dist/outline.js +42 -0
  99. package/dist/overlay-registry.d.ts +3 -1
  100. package/dist/overlay-registry.js +3 -1
  101. package/dist/overlays.js +6 -2
  102. package/dist/perf-overlay.d.ts +9 -0
  103. package/dist/perf-overlay.js +125 -0
  104. package/dist/picking.d.ts +17 -0
  105. package/dist/picking.js +40 -11
  106. package/dist/pixel-perfect-plugin.js +10 -2
  107. package/dist/plugin.d.ts +69 -7
  108. package/dist/plugin.js +223 -25
  109. package/dist/post-common.d.ts +22 -2
  110. package/dist/post-common.js +82 -2
  111. package/dist/post-nodes.d.ts +7 -1
  112. package/dist/post-nodes.js +235 -111
  113. package/dist/post-plugin.js +5 -3
  114. package/dist/post-scene.d.ts +130 -0
  115. package/dist/post-scene.js +164 -0
  116. package/dist/post-shaders.js +85 -62
  117. package/dist/post.js +11 -1
  118. package/dist/projection.d.ts +24 -0
  119. package/dist/projection.js +87 -0
  120. package/dist/readback.d.ts +1 -0
  121. package/dist/readback.js +1 -0
  122. package/dist/render-scale.js +1 -0
  123. package/dist/ribbons-plugin.d.ts +5 -0
  124. package/dist/ribbons-plugin.js +59 -0
  125. package/dist/ribbons.d.ts +44 -0
  126. package/dist/ribbons.js +288 -0
  127. package/dist/scenario.d.ts +70 -0
  128. package/dist/scenario.js +125 -0
  129. package/dist/screen-effects.d.ts +97 -0
  130. package/dist/screen-effects.js +192 -0
  131. package/dist/shaders.d.ts +13 -0
  132. package/dist/shaders.js +189 -34
  133. package/dist/shadow-catcher.d.ts +9 -0
  134. package/dist/shadow-catcher.js +133 -0
  135. package/dist/shadows.d.ts +27 -2
  136. package/dist/shadows.js +100 -20
  137. package/dist/standard-fields.d.ts +1 -0
  138. package/dist/standard-fields.js +5 -0
  139. package/dist/standard.js +18 -0
  140. package/dist/stats.d.ts +45 -1
  141. package/dist/stats.js +91 -3
  142. package/dist/surface-model.d.ts +57 -0
  143. package/dist/surface-model.js +289 -0
  144. package/dist/surface-shaders.d.ts +3 -0
  145. package/dist/surface-shaders.js +160 -0
  146. package/dist/surface.d.ts +40 -0
  147. package/dist/surface.js +86 -0
  148. package/dist/target.d.ts +1 -13
  149. package/dist/target.js +1 -26
  150. package/dist/testing.d.ts +11 -0
  151. package/dist/testing.js +39 -1
  152. package/dist/tier.d.ts +13 -0
  153. package/dist/tier.js +14 -0
  154. package/dist/timer.d.ts +18 -1
  155. package/dist/timer.js +73 -20
  156. package/dist/variants.d.ts +37 -0
  157. package/dist/variants.js +104 -0
  158. package/dist/vfx-draw.d.ts +37 -0
  159. package/dist/vfx-draw.js +291 -0
  160. package/dist/vfx-state.d.ts +42 -0
  161. package/dist/vfx-state.js +81 -0
  162. package/dist/vfx.d.ts +69 -0
  163. package/dist/vfx.js +103 -0
  164. package/dist/view-visibility-plugin.d.ts +61 -0
  165. package/dist/view-visibility-plugin.js +267 -0
  166. package/dist/view-visibility.d.ts +28 -0
  167. package/dist/view-visibility.js +46 -0
  168. package/dist/view.d.ts +29 -1
  169. package/dist/view.js +57 -34
  170. package/package.json +23 -11
@@ -0,0 +1,289 @@
1
+ import { defineSchema, t } from "@aethervtt/shard-core";
2
+ const VARIATION_PATTERNS = [
3
+ "mottle",
4
+ "streaks",
5
+ "grain",
6
+ "cells",
7
+ "stagger",
8
+ "brushed"
9
+ ];
10
+ const VARIATION_FIELDS = {
11
+ strength: t.f32({
12
+ default: 1,
13
+ min: 0,
14
+ max: 1,
15
+ description: "0 is off, 1 full. Scales the pigment shift, tone, weathering and roughness."
16
+ }),
17
+ seed: t.u32({ description: "Picks the pattern; the same seed is the same surface." }),
18
+ pattern: t.enum(VARIATION_PATTERNS, {
19
+ description: "mottle (value noise), streaks (squashed across u: plaster runs), grain (stretched along u: wood), cells (a tone per cell: tiles), stagger (cells with alternate rows offset by half, toned per 2\xD72: bricks), brushed (mottle with a slanted brush mark per cell: stone)."
20
+ }),
21
+ scale: t.vec2({
22
+ default: [1.5, 1.5],
23
+ min: 1e-3,
24
+ unit: "m",
25
+ description: "Patch size along the surface's u and v. Unequal stretches the pattern."
26
+ }),
27
+ warp: t.f32({
28
+ min: 0,
29
+ max: 2,
30
+ description: "Domain warp by a second noise, in patches: stone-like breakup of the pattern."
31
+ }),
32
+ detail: t.f32({
33
+ min: 0,
34
+ max: 1,
35
+ description: "Blend toward a 3.1\xD7 finer octave."
36
+ }),
37
+ bands: t.f32({
38
+ min: 0,
39
+ max: 1,
40
+ description: "0 soft noise \u2026 1 posterized paint bands."
41
+ }),
42
+ toneRange: t.f32({
43
+ default: 0.15,
44
+ min: 0,
45
+ max: 1,
46
+ description: "\xB1 brightness at the patch extremes."
47
+ }),
48
+ coolTint: t.vec3({
49
+ default: [0.92, 0.96, 1.07],
50
+ min: 0,
51
+ max: 2,
52
+ description: "The pigment a low patch moves toward (a multiplier, linear)."
53
+ }),
54
+ warmTint: t.vec3({
55
+ default: [1.08, 1.04, 0.93],
56
+ min: 0,
57
+ max: 2,
58
+ description: "The pigment a high patch moves toward (a multiplier, linear)."
59
+ }),
60
+ roughnessRange: t.f32({
61
+ default: 0.15,
62
+ min: 0,
63
+ max: 1,
64
+ description: "\xB1 roughness from the detail octave."
65
+ }),
66
+ weathering: t.f32({
67
+ min: 0,
68
+ max: 1,
69
+ description: "Darkens and roughens where the weather mask is on."
70
+ }),
71
+ weatherThreshold: t.vec2({
72
+ default: [0.66, 0.9],
73
+ min: 0,
74
+ max: 1,
75
+ description: "The weather mask's smoothstep edges, over the detail octave."
76
+ })
77
+ };
78
+ const VariationUniform = defineSchema("surface/VariationUniform", VARIATION_FIELDS, {
79
+ description: "A surface variation (0068), as SurfaceMaterial and custom material types hold it."
80
+ });
81
+ const VARIATION_STRUCT = "surface::variation::VariationUniform";
82
+ const PX = 1.5 / 70;
83
+ const preset = (v) => ({ ...VariationUniform.defaults(), ...v });
84
+ const SURFACE_PRESETS = {
85
+ solid: preset({
86
+ strength: 0.5,
87
+ pattern: "mottle",
88
+ scale: [72 * PX, 72 * PX],
89
+ detail: 0.2,
90
+ bands: 1,
91
+ toneRange: 0.24,
92
+ roughnessRange: 0.2,
93
+ weathering: 0.07,
94
+ weatherThreshold: [0.64, 0.9]
95
+ }),
96
+ plaster: preset({
97
+ strength: 0.5,
98
+ pattern: "streaks",
99
+ scale: [128 * PX, 128 * PX],
100
+ detail: 0.18,
101
+ bands: 1,
102
+ toneRange: 0.16,
103
+ coolTint: [0.9, 0.95, 1.07],
104
+ warmTint: [1.1, 1.05, 0.93],
105
+ roughnessRange: 0.16,
106
+ weathering: 0.11,
107
+ weatherThreshold: [0.66, 0.88]
108
+ }),
109
+ stone: preset({
110
+ strength: 0.5,
111
+ pattern: "brushed",
112
+ scale: [112 * PX, 112 * PX],
113
+ warp: 0.42,
114
+ detail: 0.18,
115
+ bands: 1,
116
+ toneRange: 0.2,
117
+ coolTint: [0.88, 0.95, 1.08],
118
+ warmTint: [1.12, 1.05, 0.92],
119
+ roughnessRange: 0.22,
120
+ weathering: 0.12,
121
+ weatherThreshold: [0.34, 0.76]
122
+ }),
123
+ brick: preset({
124
+ strength: 0.5,
125
+ pattern: "stagger",
126
+ scale: [144 * 0.56 * PX, 144 * 0.3 * PX],
127
+ toneRange: 0.18,
128
+ coolTint: [0.9, 0.98, 1.08],
129
+ warmTint: [1.12, 1.02, 0.88],
130
+ roughnessRange: 0.19,
131
+ weathering: 0.1,
132
+ weatherThreshold: [0.7, 0.92]
133
+ }),
134
+ timber: preset({
135
+ strength: 0.5,
136
+ pattern: "grain",
137
+ scale: [96 * PX, 96 * PX],
138
+ detail: 0.16,
139
+ bands: 1,
140
+ toneRange: 0.2,
141
+ coolTint: [0.88, 0.96, 1.1],
142
+ warmTint: [1.14, 1.04, 0.86],
143
+ roughnessRange: 0.2,
144
+ weathering: 0.11
145
+ }),
146
+ ground: preset({
147
+ strength: 0.5,
148
+ pattern: "mottle",
149
+ scale: [96 * PX, 96 * PX],
150
+ detail: 0.24,
151
+ bands: 1,
152
+ toneRange: 0.16,
153
+ coolTint: [0.88, 1.04, 0.9],
154
+ warmTint: [1.12, 1.02, 0.82],
155
+ roughnessRange: 0.18,
156
+ weathering: 0.09,
157
+ weatherThreshold: [0.28, 0.76]
158
+ }),
159
+ metal: preset({
160
+ strength: 0.5,
161
+ pattern: "mottle",
162
+ scale: [112 * PX, 112 * PX],
163
+ detail: 0.12,
164
+ bands: 1,
165
+ toneRange: 0.1,
166
+ coolTint: [0.9, 0.97, 1.1],
167
+ warmTint: [1.08, 1.03, 0.94],
168
+ roughnessRange: 0.12,
169
+ weathering: 0.06,
170
+ weatherThreshold: [0.74, 0.94]
171
+ }),
172
+ tile: preset({
173
+ strength: 0.5,
174
+ pattern: "cells",
175
+ scale: [0.6, 0.6],
176
+ detail: 0.18,
177
+ toneRange: 0.12,
178
+ coolTint: [0.94, 0.98, 1.06],
179
+ warmTint: [1.06, 1.02, 0.94],
180
+ roughnessRange: 0.14,
181
+ weathering: 0.06,
182
+ weatherThreshold: [0.72, 0.94]
183
+ })
184
+ };
185
+ function hash(x) {
186
+ let h = Math.imul(x, 747796405) + 2891336453 >>> 0;
187
+ h = Math.imul((h >>> (h >>> 28) + 4 ^ h) >>> 0, 277803737) >>> 0;
188
+ return (h >>> 22 ^ h) >>> 0;
189
+ }
190
+ function cell(x, z, seed) {
191
+ const h = hash((x | 0) >>> 0 ^ hash((z | 0) >>> 0 ^ hash(seed >>> 0)));
192
+ return (h >>> 8) / 16777216;
193
+ }
194
+ function clamp(x, lo, hi) {
195
+ return x < lo ? lo : x > hi ? hi : x;
196
+ }
197
+ function step(a, b, x) {
198
+ const t2 = clamp((x - a) / Math.max(b - a, 1e-5), 0, 1);
199
+ return t2 * t2 * (3 - 2 * t2);
200
+ }
201
+ const mix = (a, b, t2) => a + (b - a) * t2;
202
+ const f = Math.fround;
203
+ function noise(px, pz, seed) {
204
+ const ix = Math.floor(px);
205
+ const iz = Math.floor(pz);
206
+ const fx = px - ix;
207
+ const fz = pz - iz;
208
+ const ux = fx * fx * (3 - 2 * fx);
209
+ const uz = fz * fz * (3 - 2 * fz);
210
+ const a = cell(ix, iz, seed);
211
+ const b = cell(ix + 1, iz, seed);
212
+ const c = cell(ix, iz + 1, seed);
213
+ const d = cell(ix + 1, iz + 1, seed);
214
+ return mix(mix(a, b, ux), mix(c, d, ux), uz);
215
+ }
216
+ function bands(v) {
217
+ return (step(0.18, 0.28, v) + step(0.43, 0.53, v) + step(0.68, 0.78, v)) / 3;
218
+ }
219
+ function brush(px, pz, seed) {
220
+ const cx = Math.floor(px);
221
+ const cz = Math.floor(pz);
222
+ const lx0 = px - cx - 0.5;
223
+ const lz0 = pz - cz - 0.5;
224
+ const slant = (cell(cx, cz, seed) - 0.5) * 1.35;
225
+ const lx = lx0 + lz0 * slant;
226
+ const lz = lz0 - lx0 * slant * 0.35;
227
+ const length = 1 - step(0.3, 0.49, Math.abs(lx));
228
+ const width = 1 - step(0.11, 0.25, Math.abs(lz));
229
+ return length * width * mix(0.45, 1, cell(cx + 17, cz - 9, seed));
230
+ }
231
+ const add = (seed, k) => seed + k >>> 0;
232
+ function surfaceVariation(v, p, out = { tint: [1, 1, 1], roughness: 0 }) {
233
+ const seed = v.seed >>> 0;
234
+ let qx = f(p[0] / Math.max(v.scale[0], 1e-3));
235
+ let qz = f(p[1] / Math.max(v.scale[1], 1e-3));
236
+ if (v.warp > 0) {
237
+ const wx = noise(qx * 0.7 - 6.2, qz * 0.7 + 8.1, add(seed, 7));
238
+ const wz = noise(qx * 0.7 + 3.7, qz * 0.7 - 2.9, add(seed, 5));
239
+ qx = f(qx + (wx - 0.5) * v.warp);
240
+ qz = f(qz + (wz - 0.5) * v.warp);
241
+ }
242
+ const d = noise(qx * 3.1 + 9.7, qz * 3.1 - 4.3, add(seed, 11));
243
+ let base;
244
+ switch (v.pattern) {
245
+ case "streaks":
246
+ base = noise(qx * 1.25, qz * 0.42, add(seed, 19));
247
+ break;
248
+ case "grain":
249
+ base = noise(qx * 0.36, qz * 4.2, add(seed, 23));
250
+ break;
251
+ case "cells":
252
+ base = cell(Math.floor(qx), Math.floor(qz), add(seed, 3));
253
+ break;
254
+ case "stagger": {
255
+ const row = Math.floor(qz);
256
+ const col = Math.floor(qx + 0.5 * (row - 2 * Math.floor(row / 2)));
257
+ const group = cell(Math.floor(col / 2), Math.floor(row / 2), add(seed, 13));
258
+ base = mix(group, cell(col, row, add(seed, 17)), 0.28);
259
+ break;
260
+ }
261
+ case "brushed": {
262
+ const marks = Math.max(
263
+ brush(qx * 1.45, qz * 1.45, add(seed, 37)),
264
+ brush(qx * 1.45 + 0.47, qz * 1.45 + 0.63, add(seed, 41))
265
+ );
266
+ base = clamp(noise(qx, qz, add(seed, 31)) + (marks - 0.24) * 0.26, 0, 1);
267
+ break;
268
+ }
269
+ default:
270
+ base = noise(qx, qz, seed);
271
+ }
272
+ const value = clamp(mix(mix(base, bands(base), v.bands), d, v.detail), 0, 1);
273
+ const s = v.strength;
274
+ const weather = step(v.weatherThreshold[0], v.weatherThreshold[1], d) * v.weathering;
275
+ const tone = (value - 0.5) * 2 * v.toneRange;
276
+ const k = clamp(1 + (tone - weather) * s, 0.62, 1.3);
277
+ for (let i = 0; i < 3; i++)
278
+ out.tint[i] = mix(1, mix(v.coolTint[i], v.warmTint[i], value), s) * k;
279
+ out.roughness = ((d - 0.5) * v.roughnessRange + weather * 0.45) * s;
280
+ return out;
281
+ }
282
+ export {
283
+ SURFACE_PRESETS,
284
+ VARIATION_FIELDS,
285
+ VARIATION_PATTERNS,
286
+ VARIATION_STRUCT,
287
+ VariationUniform,
288
+ surfaceVariation
289
+ };
@@ -0,0 +1,3 @@
1
+ export declare const SURFACE_VARIATION_WGSL: string;
2
+ /** `surface::material` for variation on or off. */
3
+ export declare function surfaceMaterialWgsl(variation: boolean): string;
@@ -0,0 +1,160 @@
1
+ import { wgslLayout } from "@aethervtt/shard-shader";
2
+ import { VariationUniform } from "./surface-model.js";
3
+ const VARIATION = `
4
+ import shard::pbr::types::VertexOutput;
5
+
6
+ ${wgslLayout(VariationUniform).wgsl}
7
+
8
+ /** What a variation does at a point: a base colour multiplier and a roughness offset. */
9
+ struct SurfaceVariation {
10
+ tint: vec3f,
11
+ roughness: f32,
12
+ }
13
+
14
+ fn surface_hash(x: u32) -> u32 {
15
+ var h = x * 747796405u + 2891336453u;
16
+ h = ((h >> ((h >> 28u) + 4u)) ^ h) * 277803737u;
17
+ return (h >> 22u) ^ h;
18
+ }
19
+
20
+ /** A cell's value in [0, 1): 24 bits of its hash, exact in f32. */
21
+ fn surface_cell(c: vec2i, seed: u32) -> f32 {
22
+ let h = surface_hash(bitcast<u32>(c.x) ^ surface_hash(bitcast<u32>(c.y) ^ surface_hash(seed)));
23
+ return f32(h >> 8u) * (1.0 / 16777216.0);
24
+ }
25
+
26
+ /** smoothstep that tolerates equal edges (a step). */
27
+ fn surface_step(a: f32, b: f32, x: f32) -> f32 {
28
+ let t = clamp((x - a) / max(b - a, 1e-5), 0.0, 1.0);
29
+ return t * t * (3.0 - 2.0 * t);
30
+ }
31
+
32
+ /** Value noise in [0, 1], smooth between integer lattice points. */
33
+ fn surface_noise(p: vec2f, seed: u32) -> f32 {
34
+ let i = floor(p);
35
+ let f = p - i;
36
+ let u = f * f * (3.0 - 2.0 * f);
37
+ let c = vec2i(i);
38
+ let a = surface_cell(c, seed);
39
+ let b = surface_cell(c + vec2i(1, 0), seed);
40
+ let d = surface_cell(c + vec2i(0, 1), seed);
41
+ let e = surface_cell(c + vec2i(1, 1), seed);
42
+ return mix(mix(a, b, u.x), mix(d, e, u.x), u.y);
43
+ }
44
+
45
+ /** Three soft steps: posterized paint bands. */
46
+ fn surface_bands(v: f32) -> f32 {
47
+ return (surface_step(0.18, 0.28, v) + surface_step(0.43, 0.53, v) + surface_step(0.68, 0.78, v)) / 3.0;
48
+ }
49
+
50
+ /** A slanted brush mark in each cell of p. */
51
+ fn surface_brush(p: vec2f, seed: u32) -> f32 {
52
+ let cell = floor(p);
53
+ let l0 = p - cell - 0.5;
54
+ let c = vec2i(cell);
55
+ let slant = (surface_cell(c, seed) - 0.5) * 1.35;
56
+ let lx = l0.x + l0.y * slant;
57
+ let ly = l0.y - l0.x * slant * 0.35;
58
+ let len = 1.0 - surface_step(0.3, 0.49, abs(lx));
59
+ let width = 1.0 - surface_step(0.11, 0.25, abs(ly));
60
+ return len * width * mix(0.45, 1.0, surface_cell(c + vec2i(17, -9), seed));
61
+ }
62
+
63
+ /**
64
+ * The surface point a variation is evaluated at, in metres. projection 0 ('uv'): the mesh's UV
65
+ * (structure's are metres along its faces); 1 ('world'): the world plane the normal is closest to.
66
+ */
67
+ fn surface_point(in: VertexOutput, projection: u32) -> vec2f {
68
+ if (projection == 0u) {
69
+ return in.uv;
70
+ }
71
+ let n = abs(in.world_normal);
72
+ let p = in.world_position;
73
+ if (n.y >= n.x && n.y >= n.z) {
74
+ return p.xz;
75
+ }
76
+ if (n.x >= n.z) {
77
+ return vec2f(p.z, -p.y);
78
+ }
79
+ return vec2f(p.x, -p.y);
80
+ }
81
+
82
+ /** The variation at surface point p (metres). */
83
+ fn surface_variation(p: vec2f, v: VariationUniform) -> SurfaceVariation {
84
+ let seed = v.seed;
85
+ var q = p / max(v.scale, vec2f(1e-3));
86
+ if (v.warp > 0.0) {
87
+ let w = vec2f(
88
+ surface_noise(q * 0.7 + vec2f(-6.2, 8.1), seed + 7u),
89
+ surface_noise(q * 0.7 + vec2f(3.7, -2.9), seed + 5u),
90
+ );
91
+ q = q + (w - 0.5) * v.warp;
92
+ }
93
+ let d = surface_noise(q * 3.1 + vec2f(9.7, -4.3), seed + 11u);
94
+ var base: f32;
95
+ switch v.pattern {
96
+ case 1u: {
97
+ base = surface_noise(vec2f(q.x * 1.25, q.y * 0.42), seed + 19u);
98
+ }
99
+ case 2u: {
100
+ base = surface_noise(vec2f(q.x * 0.36, q.y * 4.2), seed + 23u);
101
+ }
102
+ case 3u: {
103
+ base = surface_cell(vec2i(floor(q)), seed + 3u);
104
+ }
105
+ case 4u: {
106
+ let row = floor(q.y);
107
+ let col = floor(q.x + 0.5 * (row - 2.0 * floor(row / 2.0)));
108
+ let group = surface_cell(vec2i(floor(vec2f(col, row) / 2.0)), seed + 13u);
109
+ base = mix(group, surface_cell(vec2i(i32(col), i32(row)), seed + 17u), 0.28);
110
+ }
111
+ case 5u: {
112
+ let marks = max(
113
+ surface_brush(q * 1.45, seed + 37u),
114
+ surface_brush(q * 1.45 + vec2f(0.47, 0.63), seed + 41u),
115
+ );
116
+ base = clamp(surface_noise(q, seed + 31u) + (marks - 0.24) * 0.26, 0.0, 1.0);
117
+ }
118
+ default: {
119
+ base = surface_noise(q, seed);
120
+ }
121
+ }
122
+ let value = clamp(mix(mix(base, surface_bands(base), v.bands), d, v.detail), 0.0, 1.0);
123
+ let s = v.strength;
124
+ let weather = surface_step(v.weatherThreshold.x, v.weatherThreshold.y, d) * v.weathering;
125
+ let tone = (value - 0.5) * 2.0 * v.toneRange;
126
+ let k = clamp(1.0 + (tone - weather) * s, 0.62, 1.3);
127
+ var out: SurfaceVariation;
128
+ out.tint = mix(vec3f(1.0), mix(v.coolTint, v.warmTint, value), s) * k;
129
+ out.roughness = ((d - 0.5) * v.roughnessRange + weather * 0.45) * s;
130
+ return out;
131
+ }
132
+ `;
133
+ const MATERIAL_ON = `
134
+ import shard::pbr::types::{ VertexOutput, PbrInput };
135
+ import shard::pbr::standard::standard_input;
136
+ import surface::variation::{ surface_point, surface_variation };
137
+ import material::surface_material::SurfaceMaterial;
138
+
139
+ override fn pbr_input(in: VertexOutput) -> PbrInput {
140
+ var p = standard_input(in);
141
+ let v = surface_variation(surface_point(in, SurfaceMaterial.projection), SurfaceMaterial.variation);
142
+ p.base_color = p.base_color * v.tint;
143
+ p.roughness = clamp(p.roughness + v.roughness, 0.045, 1.0);
144
+ return p;
145
+ }`;
146
+ const MATERIAL_OFF = `
147
+ import shard::pbr::types::{ VertexOutput, PbrInput };
148
+ import shard::pbr::standard::standard_input;
149
+
150
+ override fn pbr_input(in: VertexOutput) -> PbrInput {
151
+ return standard_input(in);
152
+ }`;
153
+ const SURFACE_VARIATION_WGSL = VARIATION;
154
+ function surfaceMaterialWgsl(variation) {
155
+ return variation ? MATERIAL_ON : MATERIAL_OFF;
156
+ }
157
+ export {
158
+ SURFACE_VARIATION_WGSL,
159
+ surfaceMaterialWgsl
160
+ };
@@ -0,0 +1,40 @@
1
+ export { SURFACE_PRESETS, type SurfaceSample, surfaceVariation, VARIATION_FIELDS, VARIATION_PATTERNS, VARIATION_STRUCT, type Variation, type VariationInit, type VariationPattern, VariationUniform, } from './surface-model.js';
2
+ export { SURFACE_VARIATION_WGSL, surfaceMaterialWgsl } from './surface-shaders.js';
3
+ /**
4
+ * A variation field. Give a custom material type one (`fields: { variation: VariationField }`) and
5
+ * pass it to `surface::variation::surface_variation` in its shader. Presets are its examples.
6
+ */
7
+ export declare const VariationField: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").InferFields<{
8
+ strength: import("@aethervtt/shard-core").FieldType<number, "f32">;
9
+ seed: import("@aethervtt/shard-core").FieldType<number, "u32">;
10
+ pattern: import("@aethervtt/shard-core").FieldType<"brushed" | "cells" | "grain" | "mottle" | "stagger" | "streaks", "u8">;
11
+ scale: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec2, "f32">;
12
+ warp: import("@aethervtt/shard-core").FieldType<number, "f32">;
13
+ detail: import("@aethervtt/shard-core").FieldType<number, "f32">;
14
+ bands: import("@aethervtt/shard-core").FieldType<number, "f32">;
15
+ toneRange: import("@aethervtt/shard-core").FieldType<number, "f32">;
16
+ coolTint: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec3, "f32">;
17
+ warmTint: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec3, "f32">;
18
+ roughnessRange: import("@aethervtt/shard-core").FieldType<number, "f32">;
19
+ weathering: import("@aethervtt/shard-core").FieldType<number, "f32">;
20
+ weatherThreshold: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec2, "f32">;
21
+ }>, "object">;
22
+ export declare const SURFACE_PROJECTIONS: readonly ['uv', 'world'];
23
+ /**
24
+ * The standard material plus surface variation. Every preset and every variation shares one
25
+ * pipeline. Needs `surfacePlugin`.
26
+ */
27
+ export declare const SurfaceMaterial: import("./materials.js").MaterialType;
28
+ export interface SurfaceSettingsValue {
29
+ /**
30
+ * false links SurfaceMaterial without the variation code: it shades exactly as the standard
31
+ * material, at the same cost. Switching relinks once.
32
+ */
33
+ variation: boolean;
34
+ }
35
+ export declare const SurfaceSettings: import("@aethervtt/shard-core").ResourceDef<SurfaceSettingsValue>;
36
+ /**
37
+ * Surface variation: the `surface::variation` WGSL library and SurfaceMaterial's shader, and
38
+ * SurfaceSettings (its quality switch).
39
+ */
40
+ export declare const surfacePlugin: import("@aethervtt/shard-runtime").Plugin;
@@ -0,0 +1,86 @@
1
+ import { defineResource, defineSystem, PostUpdate, t } from "@aethervtt/shard-core";
2
+ import { definePlugin } from "@aethervtt/shard-runtime";
3
+ import { defineMaterial } from "./materials.js";
4
+ import { Shaders } from "./plugin.js";
5
+ import {
6
+ SURFACE_PRESETS,
7
+ VARIATION_FIELDS,
8
+ VARIATION_STRUCT
9
+ } from "./surface-model.js";
10
+ import { SURFACE_VARIATION_WGSL, surfaceMaterialWgsl } from "./surface-shaders.js";
11
+ import {
12
+ SURFACE_PRESETS as SURFACE_PRESETS2,
13
+ surfaceVariation,
14
+ VARIATION_FIELDS as VARIATION_FIELDS2,
15
+ VARIATION_PATTERNS,
16
+ VARIATION_STRUCT as VARIATION_STRUCT2,
17
+ VariationUniform
18
+ } from "./surface-model.js";
19
+ import { SURFACE_VARIATION_WGSL as SURFACE_VARIATION_WGSL2, surfaceMaterialWgsl as surfaceMaterialWgsl2 } from "./surface-shaders.js";
20
+ const VariationField = t.struct(VARIATION_FIELDS, {
21
+ wgsl: VARIATION_STRUCT,
22
+ description: `Surface variation (0068): shaped noise that tints the base colour and shifts roughness. The examples are SURFACE_PRESETS: ${Object.keys(SURFACE_PRESETS).join(", ")}.`,
23
+ examples: Object.values(SURFACE_PRESETS)
24
+ });
25
+ const SURFACE_PROJECTIONS = ["uv", "world"];
26
+ const SurfaceMaterial = defineMaterial("render/SurfaceMaterial", {
27
+ extends: "standard",
28
+ fields: {
29
+ variation: VariationField,
30
+ projection: t.enum(SURFACE_PROJECTIONS, {
31
+ description: "Where the variation is evaluated: uv (the mesh's UV, metres on structure, continuous around arcs) or world (the world plane the surface's normal is closest to, for meshes without metre UVs)."
32
+ })
33
+ },
34
+ shader: "surface::material",
35
+ plugin: "surfacePlugin from '@aethervtt/shard-render/surface'",
36
+ description: "The standard material with surface variation (0068): shaped noise in surface space tints the base colour and shifts roughness, breaking up a repeat without new textures."
37
+ });
38
+ const SurfaceSettings = defineResource("render/SurfaceSettings", {
39
+ description: "Surface variation quality: variation false shades SurfaceMaterial as the standard material. Write with patchResource.",
40
+ init: () => ({ variation: true }),
41
+ hostWritable: true
42
+ });
43
+ const linked = /* @__PURE__ */ new WeakMap();
44
+ const applySurfaceSettings = defineSystem({
45
+ name: "render/surface-settings",
46
+ description: "Relinks SurfaceMaterial with or without variation when SurfaceSettings changes.",
47
+ run: (_, world) => {
48
+ const library = world.resource(Shaders);
49
+ const on = world.resource(SurfaceSettings).variation;
50
+ if (linked.get(library) === on) return;
51
+ linked.set(library, on);
52
+ library.register("surface::material", surfaceMaterialWgsl(on), "engine:surface::material");
53
+ }
54
+ });
55
+ const surfacePlugin = definePlugin({
56
+ name: "render/surface",
57
+ dependencies: ["render"],
58
+ provides: [SurfaceMaterial, SurfaceSettings],
59
+ build(app) {
60
+ app.world.initResource(SurfaceSettings);
61
+ app.addSystems(PostUpdate, applySurfaceSettings);
62
+ },
63
+ ready(app) {
64
+ const world = app.world;
65
+ const library = world.resource(Shaders);
66
+ library.register("surface::variation", SURFACE_VARIATION_WGSL, "engine:surface::variation");
67
+ const on = world.resource(SurfaceSettings).variation;
68
+ linked.set(library, on);
69
+ library.register("surface::material", surfaceMaterialWgsl(on), "engine:surface::material");
70
+ }
71
+ });
72
+ export {
73
+ SURFACE_PRESETS2 as SURFACE_PRESETS,
74
+ SURFACE_PROJECTIONS,
75
+ SURFACE_VARIATION_WGSL2 as SURFACE_VARIATION_WGSL,
76
+ SurfaceMaterial,
77
+ SurfaceSettings,
78
+ VARIATION_FIELDS2 as VARIATION_FIELDS,
79
+ VARIATION_PATTERNS,
80
+ VARIATION_STRUCT2 as VARIATION_STRUCT,
81
+ VariationField,
82
+ VariationUniform,
83
+ surfaceMaterialWgsl2 as surfaceMaterialWgsl,
84
+ surfacePlugin,
85
+ surfaceVariation
86
+ };
package/dist/target.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { GpuContext } from '@aethervtt/shard-gpu';
2
- /** Where a view renders: the window's swapchain or an offscreen texture. */
2
+ /** Where a view renders: a surface (a canvas, 0052) or an offscreen texture. */
3
3
  export interface RenderTarget {
4
4
  readonly label: string;
5
5
  readonly format: GPUTextureFormat;
@@ -15,18 +15,6 @@ export interface RenderTarget {
15
15
  /** The texture to render into this frame. */
16
16
  texture(): GPUTexture;
17
17
  }
18
- /** The canvas swapchain. Resizes with the canvas. */
19
- export declare class WindowTarget implements RenderTarget {
20
- readonly label = "window";
21
- readonly renderScale = true;
22
- private readonly gpu;
23
- constructor(gpu: GpuContext);
24
- get format(): GPUTextureFormat;
25
- get width(): number;
26
- get pixelRatio(): number;
27
- get height(): number;
28
- texture(): GPUTexture;
29
- }
30
18
  export interface OffscreenTargetOptions {
31
19
  label: string;
32
20
  width: number;
package/dist/target.js CHANGED
@@ -1,27 +1,3 @@
1
- class WindowTarget {
2
- label = "window";
3
- renderScale = true;
4
- gpu;
5
- constructor(gpu) {
6
- this.gpu = gpu;
7
- }
8
- get format() {
9
- return this.gpu.format;
10
- }
11
- get width() {
12
- return this.gpu.canvas?.width ?? 1;
13
- }
14
- get pixelRatio() {
15
- return this.gpu.pixelRatio;
16
- }
17
- get height() {
18
- return this.gpu.canvas?.height ?? 1;
19
- }
20
- texture() {
21
- this.gpu.resize();
22
- return this.gpu.context.getCurrentTexture();
23
- }
24
- }
25
1
  class OffscreenTarget {
26
2
  label;
27
3
  format;
@@ -65,6 +41,5 @@ class OffscreenTarget {
65
41
  }
66
42
  }
67
43
  export {
68
- OffscreenTarget,
69
- WindowTarget
44
+ OffscreenTarget
70
45
  };
package/dist/testing.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { App } from '@aethervtt/shard-runtime';
2
2
  import type { CapturedImage } from './graph.js';
3
+ export { expectScenario, measureScenario, type ScenarioOptions, type ScenarioRun, type ScenarioSlice, } from './scenario.js';
3
4
  /** Renders frames until shaders and pipelines are compiled and nothing is skipped. */
4
5
  export declare function settle(app: App, frames?: number): Promise<void>;
5
6
  /** Settles, then captures a view. */
@@ -32,3 +33,13 @@ export declare function compareGolden(dir: string, name: string, image: {
32
33
  height: number;
33
34
  data: Uint8Array;
34
35
  }): GoldenResult;
36
+ /**
37
+ * Watches a device for what the baseline tier (0064) must never ask for: compute passes, and
38
+ * storage buffers visible to the vertex or fragment stage. Counts from now on.
39
+ */
40
+ export declare function watchBaseline(gpu: import('@aethervtt/shard-gpu').GpuContext): {
41
+ computePasses: number;
42
+ renderStorage: string[];
43
+ };
44
+ /** Mean absolute difference of two images' bytes. */
45
+ export declare function meanDifference(a: ArrayLike<number>, b: ArrayLike<number>): number;