summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--PROJECT_CONTEXT.md2
-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
-rw-r--r--src/gpu/post_process_helper.cc4
-rw-r--r--src/gpu/shaders.cc5
-rw-r--r--src/tests/gpu/test_shader_assets.cc9
-rw-r--r--workspaces/main/assets.txt4
-rw-r--r--workspaces/main/shaders/chroma_aberration.wgsl9
-rw-r--r--workspaces/main/shaders/distort.wgsl9
-rw-r--r--workspaces/main/shaders/scene1.wgsl116
-rw-r--r--workspaces/main/shaders/solarize.wgsl9
-rw-r--r--workspaces/main/shaders/vignette.wgsl10
-rw-r--r--workspaces/test/assets.txt4
-rw-r--r--workspaces/test/shaders/chroma_aberration.wgsl9
-rw-r--r--workspaces/test/shaders/distort.wgsl9
-rw-r--r--workspaces/test/shaders/solarize.wgsl9
-rw-r--r--workspaces/test/shaders/vignette.wgsl10
20 files changed, 154 insertions, 179 deletions
diff --git a/PROJECT_CONTEXT.md b/PROJECT_CONTEXT.md
index 02c51e4..63e6f8a 100644
--- a/PROJECT_CONTEXT.md
+++ b/PROJECT_CONTEXT.md
@@ -34,7 +34,7 @@
- **Timing System:** **Beat-based timelines** for musical synchronization. Sequences defined in beats, converted to seconds at runtime. Effects receive both physical time (constant) and beat time (musical). Variable tempo affects audio only. See `doc/BEAT_TIMING.md`.
- **Workspace system:** Multi-workspace support. Easy switching with `-DDEMO_WORKSPACE=<name>`. Organized structure: `music/`, `weights/`, `obj/`, `shaders/`. Shared common shaders in `common/shaders/`. See `doc/WORKSPACE_SYSTEM.md`.
- **Audio:** Sample-accurate sync. Zero heap allocations per frame. Variable tempo. Comprehensive tests.
-- **Shaders:** Parameterized effects (UniformHelper, .seq syntax). Beat-synchronized animation support (`beat_time`, `beat_phase`). Modular WGSL composition with ShaderComposer. 20 shared common shaders (math, render, compute).
+- **Shaders:** Parameterized effects (UniformHelper, .seq syntax). Beat-synchronized animation support (`beat_time`, `beat_phase`). Modular WGSL composition with ShaderComposer. 24 shared common shaders (math, render, compute).
- **3D:** Hybrid SDF/rasterization with BVH. Binary scene loader. Blender pipeline.
- **Effects:** CNN post-processing: CNNEffect (v1) and CNNv2Effect operational. CNN v2: sigmoid activation, storage buffer weights (~3.2 KB), 7D static features, dynamic layers. Training stable, convergence validated.
- **Tools:** CNN test tool operational. Texture readback utility functional. Timeline editor (web-based, beat-aligned, audio playback).
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);
+}
diff --git a/src/gpu/post_process_helper.cc b/src/gpu/post_process_helper.cc
index 4ffa9c1..c5bef27 100644
--- a/src/gpu/post_process_helper.cc
+++ b/src/gpu/post_process_helper.cc
@@ -21,8 +21,10 @@ WGPURenderPipeline create_post_process_pipeline(WGPUDevice device,
.uniform(PP_BINDING_EFFECT_PARAMS, WGPUShaderStage_Fragment)
.build(device);
+ const std::string composed_shader = ShaderComposer::Get().Compose({}, shader_code);
+
WGPURenderPipeline pipeline = RenderPipelineBuilder(device)
- .shader(shader_code)
+ .shader(composed_shader.c_str())
.bind_group_layout(bgl)
.format(format)
.build();
diff --git a/src/gpu/shaders.cc b/src/gpu/shaders.cc
index 903b96e..1b50e8e 100644
--- a/src/gpu/shaders.cc
+++ b/src/gpu/shaders.cc
@@ -52,6 +52,11 @@ void InitShaderComposer() {
register_if_exists("ray_box", AssetId::ASSET_SHADER_RAY_BOX);
register_if_exists("ray_triangle", AssetId::ASSET_SHADER_RAY_TRIANGLE);
+ register_if_exists("render/fullscreen_vs", AssetId::ASSET_SHADER_RENDER_FULLSCREEN_VS);
+ register_if_exists("math/color", AssetId::ASSET_SHADER_MATH_COLOR);
+ register_if_exists("math/utils", AssetId::ASSET_SHADER_MATH_UTILS);
+ register_if_exists("render/raymarching", AssetId::ASSET_SHADER_RENDER_RAYMARCHING);
+
register_if_exists("cnn_activation", AssetId::ASSET_SHADER_CNN_ACTIVATION);
register_if_exists("cnn_conv1x1", AssetId::ASSET_SHADER_CNN_CONV1X1);
register_if_exists("cnn_conv3x3", AssetId::ASSET_SHADER_CNN_CONV3X3);
diff --git a/src/tests/gpu/test_shader_assets.cc b/src/tests/gpu/test_shader_assets.cc
index f1562ea..5619a61 100644
--- a/src/tests/gpu/test_shader_assets.cc
+++ b/src/tests/gpu/test_shader_assets.cc
@@ -71,12 +71,15 @@ int main() {
validate_shader(AssetId::ASSET_SHADER_GAUSSIAN_BLUR, "GAUSSIAN_BLUR",
{"@vertex", "vs_main", "@fragment", "fs_main"});
all_passed &= validate_shader(AssetId::ASSET_SHADER_SOLARIZE, "SOLARIZE",
- {"@vertex", "vs_main", "@fragment", "fs_main"});
+ {"#include \"render/fullscreen_vs\"", "@fragment", "fs_main"});
all_passed &= validate_shader(AssetId::ASSET_SHADER_DISTORT, "DISTORT",
- {"@vertex", "vs_main", "@fragment", "fs_main"});
+ {"#include \"render/fullscreen_vs\"", "@fragment", "fs_main"});
all_passed &= validate_shader(AssetId::ASSET_SHADER_CHROMA_ABERRATION,
"CHROMA_ABERRATION",
- {"@vertex", "vs_main", "@fragment", "fs_main"});
+ {"#include \"render/fullscreen_vs\"", "@fragment", "fs_main"});
+ all_passed &= validate_shader(AssetId::ASSET_SHADER_VIGNETTE,
+ "VIGNETTE",
+ {"#include \"render/fullscreen_vs\"", "@fragment", "fs_main"});
all_passed &=
validate_shader(AssetId::ASSET_SHADER_VISUAL_DEBUG, "VISUAL_DEBUG",
{"@vertex", "vs_main", "@fragment", "fs_main"});
diff --git a/workspaces/main/assets.txt b/workspaces/main/assets.txt
index 72a469b..ace1462 100644
--- a/workspaces/main/assets.txt
+++ b/workspaces/main/assets.txt
@@ -62,6 +62,10 @@ SHADER_RENDER_LIGHTING_UTILS, NONE, ../../common/shaders/render/lighting_utils.w
SHADER_MESH, NONE, shaders/mesh_render.wgsl, "Mesh Rasterization Shader"
MESH_CUBE, NONE, obj/test_mesh.obj, "A simple cube mesh"
DODECAHEDRON, NONE, obj/dodecahedron.obj, "A dodecahedron mesh"
+SHADER_RENDER_FULLSCREEN_VS, NONE, ../../common/shaders/render/fullscreen_vs.wgsl, "Fullscreen Vertex Shader"
+SHADER_MATH_COLOR, NONE, ../../common/shaders/math/color.wgsl, "Color Functions"
+SHADER_MATH_UTILS, NONE, ../../common/shaders/math/utils.wgsl, "Math Utilities"
+SHADER_RENDER_RAYMARCHING, NONE, ../../common/shaders/render/raymarching.wgsl, "Raymarching Functions"
SHADER_VIGNETTE, NONE, shaders/vignette.wgsl, "Vignette Shader"
SHADER_COMPUTE_GEN_NOISE, NONE, ../../common/shaders/compute/gen_noise.wgsl, "GPU Noise Compute Shader"
SHADER_COMPUTE_GEN_PERLIN, NONE, ../../common/shaders/compute/gen_perlin.wgsl, "GPU Perlin Noise Compute Shader"
diff --git a/workspaces/main/shaders/chroma_aberration.wgsl b/workspaces/main/shaders/chroma_aberration.wgsl
index 6c942b7..ee730b1 100644
--- a/workspaces/main/shaders/chroma_aberration.wgsl
+++ b/workspaces/main/shaders/chroma_aberration.wgsl
@@ -10,14 +10,7 @@ struct ChromaAberrationParams {
@group(0) @binding(2) var<uniform> uniforms: CommonUniforms;
@group(0) @binding(3) var<uniform> params: ChromaAberrationParams;
-@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);
-}
+#include "render/fullscreen_vs"
@fragment fn fs_main(@builtin(position) p: vec4<f32>) -> @location(0) vec4<f32> {
let uv = p.xy / uniforms.resolution;
diff --git a/workspaces/main/shaders/distort.wgsl b/workspaces/main/shaders/distort.wgsl
index 5d35129..607ac60 100644
--- a/workspaces/main/shaders/distort.wgsl
+++ b/workspaces/main/shaders/distort.wgsl
@@ -11,14 +11,7 @@ struct DistortParams {
@group(0) @binding(2) var<uniform> uniforms: CommonUniforms;
@group(0) @binding(3) var<uniform> params: DistortParams;
-@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);
-}
+#include "render/fullscreen_vs"
@fragment fn fs_main(@builtin(position) p: vec4<f32>) -> @location(0) vec4<f32> {
let uv = p.xy / uniforms.resolution;
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<uniform> 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<f32> {
- let c = cos(a);
- let s = sin(a);
- return mat2x2<f32>(c, s, -s, c);
-}
-
-// 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);
-}
// Colors (precomputed HSV conversions)
const skyCol = vec3<f32>(0.176, 0.235, 0.25); // HSV(0.57, 0.90, 0.25)
@@ -38,44 +24,10 @@ const sunDir1 = vec3<f32>(0.0, 0.04997, -0.99875); // normalize(0, 0.05, -1)
const lightPos1 = vec3<f32>(10.0, 10.0, 10.0);
const lightPos2 = vec3<f32>(-10.0, 10.0, -10.0);
-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)));
-}
-
-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));
-}
-
-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<f32>, rd: vec3<f32>, plane: vec4<f32>) -> f32 {
return -(dot(ro, plane.xyz) + plane.w) / dot(rd, plane.xyz);
}
-fn box2d(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);
-}
-
-fn box3d(p: vec3<f32>, b: vec3<f32>) -> f32 {
- let q = abs(p) - b;
- return length(max(q, vec3<f32>(0.0))) + min(max(q.x, max(q.y, q.z)), 0.0);
-}
-
-fn sphere(p: vec3<f32>, r: f32) -> f32 {
- return length(p) - r;
-}
-
var<private> g_rot0: mat2x2<f32>;
fn render0(ro: vec3<f32>, rd: vec3<f32>) -> vec3<f32> {
@@ -90,7 +42,7 @@ fn render0(ro: vec3<f32>, rd: vec3<f32>) -> vec3<f32> {
if (tp1 > 0.0) {
let pos = ro + tp1 * rd;
let pp = pos.xz;
- let db = box2d(pp, vec2<f32>(5.0, 9.0)) - 3.0;
+ let db = sdBox2D(pp, vec2<f32>(5.0, 9.0)) - 3.0;
col += vec3<f32>(4.0) * skyCol * rd.y * rd.y * smoothstep(0.25, 0.0, db);
col += vec3<f32>(0.8) * skyCol * exp(-0.5 * max(db, 0.0));
}
@@ -106,12 +58,12 @@ fn df(p_in: vec3<f32>) -> f32 {
// Cube
var pc = p;
pc -= vec3<f32>(-1.9, 0.0, 0.0);
- let dCube = box3d(pc, vec3<f32>(1.6));
+ let dCube = sdBox(pc, vec3<f32>(1.6));
// Sphere
var ps = p;
ps -= vec3<f32>(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>) -> f32 {
return d;
}
-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);
-}
-
-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;
-}
-
-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;
- 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<f32>, nsp: vec3<f32>, rd: vec3<f32>, nnor: vec3<f32>, nrcol: vec3<f32>, nshd1: f32, nshd2: f32) -> vec3<f32> {
var nfre = 1.0 + dot(rd, nnor);
nfre *= nfre;
@@ -236,14 +143,7 @@ fn effect(p: vec2<f32>) -> vec3<f32> {
return render1(ro, rd);
}
-@vertex fn vs_main(@builtin(vertex_index) i: u32) -> @builtin(position) vec4<f32> {
- var pos = array<vec2<f32>, 3>(
- vec2<f32>(-1.0, -1.0),
- vec2<f32>(3.0, -1.0),
- vec2<f32>(-1.0, 3.0)
- );
- return vec4<f32>(pos[i], 0.0, 1.0);
-}
+#include "render/fullscreen_vs"
@fragment fn fs_main(@builtin(position) p: vec4<f32>) -> @location(0) vec4<f32> {
// Flip Y to match ShaderToy convention (origin at bottom-left)
diff --git a/workspaces/main/shaders/solarize.wgsl b/workspaces/main/shaders/solarize.wgsl
index de15dfc..0a69b83 100644
--- a/workspaces/main/shaders/solarize.wgsl
+++ b/workspaces/main/shaders/solarize.wgsl
@@ -5,14 +5,7 @@
@group(0) @binding(2) var<uniform> uniforms: CommonUniforms;
-@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);
-}
+#include "render/fullscreen_vs"
@fragment fn fs_main(@builtin(position) p: vec4<f32>) -> @location(0) vec4<f32> {
let uv = p.xy / uniforms.resolution;
diff --git a/workspaces/main/shaders/vignette.wgsl b/workspaces/main/shaders/vignette.wgsl
index b129883..c4d0389 100644
--- a/workspaces/main/shaders/vignette.wgsl
+++ b/workspaces/main/shaders/vignette.wgsl
@@ -10,15 +10,7 @@ struct VignetteParams {
@group(0) @binding(2) var<uniform> common_uniforms: CommonUniforms;
@group(0) @binding(3) var<uniform> params: VignetteParams;
-@vertex
-fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> @builtin(position) vec4<f32> {
- var pos = array<vec2<f32>, 3>(
- vec2<f32>(-1.0, -1.0),
- vec2<f32>(3.0, -1.0),
- vec2<f32>(-1.0, 3.0)
- );
- return vec4<f32>(pos[vertex_idx], 0.0, 1.0);
-}
+#include "render/fullscreen_vs"
@fragment
fn fs_main(@builtin(position) pos: vec4<f32>) -> @location(0) vec4<f32> {
diff --git a/workspaces/test/assets.txt b/workspaces/test/assets.txt
index ed81885..b717e15 100644
--- a/workspaces/test/assets.txt
+++ b/workspaces/test/assets.txt
@@ -54,6 +54,10 @@ SHADER_RENDER_LIGHTING_UTILS, NONE, ../../common/shaders/render/lighting_utils.w
SHADER_MESH, NONE, shaders/mesh_render.wgsl, "Mesh Rasterization Shader"
MESH_CUBE, NONE, obj/test_mesh.obj, "A simple cube mesh"
DODECAHEDRON, NONE, obj/dodecahedron.obj, "A dodecahedron mesh"
+SHADER_RENDER_FULLSCREEN_VS, NONE, ../../common/shaders/render/fullscreen_vs.wgsl, "Fullscreen Vertex Shader"
+SHADER_MATH_COLOR, NONE, ../../common/shaders/math/color.wgsl, "Color Functions"
+SHADER_MATH_UTILS, NONE, ../../common/shaders/math/utils.wgsl, "Math Utilities"
+SHADER_RENDER_RAYMARCHING, NONE, ../../common/shaders/render/raymarching.wgsl, "Raymarching Functions"
SHADER_VIGNETTE, NONE, shaders/vignette.wgsl, "Vignette Shader"
SHADER_COMPUTE_GEN_NOISE, NONE, ../../common/shaders/compute/gen_noise.wgsl, "GPU Noise Compute Shader"
SHADER_COMPUTE_GEN_PERLIN, NONE, ../../common/shaders/compute/gen_perlin.wgsl, "GPU Perlin Noise Compute Shader"
diff --git a/workspaces/test/shaders/chroma_aberration.wgsl b/workspaces/test/shaders/chroma_aberration.wgsl
index 6c942b7..ee730b1 100644
--- a/workspaces/test/shaders/chroma_aberration.wgsl
+++ b/workspaces/test/shaders/chroma_aberration.wgsl
@@ -10,14 +10,7 @@ struct ChromaAberrationParams {
@group(0) @binding(2) var<uniform> uniforms: CommonUniforms;
@group(0) @binding(3) var<uniform> params: ChromaAberrationParams;
-@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);
-}
+#include "render/fullscreen_vs"
@fragment fn fs_main(@builtin(position) p: vec4<f32>) -> @location(0) vec4<f32> {
let uv = p.xy / uniforms.resolution;
diff --git a/workspaces/test/shaders/distort.wgsl b/workspaces/test/shaders/distort.wgsl
index 5d35129..607ac60 100644
--- a/workspaces/test/shaders/distort.wgsl
+++ b/workspaces/test/shaders/distort.wgsl
@@ -11,14 +11,7 @@ struct DistortParams {
@group(0) @binding(2) var<uniform> uniforms: CommonUniforms;
@group(0) @binding(3) var<uniform> params: DistortParams;
-@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);
-}
+#include "render/fullscreen_vs"
@fragment fn fs_main(@builtin(position) p: vec4<f32>) -> @location(0) vec4<f32> {
let uv = p.xy / uniforms.resolution;
diff --git a/workspaces/test/shaders/solarize.wgsl b/workspaces/test/shaders/solarize.wgsl
index de15dfc..0a69b83 100644
--- a/workspaces/test/shaders/solarize.wgsl
+++ b/workspaces/test/shaders/solarize.wgsl
@@ -5,14 +5,7 @@
@group(0) @binding(2) var<uniform> uniforms: CommonUniforms;
-@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);
-}
+#include "render/fullscreen_vs"
@fragment fn fs_main(@builtin(position) p: vec4<f32>) -> @location(0) vec4<f32> {
let uv = p.xy / uniforms.resolution;
diff --git a/workspaces/test/shaders/vignette.wgsl b/workspaces/test/shaders/vignette.wgsl
index b129883..c4d0389 100644
--- a/workspaces/test/shaders/vignette.wgsl
+++ b/workspaces/test/shaders/vignette.wgsl
@@ -10,15 +10,7 @@ struct VignetteParams {
@group(0) @binding(2) var<uniform> common_uniforms: CommonUniforms;
@group(0) @binding(3) var<uniform> params: VignetteParams;
-@vertex
-fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> @builtin(position) vec4<f32> {
- var pos = array<vec2<f32>, 3>(
- vec2<f32>(-1.0, -1.0),
- vec2<f32>(3.0, -1.0),
- vec2<f32>(-1.0, 3.0)
- );
- return vec4<f32>(pos[vertex_idx], 0.0, 1.0);
-}
+#include "render/fullscreen_vs"
@fragment
fn fs_main(@builtin(position) pos: vec4<f32>) -> @location(0) vec4<f32> {