/ proof-of-concept / steelsim21
Sign in
proof-of-concept / steelsim21
steelsim21 / src / render / shaders.wgsl
145 lines · 5.2 KBCodeBlameHistory
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,
8dcc11fInitial-shape imperfection (Marguerre shallow-shell strains)Jeremy Magland 19 showShape: u32, // 3D: add the stress-free initial shape to the height field
c63c76aAdd elliptical sheet shape on a masked gridJeremy Magland 20 shape: u32, // 0: rectangle, 1: ellipse inscribed in the bounding box
22 pad2: u32,
25@group(0) @binding(0) var<uniform> R: RU;
26@group(0) @binding(1) var<storage, read> w: array<f32>;
8dcc11fInitial-shape imperfection (Marguerre shallow-shell strains)Jeremy Magland 27@group(0) @binding(2) var<storage, read> w0: array<f32>;
c63c76aAdd elliptical sheet shape on a masked gridJeremy Magland 28@group(0) @binding(3) var<storage, read> nw: array<f32>; // node weights, 0 outside the sheet
30fn colormap(t: f32) -> vec3f {
31 let s = clamp(t, -1.0, 1.0);
32 let mid = vec3f(0.93, 0.93, 0.93);
33 let blue = vec3f(0.10, 0.35, 0.85);
34 let red = vec3f(0.85, 0.20, 0.12);
35 if (s < 0.0) { return mix(mid, blue, -s); }
36 return mix(mid, red, s);
39fn wAt(i: i32, j: i32) -> f32 {
40 let ii = clamp(i, 0, i32(R.nx) - 1);
41 let jj = clamp(j, 0, i32(R.ny) - 1);
42 return w[u32(jj) * R.nx + u32(ii)];
c63c76aAdd elliptical sheet shape on a masked gridJeremy Magland 44fn nwAt(i: i32, j: i32) -> f32 {
45 if (i < 0 || i >= i32(R.nx) || j < 0 || j >= i32(R.ny)) { return 0.0; }
46 return nw[u32(j) * R.nx + u32(i)];
8dcc11fInitial-shape imperfection (Marguerre shallow-shell strains)Jeremy Magland 48// Height used by the 3D view: w, plus the initial shape when requested.
49fn zAt(i: i32, j: i32) -> f32 {
50 let ii = clamp(i, 0, i32(R.nx) - 1);
51 let jj = clamp(j, 0, i32(R.ny) - 1);
52 var z = w[u32(jj) * R.nx + u32(ii)];
53 if (R.showShape != 0u) { z += w0[u32(jj) * R.nx + u32(ii)]; }
54 return z;
c63c76aAdd elliptical sheet shape on a masked gridJeremy Magland 56// Slope along one axis, one-sided where a neighbour lies outside the sheet.
57fn slope(i: i32, j: i32, di: i32, dj: i32, h: f32) -> f32 {
58 let hasP = nwAt(i + di, j + dj) > 0.0;
59 let hasM = nwAt(i - di, j - dj) > 0.0;
60 if (hasP && hasM) { return (zAt(i + di, j + dj) - zAt(i - di, j - dj)) / (2.0 * h); }
61 if (hasP) { return (zAt(i + di, j + dj) - zAt(i, j)) / h; }
62 if (hasM) { return (zAt(i, j) - zAt(i - di, j - dj)) / h; }
63 return 0.0;
66// ---------- 2D view: full-screen triangle, bilinear lookup in the fragment shader ----------
68struct V2 { @builtin(position) pos: vec4f, @location(0) uv: vec2f };
70@vertex
71fn vs2d(@builtin(vertex_index) vi: u32) -> V2 {
72 var p = array<vec2f, 3>(vec2f(-1.0, -1.0), vec2f(3.0, -1.0), vec2f(-1.0, 3.0));
73 var o: V2;
74 o.pos = vec4f(p[vi], 0.0, 1.0);
75 o.uv = p[vi];
76 return o;
79@fragment
80fn fs2d(in: V2) -> @location(0) vec4f {
81 let Lx = f32(R.nx - 1u) * R.hx;
82 let Ly = f32(R.ny - 1u) * R.hy;
83 // Fit the plate into the canvas with a margin, preserving its aspect ratio.
84 let margin = 0.92;
85 var sx = margin; var sy = margin;
86 let plateAspect = Lx / Ly;
87 if (plateAspect > R.aspect) { sy = margin * R.aspect / plateAspect; } else { sx = margin * plateAspect / R.aspect; }
88 let px = in.uv.x / sx * 0.5 * Lx;
89 let py = in.uv.y / sy * 0.5 * Ly;
90 let bg = vec4f(0.11, 0.12, 0.14, 1.0);
91 if (abs(px) > 0.5 * Lx || abs(py) > 0.5 * Ly) { return bg; }
c63c76aAdd elliptical sheet shape on a masked gridJeremy Magland 92 if (R.shape == 1u) {
93 let ex = px / (0.5 * Lx); let ey = py / (0.5 * Ly);
94 if (ex * ex + ey * ey > 1.0) { return bg; }
95 }
f8a8a6cNonlinear steel sheet gong simulator with WebGPUJeremy Magland 96 let fi = (px + 0.5 * Lx) / R.hx;
97 let fj = (py + 0.5 * Ly) / R.hy;
98 let i0 = i32(floor(fi)); let j0 = i32(floor(fj));
99 let a = fi - f32(i0); let b = fj - f32(j0);
c63c76aAdd elliptical sheet shape on a masked gridJeremy Magland 100 // Bilinear interpolation over the nodes of the sheet only (outside nodes carry no value).
101 let c = vec4f((1.0 - a) * (1.0 - b), a * (1.0 - b), (1.0 - a) * b, a * b);
102 let m = vec4f(nwAt(i0, j0), nwAt(i0 + 1, j0), nwAt(i0, j0 + 1), nwAt(i0 + 1, j0 + 1));
103 let wgt = c * step(vec4f(1e-6), m);
104 let den = wgt.x + wgt.y + wgt.z + wgt.w;
105 if (den <= 0.0) { return bg; }
106 let val = dot(wgt, vec4f(wAt(i0, j0), wAt(i0 + 1, j0), wAt(i0, j0 + 1), wAt(i0 + 1, j0 + 1))) / den;
f8a8a6cNonlinear steel sheet gong simulator with WebGPUJeremy Magland 107 var col = colormap(val / R.colorRange);
108 // Markers: hole (ring) and pushed patch (dashed-looking ring).
109 let dh = distance(vec2f(px, py), vec2f(R.holeX, R.holeY));
110 if (abs(dh - R.markerR) < 0.35 * R.markerR) { col = vec3f(0.1, 0.1, 0.1); }
111 if (dh < 0.4 * R.markerR) { col = vec3f(0.11, 0.12, 0.14); }
112 let dp = distance(vec2f(px, py), vec2f(R.pushX, R.pushY));
113 if (abs(dp - R.markerR * 1.6) < 0.25 * R.markerR) { col = vec3f(0.95, 0.75, 0.1); }
114 return vec4f(col, 1.0);
117// ---------- 3D view: height field ----------
119struct V3 { @builtin(position) pos: vec4f, @location(0) val: f32, @location(1) normal: vec3f };
121@vertex
122fn vs3d(@builtin(vertex_index) vi: u32) -> V3 {
123 let i = i32(vi % R.nx); let j = i32(vi / R.nx);
124 let x = (f32(i) - 0.5 * f32(R.nx - 1u)) * R.hx;
125 let y = (f32(j) - 0.5 * f32(R.ny - 1u)) * R.hy;
126 let val = wAt(i, j);
8dcc11fInitial-shape imperfection (Marguerre shallow-shell strains)Jeremy Magland 127 let z = zAt(i, j) * R.wScale;
c63c76aAdd elliptical sheet shape on a masked gridJeremy Magland 128 let dzdx = slope(i, j, 1, 0, R.hx) * R.wScale;
129 let dzdy = slope(i, j, 0, 1, R.hy) * R.wScale;
131 o.pos = R.mvp * vec4f(x, y, z, 1.0);
132 o.val = val;
133 o.normal = normalize(vec3f(-dzdx, -dzdy, 1.0));
134 return o;
137@fragment
138fn fs3d(in: V3) -> @location(0) vec4f {
139 let l = normalize(vec3f(R.lightX, R.lightY, R.lightZ));
140 let n = normalize(in.normal);
141 let diff = abs(dot(n, l));
142 let base = colormap(in.val / R.colorRange);
143 let col = base * (0.35 + 0.65 * diff) + vec3f(0.15) * pow(diff, 24.0);
144 return vec4f(col, 1.0);
↑↓ move↵openescclose