diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-16 09:43:06 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-16 09:43:06 +0100 |
| commit | 766c0b0a41ddb4ac1fae68f720a9176a1b5f6070 (patch) | |
| tree | b8530354de7133c7828d177889a6a4eb1d0570ba | |
| parent | 04938fc4a3335e1459e5fb23d0d091fd2a40c296 (diff) | |
feat(sequence): add inline post-process functions for v2
- Create postprocess_inline.wgsl with 7 inline effect functions
- Functions: vignette, flash, fade, theme, solarize, chroma_aberration, distort
- Add example combined_postprocess_v2.wgsl showing usage
- Register postprocess_inline snippet with ShaderComposer
- Add to main and test workspace assets
- All tests passing (36/36)
Strategy: Simple effects become inline functions instead of separate classes.
Complex effects (rotating_cube, hybrid_3d, particles) remain as TODO for v2 port.
handoff(Claude): Inline functions ready, 7 simple effects consolidated
| -rw-r--r-- | common/shaders/combined_postprocess_v2.wgsl | 36 | ||||
| -rw-r--r-- | common/shaders/postprocess_inline.wgsl | 61 | ||||
| -rw-r--r-- | src/gpu/shaders.cc | 2 | ||||
| -rw-r--r-- | workspaces/main/assets.txt | 1 | ||||
| -rw-r--r-- | workspaces/test/assets.txt | 1 |
5 files changed, 101 insertions, 0 deletions
diff --git a/common/shaders/combined_postprocess_v2.wgsl b/common/shaders/combined_postprocess_v2.wgsl new file mode 100644 index 0000000..a934dce --- /dev/null +++ b/common/shaders/combined_postprocess_v2.wgsl @@ -0,0 +1,36 @@ +// Example: Combined post-process using inline functions +// Demonstrates how to chain multiple simple effects without separate classes + +#include "sequence_v2_uniforms" +#include "postprocess_inline" + +@group(0) @binding(0) var input_sampler: sampler; +@group(0) @binding(1) var input_texture: texture_2d<f32>; +@group(0) @binding(2) var<uniform> uniforms: UniformsSequenceParams; + +struct VertexOutput { + @builtin(position) position: vec4<f32>, + @location(0) uv: vec2<f32>, +}; + +@vertex fn vs_main(@builtin(vertex_index) vid: u32) -> VertexOutput { + var out: VertexOutput; + let x = f32((vid & 1u) << 1u); + let y = f32((vid & 2u)); + out.position = vec4<f32>(x * 2.0 - 1.0, 1.0 - y * 2.0, 0.0, 1.0); + out.uv = vec2<f32>(x, y); + return out; +} + +@fragment fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> { + // Sample base color + var color = textureSample(input_texture, input_sampler, in.uv); + + // Apply effects in sequence (customize as needed) + // color = apply_solarize(color, 0.4, 0.4, uniforms.time); + // color = apply_theme(color, vec3<f32>(1.0, 0.8, 0.6), 0.3); + color = apply_vignette(color, in.uv, 0.6, 0.1, uniforms.audio_intensity); + // color = apply_flash(color, uniforms.beat_phase * 0.2); + + return color; +} diff --git a/common/shaders/postprocess_inline.wgsl b/common/shaders/postprocess_inline.wgsl new file mode 100644 index 0000000..fcc5e27 --- /dev/null +++ b/common/shaders/postprocess_inline.wgsl @@ -0,0 +1,61 @@ +// Inline post-process functions for simple effects +// Use these instead of separate effect classes for v2 sequences + +// Vignette: darkens edges based on distance from center +fn apply_vignette(color: vec4<f32>, uv: vec2<f32>, radius: f32, softness: f32, intensity: f32) -> vec4<f32> { + let d = distance(uv, vec2<f32>(0.5, 0.5)); + let vignette = smoothstep(radius, radius - softness, d); + return vec4<f32>(color.rgb * mix(1.0, vignette, intensity), color.a); +} + +// Flash: additive white flash +fn apply_flash(color: vec4<f32>, flash_intensity: f32) -> vec4<f32> { + return color + vec4<f32>(flash_intensity, flash_intensity, flash_intensity, 0.0); +} + +// Fade: linear interpolation to target color +fn apply_fade(color: vec4<f32>, fade_amount: f32, fade_color: vec3<f32>) -> vec4<f32> { + return vec4<f32>(mix(color.rgb, fade_color, fade_amount), color.a); +} + +// Theme modulation: multiply by color tint +fn apply_theme(color: vec4<f32>, theme_color: vec3<f32>, strength: f32) -> vec4<f32> { + return vec4<f32>(mix(color.rgb, color.rgb * theme_color, strength), color.a); +} + +// Solarize: threshold-based color inversion +fn apply_solarize(color: vec4<f32>, threshold: f32, strength: f32, time: f32) -> vec4<f32> { + let pattern_num = u32(time / 2.0); + let is_even = (pattern_num % 2u) == 0u; + let thr = threshold + 0.15 * sin(time); + var col = color; + + if (is_even) { + if (col.r < thr) { col.r = mix(col.r, 1.0 - col.r, strength); } + if (col.g < thr) { col.g = mix(col.g, 1.0 - col.g * 0.7, strength * 0.7); } + if (col.b < thr) { col.b = mix(col.b, col.b * 0.5, strength); } + } else { + if (col.r < thr) { col.r = mix(col.r, col.r * 0.6, strength); } + if (col.g < thr) { col.g = mix(col.g, 1.0 - col.g * 0.8, strength * 0.8); } + if (col.b < thr) { col.b = mix(col.b, 1.0 - col.b, strength); } + } + return col; +} + +// Chroma aberration: RGB channel offset +fn apply_chroma_aberration(input_tex: texture_2d<f32>, input_sampler: sampler, + uv: vec2<f32>, offset: f32, resolution: vec2<f32>) -> vec4<f32> { + let pixel_offset = offset / resolution; + let r = textureSample(input_tex, input_sampler, uv + vec2<f32>(pixel_offset.x, 0.0)).r; + let g = textureSample(input_tex, input_sampler, uv).g; + let b = textureSample(input_tex, input_sampler, uv - vec2<f32>(pixel_offset.x, 0.0)).b; + let a = textureSample(input_tex, input_sampler, uv).a; + return vec4<f32>(r, g, b, a); +} + +// Distort: UV distortion based on time +fn apply_distort(uv: vec2<f32>, time: f32, strength: f32) -> vec2<f32> { + let distort_x = sin(uv.y * 10.0 + time * 2.0) * strength; + let distort_y = cos(uv.x * 10.0 + time * 2.0) * strength; + return uv + vec2<f32>(distort_x, distort_y); +} diff --git a/src/gpu/shaders.cc b/src/gpu/shaders.cc index d768cef..4f24705 100644 --- a/src/gpu/shaders.cc +++ b/src/gpu/shaders.cc @@ -33,6 +33,8 @@ void InitShaderComposer() { register_if_exists("common_uniforms", AssetId::ASSET_SHADER_COMMON_UNIFORMS); register_if_exists("sequence_v2_uniforms", AssetId::ASSET_SHADER_SEQUENCE_V2_UNIFORMS); + register_if_exists("postprocess_inline", + AssetId::ASSET_SHADER_POSTPROCESS_INLINE); register_if_exists("camera_common", AssetId::ASSET_SHADER_CAMERA_COMMON); register_if_exists("math/sdf_shapes", AssetId::ASSET_SHADER_MATH_SDF_SHAPES); register_if_exists("math/sdf_utils", AssetId::ASSET_SHADER_MATH_SDF_UTILS); diff --git a/workspaces/main/assets.txt b/workspaces/main/assets.txt index 0558ae1..de96eab 100644 --- a/workspaces/main/assets.txt +++ b/workspaces/main/assets.txt @@ -81,6 +81,7 @@ SHADER_SCENE1, NONE, shaders/scene1.wgsl, "Scene1 effect shader" # --- Sequence v2 Shaders --- SHADER_SEQUENCE_V2_UNIFORMS, NONE, ../../common/shaders/sequence_v2_uniforms.wgsl, "Sequence v2 Uniforms Snippet" +SHADER_POSTPROCESS_INLINE, NONE, ../../common/shaders/postprocess_inline.wgsl, "Inline Post-Process Functions" SHADER_PASSTHROUGH_V2, NONE, ../../common/shaders/passthrough_v2.wgsl, "Passthrough Shader (v2)" SHADER_GAUSSIAN_BLUR_V2, NONE, ../../common/shaders/gaussian_blur_v2.wgsl, "Gaussian Blur Shader (v2)" SHADER_HEPTAGON_V2, NONE, ../../common/shaders/heptagon_v2.wgsl, "Heptagon Shader (v2)" diff --git a/workspaces/test/assets.txt b/workspaces/test/assets.txt index 5ed8af6..8550eca 100644 --- a/workspaces/test/assets.txt +++ b/workspaces/test/assets.txt @@ -70,6 +70,7 @@ MASKED_CUBE_SHADER, NONE, shaders/masked_cube.wgsl, "Masked cube shader" # --- Sequence v2 Shaders --- SHADER_SEQUENCE_V2_UNIFORMS, NONE, ../../common/shaders/sequence_v2_uniforms.wgsl, "Sequence v2 Uniforms Snippet" +SHADER_POSTPROCESS_INLINE, NONE, ../../common/shaders/postprocess_inline.wgsl, "Inline Post-Process Functions" SHADER_PASSTHROUGH_V2, NONE, ../../common/shaders/passthrough_v2.wgsl, "Passthrough Shader (v2)" SHADER_GAUSSIAN_BLUR_V2, NONE, ../../common/shaders/gaussian_blur_v2.wgsl, "Gaussian Blur Shader (v2)" SHADER_HEPTAGON_V2, NONE, ../../common/shaders/heptagon_v2.wgsl, "Heptagon Shader (v2)" |
