@yali-engine/water 0.0.0-stage → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ export { cascadeBands, donelanBanner, h0Table, jonswap, jonswapPeak, type SeaState, significantHeight } from './spectrum.ts';
2
+ export { water, Water, type WaterOptions } from './water.ts';
3
+ export { WaterWaves, type WaterWavesOptions } from './waves.ts';
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ // `@yali-engine/water` (ADR-0198, docs/research/water.md): an ocean's FFT cascades and its surface, as a render feature.
2
+ export { cascadeBands, donelanBanner, h0Table, jonswap, jonswapPeak, significantHeight } from "./spectrum.js";
3
+ export { water, Water } from "./water.js";
4
+ export { WaterWaves } from "./waves.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,yHAAyH;AACzH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAiB,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAC7H,OAAO,EAAE,KAAK,EAAE,KAAK,EAAqB,MAAM,YAAY,CAAC;AAC7D,OAAO,EAAE,UAAU,EAA0B,MAAM,YAAY,CAAC"}
@@ -0,0 +1,35 @@
1
+ /** The sea a spectrum describes. */
2
+ export interface SeaState {
3
+ /** Wind speed 10 m above the water, m/s (default 10). */
4
+ readonly wind?: number;
5
+ /** The direction the wind blows towards, radians from +x towards +z (default 0). */
6
+ readonly direction?: number;
7
+ /** Fetch: how far the wind has blown over open water, metres (default 100 km). */
8
+ readonly fetch?: number;
9
+ /** JONSWAP's peak enhancement γ (default 3.3). */
10
+ readonly gamma?: number;
11
+ /** The generator's seed (default 1). */
12
+ readonly seed?: number;
13
+ }
14
+ /** JONSWAP's peak angular frequency (rad/s) and Phillips constant for a wind and fetch. */
15
+ export declare function jonswapPeak(wind: number, fetch: number): {
16
+ omega: number;
17
+ alpha: number;
18
+ };
19
+ /** JONSWAP's spectral density at angular frequency ω, m²·s. */
20
+ export declare function jonswap(omega: number, peak: {
21
+ omega: number;
22
+ alpha: number;
23
+ }, gamma?: number): number;
24
+ /** Donelan–Banner spreading at angle θ from the wind for ω / ωp; integrates to 1 over θ ∈ (−π, π]. */
25
+ export declare function donelanBanner(theta: number, ratio: number): number;
26
+ /** A cascade's band of wavenumber magnitudes, rad/m: [lo, hi). */
27
+ export declare function cascadeBands(sizes: readonly number[], n: number): [number, number][];
28
+ /**
29
+ * One cascade's h0 table, `n × n` texels of four floats: h0(k) and conj(h0(−k)) (what `evolve` reads), k on the grid
30
+ * centred on n / 2, zero outside the band. E|h0(k)|² is half the directional spectrum times the grid cell's area in
31
+ * wavenumber, so the heights' variance (h(k) holds h0(k) and h0(−k)) is the band's share of the sea's m0.
32
+ */
33
+ export declare function h0Table(n: number, size: number, band: readonly [number, number], sea?: SeaState, cascade?: number): Float32Array<ArrayBuffer>;
34
+ /** The sea's significant wave height (4 √m0, m) from JONSWAP integrated over frequency: what the cascades together carry. */
35
+ export declare function significantHeight(sea?: SeaState): number;
@@ -0,0 +1,112 @@
1
+ // The ocean's spectrum on the CPU (ADR-0198, docs/research/water.md §4): JONSWAP for a fetch-limited sea (Hasselmann et al.
2
+ // 1973) with Donelan–Banner directional spreading (Donelan, Hamilton and Hui 1985), in deep water (ω² = g k), sampled on
3
+ // each cascade's grid of wavenumbers, its band only (the cascades' bands are disjoint, so no wave is counted twice), and
4
+ // drawn as Tessendorf's h0: a complex Gaussian with the spectrum's variance, from a seeded generator (the same seed, the same
5
+ // sea on every machine).
6
+ const G = 9.81;
7
+ /** JONSWAP's peak angular frequency (rad/s) and Phillips constant for a wind and fetch. */
8
+ export function jonswapPeak(wind, fetch) {
9
+ const x = (G * fetch) / (wind * wind);
10
+ return { omega: 22 * (G / wind) * Math.pow(x, -1 / 3), alpha: 0.076 * Math.pow(x, -0.22) };
11
+ }
12
+ /** JONSWAP's spectral density at angular frequency ω, m²·s. */
13
+ export function jonswap(omega, peak, gamma = 3.3) {
14
+ if (omega <= 0)
15
+ return 0;
16
+ const wp = peak.omega;
17
+ const sigma = omega <= wp ? 0.07 : 0.09;
18
+ const r = Math.exp(-((omega - wp) ** 2) / (2 * sigma * sigma * wp * wp));
19
+ return ((peak.alpha * G * G) / omega ** 5) * Math.exp(-1.25 * (wp / omega) ** 4) * Math.pow(gamma, r);
20
+ }
21
+ /** Donelan–Banner spreading at angle θ from the wind for ω / ωp; integrates to 1 over θ ∈ (−π, π]. */
22
+ export function donelanBanner(theta, ratio) {
23
+ const beta = ratio < 0.95 ? 2.61 * ratio ** 1.3 : ratio < 1.6 ? 2.28 * ratio ** -1.3 : 10 ** (-0.4 + 0.8393 * Math.exp(-0.567 * Math.log(ratio * ratio)));
24
+ const c = Math.cosh(beta * theta);
25
+ return beta / (2 * Math.tanh(beta * Math.PI)) / (c * c);
26
+ }
27
+ /** A cascade's band of wavenumber magnitudes, rad/m: [lo, hi). */
28
+ export function cascadeBands(sizes, n) {
29
+ // Each cascade starts at 4 of its tile's fundamentals (its longest waves would tile visibly); the one before ends there.
30
+ const lo = sizes.map((l, i) => (i === 0 ? 0 : (4 * 2 * Math.PI) / l));
31
+ return sizes.map((l, i) => [lo[i], i + 1 < sizes.length ? lo[i + 1] : (Math.PI * n) / l * Math.SQRT2]);
32
+ }
33
+ /** A seeded uniform generator (mulberry32). */
34
+ function uniform(seed) {
35
+ let s = seed >>> 0;
36
+ return () => {
37
+ s = (s + 0x6d2b79f5) >>> 0;
38
+ let t = s;
39
+ t = Math.imul(t ^ (t >>> 15), t | 1);
40
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
41
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
42
+ };
43
+ }
44
+ /**
45
+ * One cascade's h0 table, `n × n` texels of four floats: h0(k) and conj(h0(−k)) (what `evolve` reads), k on the grid
46
+ * centred on n / 2, zero outside the band. E|h0(k)|² is half the directional spectrum times the grid cell's area in
47
+ * wavenumber, so the heights' variance (h(k) holds h0(k) and h0(−k)) is the band's share of the sea's m0.
48
+ */
49
+ export function h0Table(n, size, band, sea = {}, cascade = 0) {
50
+ const wind = sea.wind ?? 10;
51
+ const dir = sea.direction ?? 0;
52
+ const peak = jonswapPeak(wind, sea.fetch ?? 100_000);
53
+ const gamma = sea.gamma ?? 3.3;
54
+ const dk = (2 * Math.PI) / size;
55
+ const rand = uniform(((sea.seed ?? 1) * 0x9e3779b1 + cascade * 0x85ebca6b) >>> 0);
56
+ // Two Gaussians per texel (Box–Muller), drawn for every texel in order: the table is the same for the same seed.
57
+ const xi = new Float32Array(n * n * 2);
58
+ for (let i = 0; i < n * n; i++) {
59
+ const u1 = Math.max(rand(), 1e-12);
60
+ const u2 = rand();
61
+ const r = Math.sqrt(-2 * Math.log(u1));
62
+ xi[i * 2] = r * Math.cos(2 * Math.PI * u2);
63
+ xi[i * 2 + 1] = r * Math.sin(2 * Math.PI * u2);
64
+ }
65
+ const amp = new Float32Array(n * n);
66
+ for (let z = 0; z < n; z++) {
67
+ for (let x = 0; x < n; x++) {
68
+ const kx = (x - n / 2) * dk;
69
+ const kz = (z - n / 2) * dk;
70
+ const k = Math.hypot(kx, kz);
71
+ // The Nyquist row and column (index 0: −n/2) have no mirror on the grid: their h(k) is not Hermitian, and two real
72
+ // fields packed in one complex FFT would leak into each other there. Left empty.
73
+ if (x === 0 || z === 0 || k < band[0] || k >= band[1] || k === 0)
74
+ continue;
75
+ const omega = Math.sqrt(G * k);
76
+ // S(k) = S(ω) D(θ) dω/dk / k (polar to Cartesian), dω/dk = g / (2ω) in deep water.
77
+ let theta = Math.atan2(kz, kx) - dir;
78
+ theta = Math.atan2(Math.sin(theta), Math.cos(theta));
79
+ const s = (jonswap(omega, peak, gamma) * donelanBanner(theta, omega / peak.omega) * (G / (2 * omega))) / k;
80
+ // h(k) = h0(k) + conj(h0(−k)) carries the variance of both: each half of S Δk².
81
+ amp[z * n + x] = Math.sqrt((s * dk * dk) / 2);
82
+ }
83
+ }
84
+ const out = new Float32Array(n * n * 4);
85
+ for (let z = 0; z < n; z++) {
86
+ for (let x = 0; x < n; x++) {
87
+ const i = z * n + x;
88
+ // −k on the grid: (n − x, n − z) mod n about the centre.
89
+ const j = ((n - z) % n) * n + ((n - x) % n);
90
+ const a = amp[i] / Math.SQRT2;
91
+ const b = amp[j] / Math.SQRT2;
92
+ out[i * 4] = a * xi[i * 2];
93
+ out[i * 4 + 1] = a * xi[i * 2 + 1];
94
+ out[i * 4 + 2] = b * xi[j * 2];
95
+ out[i * 4 + 3] = -b * xi[j * 2 + 1];
96
+ }
97
+ }
98
+ return out;
99
+ }
100
+ /** The sea's significant wave height (4 √m0, m) from JONSWAP integrated over frequency: what the cascades together carry. */
101
+ export function significantHeight(sea = {}) {
102
+ const peak = jonswapPeak(sea.wind ?? 10, sea.fetch ?? 100_000);
103
+ let m0 = 0;
104
+ const steps = 20000;
105
+ const top = peak.omega * 20;
106
+ for (let i = 1; i <= steps; i++) {
107
+ const w = (i - 0.5) * (top / steps);
108
+ m0 += jonswap(w, peak, sea.gamma ?? 3.3) * (top / steps);
109
+ }
110
+ return 4 * Math.sqrt(m0);
111
+ }
112
+ //# sourceMappingURL=spectrum.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spectrum.js","sourceRoot":"","sources":["../src/spectrum.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /** Linked from shaders/water/waves.wesl. */
2
+ export declare const waterWaves: {
3
+ readonly hash: "851c72a24f5654c5";
4
+ readonly wgsl: "override N: u32 = 128u;\noverride HALF: u32 = 64u;\nconst PI = 3.14159265358979;\nconst G = 9.81;\nstruct WaveParams {\nt: vec4f,\nv: vec4u,\n}\n@group(0) @binding(0) var<storage, read> h0: array<vec4f>;\n@group(0) @binding(1) var<storage, read_write> a: array<vec2f>;\n@group(0) @binding(2) var<storage, read_write> b: array<vec2f>;\n@group(0) @binding(3) var<uniform> wp: WaveParams;\n@group(0) @binding(4) var displacement: texture_storage_2d_array<rgba16float, write>;\n@group(0) @binding(5) var slopes: texture_storage_2d_array<rgba16float, write>;\nfn cmul(x: vec2f, y: vec2f) -> vec2f {\nreturn vec2f(x.x * y.x - x.y * y.y, x.x * y.y + x.y * y.x);\n}\nfn ci(x: vec2f) -> vec2f {\nreturn vec2f(-x.y, x.x);\n}\n@compute @workgroup_size(8, 8) fn evolve(@builtin(global_invocation_id) id: vec3u) {\nif (id.x >= N || id.y >= N) {\nreturn;\n}\nlet i = id.y * N + id.x;\nlet size = wp.t.y;\nlet kx = 2.0 * PI * (f32(id.x) - f32(HALF)) / size;\nlet kz = 2.0 * PI * (f32(id.y) - f32(HALF)) / size;\nlet kl = sqrt(kx * kx + kz * kz);\nlet inv = select(0.0, 1.0 / kl, kl > 1e-6);\nlet w = sqrt(G * kl);\nlet e = vec2f(cos(w * wp.t.x), sin(w * wp.t.x));\nlet s = h0[i];\nlet h = cmul(s.xy, e) + cmul(s.zw, vec2f(e.x, -e.y));\nlet dx = -ci(h) * (kx * inv);\nlet dz = -ci(h) * (kz * inv);\nlet sx = ci(h) * kx;\nlet sz = ci(h) * kz;\nlet dxx = h * (kx * kx * inv);\nlet dzz = h * (kz * kz * inv);\nlet dxz = h * (kx * kz * inv);\nlet nn = N * N;\na[i] = h + ci(dx);\na[nn + i] = dz + ci(sx);\na[2u * nn + i] = sz + ci(dxx);\na[3u * nn + i] = dzz + ci(dxz);\n}\nvar<workgroup> line: array<vec2f, 2u * N>;\n@compute @workgroup_size(HALF) fn fft(@builtin(workgroup_id) wg: vec3u, @builtin(local_invocation_index) t: u32) {\nlet field = wg.y * N * N;\nlet l = wg.x;\nlet rows = wp.v.x == 0u;\nlet i0 = select(t * N + l, l * N + t, rows) + field;\nlet i1 = select((t + HALF) * N + l, l * N + t + HALF, rows) + field;\nif (rows) {\nline[t] = a[i0];\nline[t + HALF] = a[i1];\n} else {\nline[t] = b[i0];\nline[t + HALF] = b[i1];\n}\nworkgroupBarrier();\nvar src = 0u;\nfor (var q = 1u; q < N; q <<= 1u) {\nlet k = t & (q - 1u);\nlet x = line[src * N + t];\nlet y = line[src * N + t + HALF];\nlet ang = PI * f32(k) / f32(q);\nlet yw = cmul(y, vec2f(cos(ang), sin(ang)));\nlet j = ((t - k) << 1u) + k;\nline[(1u - src) * N + j] = x + yw;\nline[(1u - src) * N + j + q] = x - yw;\nsrc = 1u - src;\nworkgroupBarrier();\n}\nif (rows) {\nb[i0] = line[src * N + t];\nb[i1] = line[src * N + t + HALF];\n} else {\na[i0] = line[src * N + t];\na[i1] = line[src * N + t + HALF];\n}\n}\n@compute @workgroup_size(8, 8) fn pack(@builtin(global_invocation_id) id: vec3u) {\nif (id.x >= N || id.y >= N) {\nreturn;\n}\nlet i = id.y * N + id.x;\nlet nn = N * N;\nlet sign = select(1.0, -1.0, ((id.x + id.y) & 1u) == 1u);\nlet f0 = a[i] * sign;\nlet f1 = a[nn + i] * sign;\nlet f2 = a[2u * nn + i] * sign;\nlet f3 = a[3u * nn + i] * sign;\nlet lambda = wp.t.z;\nlet h = f0.x;\nlet dx = f0.y * lambda;\nlet dz = f1.x * lambda;\nlet jx = 1.0 + lambda * f2.y;\nlet jz = 1.0 + lambda * f3.x;\nlet jxz = lambda * f3.y;\nlet det = jx * jz - jxz * jxz;\nlet layer = i32(wp.v.y);\ntextureStore(displacement, vec2i(id.xy), layer, vec4f(dx, h, dz, det));\ntextureStore(slopes, vec2i(id.xy), layer, vec4f(f1.y, f2.x, det, 0.0));\n}";
5
+ };
6
+ /** Linked from shaders/water/surface.wesl. */
7
+ export declare const waterSurface: {
8
+ readonly hash: "b15f2cb4cff18a4e";
9
+ readonly wgsl: "const PI = 3.14159265;\nconst EARTH_RADIUS = 6371000.0;\nconst MAX_CASCADES = 4u;\nstruct SurfaceParams {\ncascade: array<vec4f, MAX_CASCADES>,\ngrid: vec4f,\nsea: vec4f,\nbody: vec4f,\nsss: vec4f,\nsize: vec4f,\noptics: vec4f,\nshore: vec4f,\nmirror: vec4f,\n}\n@group(0) @binding(0) var<uniform> camera: Camera;\n@group(0) @binding(1) var<uniform> lighting: Lighting;\n@group(0) @binding(2) var<uniform> sp: SurfaceParams;\n@group(0) @binding(3) var displacement: texture_2d_array<f32>;\n@group(0) @binding(4) var slopes: texture_2d_array<f32>;\n@group(0) @binding(5) var wrap: sampler;\n@group(0) @binding(6) var env_cube: texture_cube<f32>;\n@group(0) @binding(7) var under: texture_2d<f32>;\n@group(0) @binding(8) var under_depth: texture_2d<f32>;\n@group(0) @binding(9) var clamp_smp: sampler;\n@group(0) @binding(10) var scene_depth: texture_depth_2d;\n@group(0) @binding(11) var depth_out: texture_storage_2d<r32float, write>;\nstruct VsOut {\n@builtin(position) clip: vec4f,\n@location(0) p: vec3f,\n@location(1) rest: vec2f,\n@location(2) height: f32,\n}\nfn ring_radius(i: f32) -> f32 {\nlet k = sp.grid.y;\nreturn sp.grid.x * (pow(1.0 + k, i) - 1.0) / k;\n}\n@vertex fn vs(@builtin(vertex_index) vi: u32) -> VsOut {\nlet segs = u32(sp.grid.w);\nlet quad = vi / 6u;\nlet corner = vi % 6u;\nlet dr = select(select(0u, 1u, corner == 1u || corner == 2u || corner == 4u), 0u, corner == 0u || corner == 3u);\nlet ds = select(0u, 1u, corner == 2u || corner == 4u || corner == 5u);\nlet ring = f32(quad / segs + dr);\nlet seg = f32(quad % segs + ds);\nlet r = ring_radius(ring);\nlet a = 2.0 * PI * seg / f32(segs);\nlet rest = vec2f(cos(a), sin(a)) * r;\nlet spacing = max(ring_radius(ring + 1.0) - r, 2.0 * PI * r / f32(segs));\nvar d = vec3f(0.0);\nfor (var c = 0u; c < u32(sp.sea.y); c++) {\nlet cc = sp.cascade[c];\nlet texel = 1.0 / (cc.z * sp.size.x);\nlet w = 1.0 - smoothstep(1.0, 4.0, spacing / texel);\nif (w <= 0.0) {\ncontinue;\n}\nd += textureSampleLevel(displacement, wrap, cc.xy + rest * cc.z, c, 0.0).xyz * w;\n}\nlet drop = dot(rest, rest) / (2.0 * EARTH_RADIUS);\nlet p = vec3f(rest.x + d.x, sp.sea.x + d.y - drop, rest.y + d.z);\nvar o: VsOut;\no.clip = camera.view_proj * vec4f(p, 1.0);\no.p = p;\no.rest = rest;\no.height = d.y;\nreturn o;\n}\nfn sh_irradiance(n: vec3f) -> vec3f {\nreturn max(lighting.sh[0].rgb * 0.282095 + lighting.sh[1].rgb * (0.488603 * n.y) + lighting.sh[2].rgb * (0.488603 * n.z) + lighting.sh[3].rgb * (0.488603 * n.x) + lighting.sh[4].rgb * (1.092548 * n.x * n.y) + lighting.sh[5].rgb * (1.092548 * n.y * n.z) + lighting.sh[6].rgb * (0.315392 * (3.0 * n.z * n.z - 1.0)) + lighting.sh[7].rgb * (1.092548 * n.x * n.z) + lighting.sh[8].rgb * (0.546274 * (n.x * n.x - n.y * n.y)), vec3f(0.0));\n}\n@compute @workgroup_size(8, 8) fn copy_depth(@builtin(global_invocation_id) id: vec3u) {\nlet size = textureDimensions(scene_depth);\nif (id.x >= size.x || id.y >= size.y) {\nreturn;\n}\ntextureStore(depth_out, vec2i(id.xy), vec4f(textureLoad(scene_depth, vec2i(id.xy), 0), 0.0, 0.0, 0.0));\n}\nfn thickness(dist: f32, ds: f32, dop: f32) -> f32 {\nreturn select(1.0e9, dist * max(ds / dop - 1.0, 0.0), dop > 0.0);\n}\nfn mirror_scene(p: vec3f, r: vec3f, size: vec2f) -> vec4f {\nlet len = sp.mirror.x;\nlet steps = u32(sp.mirror.y);\nif (len <= 0.0 || steps == 0u || sp.shore.y < 0.5 || r.y <= 0.0) {\nreturn vec4f(0.0);\n}\nfor (var i = 1u; i <= steps; i++) {\nlet f = f32(i) / f32(steps);\nlet q = p + r * (len * f * f);\nlet c = camera.view_proj * vec4f(q, 1.0);\nif (c.w <= 0.0) {\nbreak;\n}\nlet ndc = c.xyz / c.w;\nlet uv = vec2f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);\nif (any(uv < vec2f(0.0)) || any(uv > vec2f(1.0))) {\nbreak;\n}\nlet d = textureLoad(under_depth, vec2i(uv * size), 0).x;\nif (d > ndc.z && d > 0.0) {\nlet gap = c.w * (1.0 - ndc.z / d);\nif (gap < sp.mirror.z + 0.03 * c.w) {\nlet edge = min(min(uv.x, 1.0 - uv.x), min(uv.y, 1.0 - uv.y));\nlet fade = saturate(edge * 12.0) * (1.0 - f * f);\nreturn vec4f(textureSampleLevel(under, clamp_smp, uv, 0.0).rgb * sp.shore.z, fade);\n}\n}\n}\nreturn vec4f(0.0);\n}\n@fragment fn fs(in: VsOut) -> @location(0) vec4f {\nlet frag = in.clip;\nvar sx = 0.0;\nvar sz = 0.0;\nvar lost = 0.0;\nvar fold = 0.0;\nfor (var c = 0u; c < u32(sp.sea.y); c++) {\nlet cc = sp.cascade[c];\nlet uv = cc.xy + in.rest * cc.z;\nlet texels = length(fwidth(uv)) * sp.size.x;\nlet w = 1.0 - smoothstep(0.5, 2.0, texels);\nlet s = textureSampleLevel(slopes, wrap, uv, c, 0.0);\nsx += s.x * w;\nsz += s.y * w;\nlost += cc.w * (1.0 - w);\nfold = max(fold, saturate((sp.sea.z - s.z) * 2.0) * w);\n}\nlet n = normalize(vec3f(-sx, 1.0, -sz));\nlet v = normalize(-in.p);\nlet l = camera.light_dir.xyz;\nlet nv = max(dot(n, v), 1e-3);\nlet nl = max(dot(n, l), 0.0);\nlet a2 = max(sp.body.w * sp.body.w + 2.0 * lost, 1e-4);\nlet f0 = 0.02;\nlet fresnel = f0 + (1.0 - f0) * pow(1.0 - nv, 5.0);\nvar r = reflect(-v, n);\nr.y = abs(r.y);\nlet lod = clamp(sqrt(sqrt(a2)) * (lighting.env.x - 1.0), 0.0, lighting.env.x - 1.0);\nvar sky = textureSampleLevel(env_cube, wrap, r, lod).rgb * lighting.view.rgb;\nlet seen = mirror_scene(in.p, reflect(-v, n), vec2f(textureDimensions(under)));\nsky = mix(sky, seen.rgb, seen.a);\nif (sp.mirror.w > 0.5) {\nreturn vec4f(select(vec3f(seen.a * 0.2, 0.0, 0.0) + seen.rgb * seen.a, vec3f(0.3, 0.0, 0.0), seen.a <= 0.0) * lighting.env.z, 1.0);\n}\nlet h = normalize(v + l);\nlet nh = max(dot(n, h), 0.0);\nlet dd = nh * nh * (a2 - 1.0) + 1.0;\nlet ggx = a2 / (PI * dd * dd);\nlet vis = 0.5 / (nl * sqrt(nv * nv * (1.0 - a2) + a2) + nv * sqrt(nl * nl * (1.0 - a2) + a2) + 1e-5);\nlet fh = f0 + (1.0 - f0) * pow(1.0 - max(dot(v, h), 0.0), 5.0);\nlet sun = lighting.sun_color.rgb;\nlet spec = sun * (ggx * vis * fh * nl);\nlet up = sh_irradiance(vec3f(0.0, 1.0, 0.0));\nlet body = sp.body.rgb * (up + sun * (max(l.y, 0.0) / PI) * 0.5);\nlet behind = pow(saturate(dot(-v, l)), 8.0);\nlet crest = saturate(in.height * 0.5) * saturate(1.0 - n.y * 0.9 + 0.2);\nlet sss = sp.sss.rgb * sun * (behind * crest * sp.sss.w);\nvar below = body;\nvar shore_foam = 0.0;\nif (sp.shore.y > 0.5) {\nlet px = vec2i(frag.xy);\nlet size = vec2f(textureDimensions(under));\nlet dist = length(in.p);\nlet t0 = thickness(dist, frag.z, textureLoad(under_depth, px, 0).x);\nlet bend = n.xz * sp.optics.w * min(t0, 1.0);\nvar uv = (frag.xy + bend) / size;\nvar t = thickness(dist, frag.z, textureLoad(under_depth, vec2i(uv * size), 0).x);\nif (textureLoad(under_depth, vec2i(uv * size), 0).x > frag.z) {\nuv = frag.xy / size;\nt = t0;\n}\nlet behind_light = textureSampleLevel(under, clamp_smp, uv, 0.0).rgb * sp.shore.z;\nlet trans = exp(-sp.optics.rgb * t);\nbelow = behind_light * trans + body * (vec3f(1.0) - trans);\nlet ripple = textureSampleLevel(displacement, wrap, sp.cascade[u32(sp.sea.y) - 1u].xy + in.rest * sp.cascade[u32(sp.sea.y) - 1u].z, u32(sp.sea.y) - 1u, 0.0).y;\nshore_foam = saturate((1.0 - t0 / sp.shore.x) * 1.4 + ripple * 2.0) * saturate(t0 * 8.0);\n}\nlet water = sky * fresnel + (below + sss) * (1.0 - fresnel) + spec;\nlet foam_light = (sh_irradiance(n) + sun * (nl / PI)) * 0.85;\nlet foam = saturate(fold * sp.sea.w + shore_foam);\nlet lit = mix(water, foam_light, foam);\nlet fogged = apply_fog(lit, in.p, lighting.fog, lighting.fog2, lighting.fog_air.rgb, sh_irradiance(normalize(in.p)), sun, l);\nreturn vec4f(fogged * lighting.env.z, 1.0);\n}\nstruct Camera {\nview_proj: mat4x4f,\nfrustum: array<vec4f, 6>,\nlight_dir: vec4f,\norigin: vec4f,\n}\nstruct Lighting {\nsun_color: vec4f,\nenv: vec4f,\nsh: array<vec4f, 9>,\nscreen: vec4f,\nview: vec4f,\nao_reproject: mat4x4f,\nao: vec4f,\nfog: vec4f,\nfog2: vec4f,\nfog_air: vec4f,\ngi_grid: vec4f,\ngi_counts: vec4f,\nssr: vec4f,\ngi_cascades: array<vec4f, 3>,\n}\nfn apply_fog(color: vec3f, p: vec3f, fog: vec4f, fog2: vec4f, air: vec3f, ambient: vec3f, sun: vec3f, sun_dir: vec3f) -> vec3f {\nlet dist = length(p);\nif (dist <= 0.0 || (fog.x <= 0.0 && fog2.x <= 0.0)) {\nreturn color;\n}\nlet dir = p / dist;\nlet t = exp(-(air * (fog2.x * dist) + vec3f(height_fog_depth(fog, dir, dist))));\nreturn color * t + fog_inscatter(fog2, dir, ambient, sun, sun_dir) * (vec3f(1.0) - t);\n}\nfn height_fog_depth(fog: vec4f, dir: vec3f, dist: f32) -> f32 {\nif (fog.x <= 0.0) {\nreturn 0.0;\n}\nlet falloff = max(fog.y, 1e-5);\nlet len = max(dist - fog.w, 0.0);\nlet h0 = fog.z + dir.y * min(dist, fog.w);\nlet k = falloff * dir.y * len;\nlet shape = select((1.0 - exp(-k)) / k, 1.0 - 0.5 * k, abs(k) < 1e-3);\nreturn fog.x * exp(-falloff * h0) * len * shape;\n}\nfn fog_inscatter(fog2: vec4f, dir: vec3f, ambient: vec3f, sun: vec3f, sun_dir: vec3f) -> vec3f {\nlet g = fog2.z;\nlet c = dot(dir, sun_dir);\nlet phase = (1.0 - g * g) / (FOUR_PI * pow(max(1.0 + g * g - 2.0 * g * c, 1e-4), 1.5));\nreturn ambient + sun * (phase * fog2.y);\n}\nconst FOUR_PI = 12.5663706;";
10
+ };
@@ -0,0 +1,13 @@
1
+ // GENERATED by tools/shaders-gen.mjs from shaders/**/*.wesl. Do not edit.
2
+ // Regenerate with `node tools/shaders-gen.mjs`; CI runs it with --check.
3
+ /** Linked from shaders/water/waves.wesl. */
4
+ export const waterWaves = {
5
+ hash: '851c72a24f5654c5',
6
+ wgsl: "override N: u32 = 128u;\noverride HALF: u32 = 64u;\nconst PI = 3.14159265358979;\nconst G = 9.81;\nstruct WaveParams {\nt: vec4f,\nv: vec4u,\n}\n@group(0) @binding(0) var<storage, read> h0: array<vec4f>;\n@group(0) @binding(1) var<storage, read_write> a: array<vec2f>;\n@group(0) @binding(2) var<storage, read_write> b: array<vec2f>;\n@group(0) @binding(3) var<uniform> wp: WaveParams;\n@group(0) @binding(4) var displacement: texture_storage_2d_array<rgba16float, write>;\n@group(0) @binding(5) var slopes: texture_storage_2d_array<rgba16float, write>;\nfn cmul(x: vec2f, y: vec2f) -> vec2f {\nreturn vec2f(x.x * y.x - x.y * y.y, x.x * y.y + x.y * y.x);\n}\nfn ci(x: vec2f) -> vec2f {\nreturn vec2f(-x.y, x.x);\n}\n@compute @workgroup_size(8, 8) fn evolve(@builtin(global_invocation_id) id: vec3u) {\nif (id.x >= N || id.y >= N) {\nreturn;\n}\nlet i = id.y * N + id.x;\nlet size = wp.t.y;\nlet kx = 2.0 * PI * (f32(id.x) - f32(HALF)) / size;\nlet kz = 2.0 * PI * (f32(id.y) - f32(HALF)) / size;\nlet kl = sqrt(kx * kx + kz * kz);\nlet inv = select(0.0, 1.0 / kl, kl > 1e-6);\nlet w = sqrt(G * kl);\nlet e = vec2f(cos(w * wp.t.x), sin(w * wp.t.x));\nlet s = h0[i];\nlet h = cmul(s.xy, e) + cmul(s.zw, vec2f(e.x, -e.y));\nlet dx = -ci(h) * (kx * inv);\nlet dz = -ci(h) * (kz * inv);\nlet sx = ci(h) * kx;\nlet sz = ci(h) * kz;\nlet dxx = h * (kx * kx * inv);\nlet dzz = h * (kz * kz * inv);\nlet dxz = h * (kx * kz * inv);\nlet nn = N * N;\na[i] = h + ci(dx);\na[nn + i] = dz + ci(sx);\na[2u * nn + i] = sz + ci(dxx);\na[3u * nn + i] = dzz + ci(dxz);\n}\nvar<workgroup> line: array<vec2f, 2u * N>;\n@compute @workgroup_size(HALF) fn fft(@builtin(workgroup_id) wg: vec3u, @builtin(local_invocation_index) t: u32) {\nlet field = wg.y * N * N;\nlet l = wg.x;\nlet rows = wp.v.x == 0u;\nlet i0 = select(t * N + l, l * N + t, rows) + field;\nlet i1 = select((t + HALF) * N + l, l * N + t + HALF, rows) + field;\nif (rows) {\nline[t] = a[i0];\nline[t + HALF] = a[i1];\n} else {\nline[t] = b[i0];\nline[t + HALF] = b[i1];\n}\nworkgroupBarrier();\nvar src = 0u;\nfor (var q = 1u; q < N; q <<= 1u) {\nlet k = t & (q - 1u);\nlet x = line[src * N + t];\nlet y = line[src * N + t + HALF];\nlet ang = PI * f32(k) / f32(q);\nlet yw = cmul(y, vec2f(cos(ang), sin(ang)));\nlet j = ((t - k) << 1u) + k;\nline[(1u - src) * N + j] = x + yw;\nline[(1u - src) * N + j + q] = x - yw;\nsrc = 1u - src;\nworkgroupBarrier();\n}\nif (rows) {\nb[i0] = line[src * N + t];\nb[i1] = line[src * N + t + HALF];\n} else {\na[i0] = line[src * N + t];\na[i1] = line[src * N + t + HALF];\n}\n}\n@compute @workgroup_size(8, 8) fn pack(@builtin(global_invocation_id) id: vec3u) {\nif (id.x >= N || id.y >= N) {\nreturn;\n}\nlet i = id.y * N + id.x;\nlet nn = N * N;\nlet sign = select(1.0, -1.0, ((id.x + id.y) & 1u) == 1u);\nlet f0 = a[i] * sign;\nlet f1 = a[nn + i] * sign;\nlet f2 = a[2u * nn + i] * sign;\nlet f3 = a[3u * nn + i] * sign;\nlet lambda = wp.t.z;\nlet h = f0.x;\nlet dx = f0.y * lambda;\nlet dz = f1.x * lambda;\nlet jx = 1.0 + lambda * f2.y;\nlet jz = 1.0 + lambda * f3.x;\nlet jxz = lambda * f3.y;\nlet det = jx * jz - jxz * jxz;\nlet layer = i32(wp.v.y);\ntextureStore(displacement, vec2i(id.xy), layer, vec4f(dx, h, dz, det));\ntextureStore(slopes, vec2i(id.xy), layer, vec4f(f1.y, f2.x, det, 0.0));\n}",
7
+ };
8
+ /** Linked from shaders/water/surface.wesl. */
9
+ export const waterSurface = {
10
+ hash: 'b15f2cb4cff18a4e',
11
+ wgsl: "const PI = 3.14159265;\nconst EARTH_RADIUS = 6371000.0;\nconst MAX_CASCADES = 4u;\nstruct SurfaceParams {\ncascade: array<vec4f, MAX_CASCADES>,\ngrid: vec4f,\nsea: vec4f,\nbody: vec4f,\nsss: vec4f,\nsize: vec4f,\noptics: vec4f,\nshore: vec4f,\nmirror: vec4f,\n}\n@group(0) @binding(0) var<uniform> camera: Camera;\n@group(0) @binding(1) var<uniform> lighting: Lighting;\n@group(0) @binding(2) var<uniform> sp: SurfaceParams;\n@group(0) @binding(3) var displacement: texture_2d_array<f32>;\n@group(0) @binding(4) var slopes: texture_2d_array<f32>;\n@group(0) @binding(5) var wrap: sampler;\n@group(0) @binding(6) var env_cube: texture_cube<f32>;\n@group(0) @binding(7) var under: texture_2d<f32>;\n@group(0) @binding(8) var under_depth: texture_2d<f32>;\n@group(0) @binding(9) var clamp_smp: sampler;\n@group(0) @binding(10) var scene_depth: texture_depth_2d;\n@group(0) @binding(11) var depth_out: texture_storage_2d<r32float, write>;\nstruct VsOut {\n@builtin(position) clip: vec4f,\n@location(0) p: vec3f,\n@location(1) rest: vec2f,\n@location(2) height: f32,\n}\nfn ring_radius(i: f32) -> f32 {\nlet k = sp.grid.y;\nreturn sp.grid.x * (pow(1.0 + k, i) - 1.0) / k;\n}\n@vertex fn vs(@builtin(vertex_index) vi: u32) -> VsOut {\nlet segs = u32(sp.grid.w);\nlet quad = vi / 6u;\nlet corner = vi % 6u;\nlet dr = select(select(0u, 1u, corner == 1u || corner == 2u || corner == 4u), 0u, corner == 0u || corner == 3u);\nlet ds = select(0u, 1u, corner == 2u || corner == 4u || corner == 5u);\nlet ring = f32(quad / segs + dr);\nlet seg = f32(quad % segs + ds);\nlet r = ring_radius(ring);\nlet a = 2.0 * PI * seg / f32(segs);\nlet rest = vec2f(cos(a), sin(a)) * r;\nlet spacing = max(ring_radius(ring + 1.0) - r, 2.0 * PI * r / f32(segs));\nvar d = vec3f(0.0);\nfor (var c = 0u; c < u32(sp.sea.y); c++) {\nlet cc = sp.cascade[c];\nlet texel = 1.0 / (cc.z * sp.size.x);\nlet w = 1.0 - smoothstep(1.0, 4.0, spacing / texel);\nif (w <= 0.0) {\ncontinue;\n}\nd += textureSampleLevel(displacement, wrap, cc.xy + rest * cc.z, c, 0.0).xyz * w;\n}\nlet drop = dot(rest, rest) / (2.0 * EARTH_RADIUS);\nlet p = vec3f(rest.x + d.x, sp.sea.x + d.y - drop, rest.y + d.z);\nvar o: VsOut;\no.clip = camera.view_proj * vec4f(p, 1.0);\no.p = p;\no.rest = rest;\no.height = d.y;\nreturn o;\n}\nfn sh_irradiance(n: vec3f) -> vec3f {\nreturn max(lighting.sh[0].rgb * 0.282095 + lighting.sh[1].rgb * (0.488603 * n.y) + lighting.sh[2].rgb * (0.488603 * n.z) + lighting.sh[3].rgb * (0.488603 * n.x) + lighting.sh[4].rgb * (1.092548 * n.x * n.y) + lighting.sh[5].rgb * (1.092548 * n.y * n.z) + lighting.sh[6].rgb * (0.315392 * (3.0 * n.z * n.z - 1.0)) + lighting.sh[7].rgb * (1.092548 * n.x * n.z) + lighting.sh[8].rgb * (0.546274 * (n.x * n.x - n.y * n.y)), vec3f(0.0));\n}\n@compute @workgroup_size(8, 8) fn copy_depth(@builtin(global_invocation_id) id: vec3u) {\nlet size = textureDimensions(scene_depth);\nif (id.x >= size.x || id.y >= size.y) {\nreturn;\n}\ntextureStore(depth_out, vec2i(id.xy), vec4f(textureLoad(scene_depth, vec2i(id.xy), 0), 0.0, 0.0, 0.0));\n}\nfn thickness(dist: f32, ds: f32, dop: f32) -> f32 {\nreturn select(1.0e9, dist * max(ds / dop - 1.0, 0.0), dop > 0.0);\n}\nfn mirror_scene(p: vec3f, r: vec3f, size: vec2f) -> vec4f {\nlet len = sp.mirror.x;\nlet steps = u32(sp.mirror.y);\nif (len <= 0.0 || steps == 0u || sp.shore.y < 0.5 || r.y <= 0.0) {\nreturn vec4f(0.0);\n}\nfor (var i = 1u; i <= steps; i++) {\nlet f = f32(i) / f32(steps);\nlet q = p + r * (len * f * f);\nlet c = camera.view_proj * vec4f(q, 1.0);\nif (c.w <= 0.0) {\nbreak;\n}\nlet ndc = c.xyz / c.w;\nlet uv = vec2f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);\nif (any(uv < vec2f(0.0)) || any(uv > vec2f(1.0))) {\nbreak;\n}\nlet d = textureLoad(under_depth, vec2i(uv * size), 0).x;\nif (d > ndc.z && d > 0.0) {\nlet gap = c.w * (1.0 - ndc.z / d);\nif (gap < sp.mirror.z + 0.03 * c.w) {\nlet edge = min(min(uv.x, 1.0 - uv.x), min(uv.y, 1.0 - uv.y));\nlet fade = saturate(edge * 12.0) * (1.0 - f * f);\nreturn vec4f(textureSampleLevel(under, clamp_smp, uv, 0.0).rgb * sp.shore.z, fade);\n}\n}\n}\nreturn vec4f(0.0);\n}\n@fragment fn fs(in: VsOut) -> @location(0) vec4f {\nlet frag = in.clip;\nvar sx = 0.0;\nvar sz = 0.0;\nvar lost = 0.0;\nvar fold = 0.0;\nfor (var c = 0u; c < u32(sp.sea.y); c++) {\nlet cc = sp.cascade[c];\nlet uv = cc.xy + in.rest * cc.z;\nlet texels = length(fwidth(uv)) * sp.size.x;\nlet w = 1.0 - smoothstep(0.5, 2.0, texels);\nlet s = textureSampleLevel(slopes, wrap, uv, c, 0.0);\nsx += s.x * w;\nsz += s.y * w;\nlost += cc.w * (1.0 - w);\nfold = max(fold, saturate((sp.sea.z - s.z) * 2.0) * w);\n}\nlet n = normalize(vec3f(-sx, 1.0, -sz));\nlet v = normalize(-in.p);\nlet l = camera.light_dir.xyz;\nlet nv = max(dot(n, v), 1e-3);\nlet nl = max(dot(n, l), 0.0);\nlet a2 = max(sp.body.w * sp.body.w + 2.0 * lost, 1e-4);\nlet f0 = 0.02;\nlet fresnel = f0 + (1.0 - f0) * pow(1.0 - nv, 5.0);\nvar r = reflect(-v, n);\nr.y = abs(r.y);\nlet lod = clamp(sqrt(sqrt(a2)) * (lighting.env.x - 1.0), 0.0, lighting.env.x - 1.0);\nvar sky = textureSampleLevel(env_cube, wrap, r, lod).rgb * lighting.view.rgb;\nlet seen = mirror_scene(in.p, reflect(-v, n), vec2f(textureDimensions(under)));\nsky = mix(sky, seen.rgb, seen.a);\nif (sp.mirror.w > 0.5) {\nreturn vec4f(select(vec3f(seen.a * 0.2, 0.0, 0.0) + seen.rgb * seen.a, vec3f(0.3, 0.0, 0.0), seen.a <= 0.0) * lighting.env.z, 1.0);\n}\nlet h = normalize(v + l);\nlet nh = max(dot(n, h), 0.0);\nlet dd = nh * nh * (a2 - 1.0) + 1.0;\nlet ggx = a2 / (PI * dd * dd);\nlet vis = 0.5 / (nl * sqrt(nv * nv * (1.0 - a2) + a2) + nv * sqrt(nl * nl * (1.0 - a2) + a2) + 1e-5);\nlet fh = f0 + (1.0 - f0) * pow(1.0 - max(dot(v, h), 0.0), 5.0);\nlet sun = lighting.sun_color.rgb;\nlet spec = sun * (ggx * vis * fh * nl);\nlet up = sh_irradiance(vec3f(0.0, 1.0, 0.0));\nlet body = sp.body.rgb * (up + sun * (max(l.y, 0.0) / PI) * 0.5);\nlet behind = pow(saturate(dot(-v, l)), 8.0);\nlet crest = saturate(in.height * 0.5) * saturate(1.0 - n.y * 0.9 + 0.2);\nlet sss = sp.sss.rgb * sun * (behind * crest * sp.sss.w);\nvar below = body;\nvar shore_foam = 0.0;\nif (sp.shore.y > 0.5) {\nlet px = vec2i(frag.xy);\nlet size = vec2f(textureDimensions(under));\nlet dist = length(in.p);\nlet t0 = thickness(dist, frag.z, textureLoad(under_depth, px, 0).x);\nlet bend = n.xz * sp.optics.w * min(t0, 1.0);\nvar uv = (frag.xy + bend) / size;\nvar t = thickness(dist, frag.z, textureLoad(under_depth, vec2i(uv * size), 0).x);\nif (textureLoad(under_depth, vec2i(uv * size), 0).x > frag.z) {\nuv = frag.xy / size;\nt = t0;\n}\nlet behind_light = textureSampleLevel(under, clamp_smp, uv, 0.0).rgb * sp.shore.z;\nlet trans = exp(-sp.optics.rgb * t);\nbelow = behind_light * trans + body * (vec3f(1.0) - trans);\nlet ripple = textureSampleLevel(displacement, wrap, sp.cascade[u32(sp.sea.y) - 1u].xy + in.rest * sp.cascade[u32(sp.sea.y) - 1u].z, u32(sp.sea.y) - 1u, 0.0).y;\nshore_foam = saturate((1.0 - t0 / sp.shore.x) * 1.4 + ripple * 2.0) * saturate(t0 * 8.0);\n}\nlet water = sky * fresnel + (below + sss) * (1.0 - fresnel) + spec;\nlet foam_light = (sh_irradiance(n) + sun * (nl / PI)) * 0.85;\nlet foam = saturate(fold * sp.sea.w + shore_foam);\nlet lit = mix(water, foam_light, foam);\nlet fogged = apply_fog(lit, in.p, lighting.fog, lighting.fog2, lighting.fog_air.rgb, sh_irradiance(normalize(in.p)), sun, l);\nreturn vec4f(fogged * lighting.env.z, 1.0);\n}\nstruct Camera {\nview_proj: mat4x4f,\nfrustum: array<vec4f, 6>,\nlight_dir: vec4f,\norigin: vec4f,\n}\nstruct Lighting {\nsun_color: vec4f,\nenv: vec4f,\nsh: array<vec4f, 9>,\nscreen: vec4f,\nview: vec4f,\nao_reproject: mat4x4f,\nao: vec4f,\nfog: vec4f,\nfog2: vec4f,\nfog_air: vec4f,\ngi_grid: vec4f,\ngi_counts: vec4f,\nssr: vec4f,\ngi_cascades: array<vec4f, 3>,\n}\nfn apply_fog(color: vec3f, p: vec3f, fog: vec4f, fog2: vec4f, air: vec3f, ambient: vec3f, sun: vec3f, sun_dir: vec3f) -> vec3f {\nlet dist = length(p);\nif (dist <= 0.0 || (fog.x <= 0.0 && fog2.x <= 0.0)) {\nreturn color;\n}\nlet dir = p / dist;\nlet t = exp(-(air * (fog2.x * dist) + vec3f(height_fog_depth(fog, dir, dist))));\nreturn color * t + fog_inscatter(fog2, dir, ambient, sun, sun_dir) * (vec3f(1.0) - t);\n}\nfn height_fog_depth(fog: vec4f, dir: vec3f, dist: f32) -> f32 {\nif (fog.x <= 0.0) {\nreturn 0.0;\n}\nlet falloff = max(fog.y, 1e-5);\nlet len = max(dist - fog.w, 0.0);\nlet h0 = fog.z + dir.y * min(dist, fog.w);\nlet k = falloff * dir.y * len;\nlet shape = select((1.0 - exp(-k)) / k, 1.0 - 0.5 * k, abs(k) < 1e-3);\nreturn fog.x * exp(-falloff * h0) * len * shape;\n}\nfn fog_inscatter(fog2: vec4f, dir: vec3f, ambient: vec3f, sun: vec3f, sun_dir: vec3f) -> vec3f {\nlet g = fog2.z;\nlet c = dot(dir, sun_dir);\nlet phase = (1.0 - g * g) / (FOUR_PI * pow(max(1.0 + g * g - 2.0 * g * c, 1e-4), 1.5));\nreturn ambient + sun * (phase * fog2.y);\n}\nconst FOUR_PI = 12.5663706;",
12
+ };
13
+ //# sourceMappingURL=water-shaders.gen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"water-shaders.gen.js","sourceRoot":"","sources":["../src/water-shaders.gen.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,yEAAyE;AAEzE,4CAA4C;AAC5C,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,IAAI,EAAE,kBAAkB;IACxB,IAAI,EAAE,stGAAstG;CACptG,CAAC;AAEX,8CAA8C;AAC9C,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,IAAI,EAAE,kBAAkB;IACxB,IAAI,EAAE,ynRAAynR;CACvnR,CAAC"}
@@ -0,0 +1,71 @@
1
+ import type { FrameGraph, PipelineCache, RenderFeature, RenderFeatureFactory, RenderFeatureFrame, SceneRenderer } from '@yali-engine/render';
2
+ import { WaterWaves, type WaterWavesOptions } from './waves.ts';
3
+ /** The ocean's knobs. */
4
+ export interface WaterOptions extends WaterWavesOptions {
5
+ /** The sea's mean level, world metres (default 0). */
6
+ readonly level?: number;
7
+ /** Rings and segments of the surface's grid (defaults 240 × 384: 184,320 triangles out to ~9 km). */
8
+ readonly rings?: number;
9
+ readonly segments?: number;
10
+ /** The Jacobian below which foam starts (default 0.6; lower: less foam), and foam's strength (default 1). */
11
+ readonly foamThreshold?: number;
12
+ readonly foam?: number;
13
+ /** The water body's scattering colour (linear; default a deep teal-blue) and calm water's roughness (default 0.04). */
14
+ readonly bodyColor?: readonly [number, number, number];
15
+ readonly roughness?: number;
16
+ /** The colour of light through a crest (linear) and its strength (defaults a green-cyan, 0.5). */
17
+ readonly subsurfaceColor?: readonly [number, number, number];
18
+ readonly subsurface?: number;
19
+ /** The water's absorption per metre, red green blue (default coastal seawater: red gone in ~2 m, blue in ~20). */
20
+ readonly absorption?: readonly [number, number, number];
21
+ /** How far the refraction bends the view, pixels at a metre of water (default 60). */
22
+ readonly refraction?: number;
23
+ /** Water shallower than this foams at a shore, metres (default 0.12). */
24
+ readonly shoreFoam?: number;
25
+ /** How far the mirror ray is marched for reflections of the scene, metres (default 40; 0: the sky only), and its steps (24). */
26
+ readonly reflections?: number;
27
+ readonly reflectionSteps?: number;
28
+ }
29
+ /**
30
+ * An ocean (ADR-0198): FFT waves (JONSWAP, Donelan–Banner) on a radial grid to the horizon, lit by the scene's sky, sun and
31
+ * fog. WebGPU with HDR only.
32
+ *
33
+ * @example
34
+ * import { water } from '@yali-engine/water';
35
+ * renderPlugin({ canvas, sky: { model: 'atmosphere' }, post: { passes: postPasses, taa: true }, features: [water({ wind: 12 })] });
36
+ */
37
+ export declare function water(o?: WaterOptions): RenderFeatureFactory;
38
+ export declare class Water implements RenderFeature {
39
+ #private;
40
+ readonly name = "water";
41
+ readonly device: GPUDevice;
42
+ readonly waves: WaterWaves;
43
+ /** Live knobs. */
44
+ level: number;
45
+ foamThreshold: number;
46
+ foam: number;
47
+ bodyColor: [number, number, number];
48
+ roughness: number;
49
+ subsurfaceColor: [number, number, number];
50
+ subsurface: number;
51
+ absorption: [number, number, number];
52
+ refraction: number;
53
+ shoreFoam: number;
54
+ reflections: number;
55
+ reflectionSteps: number;
56
+ /** Holds the waves at this time, seconds (live; null: they follow the app's clock). For stills and A/B checks. */
57
+ frozenAt: number | null;
58
+ /** A debug view: only the scene the reflections found (red where none). */
59
+ debugMirror: boolean;
60
+ /** Off: nothing is drawn or computed (live). */
61
+ enabled: boolean;
62
+ readonly rings: number;
63
+ readonly segments: number;
64
+ constructor(device: GPUDevice, o: WaterOptions);
65
+ init(cache: PipelineCache, format: GPUTextureFormat): Promise<void>;
66
+ prepare(f: RenderFeatureFrame): void;
67
+ addPasses(graph: FrameGraph, color: string, depth: string, scene: SceneRenderer): void;
68
+ /** What the sea holds on the GPU (its waves; the surface has no buffers). */
69
+ get gpuBytes(): number;
70
+ destroy(): void;
71
+ }
package/dist/water.js ADDED
@@ -0,0 +1,209 @@
1
+ import { waterSurface } from "./water-shaders.gen.js";
2
+ import { WaterWaves } from "./waves.js";
3
+ /** Bytes of `SurfaceParams` (`shaders/water/surface.wesl`): 4 cascades + 8 vectors. */
4
+ const PARAMS_BYTES = (4 + 8) * 16;
5
+ /** The first ring's spacing (m) and how much wider each next ring is. */
6
+ const FIRST_RING = 0.12;
7
+ const RING_GROWTH = 0.036;
8
+ /**
9
+ * An ocean (ADR-0198): FFT waves (JONSWAP, Donelan–Banner) on a radial grid to the horizon, lit by the scene's sky, sun and
10
+ * fog. WebGPU with HDR only.
11
+ *
12
+ * @example
13
+ * import { water } from '@yali-engine/water';
14
+ * renderPlugin({ canvas, sky: { model: 'atmosphere' }, post: { passes: postPasses, taa: true }, features: [water({ wind: 12 })] });
15
+ */
16
+ export function water(o = {}) {
17
+ return (device) => new Water(device, o);
18
+ }
19
+ export class Water {
20
+ name = 'water';
21
+ device;
22
+ waves;
23
+ /** Live knobs. */
24
+ level;
25
+ foamThreshold;
26
+ foam;
27
+ bodyColor;
28
+ roughness;
29
+ subsurfaceColor;
30
+ subsurface;
31
+ absorption;
32
+ refraction;
33
+ shoreFoam;
34
+ reflections;
35
+ reflectionSteps;
36
+ /** Holds the waves at this time, seconds (live; null: they follow the app's clock). For stills and A/B checks. */
37
+ frozenAt = null;
38
+ /** A debug view: only the scene the reflections found (red where none). */
39
+ debugMirror = false;
40
+ /** Off: nothing is drawn or computed (live). */
41
+ enabled = true;
42
+ rings;
43
+ segments;
44
+ #pipeline;
45
+ #copyDepth;
46
+ #depthGroup = null;
47
+ #under = false;
48
+ #inv = 1;
49
+ #params;
50
+ #data = new Float32Array(PARAMS_BYTES / 4);
51
+ #sampler;
52
+ #clamp;
53
+ #group = null;
54
+ #groupKey = '';
55
+ #depthKey = -1;
56
+ #seconds = 0;
57
+ #env = null;
58
+ constructor(device, o) {
59
+ this.device = device;
60
+ this.waves = new WaterWaves(device, o);
61
+ this.level = o.level ?? 0;
62
+ this.foamThreshold = o.foamThreshold ?? 0.6;
63
+ this.foam = o.foam ?? 1;
64
+ this.bodyColor = [...(o.bodyColor ?? [0.012, 0.045, 0.06])];
65
+ this.roughness = o.roughness ?? 0.04;
66
+ this.subsurfaceColor = [...(o.subsurfaceColor ?? [0.04, 0.26, 0.22])];
67
+ this.subsurface = o.subsurface ?? 0.5;
68
+ this.absorption = [...(o.absorption ?? [0.35, 0.1, 0.07])];
69
+ this.refraction = o.refraction ?? 60;
70
+ this.shoreFoam = o.shoreFoam ?? 0.12;
71
+ this.reflections = o.reflections ?? 40;
72
+ this.reflectionSteps = o.reflectionSteps ?? 24;
73
+ this.rings = o.rings ?? 240;
74
+ this.segments = o.segments ?? 384;
75
+ this.#params = device.createBuffer({ label: 'yali:water:surface', size: PARAMS_BYTES, usage: 0x40 | 0x08 });
76
+ this.#clamp = device.createSampler({ label: 'yali:water:clamp', magFilter: 'linear', minFilter: 'linear' });
77
+ this.#sampler = device.createSampler({ label: 'yali:water:wrap', addressModeU: 'repeat', addressModeV: 'repeat', magFilter: 'linear', minFilter: 'linear', mipmapFilter: 'linear' });
78
+ }
79
+ async init(cache, format) {
80
+ const [pipeline] = await Promise.all([
81
+ cache.render({
82
+ label: 'yali:water:surface',
83
+ vertex: { shader: waterSurface, entryPoint: 'vs' },
84
+ fragment: { shader: waterSurface, entryPoint: 'fs', targets: [{ format }] },
85
+ // Both faces: from under a crest the camera sees the surface's back.
86
+ primitive: { topology: 'triangle-list', cullMode: 'none' },
87
+ depthStencil: { format: 'depth32float', depthWriteEnabled: true, depthCompare: 'greater' },
88
+ }),
89
+ this.waves.init(cache),
90
+ cache.compute({ label: 'yali:water:copy-depth', compute: { shader: waterSurface, entryPoint: 'copy_depth' } }).then((p) => (this.#copyDepth = p)),
91
+ ]);
92
+ this.#pipeline = pipeline;
93
+ }
94
+ prepare(f) {
95
+ this.#seconds = this.frozenAt ?? f.seconds;
96
+ this.#env = f.environment;
97
+ const d = this.#data;
98
+ const eye = f.camera.eye;
99
+ const n = this.waves.tiles.length;
100
+ for (let c = 0; c < 4; c++) {
101
+ if (c >= n) {
102
+ d.fill(0, c * 4, c * 4 + 4);
103
+ continue;
104
+ }
105
+ const tile = this.waves.tiles[c];
106
+ // The eye on the tile in f64, then small (the waves' phase stays exact far from the origin, ADR-0008).
107
+ const mod = (v) => (((v % tile) + tile) % tile) / tile;
108
+ d.set([mod(eye[0]), mod(eye[2]), 1 / tile, this.waves.slopeVariance[c]], c * 4);
109
+ }
110
+ d.set([FIRST_RING, RING_GROWTH, this.rings, this.segments], 16);
111
+ d.set([this.level - eye[1], n, this.foamThreshold, this.foam], 20);
112
+ d.set([...this.bodyColor, this.roughness], 24);
113
+ d.set([...this.subsurfaceColor, this.subsurface], 28);
114
+ d.set([this.waves.size, 0, 0, 0], 32);
115
+ d.set([...this.absorption, this.refraction], 36);
116
+ // The scene behind is read at the pre-exposure it was written with (this frame's).
117
+ this.#inv = 1 / Math.max(1e-6, f.scene.preExposure);
118
+ d.set([this.shoreFoam, this.#under ? 1 : 0, this.#inv, 0], 40);
119
+ d.set([this.reflections, this.reflectionSteps, 0.6, this.debugMirror ? 1 : 0], 44);
120
+ this.device.queue.writeBuffer(this.#params, 0, d);
121
+ }
122
+ addPasses(graph, color, depth, scene) {
123
+ // What the surface looks through (ADR-0199): the opaque scene's colour and depth, copied before it draws.
124
+ this.#under = true;
125
+ graph.transientTexture('water.under', { format: 'rgba16float', usage: 0x02 | 0x04 });
126
+ graph.transientTexture('water.depth', { format: 'r32float', usage: 0x08 | 0x04 });
127
+ graph.addPass({
128
+ name: 'water.under',
129
+ kind: 'encoder',
130
+ reads: [],
131
+ writes: ['water.under'],
132
+ execute: (encoder, ctx) => {
133
+ if (this.enabled)
134
+ encoder.copyTextureToTexture({ texture: ctx.texture(color) }, { texture: ctx.texture('water.under') }, [ctx.width, ctx.height]);
135
+ },
136
+ });
137
+ graph.addPass({
138
+ name: 'water.depth',
139
+ kind: 'compute',
140
+ reads: [depth],
141
+ writes: ['water.depth'],
142
+ execute: (pass, ctx) => {
143
+ if (!this.enabled)
144
+ return;
145
+ if (!this.#depthGroup || this.#depthKey !== ctx.generation) {
146
+ this.#depthKey = ctx.generation;
147
+ this.#depthGroup = this.device.createBindGroup({ label: 'yali:water:copy-depth', layout: this.#copyDepth.getBindGroupLayout(0), entries: [{ binding: 10, resource: ctx.view(depth) }, { binding: 11, resource: ctx.view('water.depth') }] });
148
+ }
149
+ pass.setPipeline(this.#copyDepth);
150
+ pass.setBindGroup(0, this.#depthGroup);
151
+ pass.dispatchWorkgroups(Math.ceil(ctx.width / 8), Math.ceil(ctx.height / 8));
152
+ },
153
+ });
154
+ graph.addPass({
155
+ name: 'water.waves',
156
+ kind: 'compute',
157
+ reads: [],
158
+ writes: ['water.waves'],
159
+ execute: (pass) => {
160
+ if (this.enabled)
161
+ this.waves.encode(pass, this.#seconds);
162
+ },
163
+ });
164
+ graph.addPass({
165
+ name: 'water.surface',
166
+ kind: 'render',
167
+ reads: ['water.waves', 'water.under', 'water.depth'],
168
+ color: { target: color, clear: { r: 0, g: 0, b: 0, a: 0 }, load: true },
169
+ depth: { target: depth, clear: 0, load: true, store: true },
170
+ execute: (pass, ctx) => {
171
+ const env = this.#env;
172
+ if (!this.enabled || !env)
173
+ return;
174
+ const key = `${env.version}:${ctx.generation}`;
175
+ if (!this.#group || key !== this.#groupKey) {
176
+ this.#groupKey = key;
177
+ this.#group = this.device.createBindGroup({
178
+ label: 'yali:water:surface',
179
+ layout: this.#pipeline.getBindGroupLayout(0),
180
+ entries: [
181
+ { binding: 0, resource: { buffer: scene.cameraBuffer } },
182
+ { binding: 1, resource: { buffer: scene.lightingBuffer } },
183
+ { binding: 2, resource: { buffer: this.#params } },
184
+ { binding: 3, resource: this.waves.displacement.createView({ dimension: '2d-array' }) },
185
+ { binding: 4, resource: this.waves.slopes.createView({ dimension: '2d-array' }) },
186
+ { binding: 5, resource: this.#sampler },
187
+ { binding: 6, resource: env.prefiltered.createView({ dimension: 'cube' }) },
188
+ { binding: 7, resource: ctx.view('water.under') },
189
+ { binding: 8, resource: ctx.view('water.depth') },
190
+ { binding: 9, resource: this.#clamp },
191
+ ],
192
+ });
193
+ }
194
+ pass.setPipeline(this.#pipeline);
195
+ pass.setBindGroup(0, this.#group);
196
+ pass.draw(this.rings * this.segments * 6);
197
+ },
198
+ });
199
+ }
200
+ /** What the sea holds on the GPU (its waves; the surface has no buffers). */
201
+ get gpuBytes() {
202
+ return this.waves.gpuBytes;
203
+ }
204
+ destroy() {
205
+ this.waves.destroy();
206
+ this.#params.destroy();
207
+ }
208
+ }
209
+ //# sourceMappingURL=water.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"water.js","sourceRoot":"","sources":["../src/water.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import type { PipelineCache } from '@yali-engine/render';
2
+ import { type SeaState } from './spectrum.ts';
3
+ /** What `WaterWaves` takes. */
4
+ export interface WaterWavesOptions extends SeaState {
5
+ /** Texels a side of every cascade (a power of two, 32–256; default 128: the Medium tier's). */
6
+ readonly size?: number;
7
+ /** Each cascade's tile, metres (default [250, 37, 7]: the research's three disjoint bands). */
8
+ readonly tiles?: readonly number[];
9
+ /** How far the horizontal displacement goes, 0–1 (choppy waves; default 0.8). */
10
+ readonly choppiness?: number;
11
+ }
12
+ /**
13
+ * The FFT cascades of one sea. `encode` each frame (any compute pass); read `displacement` and `slopes` (texture arrays, a
14
+ * layer per cascade) after it.
15
+ *
16
+ * @example
17
+ * const waves = new WaterWaves(device, { wind: 12, direction: 0.3 });
18
+ * await waves.init();
19
+ * // each frame:
20
+ * waves.encode(pass, timeSeconds);
21
+ */
22
+ export declare class WaterWaves {
23
+ #private;
24
+ readonly device: GPUDevice;
25
+ readonly size: number;
26
+ readonly tiles: readonly number[];
27
+ choppiness: number;
28
+ /**
29
+ * Each cascade's slope variance (per axis, its expected ∂h/∂x²): what the surface folds into roughness where a pixel
30
+ * cannot resolve the cascade.
31
+ */
32
+ readonly slopeVariance: number[];
33
+ readonly displacement: GPUTexture;
34
+ readonly slopes: GPUTexture;
35
+ constructor(device: GPUDevice, o?: WaterWavesOptions);
36
+ /**
37
+ * A new sea on the same cascades (ADR-0198): the spectra rebuilt for `sea`'s wind, fetch and direction and uploaded;
38
+ * the waves change at the next `encode` (no pipeline or texture is remade).
39
+ */
40
+ reseed(sea: SeaState): void;
41
+ /** Builds the pipelines, through the render plugin's cache when given (`size` sets their override constants). */
42
+ init(cache?: PipelineCache): Promise<void>;
43
+ /** Every cascade at time `seconds`: evolve, the FFT along rows then columns, pack into its layer. */
44
+ encode(pass: GPUComputePassEncoder, seconds: number): void;
45
+ /** The bytes this sea holds on the GPU: spectra, the FFT's two buffers, and the two texture arrays. */
46
+ get gpuBytes(): number;
47
+ destroy(): void;
48
+ }
package/dist/waves.js ADDED
@@ -0,0 +1,161 @@
1
+ import { cascadeBands, h0Table } from "./spectrum.js";
2
+ import { waterWaves } from "./water-shaders.gen.js";
3
+ /** Fields the FFT carries (eight real ones, two to a complex field). */
4
+ const FIELDS = 4;
5
+ /** STORAGE | COPY_DST | COPY_SRC (to read a cascade back for checking). */
6
+ const STORAGE = 0x80 | 0x08 | 0x04;
7
+ /** UNIFORM | COPY_DST. */
8
+ const UNIFORM = 0x40 | 0x08;
9
+ /** STORAGE_BINDING | TEXTURE_BINDING | COPY_SRC. */
10
+ const TEXTURE = 0x08 | 0x04 | 0x01;
11
+ /** One pass's parameters in their own 256-byte slot: per cascade, rows and columns. */
12
+ const SLOT = 256;
13
+ /**
14
+ * The FFT cascades of one sea. `encode` each frame (any compute pass); read `displacement` and `slopes` (texture arrays, a
15
+ * layer per cascade) after it.
16
+ *
17
+ * @example
18
+ * const waves = new WaterWaves(device, { wind: 12, direction: 0.3 });
19
+ * await waves.init();
20
+ * // each frame:
21
+ * waves.encode(pass, timeSeconds);
22
+ */
23
+ export class WaterWaves {
24
+ device;
25
+ size;
26
+ tiles;
27
+ choppiness;
28
+ /**
29
+ * Each cascade's slope variance (per axis, its expected ∂h/∂x²): what the surface folds into roughness where a pixel
30
+ * cannot resolve the cascade.
31
+ */
32
+ slopeVariance = [];
33
+ displacement;
34
+ slopes;
35
+ #cascades;
36
+ #bands = [];
37
+ #params;
38
+ #data = new ArrayBuffer(SLOT);
39
+ #evolve;
40
+ #fft;
41
+ #pack;
42
+ #groups = [];
43
+ constructor(device, o = {}) {
44
+ const size = o.size ?? 128;
45
+ if (!(size >= 32 && size <= 256 && (size & (size - 1)) === 0)) {
46
+ throw Object.assign(new Error(`WATER_SIZE: ${size} texels a side is not a power of two from 32 to 256.`), { code: 'WATER_SIZE', path: 'size' });
47
+ }
48
+ const tiles = o.tiles ?? [250, 37, 7];
49
+ if (tiles.length < 1 || tiles.some((t) => !(t > 0)))
50
+ throw Object.assign(new Error('WATER_TILES: every cascade needs a tile size above 0 m.'), { code: 'WATER_TILES', path: 'tiles' });
51
+ this.device = device;
52
+ this.size = size;
53
+ this.tiles = tiles;
54
+ this.choppiness = o.choppiness ?? 0.8;
55
+ const bands = cascadeBands(tiles, size);
56
+ this.#cascades = tiles.map((tile, c) => {
57
+ const h0 = device.createBuffer({ label: `yali:water:h0-${c}`, size: size * size * 16, usage: STORAGE });
58
+ return {
59
+ tile,
60
+ h0,
61
+ a: device.createBuffer({ label: `yali:water:a-${c}`, size: FIELDS * size * size * 8, usage: STORAGE }),
62
+ b: device.createBuffer({ label: `yali:water:b-${c}`, size: FIELDS * size * size * 8, usage: STORAGE }),
63
+ };
64
+ });
65
+ this.#bands = bands;
66
+ this.reseed(o);
67
+ // Per cascade three slots: evolve and rows (axis 0), columns (axis 1), pack (axis 0).
68
+ this.#params = device.createBuffer({ label: 'yali:water:params', size: SLOT * 3 * tiles.length, usage: UNIFORM });
69
+ const layers = { width: size, height: size, depthOrArrayLayers: tiles.length };
70
+ this.displacement = device.createTexture({ label: 'yali:water:displacement', size: layers, format: 'rgba16float', usage: TEXTURE });
71
+ this.slopes = device.createTexture({ label: 'yali:water:slopes', size: layers, format: 'rgba16float', usage: TEXTURE });
72
+ }
73
+ /**
74
+ * A new sea on the same cascades (ADR-0198): the spectra rebuilt for `sea`'s wind, fetch and direction and uploaded;
75
+ * the waves change at the next `encode` (no pipeline or texture is remade).
76
+ */
77
+ reseed(sea) {
78
+ const size = this.size;
79
+ this.slopeVariance.length = 0;
80
+ this.#cascades.forEach((c, ci) => {
81
+ const table = h0Table(size, c.tile, this.#bands[ci], sea, ci);
82
+ this.device.queue.writeBuffer(c.h0, 0, table);
83
+ // Σ k² |h(k)|² / 2: the variance of one axis's slope (h(k) carries h0(k) and h0(−k)).
84
+ const dk = (2 * Math.PI) / c.tile;
85
+ let v = 0;
86
+ for (let z = 0; z < size; z++) {
87
+ for (let x = 0; x < size; x++) {
88
+ const i = (z * size + x) * 4;
89
+ const k2 = ((x - size / 2) * dk) ** 2 + ((z - size / 2) * dk) ** 2;
90
+ v += k2 * (table[i] ** 2 + table[i + 1] ** 2 + table[i + 2] ** 2 + table[i + 3] ** 2);
91
+ }
92
+ }
93
+ this.slopeVariance.push(v / 2);
94
+ });
95
+ }
96
+ /** Builds the pipelines, through the render plugin's cache when given (`size` sets their override constants). */
97
+ async init(cache) {
98
+ const d = this.device;
99
+ const constants = { N: this.size, HALF: this.size / 2 };
100
+ const module = cache ? null : d.createShaderModule({ label: 'yali:water:waves', code: waterWaves.wgsl });
101
+ [this.#evolve, this.#fft, this.#pack] = await Promise.all(['evolve', 'fft', 'pack'].map((entryPoint) => cache ? cache.compute({ label: `yali:water:${entryPoint}`, compute: { shader: waterWaves, entryPoint, constants } }) : d.createComputePipelineAsync({ label: `yali:water:${entryPoint}`, layout: 'auto', compute: { module: module, entryPoint, constants } })));
102
+ const dv = this.displacement.createView({ dimension: '2d-array' });
103
+ const sv = this.slopes.createView({ dimension: '2d-array' });
104
+ this.#groups = this.#cascades.map((c, ci) => {
105
+ const slot = (k) => ({ binding: 3, resource: { buffer: this.#params, offset: (ci * 3 + k) * SLOT, size: 32 } });
106
+ return [
107
+ d.createBindGroup({ label: `yali:water:evolve-${ci}`, layout: this.#evolve.getBindGroupLayout(0), entries: [{ binding: 0, resource: { buffer: c.h0 } }, { binding: 1, resource: { buffer: c.a } }, slot(0)] }),
108
+ d.createBindGroup({ label: `yali:water:rows-${ci}`, layout: this.#fft.getBindGroupLayout(0), entries: [{ binding: 1, resource: { buffer: c.a } }, { binding: 2, resource: { buffer: c.b } }, slot(0)] }),
109
+ d.createBindGroup({ label: `yali:water:columns-${ci}`, layout: this.#fft.getBindGroupLayout(0), entries: [{ binding: 1, resource: { buffer: c.a } }, { binding: 2, resource: { buffer: c.b } }, slot(1)] }),
110
+ d.createBindGroup({ label: `yali:water:pack-${ci}`, layout: this.#pack.getBindGroupLayout(0), entries: [{ binding: 1, resource: { buffer: c.a } }, slot(2), { binding: 4, resource: dv }, { binding: 5, resource: sv }] }),
111
+ ];
112
+ });
113
+ }
114
+ /** Every cascade at time `seconds`: evolve, the FFT along rows then columns, pack into its layer. */
115
+ encode(pass, seconds) {
116
+ if (this.#groups.length === 0)
117
+ return;
118
+ const f = new Float32Array(this.#data);
119
+ const u = new Uint32Array(this.#data);
120
+ this.#cascades.forEach((c, ci) => {
121
+ for (let k = 0; k < 3; k++) {
122
+ f[0] = seconds;
123
+ f[1] = c.tile;
124
+ f[2] = this.choppiness;
125
+ f[3] = 0;
126
+ u[4] = k === 1 ? 1 : 0;
127
+ u[5] = ci;
128
+ this.device.queue.writeBuffer(this.#params, (ci * 3 + k) * SLOT, this.#data, 0, 32);
129
+ }
130
+ });
131
+ const n = this.size;
132
+ this.#cascades.forEach((_, ci) => {
133
+ const [evolve, rows, columns, pack] = this.#groups[ci];
134
+ pass.setPipeline(this.#evolve);
135
+ pass.setBindGroup(0, evolve);
136
+ pass.dispatchWorkgroups(n / 8, n / 8);
137
+ pass.setPipeline(this.#fft);
138
+ pass.setBindGroup(0, rows);
139
+ pass.dispatchWorkgroups(n, FIELDS);
140
+ pass.setBindGroup(0, columns);
141
+ pass.dispatchWorkgroups(n, FIELDS);
142
+ pass.setPipeline(this.#pack);
143
+ pass.setBindGroup(0, pack);
144
+ pass.dispatchWorkgroups(n / 8, n / 8);
145
+ });
146
+ }
147
+ /** The bytes this sea holds on the GPU: spectra, the FFT's two buffers, and the two texture arrays. */
148
+ get gpuBytes() {
149
+ const n = this.size * this.size;
150
+ return this.tiles.length * (n * 16 + 2 * FIELDS * n * 8 + 2 * n * 8);
151
+ }
152
+ destroy() {
153
+ for (const c of this.#cascades)
154
+ for (const b of [c.h0, c.a, c.b])
155
+ b.destroy();
156
+ this.#params.destroy();
157
+ this.displacement.destroy();
158
+ this.slopes.destroy();
159
+ }
160
+ }
161
+ //# sourceMappingURL=waves.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"waves.js","sourceRoot":"","sources":["../src/waves.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,27 @@
1
1
  {
2
2
  "name": "@yali-engine/water",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.11.0",
4
+ "description": "Yali water: an ocean's FFT cascades (JONSWAP, Donelan–Banner) and its surface, a render feature (ADR-0198).",
5
+ "license": "MIT OR Apache-2.0",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "dependencies": {
21
+ "@yali-engine/render": "0.11.0"
22
+ },
23
+ "scripts": {
24
+ "typecheck": "tsc -p tsconfig.json",
25
+ "test": "node --experimental-strip-types --no-warnings --test \"test/*.test.ts\""
26
+ }
6
27
  }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.