summaryrefslogtreecommitdiff
path: root/common/shaders
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-14 15:13:09 +0100
committerskal <pascal.massimino@gmail.com>2026-02-14 15:13:09 +0100
commite38be0dbf5816338ff97e2ee2f9adfff2902dc2b (patch)
tree42e380626c6f9d0e0137336c8c2dfb68df327dae /common/shaders
parentd6cc50eb49275bbc0de21d4c65a5172d5d65f790 (diff)
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.
Diffstat (limited to 'common/shaders')
-rw-r--r--common/shaders/math/color.wgsl27
-rw-r--r--common/shaders/math/utils.wgsl14
-rw-r--r--common/shaders/render/fullscreen_vs.wgsl10
-rw-r--r--common/shaders/render/raymarching.wgsl59
-rw-r--r--common/shaders/sdf_primitives.wgsl5
5 files changed, 115 insertions, 0 deletions
diff --git a/common/shaders/math/color.wgsl b/common/shaders/math/color.wgsl
new file mode 100644
index 0000000..b63c915
--- /dev/null
+++ b/common/shaders/math/color.wgsl
@@ -0,0 +1,27 @@
+// Common color space and tone mapping functions.
+
+// sRGB to Linear approximation
+// Note: Assumes input is in sRGB color space.
+fn sRGB(t: vec3<f32>) -> vec3<f32> {
+ return mix(1.055 * pow(t, vec3<f32>(1.0/2.4)) - 0.055, 12.92 * t, step(t, vec3<f32>(0.0031308)));
+}
+
+// ACES Filmic Tone Mapping (Approximate)
+// A common tone mapping algorithm used in games and film.
+fn aces_approx(v_in: vec3<f32>) -> vec3<f32> {
+ var v = max(v_in, vec3<f32>(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<f32>(0.0), vec3<f32>(1.0));
+}
+
+// HSV to RGB conversion
+const hsv2rgb_K = vec4<f32>(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
+fn hsv2rgb(c: vec3<f32>) -> vec3<f32> {
+ 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<f32>(0.0), vec3<f32>(1.0)), c.y);
+}
diff --git a/common/shaders/math/utils.wgsl b/common/shaders/math/utils.wgsl
new file mode 100644
index 0000000..85f0bdf
--- /dev/null
+++ b/common/shaders/math/utils.wgsl
@@ -0,0 +1,14 @@
+// General-purpose math utility functions.
+
+// Returns a 2x2 rotation matrix.
+fn rot(a: f32) -> mat2x2<f32> {
+ let c = cos(a);
+ let s = sin(a);
+ return mat2x2<f32>(c, s, -s, c);
+}
+
+// Fast approximation of tanh.
+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);
+}
diff --git a/common/shaders/render/fullscreen_vs.wgsl b/common/shaders/render/fullscreen_vs.wgsl
new file mode 100644
index 0000000..a68604b
--- /dev/null
+++ b/common/shaders/render/fullscreen_vs.wgsl
@@ -0,0 +1,10 @@
+// Common vertex shader for fullscreen post-processing effects.
+// Draws a single triangle that covers the entire screen.
+@vertex fn vs_main(@builtin(vertex_index) i: u32) -> @builtin(position) vec4<f32> {
+ var pos = array<vec2<f32>, 3>(
+ vec2<f32>(-1, -1),
+ vec2<f32>(3, -1),
+ vec2<f32>(-1, 3)
+ );
+ return vec4<f32>(pos[i], 0.0, 1.0);
+}
diff --git a/common/shaders/render/raymarching.wgsl b/common/shaders/render/raymarching.wgsl
new file mode 100644
index 0000000..5a86840
--- /dev/null
+++ b/common/shaders/render/raymarching.wgsl
@@ -0,0 +1,59 @@
+// Common functions for Signed Distance Field (SDF) raymarching.
+// These functions require the user to define a `df(vec3<f32>) -> f32` function
+// which represents the scene's distance field.
+
+const TOLERANCE: f32 = 0.0005;
+const MAX_RAY_LENGTH: f32 = 20.0;
+const MAX_RAY_MARCHES: i32 = 80;
+const NORM_OFF: f32 = 0.005;
+
+// Computes the surface normal of the distance field at a point `pos`.
+fn normal(pos: vec3<f32>) -> vec3<f32> {
+ let eps = vec2<f32>(NORM_OFF, 0.0);
+ var nor: vec3<f32>;
+ 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);
+}
+
+// Performs the raymarching operation.
+// Returns the distance along the ray to the surface, or MAX_RAY_LENGTH if no surface is hit.
+fn rayMarch(ro: vec3<f32>, rd: vec3<f32>, 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;
+}
+
+// Computes a soft shadow for a given point.
+fn shadow(lp: vec3<f32>, ld: vec3<f32>, 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;
+ let MAX_SHD_MARCHES = 20;
+
+ 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;
+}
diff --git a/common/shaders/sdf_primitives.wgsl b/common/shaders/sdf_primitives.wgsl
index 31bbe2d..407fb29 100644
--- a/common/shaders/sdf_primitives.wgsl
+++ b/common/shaders/sdf_primitives.wgsl
@@ -12,3 +12,8 @@ fn sdTorus(p: vec3<f32>, t: vec2<f32>) -> f32 {
fn sdPlane(p: vec3<f32>, n: vec3<f32>, h: f32) -> f32 {
return dot(p, n) + h;
}
+
+fn sdBox2D(p: vec2<f32>, b: vec2<f32>) -> f32 {
+ let d = abs(p) - b;
+ return length(max(d, vec2<f32>(0.0))) + min(max(d.x, d.y), 0.0);
+}