1struct RU {
2 mvp: mat4x4f,
3 nx: u32,
4 ny: u32,
5 hx: f32,
6 hy: f32,
7 wScale: f32, // vertical exaggeration in the 3D view
8 colorRange: f32, // |w| mapped to full color [m]
9 aspect: f32, // canvas width / height
10 mode: u32, // 0: 2D, 1: 3D
11 holeX: f32,
12 holeY: f32,
13 pushX: f32,
14 pushY: f32,
15 markerR: f32,
16 lightX: f32,
17 lightY: f32,
18 lightZ: f32,
19 showShape: u32, // 3D: add the stress-free initial shape to the height field
20 pad0: u32,
21 pad1: u32,
22 pad2: u32,
23};
25@group(0) @binding(0) var<uniform> R: RU;
26@group(0) @binding(1) var<storage, read> w: array<f32>;
27@group(0) @binding(2) var<storage, read> w0: array<f32>;
29fn colormap(t: f32) -> vec3f {
30 let s = clamp(t, -1.0, 1.0);
31 let mid = vec3f(0.93, 0.93, 0.93);
32 let blue = vec3f(0.10, 0.35, 0.85);
33 let red = vec3f(0.85, 0.20, 0.12);
34 if (s < 0.0) { return mix(mid, blue, -s); }
35 return mix(mid, red, s);
36}
38fn wAt(i: i32, j: i32) -> f32 {
39 let ii = clamp(i, 0, i32(R.nx) - 1);
40 let jj = clamp(j, 0, i32(R.ny) - 1);
41 return w[u32(jj) * R.nx + u32(ii)];
42}
43// Height used by the 3D view: w, plus the initial shape when requested.
44fn zAt(i: i32, j: i32) -> f32 {
45 let ii = clamp(i, 0, i32(R.nx) - 1);
46 let jj = clamp(j, 0, i32(R.ny) - 1);
47 var z = w[u32(jj) * R.nx + u32(ii)];
48 if (R.showShape != 0u) { z += w0[u32(jj) * R.nx + u32(ii)]; }
49 return z;
50}
52// ---------- 2D view: full-screen triangle, bilinear lookup in the fragment shader ----------
54struct V2 { @builtin(position) pos: vec4f, @location(0) uv: vec2f };
56@vertex
57fn vs2d(@builtin(vertex_index) vi: u32) -> V2 {
58 var p = array<vec2f, 3>(vec2f(-1.0, -1.0), vec2f(3.0, -1.0), vec2f(-1.0, 3.0));
59 var o: V2;
60 o.pos = vec4f(p[vi], 0.0, 1.0);
61 o.uv = p[vi];
62 return o;
63}
65@fragment
66fn fs2d(in: V2) -> @location(0) vec4f {
67 let Lx = f32(R.nx - 1u) * R.hx;
68 let Ly = f32(R.ny - 1u) * R.hy;
69 // Fit the plate into the canvas with a margin, preserving its aspect ratio.
70 let margin = 0.92;
71 var sx = margin; var sy = margin;
72 let plateAspect = Lx / Ly;
73 if (plateAspect > R.aspect) { sy = margin * R.aspect / plateAspect; } else { sx = margin * plateAspect / R.aspect; }
74 let px = in.uv.x / sx * 0.5 * Lx;
75 let py = in.uv.y / sy * 0.5 * Ly;
76 let bg = vec4f(0.11, 0.12, 0.14, 1.0);
77 if (abs(px) > 0.5 * Lx || abs(py) > 0.5 * Ly) { return bg; }
78 let fi = (px + 0.5 * Lx) / R.hx;
79 let fj = (py + 0.5 * Ly) / R.hy;
80 let i0 = i32(floor(fi)); let j0 = i32(floor(fj));
81 let a = fi - f32(i0); let b = fj - f32(j0);
82 let val = mix(mix(wAt(i0, j0), wAt(i0 + 1, j0), a), mix(wAt(i0, j0 + 1), wAt(i0 + 1, j0 + 1), a), b);
83 var col = colormap(val / R.colorRange);
84 // Markers: hole (ring) and pushed patch (dashed-looking ring).
85 let dh = distance(vec2f(px, py), vec2f(R.holeX, R.holeY));
86 if (abs(dh - R.markerR) < 0.35 * R.markerR) { col = vec3f(0.1, 0.1, 0.1); }
87 if (dh < 0.4 * R.markerR) { col = vec3f(0.11, 0.12, 0.14); }
88 let dp = distance(vec2f(px, py), vec2f(R.pushX, R.pushY));
89 if (abs(dp - R.markerR * 1.6) < 0.25 * R.markerR) { col = vec3f(0.95, 0.75, 0.1); }
90 return vec4f(col, 1.0);
91}
93// ---------- 3D view: height field ----------
95struct V3 { @builtin(position) pos: vec4f, @location(0) val: f32, @location(1) normal: vec3f };
97@vertex
98fn vs3d(@builtin(vertex_index) vi: u32) -> V3 {
99 let i = i32(vi % R.nx); let j = i32(vi / R.nx);
100 let x = (f32(i) - 0.5 * f32(R.nx - 1u)) * R.hx;
101 let y = (f32(j) - 0.5 * f32(R.ny - 1u)) * R.hy;
102 let val = wAt(i, j);
103 let z = zAt(i, j) * R.wScale;
104 let dzdx = (zAt(i + 1, j) - zAt(i - 1, j)) * R.wScale / (2.0 * R.hx);
105 let dzdy = (zAt(i, j + 1) - zAt(i, j - 1)) * R.wScale / (2.0 * R.hy);
106 var o: V3;
107 o.pos = R.mvp * vec4f(x, y, z, 1.0);
108 o.val = val;
109 o.normal = normalize(vec3f(-dzdx, -dzdy, 1.0));
110 return o;
111}
113@fragment
114fn fs3d(in: V3) -> @location(0) vec4f {
115 let l = normalize(vec3f(R.lightX, R.lightY, R.lightZ));
116 let n = normalize(in.normal);
117 let diff = abs(dot(n, l));
118 let base = colormap(in.val / R.colorRange);
119 let col = base * (0.35 + 0.65 * diff) + vec3f(0.15) * pow(diff, 24.0);
120 return vec4f(col, 1.0);
121}