372 lines
20 KiB
JavaScript
372 lines
20 KiB
JavaScript
// Literal port of the legacy smooth shader; dpdy is reversed for WebGPU's top-left origin.
|
|
const GPU_SMOOTH = `
|
|
struct Params { view: vec4f, world: vec4f, style: vec4f, effects: vec4f, camera: vec4f, light: vec4f, screen: vec4f, extra: vec4f }
|
|
@group(0) @binding(0) var<uniform> u: Params;
|
|
@group(0) @binding(1) var tCol: texture_2d<f32>;
|
|
@group(0) @binding(2) var tBack: texture_2d<f32>;
|
|
@group(0) @binding(3) var tTree: texture_2d<f32>;
|
|
@group(0) @binding(4) var tField: texture_2d<f32>;
|
|
@group(0) @binding(5) var tLight: texture_2d<f32>;
|
|
@group(0) @binding(6) var tGlow: texture_2d<f32>;
|
|
@group(0) @binding(7) var linear: sampler;
|
|
@group(0) @binding(8) var<uniform> su: vec4f;
|
|
@group(0) @binding(9) var tMotion: texture_2d<f32>;
|
|
fn sampleLevel(t: texture_2d<f32>, uv: vec2f, level: f32) -> vec4f { return textureSampleLevel(t,linear,uv,level); }
|
|
fn modulo(x: f32,y: f32) -> f32 { return x-y*floor(x/y); }
|
|
fn h21(inputP: vec2f) -> f32 { var p=inputP; p = fract(p * vec2f(123.34, 456.21)); p += dot(p, p + 45.32); return fract(p.x * p.y); }
|
|
fn vn(p: vec2f) -> f32 {
|
|
var i: vec2f = floor(p); var f: vec2f = fract(p); f = f * f * (3.0 - 2.0 * f);
|
|
// Round corner sums before hashing so fused arithmetic cannot give shared corners different hashes.
|
|
return mix(mix(h21(i), h21(floor(i + vec2f(1, 0))), f.x), mix(h21(floor(i + vec2f(0, 1))), h21(floor(i + vec2f(1, 1))), f.x), f.y);
|
|
}
|
|
fn fbm(inputP: vec2f) -> f32 { var p=inputP; var a: f32 = 0.5; var s: f32 = 0.0; for (var k: i32 = 0; k < 4; k++) { s += a * vn(p); p = p * 2.03 + 17.1; a *= 0.5; } return s / 0.9375; }
|
|
|
|
fn lin(t: texture_2d<f32>, q: vec2f) -> vec4f { return sampleLevel(t, q / (u.view.zw), 0.0); }
|
|
|
|
fn bsp(t: texture_2d<f32>, q: vec2f) -> vec4f {
|
|
var tc: vec2f = q - 0.5; var i: vec2f = floor(tc); var f: vec2f = tc - i; var f2: vec2f = f * f; var f3: vec2f = f2 * f;
|
|
var w0: vec2f = (1.0 - 3.0 * f + 3.0 * f2 - f3) / 6.0; var w1: vec2f = (4.0 - 6.0 * f2 + 3.0 * f3) / 6.0;
|
|
var w2: vec2f = (1.0 + 3.0 * f + 3.0 * f2 - 3.0 * f3) / 6.0; var w3: vec2f = f3 / 6.0;
|
|
var g0: vec2f = w0 + w1; var g1: vec2f = w2 + w3;
|
|
var h0: vec2f = (i - 0.5 + w1 / g0) / (u.view.zw); var h1: vec2f = (i + 1.5 + w3 / g1) / (u.view.zw);
|
|
return g0.y * (g0.x * sampleLevel(t, h0, 0.0) + g1.x * sampleLevel(t, vec2f(h1.x, h0.y), 0.0))
|
|
+ g1.y * (g0.x * sampleLevel(t, vec2f(h0.x, h1.y), 0.0) + g1.x * sampleLevel(t, h1, 0.0));
|
|
}
|
|
fn cellAt(t: texture_2d<f32>, c: vec2i) -> vec4f { return textureLoad(t, clamp(c, vec2i(0), vec2i((u.view.zw)) - 1), 0); }
|
|
fn cls(c: vec4f) -> i32 { return i32(c.a * 255.0 + 0.5); }
|
|
fn isT(n: i32) -> f32 { return select( 0.0, 1.0 ,(n == 1 || n == 4 || n == 6 || (n >= 7 && n <= 12)) ); }
|
|
fn isM(n: i32) -> f32 { return select( isT(n), 1.0 ,n == 2 ); }
|
|
fn isF(n: i32) -> f32 { return select( 0.0, 1.0 ,(n == 13 || n == 14) ); }
|
|
fn isBelt(n: i32) -> f32 { return select( 0.0, 1.0 ,(n == 7 || n == 8) ); }
|
|
fn isN(n: i32, k: i32) -> f32 { return select( 0.0, 1.0 ,n == k ); }
|
|
|
|
fn beltSD(lb: vec2f, endL: bool, endR: bool) -> f32 {
|
|
var x0: f32 = select( -2.0, 0.15 , endL ); var x1: f32 = select( 6.0, 3.85 , endR ); var r: f32 = 0.75;
|
|
var c: vec2f = vec2f((x0 + x1) * 0.5, 2.0); var hs: vec2f = vec2f((x1 - x0) * 0.5, 1.85);
|
|
var q: vec2f = abs(lb - c) - hs + r;
|
|
return length(max(q, vec2f(0.0))) + min(max(q.x, q.y), 0.0) - r;
|
|
}
|
|
|
|
fn beltLineSD(c: vec2i, lb: vec2f) -> f32 {
|
|
var caps: i32 = i32(cellAt(tTree, c).a * 255.0 + 0.5);
|
|
return beltSD(lb, (caps & 1) != 0, (caps & 2) != 0);
|
|
}
|
|
|
|
fn bandRidge(w: vec2f, lb: vec2f, dir: f32, time: f32) -> f32 {
|
|
var run: f32 = select( -1.0, 1.0 , lb.y < 2.0 );
|
|
return step(0.72, fract((w.x - dir * run * time * 30.0) * 0.5));
|
|
}
|
|
fn bandColor(lb: vec2f, rg: f32) -> vec3f { return mix(vec3f(0.2, 0.21, 0.24), vec3f(0.34, 0.35, 0.39), rg) * (select( 0.72, 1.0 ,lb.y < 2.0 )); }
|
|
|
|
${GPU_SMOOTH_SLOPE_HELPERS}
|
|
@fragment fn fs(@builtin(position) position: vec4f) -> @location(0) vec4f {
|
|
let uView=vec4f(u.extra.xy-u.view.xy,u.camera.z,u.screen.y);
|
|
let uLightMap=vec4f(u.light.xy*u.light.z-u.view.xy,u.light.z,u.light.w);
|
|
var zp: f32 = uView.z; var px: f32 = 1.0 / zp;
|
|
var sp: vec2f = vec2f(position.x, position.y);
|
|
var q: vec2f = sp * px + uView.xy;
|
|
var w: vec2f = q + (u.view.xy);
|
|
var cam: vec2f = (u.view.xy) + uView.xy;
|
|
var scr: vec2f = q - uView.xy;
|
|
var shc: f32 = uView.w * px;
|
|
var c: vec2i = vec2i(floor(q));
|
|
var qq: vec2f = q - 0.5; var c0: vec2i = vec2i(floor(qq)); var t: vec2f = qq - floor(qq);
|
|
var k0: vec4f = cellAt(tCol, c0); var k1: vec4f = cellAt(tCol, c0 + vec2i(1, 0)); var k2: vec4f = cellAt(tCol, c0 + vec2i(0, 1)); var k3: vec4f = cellAt(tCol, c0 + vec2i(1, 1));
|
|
var n0: i32 = cls(k0); var n1: i32 = cls(k1); var n2: i32 = cls(k2); var n3: i32 = cls(k3);
|
|
let motionTime = cellAt(tMotion, c).r;
|
|
var own: vec4f = cellAt(tCol, c); var nc: i32 = cls(own);
|
|
var s: vec2f = smoothstep(vec2f(0.1), vec2f(0.9), t);
|
|
var wq: vec4f = vec4f((1.0 - s.x) * (1.0 - s.y), s.x * (1.0 - s.y), (1.0 - s.x) * s.y, s.x * s.y);
|
|
|
|
// Fields and screen derivatives before branching.
|
|
var Fb: vec4f = bsp(tField, q);
|
|
var F: vec4f = mix(lin(tField, q), Fb, 0.62);
|
|
|
|
var wl: vec4f = vec4f((1.0 - t.x) * (1.0 - t.y), t.x * (1.0 - t.y), (1.0 - t.x) * t.y, t.x * t.y);
|
|
var pw: f32 = dot(wl, vec4f(isN(n0, 2), isN(n1, 2), isN(n2, 2), isN(n3, 2)));
|
|
var M: f32 = clamp(F.r + pw, 0.0, 1.0);
|
|
var lf: f32 = clamp(mix(F.g, Fb.g, 0.6) + F.r + pw, 0.0, 1.0);
|
|
var fwT: f32 = max(fwidth(F.r), 1e-4); var fwM: f32 = max(fwidth(M), 1e-4); var fwA: f32 = max(fwidth(F.a), 1e-4); var fwL: f32 = max(fwidth(lf), 1e-4);
|
|
var gT: vec2f = vec2f(dpdx(mix(Fb.r, M, 0.5)), -dpdy(mix(Fb.r, M, 0.5))); var gL: vec2f = vec2f(dpdx(F.g), -dpdy(F.g));
|
|
|
|
// Sky, clouds, parallax hills and back-wall AO.
|
|
var back: vec4f = lin(tBack, q);
|
|
var C: vec3f = back.rgb;
|
|
var sky: f32 = back.a;
|
|
if (sky > 0.01) {
|
|
var S: vec3f = C;
|
|
var P0: vec2f = scr + cam * 0.08;
|
|
|
|
var cl: f32 = smoothstep(0.56 - 0.3 * (u.style.y), 0.78 - 0.2 * (u.style.y), fbm(vec2f(P0.x * 0.016 + (su.x) * 0.006, P0.y * 0.05)));
|
|
var hi: f32 = clamp(((su.y) - 40.0 - w.y) / 50.0, 0.0, 1.0);
|
|
S = mix(S, mix(vec3f(0.96, 0.95, 0.93), vec3f(0.5, 0.52, 0.56), (u.style.y)), cl * (0.6 + 0.3 * (u.style.y)) * hi);
|
|
var k1: f32 = 0.16; var P1: vec2f = scr + cam * k1;
|
|
var base1: f32 = shc * 0.5 + 8.0 + ((su.y) - shc * 0.5) * k1;
|
|
var top1: f32 = base1 - 36.0 * (0.25 + 0.9 * fbm(vec2f(P1.x * 0.009, 1.7)));
|
|
var m1: f32 = smoothstep(top1 - px * 1.5, top1 + px * 1.5, P1.y);
|
|
var far: vec3f = mix(vec3f(0.50, 0.60, 0.72), S, 0.35) * (0.94 + 0.08 * fbm(P1 * 0.05));
|
|
S = mix(S, far, m1);
|
|
var k2: f32 = 0.38; var P2: vec2f = scr + cam * k2;
|
|
var base2: f32 = shc * 0.5 + 14.0 + ((su.y) - shc * 0.5) * k2;
|
|
var top2: f32 = base2 - 20.0 * (0.2 + 0.9 * fbm(vec2f(P2.x * 0.016, 7.3)));
|
|
var m2: f32 = smoothstep(top2 - px * 1.5, top2 + px * 1.5, P2.y);
|
|
var near: vec3f = vec3f(0.36, 0.5, 0.47) * (0.92 + 0.14 * fbm(P2 * 0.08));
|
|
near = mix(near, S, 0.42 - 0.2 * smoothstep(top2, top2 + 30.0, P2.y));
|
|
S = mix(S, near, m2);
|
|
C = mix(C, S, sky);
|
|
}
|
|
|
|
var notSky: f32 = 1.0 - sky;
|
|
C *= mix(1.0, 1.08 + 0.4 * fbm(w * 0.21), notSky);
|
|
var near: f32 = sampleLevel(tField, q / (u.view.zw), 2.2).r;
|
|
C *= 1.0 - 0.34 * smoothstep(0.12, 0.85, near) * (1.0 - 0.75 * sky);
|
|
|
|
// Trees behind terrain and built pieces.
|
|
if (F.a > 0.2) {
|
|
var a0: vec4f = cellAt(tTree, c0); var a1: vec4f = cellAt(tTree, c0 + vec2i(1, 0)); var a2: vec4f = cellAt(tTree, c0 + vec2i(0, 1)); var a3: vec4f = cellAt(tTree, c0 + vec2i(1, 1));
|
|
var m: vec4f = vec4f(step(0.4, a0.a), step(0.4, a1.a), step(0.4, a2.a), step(0.4, a3.a)) * wq;
|
|
var sm: f32 = m.x + m.y + m.z + m.w;
|
|
if (sm > 1e-4) {
|
|
var tc: vec3f = (a0.rgb * m.x + a1.rgb * m.y + a2.rgb * m.z + a3.rgb * m.w) / sm;
|
|
var leaf: f32 = (a0.a * m.x + a1.a * m.y + a2.a * m.z + a3.a * m.w) / sm;
|
|
var lv: f32 = smoothstep(0.6, 0.9, leaf);
|
|
tc *= mix(0.86 + 0.22 * vn(vec2f(w.x * 1.4, w.y * 0.25)), 0.72 + 0.5 * fbm(w * 0.42), lv);
|
|
var d: f32 = (F.a - 0.36) / fwA;
|
|
tc *= mix(0.55, 1.0, smoothstep(0.4, 1.6, d));
|
|
C = mix(C, tc, smoothstep(-0.7, 0.7, d));
|
|
}
|
|
}
|
|
|
|
// Smoke, steam and flame.
|
|
if (Fb.b > 0.02) {
|
|
var m: vec4f = vec4f(isN(n0, 5), isN(n1, 5), isN(n2, 5), isN(n3, 5)) * wq;
|
|
var sm: f32 = m.x + m.y + m.z + m.w;
|
|
var gc: vec3f = select( own.rgb, (k0.rgb * m.x + k1.rgb * m.y + k2.rgb * m.z + k3.rgb * m.w) / sm , sm > 1e-4 );
|
|
if (sm > 1e-4 || nc == 5) { C = mix(C, gc, smoothstep(0.03, 0.55, Fb.b) * 0.92); }
|
|
}
|
|
|
|
// Liquids with a glossy top edge.
|
|
var anyL: f32 = isN(n0, 3) + isN(n1, 3) + isN(n2, 3) + isN(n3, 3);
|
|
if (anyL > 0.0 && lf > 0.3) {
|
|
var m: vec4f = vec4f(isN(n0, 3), isN(n1, 3), isN(n2, 3), isN(n3, 3)) * (wq + 0.02);
|
|
var sm: f32 = m.x + m.y + m.z + m.w;
|
|
var lc: vec3f = (k0.rgb * m.x + k1.rgb * m.y + k2.rgb * m.z + k3.rgb * m.w) / sm;
|
|
lc *= 0.94 + 0.1 * vn(w * 0.55 + vec2f((su.x) * 0.7, (su.x) * 0.25));
|
|
var gl: f32 = length(gL);
|
|
var topSide: f32 = select( 0.0, clamp(gL.y / gl, 0.0, 1.0) , gl > 1e-5 );
|
|
var band: f32 = 1.0 - smoothstep(0.5, 0.75, F.g);
|
|
lc += vec3f(0.16, 0.18, 0.2) * topSide * band;
|
|
C = mix(C, lc, smoothstep(0.5 - fwL, 0.5 + fwL, lf));
|
|
}
|
|
|
|
// Rounded ground and the seam against loose grains.
|
|
if (F.r > 0.3) {
|
|
var m: vec4f = vec4f(isT(n0), isT(n1), isT(n2), isT(n3)) * wq;
|
|
var sm: f32 = m.x + m.y + m.z + m.w;
|
|
var blur: vec3f = bsp(tCol, q).rgb; var tc: vec3f = own.rgb;
|
|
var bd: f32 = 1e9;
|
|
if (m.x > 0.0) { var e: vec3f = k0.rgb - blur; var dd: f32 = dot(e, e); if (dd < bd) { bd = dd; tc = k0.rgb; } }
|
|
if (m.y > 0.0) { var e: vec3f = k1.rgb - blur; var dd: f32 = dot(e, e); if (dd < bd) { bd = dd; tc = k1.rgb; } }
|
|
if (m.z > 0.0) { var e: vec3f = k2.rgb - blur; var dd: f32 = dot(e, e); if (dd < bd) { bd = dd; tc = k2.rgb; } }
|
|
if (m.w > 0.0) { var e: vec3f = k3.rgb - blur; var dd: f32 = dot(e, e); if (dd < bd) { bd = dd; tc = k3.rgb; } }
|
|
if (sm < 1e-4) { tc = own.rgb; }
|
|
tc *= 0.84 + 0.3 * fbm(w * 0.28);
|
|
tc *= 0.95 + 0.1 * vn(w * 1.6);
|
|
var gl: f32 = length(gT);
|
|
var nrm: vec2f = select( vec2f(0.0), -gT / gl , gl > 1e-6 );
|
|
var band: f32 = 1.0 - smoothstep(0.5, 0.9, M);
|
|
var lit: f32 = dot(nrm, normalize(vec2f(-0.5, -1.0)));
|
|
tc *= 1.0 + 0.42 * lit * band;
|
|
|
|
var dm: f32 = (M - 0.5) / fwM;
|
|
tc *= mix(0.4, 1.0, smoothstep(0.5, 1.4 + zp * 0.08, dm));
|
|
var dt: f32 = (F.r - 0.5) / fwT;
|
|
if (nc == 1 && pw > 0.0) { tc *= mix(0.65, 1.0, smoothstep(0.0, 1.5, dt)); }
|
|
C = mix(C, tc, smoothstep(-0.6, 0.6, (F.r - 0.5) / fwT));
|
|
}
|
|
|
|
${GPU_SMOOTH_SLOPE_DRAW}
|
|
// Round loose grains.
|
|
var np: i32 = i32(isN(n0, 2) + isN(n1, 2) + isN(n2, 2) + isN(n3, 2));
|
|
var closed: bool = isM(n0) + isM(n1) + isM(n2) + isM(n3) > 3.5;
|
|
if (np > 0) {
|
|
var best: f32 = 1e9; var gc: vec3f = vec3f(0.0); var gd: vec2f = vec2f(0.0); var gr: f32 = 0.6;
|
|
for (var k: i32 = 0; k < 4; k++) {
|
|
var o4: vec2i = vec2i(k & 1, k >> 1);
|
|
var nk: i32 = select(select(select( n3, n2 , k == 2 ), n1 , k == 1 ), n0 , k == 0 );
|
|
if (nk != 2) { continue; }
|
|
var ck: vec4f = select(select(select( k3, k2 , k == 2 ), k1 , k == 1 ), k0 , k == 0 );
|
|
var ce: vec2f = vec2f(c0 + o4) + 0.5;
|
|
var r: f32 = 0.6 + 0.1 * (h21(ce + (u.view.xy)) - 0.5);
|
|
var dv: vec2f = (q - ce) / r;
|
|
var dd: f32 = length(dv);
|
|
if (dd < best) { best = dd; gc = ck.rgb; gd = dv; gr = r; }
|
|
}
|
|
var cover: f32 = select( 1.0 - smoothstep(1.0 - px / gr, 1.0 + px / gr, best), 1.0 , np == 4 );
|
|
if (closed && F.r < 0.5) { cover = 1.0; }
|
|
|
|
cover *= 1.0 - smoothstep(-0.6, 0.6, (F.r - 0.5) / fwT);
|
|
if (cover > 0.0) {
|
|
var r2: f32 = min(1.0, dot(gd, gd));
|
|
var n3: vec3f = normalize(vec3f(gd * 0.85, sqrt(1.0 - r2) + 0.25));
|
|
var sh: f32 = 0.98 + 0.18 * max(0.0, dot(n3, normalize(vec3f(-0.45, -0.8, 0.6))));
|
|
sh *= mix(1.0, 0.94, smoothstep(0.7 - px * 0.2, 1.15 + px * 0.2, best));
|
|
var pc: vec3f = (select( gc, (k0.rgb * wq.x + k1.rgb * wq.y + k2.rgb * wq.z + k3.rgb * wq.w) ,np == 4 )) * sh;
|
|
if (!closed) { pc *= mix(1.0, 0.72, smoothstep(0.7, 1.0, best)); }
|
|
C = mix(C, pc, cover);
|
|
}
|
|
}
|
|
|
|
// Built pieces: belts, screws, wall tiles and filters.
|
|
if (nc == 4) { C = own.rgb; }
|
|
else if (nc == 7 || nc == 8) {
|
|
|
|
var dir: f32 = select( -1.0, 1.0 , nc == 8 );
|
|
var lb: vec2f = fract(w * 0.25) * 4.0;
|
|
|
|
var sd: f32 = beltLineSD(c, lb);
|
|
var inside: f32 = 1.0 - smoothstep(-px, px, sd);
|
|
if (inside > 0.0) {
|
|
var edge: f32 = -sd;
|
|
var bc: vec3f;
|
|
if (edge < 0.8) {
|
|
bc = bandColor(lb, bandRidge(w, lb, dir, motionTime));
|
|
bc *= mix(0.55, 1.0, smoothstep(0.0, max(px * 1.4, 0.12), edge));
|
|
} else {
|
|
var rc: vec2f = vec2f(select( 3.0, 1.0 ,lb.x < 2.0 ), 2.0); var dv: vec2f = lb - rc;
|
|
var rd: f32 = length(dv);
|
|
bc = vec3f(0.15, 0.16, 0.18);
|
|
if (rd < 0.95) {
|
|
var ang: f32 = atan2(dv.y, dv.x) - dir * motionTime * 30.0 / 0.95;
|
|
var spoke: f32 = smoothstep(0.55, 0.85, cos(ang * 3.0));
|
|
bc = mix(vec3f(0.5, 0.36, 0.25), vec3f(0.72, 0.53, 0.36), 0.5 + 0.5 * dot(normalize(dv + 1e-4), vec2f(-0.6, -0.8)) * smoothstep(0.2, 0.95, rd));
|
|
bc *= mix(1.0, 0.72, spoke * smoothstep(0.25, 0.4, rd));
|
|
bc *= mix(1.0, 0.55, smoothstep(0.82, 0.95, rd));
|
|
}
|
|
}
|
|
C = mix(C, bc, inside);
|
|
}
|
|
}
|
|
else if ((nc >= 9 && nc <= 12) || (nc >= 19 && nc <= 22)) {
|
|
|
|
var vert: bool = nc == 9 || nc == 11;
|
|
var sp2: f32 = select(select(select( -1.0, 1.0 , nc == 11 ), 1.0 , nc == 10 ), -1.0 , nc == 9 );
|
|
var a: f32 = (select( fract(w.y * 0.25), fract(w.x * 0.25) ,vert )) * 4.0; var along: f32 = select( w.x, w.y , vert );
|
|
var screwSD: f32 = -1.0; var capRing: f32 = 0.0;
|
|
var screwTag: vec4f = cellAt(tTree, c);
|
|
var screwJoins: i32 = i32(screwTag.a * 255.0 + 0.5);
|
|
|
|
var shaped: bool = nc >= 19 || (screwJoins > 0 && screwJoins < 64);
|
|
if (shaped) {
|
|
var d: vec2f = vec2f(1.0,select( 1.0, -1.0 , nc == 19 || nc == 21 ));
|
|
var tag: vec4f = screwTag;
|
|
var local: vec2f = floor(tag.gb * 255.0 + 0.5) - 8.0 + fract(w) - 2.0;
|
|
var joins: i32 = screwJoins;
|
|
screwSD = 100.0;
|
|
for (var end: i32 = 0; end < 2; end++) {
|
|
var join: i32 = (joins >> u32(end * 3)) & 7;
|
|
var side: f32 = select( 1.0, -1.0 , end == 0 );
|
|
var diagonal: bool = select( join != 0, join == 0 || join >= 5 , nc >= 19 );
|
|
var grade: f32 = select(select( 1.0, -1.0 , join == 1 ), d.y , nc >= 19 );
|
|
var ray: vec2f = select(select(select(select(select( vec2f(-1, 0), vec2f(0, 1) , join == 3 ), vec2f(1, 0) , join == 2 ), vec2f(0, -1) ,
|
|
join == 1 ), (select( vec2f(side, 0), vec2f(0, side) ,vert )) ,
|
|
nc < 19 ), (select( vec2f(side, side * grade), vec2f(side * grade, side) ,vert )) * 0.70710678 , diagonal );
|
|
var t: f32 = dot(local, ray); var cross2: f32 = dot(local, vec2f(-ray.y, ray.x));
|
|
var radius: f32 = length(vec2f(cross2, min(0.0, t))); var cap: f32 = t - (select( 2.0, 2.82842712 ,diagonal ));
|
|
|
|
var sd: f32 = max(radius - 2.0,select( -10.0, cap , (joins & (64 << u32(end))) != 0 ));
|
|
if (sd < screwSD) {
|
|
screwSD = sd;
|
|
a = 2.0 + (select( radius, -radius ,side * cross2 < 0.0 ));
|
|
along = (select(select( w.x - local.x, w.y - local.y , vert ), dot(w - local, d) * 0.70710678 ,nc >= 19 )) + side * t;
|
|
capRing = select( 0.0, smoothstep(-0.55, -0.15, cap) ,(joins & (64 << u32(end))) != 0 );
|
|
}
|
|
}
|
|
if (nc >= 19) { sp2 = select( -1.0, 1.0 ,nc == 19 || nc == 20 ); }
|
|
}
|
|
var wall: f32 = 0.62;
|
|
var tc: vec3f;
|
|
if (a < wall || a > 4.0 - wall || capRing > 0.5) {
|
|
var u2: f32 = select( (4.0 - a) / wall, a / wall , a < wall );
|
|
var cyl: f32 = sin(clamp(u2, 0.0, 1.0) * 1.5708);
|
|
tc = vec3f(0.62, 0.41, 0.27) * (0.6 + 0.55 * cyl);
|
|
if (a < wall) { tc *= 1.08; }
|
|
var ring: f32 = 1.0 - smoothstep(0.0, 0.3, min(fract(along * 0.25), 1.0 - fract(along * 0.25)) * 4.0);
|
|
if (nc < 19) { tc *= 1.0 - 0.3 * ring; }
|
|
tc *= mix(0.5, 1.0, smoothstep(0.0, max(px * 1.3, 0.1), min(a, 4.0 - a)));
|
|
} else {
|
|
var full: bool = own.r + own.g + own.b > 0.0;
|
|
var fl: f32 = fract((along + a * 0.9 - sp2 * motionTime * 14.0) * 0.5);
|
|
var flight: f32 = smoothstep(0.82, 0.92, fl) * (1.0 - smoothstep(0.92, 1.0, fl));
|
|
var bore: vec3f = vec3f(0.11, 0.1, 0.11) * (0.8 + 0.4 * (1.0 - abs(a - 2.0) / 1.4));
|
|
tc = select( bore, own.rgb * (0.9 + 0.1 * vn(w * 1.3)) ,full );
|
|
tc = mix(tc, vec3f(0.6, 0.48, 0.38), flight * (select( 0.8, 0.35 ,full )));
|
|
tc *= 1.0 - 0.25 * smoothstep(0.9, 1.3, abs(a - 2.0));
|
|
}
|
|
if (shaped) {
|
|
var full: bool = own.r + own.g + own.b > 0.0;
|
|
if (full) { C = mix(C, own.rgb, 1.0 - smoothstep(0.36, 0.5, length(fract(w) - 0.5))); }
|
|
C = mix(C, tc, 1.0 - smoothstep(2.0 - px, 2.0 + px, screwSD + 2.0));
|
|
} else { C = tc; }
|
|
}
|
|
else if (nc == 6 || (nc >= 15 && nc <= 18)) {
|
|
var lb: vec2f = fract(w * 0.25) * 4.0; var rb: vec2f = 4.0 - lb;
|
|
var e: f32 = min(min(lb.x, lb.y), min(rb.x, rb.y));
|
|
var tc: vec3f = own.rgb * (0.92 + 0.14 * fbm(w * 0.55));
|
|
var hl: f32 = max(1.0 - smoothstep(0.0, 0.8, lb.x), 1.0 - smoothstep(0.0, 0.8, lb.y));
|
|
var shd: f32 = max(1.0 - smoothstep(0.0, 0.8, rb.x), 1.0 - smoothstep(0.0, 0.8, rb.y));
|
|
tc *= 1.0 + 0.2 * hl - 0.26 * shd;
|
|
tc *= mix(0.5, 1.0, smoothstep(px * 0.5, px * 0.5 + max(px * 1.2, 0.14), e));
|
|
if (nc >= 15) {
|
|
var sl: vec2f = lb;
|
|
if (nc == 16 || nc == 18) { sl.x = 4.0 - sl.x; }
|
|
if (nc >= 17) { sl.y = 4.0 - sl.y; }
|
|
var d: f32 = (sl.y - sl.x) * 0.70710678;
|
|
tc *= 1.0 + 0.2 * (1.0 - smoothstep(0.0, 0.6, d));
|
|
C = mix(C, tc, smoothstep(-px * 0.5, px * 0.5, d));
|
|
} else { C = tc; }
|
|
}
|
|
|
|
else if (nc == 13 || nc == 14) {
|
|
|
|
var dir: f32 = select( -1.0, 1.0 , nc == 14 );
|
|
var lb: vec2f = fract(w * 0.25) * 4.0;
|
|
var tag: vec4f = cellAt(tTree, c);
|
|
var carry: bool = tag.a > 0.16; var tinted: bool = tag.r + tag.g + tag.b > 0.0;
|
|
var sd: f32 = beltLineSD(c, lb);
|
|
var inside: f32 = 1.0 - smoothstep(-px, px, sd);
|
|
if (inside > 0.0) {
|
|
var edge: f32 = -sd;
|
|
var steel: vec3f = vec3f(0.42, 0.44, 0.5);
|
|
|
|
var behind: vec3f = select( C * 0.8, own.rgb , own.r + own.g + own.b > 0.0 );
|
|
var cg: vec2f = abs(fract(w) - 0.5);
|
|
var bw2: f32 = max(px * 0.9, 0.09);
|
|
var hole: f32 = 1.0 - smoothstep(0.5 - bw2 - px, 0.5 - bw2, max(cg.x, cg.y));
|
|
var fc: vec3f;
|
|
if (edge < 0.8) {
|
|
var rg: f32 = bandRidge(w, lb, dir, motionTime);
|
|
var band: vec3f = bandColor(lb, rg);
|
|
if (tinted && (carry == (lb.y < 2.0))) { band = mix(band, tag.rgb * (select( 0.8, 1.0 ,lb.y < 2.0 )), 0.75 * rg); }
|
|
fc = mix(band, behind * 0.85, hole * (1.0 - rg) * 0.75);
|
|
fc *= mix(0.55, 1.0, smoothstep(0.0, max(px * 1.4, 0.12), edge));
|
|
} else {
|
|
fc = mix(steel * 0.78, behind, hole);
|
|
fc *= mix(0.7, 1.0, smoothstep(0.0, max(px * 1.4, 0.12), edge - 0.8));
|
|
}
|
|
C = mix(C, fc, inside);
|
|
}
|
|
}
|
|
|
|
// Light, then the legacy nine-tap glow filter.
|
|
if (uLightMap.w > 0.5) { C *= sampleLevel(tLight, (q - uLightMap.xy) / uLightMap.z / (vec2f(textureDimensions(tLight))), 0.0).rgb; }
|
|
if ((su.z) > 0.0) {
|
|
var gu: vec2f = q / (2.0 * (vec2f(textureDimensions(tGlow)))); var d: vec2f = 1.0 / (vec2f(textureDimensions(tGlow)));
|
|
var g: vec3f = sampleLevel(tGlow, gu, 0.0).rgb * 0.25;
|
|
g += (sampleLevel(tGlow, gu + vec2f(d.x, 0), 0.0).rgb + sampleLevel(tGlow, gu - vec2f(d.x, 0), 0.0).rgb + sampleLevel(tGlow, gu + vec2f(0, d.y), 0.0).rgb + sampleLevel(tGlow, gu - vec2f(0, d.y), 0.0).rgb) * 0.125;
|
|
g += (sampleLevel(tGlow, gu + d, 0.0).rgb + sampleLevel(tGlow, gu - d, 0.0).rgb + sampleLevel(tGlow, gu + vec2f(d.x, -d.y), 0.0).rgb + sampleLevel(tGlow, gu + vec2f(-d.x, d.y), 0.0).rgb) * 0.0625;
|
|
C += g * (su.z);
|
|
}
|
|
return vec4f(C, 1.0);
|
|
}`;
|