From e38be0dbf5816338ff97e2ee2f9adfff2902dc2b Mon Sep 17 00:00:00 2001 From: skal Date: Sat, 14 Feb 2026 15:13:09 +0100 Subject: refactor(wgsl): modularize common shader functions Extracted common WGSL functions into separate files in `common/shaders/` to improve reusability and maintainability. - Created `common/shaders/render/fullscreen_vs.wgsl` for a reusable fullscreen vertex shader. - Created `common/shaders/math/color.wgsl` for color conversion and tone mapping functions. - Created `common/shaders/math/utils.wgsl` for general math utilities. - Created `common/shaders/render/raymarching.wgsl` for SDF raymarching logic. - Updated multiple shaders to use these new common snippets via `#include`. - Fixed the shader asset validation test to correctly handle shaders that include the common vertex shader. This refactoring makes the shader code more modular and easier to manage. --- workspaces/main/shaders/scene1.wgsl | 116 +++--------------------------------- 1 file changed, 8 insertions(+), 108 deletions(-) (limited to 'workspaces/main/shaders/scene1.wgsl') diff --git a/workspaces/main/shaders/scene1.wgsl b/workspaces/main/shaders/scene1.wgsl index 7af3811..2a811f7 100644 --- a/workspaces/main/shaders/scene1.wgsl +++ b/workspaces/main/shaders/scene1.wgsl @@ -2,29 +2,15 @@ // Source: Saturday cubism experiment by skal #include "common_uniforms" +#include "math/color" +#include "math/utils" +#include "sdf_primitives" +#include "render/raymarching" @group(0) @binding(0) var uniforms: CommonUniforms; const PI: f32 = 3.141592654; const TAU: f32 = 6.283185307; -const TOLERANCE: f32 = 0.0005; -const MAX_RAY_LENGTH: f32 = 20.0; -const MAX_RAY_MARCHES: i32 = 80; -const MAX_SHD_MARCHES: i32 = 20; -const NORM_OFF: f32 = 0.005; - -fn rot(a: f32) -> mat2x2 { - let c = cos(a); - let s = sin(a); - return mat2x2(c, s, -s, c); -} - -// HSV to RGB conversion -const hsv2rgb_K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); -fn hsv2rgb(c: vec3) -> vec3 { - let p = abs(fract(c.xxx + hsv2rgb_K.xyz) * 6.0 - hsv2rgb_K.www); - return c.z * mix(hsv2rgb_K.xxx, clamp(p - hsv2rgb_K.xxx, vec3(0.0), vec3(1.0)), c.y); -} // Colors (precomputed HSV conversions) const skyCol = vec3(0.176, 0.235, 0.25); // HSV(0.57, 0.90, 0.25) @@ -38,44 +24,10 @@ const sunDir1 = vec3(0.0, 0.04997, -0.99875); // normalize(0, 0.05, -1) const lightPos1 = vec3(10.0, 10.0, 10.0); const lightPos2 = vec3(-10.0, 10.0, -10.0); -fn sRGB(t: vec3) -> vec3 { - return mix(1.055 * pow(t, vec3(1.0/2.4)) - 0.055, 12.92 * t, step(t, vec3(0.0031308))); -} - -fn aces_approx(v_in: vec3) -> vec3 { - var v = max(v_in, vec3(0.0)); - v *= 0.6; - let a = 2.51; - let b = 0.03; - let c = 2.43; - let d = 0.59; - let e = 0.14; - return clamp((v * (a * v + b)) / (v * (c * v + d) + e), vec3(0.0), vec3(1.0)); -} - -fn tanh_approx(x: f32) -> f32 { - let x2 = x * x; - return clamp(x * (27.0 + x2) / (27.0 + 9.0 * x2), -1.0, 1.0); -} - fn rayPlane(ro: vec3, rd: vec3, plane: vec4) -> f32 { return -(dot(ro, plane.xyz) + plane.w) / dot(rd, plane.xyz); } -fn box2d(p: vec2, b: vec2) -> f32 { - let d = abs(p) - b; - return length(max(d, vec2(0.0))) + min(max(d.x, d.y), 0.0); -} - -fn box3d(p: vec3, b: vec3) -> f32 { - let q = abs(p) - b; - return length(max(q, vec3(0.0))) + min(max(q.x, max(q.y, q.z)), 0.0); -} - -fn sphere(p: vec3, r: f32) -> f32 { - return length(p) - r; -} - var g_rot0: mat2x2; fn render0(ro: vec3, rd: vec3) -> vec3 { @@ -90,7 +42,7 @@ fn render0(ro: vec3, rd: vec3) -> vec3 { if (tp1 > 0.0) { let pos = ro + tp1 * rd; let pp = pos.xz; - let db = box2d(pp, vec2(5.0, 9.0)) - 3.0; + let db = sdBox2D(pp, vec2(5.0, 9.0)) - 3.0; col += vec3(4.0) * skyCol * rd.y * rd.y * smoothstep(0.25, 0.0, db); col += vec3(0.8) * skyCol * exp(-0.5 * max(db, 0.0)); } @@ -106,12 +58,12 @@ fn df(p_in: vec3) -> f32 { // Cube var pc = p; pc -= vec3(-1.9, 0.0, 0.0); - let dCube = box3d(pc, vec3(1.6)); + let dCube = sdBox(pc, vec3(1.6)); // Sphere var ps = p; ps -= vec3(1.3, 0.0, 0.0); - let dSphere = sphere(ps, 1.2); + let dSphere = sdSphere(ps, 1.2); // Ground plane let dPlane = p.y + 1.0; @@ -123,51 +75,6 @@ fn df(p_in: vec3) -> f32 { return d; } -fn normal(pos: vec3) -> vec3 { - let eps = vec2(NORM_OFF, 0.0); - var nor: vec3; - nor.x = df(pos + eps.xyy) - df(pos - eps.xyy); - nor.y = df(pos + eps.yxy) - df(pos - eps.yxy); - nor.z = df(pos + eps.yyx) - df(pos - eps.yyx); - return normalize(nor); -} - -fn rayMarch(ro: vec3, rd: vec3, initt: f32) -> f32 { - var t = initt; - for (var i = 0; i < MAX_RAY_MARCHES; i++) { - if (t > MAX_RAY_LENGTH) { - t = MAX_RAY_LENGTH; - break; - } - let d = df(ro + rd * t); - if (d < TOLERANCE) { - break; - } - t += d; - } - return t; -} - -fn shadow(lp: vec3, ld: vec3, mint: f32, maxt: f32) -> f32 { - let ds = 1.0 - 0.4; - var t = mint; - var nd = 1e6; - let soff = 0.05; - let smul = 1.5; - for (var i = 0; i < MAX_SHD_MARCHES; i++) { - let p = lp + ld * t; - let d = df(p); - if (d < TOLERANCE || t >= maxt) { - let sd = 1.0 - exp(-smul * max(t / maxt - soff, 0.0)); - return select(mix(sd, 1.0, smoothstep(0.0, 0.025, nd)), sd, t >= maxt); - } - nd = min(nd, d); - t += ds * d; - } - let sd = 1.0 - exp(-smul * max(t / maxt - soff, 0.0)); - return sd; -} - fn boxCol(col: vec3, nsp: vec3, rd: vec3, nnor: vec3, nrcol: vec3, nshd1: f32, nshd2: f32) -> vec3 { var nfre = 1.0 + dot(rd, nnor); nfre *= nfre; @@ -236,14 +143,7 @@ fn effect(p: vec2) -> vec3 { return render1(ro, rd); } -@vertex fn vs_main(@builtin(vertex_index) i: u32) -> @builtin(position) vec4 { - var pos = array, 3>( - vec2(-1.0, -1.0), - vec2(3.0, -1.0), - vec2(-1.0, 3.0) - ); - return vec4(pos[i], 0.0, 1.0); -} +#include "render/fullscreen_vs" @fragment fn fs_main(@builtin(position) p: vec4) -> @location(0) vec4 { // Flip Y to match ShaderToy convention (origin at bottom-left) -- cgit v1.2.3