diff options
26 files changed, 200 insertions, 106 deletions
@@ -56,7 +56,6 @@ compile_commands.json *.sublime-workspace *.gemini /assets/originals -/assets/final /third_party/windows /archive src/generated/ diff --git a/CMakeLists.txt b/CMakeLists.txt index ea53876..2f939bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -115,6 +115,8 @@ set(GPU_SOURCES src/gpu/effects/fade_effect.cc src/gpu/effects/flash_effect.cc src/gpu/effects/shader_composer.cc + src/gpu/effects/circle_mask_effect.cc + src/gpu/effects/rotating_cube_effect.cc src/gpu/texture_manager.cc ) set(3D_SOURCES diff --git a/assets/demo.seq b/assets/demo.seq index 9c55fe5..0dfb108 100644 --- a/assets/demo.seq +++ b/assets/demo.seq @@ -29,6 +29,11 @@ SEQUENCE 0b 0 EFFECT + SolarizeEffect 0 4b # Priority 2 (was 3, now contiguous) EFFECT + VignetteEffect 0 6 radius=0.6 softness=0.1 +SEQUENCE 2.0 0 + EFFECT + CircleMaskEffect 0.0 2.0 0.35 # Priority 0 (mask generator, radius 0.35) + EFFECT + RotatingCubeEffect 0.0 2.0 # Priority 1 (renders inside circle) + EFFECT + GaussianBlurEffect 0.0 2.0 strength=2.0 # Priority 2 (post-process blur) + SEQUENCE 4b 0 EFFECT - FlashCubeEffect 0.1 3. # Priority -1 EFFECT + FlashEffect 0.0 0.2 # Priority 0 (was 4, now contiguous) diff --git a/assets/final/demo_assets.txt b/assets/final/demo_assets.txt index bf39c5d..05eee17 100644 --- a/assets/final/demo_assets.txt +++ b/assets/final/demo_assets.txt @@ -52,3 +52,6 @@ SHADER_MESH, NONE, shaders/mesh_render.wgsl, "Mesh Rasterization Shader" MESH_CUBE, NONE, test_mesh.obj, "A simple cube mesh" DODECAHEDRON, NONE, dodecahedron.obj, "A dodecahedron mesh" SHADER_VIGNETTE, NONE, shaders/vignette.wgsl, "Vignette Shader" +CIRCLE_MASK_COMPUTE_SHADER, NONE, shaders/circle_mask_compute.wgsl, "Circle mask compute shader" +CIRCLE_MASK_RENDER_SHADER, NONE, shaders/circle_mask_render.wgsl, "Circle mask render shader" +MASKED_CUBE_SHADER, NONE, shaders/masked_cube.wgsl, "Masked cube shader" diff --git a/assets/final/shaders/chroma_aberration.wgsl b/assets/final/shaders/chroma_aberration.wgsl index f1a3034..ca1e17d 100644 --- a/assets/final/shaders/chroma_aberration.wgsl +++ b/assets/final/shaders/chroma_aberration.wgsl @@ -1,18 +1,20 @@ @group(0) @binding(0) var smplr: sampler; @group(0) @binding(1) var txt: texture_2d<f32>; -struct Uniforms { +struct CommonUniforms { + resolution: vec2<f32>, + aspect_ratio: f32, time: f32, beat: f32, - intensity: f32, - aspect_ratio: f32, - width: f32, - height: f32, + audio_intensity: f32, +}; +struct EffectParams { offset_scale: f32, angle: f32, }; -@group(0) @binding(2) var<uniform> uniforms: Uniforms; +@group(0) @binding(2) var<uniform> common: CommonUniforms; +@group(0) @binding(3) var<uniform> params: EffectParams; @vertex fn vs_main(@builtin(vertex_index) i: u32) -> @builtin(position) vec4<f32> { var pos = array<vec2<f32>, 3>( @@ -24,11 +26,11 @@ struct Uniforms { } @fragment fn fs_main(@builtin(position) p: vec4<f32>) -> @location(0) vec4<f32> { - let uv = p.xy / vec2<f32>(uniforms.width, uniforms.height); + let uv = p.xy / common.resolution; // Compute offset magnitude and direction - let offset_mag = uniforms.offset_scale * uniforms.intensity; - let offset_dir = vec2<f32>(cos(uniforms.angle), sin(uniforms.angle)); + let offset_mag = params.offset_scale * common.audio_intensity; + let offset_dir = vec2<f32>(cos(params.angle), sin(params.angle)); let offset = offset_mag * offset_dir; // Sample RGB channels with chromatic aberration diff --git a/assets/final/shaders/circle_mask_compute.wgsl b/assets/final/shaders/circle_mask_compute.wgsl index 610ee67..9bb03ff 100644 --- a/assets/final/shaders/circle_mask_compute.wgsl +++ b/assets/final/shaders/circle_mask_compute.wgsl @@ -1,14 +1,20 @@ // Circle mask compute shader // Generates a circular mask (1.0 inside, 0.0 outside) -struct Uniforms { - radius: f32, +struct CommonUniforms { + resolution: vec2<f32>, aspect_ratio: f32, - width: f32, - height: f32, + time: f32, + beat: f32, + audio_intensity: f32, +}; +struct EffectParams { + radius: f32, + _pad: vec3<f32>, }; -@group(0) @binding(0) var<uniform> uniforms: Uniforms; +@group(0) @binding(0) var<uniform> common: CommonUniforms; +@group(0) @binding(1) var<uniform> params: EffectParams; struct VSOutput { @builtin(position) position: vec4<f32>, @@ -21,13 +27,13 @@ struct VSOutput { } @fragment fn fs_main(@builtin(position) p: vec4<f32>) -> @location(0) vec4<f32> { - let uv = p.xy / vec2<f32>(uniforms.width, uniforms.height); + let uv = p.xy / common.resolution; let center = vec2<f32>(0.5, 0.5); - let aspect_corrected_uv = (uv - center) * vec2<f32>(uniforms.aspect_ratio, 1.0); + let aspect_corrected_uv = (uv - center) * vec2<f32>(common.aspect_ratio, 1.0); let dist = length(aspect_corrected_uv); let edge_width = 0.01; - let mask = smoothstep(uniforms.radius + edge_width, uniforms.radius - edge_width, dist); + let mask = smoothstep(params.radius + edge_width, params.radius - edge_width, dist); return vec4<f32>(mask, mask, mask, 1.0); } diff --git a/assets/final/shaders/circle_mask_render.wgsl b/assets/final/shaders/circle_mask_render.wgsl index 902600e..6855c95 100644 --- a/assets/final/shaders/circle_mask_render.wgsl +++ b/assets/final/shaders/circle_mask_render.wgsl @@ -4,14 +4,15 @@ @group(0) @binding(0) var mask_tex: texture_2d<f32>; @group(0) @binding(1) var mask_sampler: sampler; -struct Uniforms { - width: f32, - height: f32, - _pad1: f32, - _pad2: f32, +struct CommonUniforms { + resolution: vec2<f32>, + aspect_ratio: f32, + time: f32, + beat: f32, + audio_intensity: f32, }; -@group(0) @binding(2) var<uniform> uniforms: Uniforms; +@group(0) @binding(2) var<uniform> uniforms: CommonUniforms; struct VSOutput { @builtin(position) position: vec4<f32>, @@ -24,7 +25,7 @@ struct VSOutput { } @fragment fn fs_main(@builtin(position) p: vec4<f32>) -> @location(0) vec4<f32> { - let uv = p.xy / vec2<f32>(uniforms.width, uniforms.height); + let uv = p.xy / uniforms.resolution; let mask_value = textureSample(mask_tex, mask_sampler, uv).r; if (mask_value > 0.5) { diff --git a/assets/final/shaders/common_uniforms.wgsl b/assets/final/shaders/common_uniforms.wgsl index 4b5cf33..1e0e242 100644 --- a/assets/final/shaders/common_uniforms.wgsl +++ b/assets/final/shaders/common_uniforms.wgsl @@ -1,3 +1,10 @@ +struct CommonUniforms { + resolution: vec2<f32>, + aspect_ratio: f32, + time: f32, + beat: f32, + audio_intensity: f32, +}; struct GlobalUniforms { view_proj: mat4x4<f32>, inv_view_proj: mat4x4<f32>, diff --git a/assets/final/shaders/distort.wgsl b/assets/final/shaders/distort.wgsl index 2d5f276..cca01c4 100644 --- a/assets/final/shaders/distort.wgsl +++ b/assets/final/shaders/distort.wgsl @@ -1,15 +1,15 @@ @group(0) @binding(0) var smplr: sampler; @group(0) @binding(1) var txt: texture_2d<f32>; -struct Uniforms { +struct CommonUniforms { + resolution: vec2<f32>, + aspect_ratio: f32, time: f32, beat: f32, - intensity: f32, - aspect_ratio: f32, - resolution: vec2<f32>, + audio_intensity: f32, }; -@group(0) @binding(2) var<uniform> uniforms: Uniforms; +@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>( @@ -22,6 +22,6 @@ struct Uniforms { @fragment fn fs_main(@builtin(position) p: vec4<f32>) -> @location(0) vec4<f32> { let uv = p.xy / uniforms.resolution; - let dist = 0.1 * uniforms.intensity * sin(uv.y * 20.0 + uniforms.time * 5.0); + let dist = 0.1 * uniforms.audio_intensity * sin(uv.y * 20.0 + uniforms.time * 5.0); return textureSample(txt, smplr, uv + vec2<f32>(dist, 0.0)); } diff --git a/assets/final/shaders/ellipse.wgsl b/assets/final/shaders/ellipse.wgsl index be236c8..1b6df0b 100644 --- a/assets/final/shaders/ellipse.wgsl +++ b/assets/final/shaders/ellipse.wgsl @@ -1,12 +1,12 @@ -struct Uniforms { +struct CommonUniforms { + resolution: vec2<f32>, + aspect_ratio: f32, time: f32, beat: f32, - intensity: f32, - aspect_ratio: f32, - resolution: vec2<f32>, + audio_intensity: f32, }; -@group(0) @binding(0) var<uniform> uniforms: Uniforms; +@group(0) @binding(0) var<uniform> uniforms: CommonUniforms; @vertex fn vs_main(@builtin(vertex_index) i: u32) -> @builtin(position) vec4<f32> { var pos = array<vec2<f32>, 3>( diff --git a/assets/final/shaders/gaussian_blur.wgsl b/assets/final/shaders/gaussian_blur.wgsl index e848c6b..39cbf54 100644 --- a/assets/final/shaders/gaussian_blur.wgsl +++ b/assets/final/shaders/gaussian_blur.wgsl @@ -1,18 +1,20 @@ @group(0) @binding(0) var smplr: sampler; @group(0) @binding(1) var txt: texture_2d<f32>; -struct Uniforms { +struct CommonUniforms { + resolution: vec2<f32>, + aspect_ratio: f32, time: f32, beat: f32, - intensity: f32, - aspect_ratio: f32, - width: f32, - height: f32, + audio_intensity: f32, +}; +struct EffectParams { strength: f32, _pad: f32, }; -@group(0) @binding(2) var<uniform> uniforms: Uniforms; +@group(0) @binding(2) var<uniform> common: CommonUniforms; +@group(0) @binding(3) var<uniform> params: EffectParams; @vertex fn vs_main(@builtin(vertex_index) i: u32) -> @builtin(position) vec4<f32> { var pos = array<vec2<f32>, 3>( @@ -24,16 +26,16 @@ struct Uniforms { } @fragment fn fs_main(@builtin(position) p: vec4<f32>) -> @location(0) vec4<f32> { - let uv = p.xy / vec2<f32>(uniforms.width, uniforms.height); + let uv = p.xy / common.resolution; var res = vec4<f32>(0.0); // Parameterized strength + dramatic beat pulsation - let pulse = 0.5 + uniforms.intensity * 2.0; // Pulsate between 0.5x and 2.5x with beat - let size = uniforms.strength * pulse; + let pulse = 0.5 + common.audio_intensity * 2.0; // Pulsate between 0.5x and 2.5x with beat + let size = params.strength * pulse; for (var x: f32 = -2.0; x <= 2.0; x += 1.0) { for (var y: f32 = -2.0; y <= 2.0; y += 1.0) { - res += textureSample(txt, smplr, uv + vec2<f32>(x, y) * size / uniforms.width); + res += textureSample(txt, smplr, uv + vec2<f32>(x, y) * size / common.resolution.x); } } return res / 25.0; diff --git a/assets/final/shaders/main_shader.wgsl b/assets/final/shaders/main_shader.wgsl index 7011159..5bb7d46 100644 --- a/assets/final/shaders/main_shader.wgsl +++ b/assets/final/shaders/main_shader.wgsl @@ -1,15 +1,17 @@ -struct Uniforms { - audio_peak: f32, +struct CommonUniforms { + resolution: vec2<f32>, aspect_ratio: f32, time: f32, + beat: f32, + audio_intensity: f32, }; -@group(0) @binding(0) var<uniform> uniforms: Uniforms; +@group(0) @binding(0) var<uniform> uniforms: CommonUniforms; @vertex fn vs_main(@builtin(vertex_index) i: u32) -> @builtin(position) vec4<f32> { let PI = 3.14159265; let num_sides = 7.0; - let scale = 0.5 + 0.3 * uniforms.audio_peak; + let scale = 0.5 + 0.3 * uniforms.audio_intensity; let tri_idx = f32(i / 3u); let sub_idx = i % 3u; if (sub_idx == 0u) { @@ -20,10 +22,10 @@ struct Uniforms { } @fragment fn fs_main() -> @location(0) vec4<f32> { - let h = uniforms.time * 2.0 + uniforms.audio_peak * 3.0; + let h = uniforms.time * 2.0 + uniforms.audio_intensity * 3.0; let r = sin(h) * 0.5 + 0.5; let g = sin(h + 2.0) * 0.9 + 0.3; let b = sin(h + 4.0) * 0.5 + 0.5; - let boost = uniforms.audio_peak * 0.5; + let boost = uniforms.audio_intensity * 0.5; return vec4<f32>(r + boost, g + boost, b + boost, 1.0); } diff --git a/assets/final/shaders/masked_cube.wgsl b/assets/final/shaders/masked_cube.wgsl index 77e2fb9..5e673a3 100644 --- a/assets/final/shaders/masked_cube.wgsl +++ b/assets/final/shaders/masked_cube.wgsl @@ -7,7 +7,6 @@ @group(0) @binding(1) var<storage, read> object_data: ObjectsBuffer; @group(0) @binding(3) var noise_tex: texture_2d<f32>; @group(0) @binding(4) var noise_sampler: sampler; -@group(0) @binding(5) var sky_tex: texture_2d<f32>; @group(1) @binding(0) var mask_tex: texture_2d<f32>; @group(1) @binding(1) var mask_sampler: sampler; @@ -89,13 +88,13 @@ fn fs_main(in: VertexOutput) -> FragmentOutput { let local_origin = (inv_model * vec4<f32>(ray_origin, 1.0)).xyz; let local_dir = normalize((inv_model * vec4<f32>(ray_dir, 0.0)).xyz); - let t = ray_box(local_origin, local_dir, vec3<f32>(-1.0), vec3<f32>(1.0)); - if (t.y < 0.0) { + let bounds = ray_box_intersection(local_origin, local_dir, vec3<f32>(1.0)); + if (!bounds.hit) { discard; } - let t_start = max(t.x, 0.0); - let t_end = t.y; + let t_start = bounds.t_entry; + let t_end = bounds.t_exit; var t_march = t_start; let max_steps = 128; diff --git a/assets/final/shaders/particle_compute.wgsl b/assets/final/shaders/particle_compute.wgsl index a6c96db..f2f7ae3 100644 --- a/assets/final/shaders/particle_compute.wgsl +++ b/assets/final/shaders/particle_compute.wgsl @@ -5,15 +5,16 @@ struct Particle { color: vec4<f32>, }; -struct Uniforms { - audio_peak: f32, +struct CommonUniforms { + resolution: vec2<f32>, aspect_ratio: f32, time: f32, beat: f32, + audio_intensity: f32, }; @group(0) @binding(0) var<storage, read_write> particles: array<Particle>; -@group(0) @binding(1) var<uniform> uniforms: Uniforms; +@group(0) @binding(1) var<uniform> uniforms: CommonUniforms; @compute @workgroup_size(64) fn main(@builtin(global_invocation_id) id: vec3<u32>) { @@ -24,11 +25,11 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) { var p = particles[i]; let new_pos = p.pos.xyz + p.vel.xyz * 0.016; p.pos = vec4<f32>(new_pos, p.pos.w); - p.vel.y = p.vel.y - 0.01 * (1.0 + uniforms.audio_peak * 5.0); + p.vel.y = p.vel.y - 0.01 * (1.0 + uniforms.audio_intensity * 5.0); p.rot.x = p.rot.x + p.rot.y * 0.016; if (p.pos.y < -1.5) { p.pos.y = 1.5; - p.pos.x = (f32(i % 100u) / 50.0) - 1.0 + (uniforms.audio_peak * 0.5); + p.pos.x = (f32(i % 100u) / 50.0) - 1.0 + (uniforms.audio_intensity * 0.5); p.vel.y = 0.0; } particles[i] = p; diff --git a/assets/final/shaders/particle_render.wgsl b/assets/final/shaders/particle_render.wgsl index 6a955f0..a0740dc 100644 --- a/assets/final/shaders/particle_render.wgsl +++ b/assets/final/shaders/particle_render.wgsl @@ -5,15 +5,16 @@ struct Particle { color: vec4<f32>, }; -struct Uniforms { - audio_peak: f32, +struct CommonUniforms { + resolution: vec2<f32>, aspect_ratio: f32, time: f32, beat: f32, + audio_intensity: f32, }; @group(0) @binding(0) var<storage, read> particles: array<Particle>; -@group(0) @binding(1) var<uniform> uniforms: Uniforms; +@group(0) @binding(1) var<uniform> uniforms: CommonUniforms; struct VSOut { @builtin(position) pos: vec4<f32>, @@ -23,7 +24,7 @@ struct VSOut { @vertex fn vs_main(@builtin(vertex_index) vi: u32, @builtin(instance_index) ii: u32) -> VSOut { let p = particles[ii]; - let size = 0.02 + p.pos.z * 0.01 + uniforms.audio_peak * 0.02; + let size = 0.02 + p.pos.z * 0.01 + uniforms.audio_intensity * 0.02; var offsets = array<vec2<f32>, 6>( vec2<f32>(-1, -1), vec2<f32>(1, -1), @@ -40,7 +41,7 @@ struct VSOut { // Fade based on lifetime (p.pos.w goes from 1.0 to 0.0) let lifetime_fade = p.pos.w; - let color_with_fade = vec4<f32>(p.color.rgb * (0.5 + 0.5 * uniforms.audio_peak), p.color.a * lifetime_fade); + let color_with_fade = vec4<f32>(p.color.rgb * (0.5 + 0.5 * uniforms.audio_intensity), p.color.a * lifetime_fade); return VSOut(vec4<f32>(pos, 0.0, 1.0), color_with_fade, offset); } diff --git a/assets/final/shaders/particle_spray_compute.wgsl b/assets/final/shaders/particle_spray_compute.wgsl index 55fa8e9..f1a4d43 100644 --- a/assets/final/shaders/particle_spray_compute.wgsl +++ b/assets/final/shaders/particle_spray_compute.wgsl @@ -5,15 +5,16 @@ struct Particle { color: vec4<f32>, }; -struct Uniforms { - intensity: f32, +struct CommonUniforms { + resolution: vec2<f32>, aspect_ratio: f32, time: f32, beat: f32, + audio_intensity: f32, }; @group(0) @binding(0) var<storage, read_write> particles: array<Particle>; -@group(0) @binding(1) var<uniform> uniforms: Uniforms; +@group(0) @binding(1) var<uniform> uniforms: CommonUniforms; fn hash(p: f32) -> f32 { return fract(sin(p) * 43758.5453); @@ -30,7 +31,7 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) { let r = hash(f32(i) + uniforms.time); let angle = r * 6.28318; p.pos = vec4<f32>(0.0, 0.0, 0.0, 1.0); - p.vel = vec4<f32>(cos(angle), sin(angle), 0.0, 0.0) * (0.5 + hash(r) * 0.5) * (1.0 + uniforms.intensity * 2.0); + p.vel = vec4<f32>(cos(angle), sin(angle), 0.0, 0.0) * (0.5 + hash(r) * 0.5) * (1.0 + uniforms.audio_intensity * 2.0); p.color = vec4<f32>(hash(r + 0.1), hash(r + 0.2), 1.0, 1.0); } let new_pos = p.pos.xyz + p.vel.xyz * 0.016; diff --git a/assets/final/shaders/passthrough.wgsl b/assets/final/shaders/passthrough.wgsl index aa4de1c..c1ac60a 100644 --- a/assets/final/shaders/passthrough.wgsl +++ b/assets/final/shaders/passthrough.wgsl @@ -1,14 +1,14 @@ @group(0) @binding(0) var smplr: sampler; @group(0) @binding(1) var txt: texture_2d<f32>; -struct Uniforms { +struct CommonUniforms { + resolution: vec2<f32>, + aspect_ratio: f32, time: f32, beat: f32, - intensity: f32, - aspect_ratio: f32, - resolution: vec2<f32>, + audio_intensity: f32, }; -@group(0) @binding(2) var<uniform> uniforms: Uniforms; +@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>( diff --git a/assets/final/shaders/solarize.wgsl b/assets/final/shaders/solarize.wgsl index fcb9d80..c9f19a2 100644 --- a/assets/final/shaders/solarize.wgsl +++ b/assets/final/shaders/solarize.wgsl @@ -1,15 +1,15 @@ @group(0) @binding(0) var smplr: sampler; @group(0) @binding(1) var txt: texture_2d<f32>; -struct Uniforms { +struct CommonUniforms { + resolution: vec2<f32>, + aspect_ratio: f32, time: f32, beat: f32, - intensity: f32, - aspect_ratio: f32, - resolution: vec2<f32>, + audio_intensity: f32, }; -@group(0) @binding(2) var<uniform> uniforms: Uniforms; +@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>( diff --git a/assets/final/shaders/vignette.wgsl b/assets/final/shaders/vignette.wgsl new file mode 100644 index 0000000..9b7f43f --- /dev/null +++ b/assets/final/shaders/vignette.wgsl @@ -0,0 +1,37 @@ +@group(0) @binding(0) var input_sampler: sampler; +@group(0) @binding(1) var input_tex: texture_2d<f32>; +struct CommonUniforms { + resolution: vec2<f32>, + aspect_ratio: f32, + time: f32, + beat: f32, + audio_intensity: f32, +}; +struct EffectParams { + radius: f32, + softness: f32, +}; + +@group(0) @binding(2) var<uniform> common: CommonUniforms; +@group(0) @binding(3) var<uniform> params: EffectParams; + +@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); +} + +@fragment +fn fs_main(@builtin(position) pos: vec4<f32>) -> @location(0) vec4<f32> { + let uv = pos.xy / common.resolution; + let color = textureSample(input_tex, input_sampler, uv); + + let d = distance(uv, vec2<f32>(0.5, 0.5)); + let vignette = smoothstep(params.radius, params.radius - params.softness, d); + + return vec4<f32>(color.rgb * mix(1.0, vignette, common.audio_intensity), color.a); +}
\ No newline at end of file diff --git a/assets/final/shaders/visual_debug.wgsl b/assets/final/shaders/visual_debug.wgsl index e91c1a9..63e1f13 100644 --- a/assets/final/shaders/visual_debug.wgsl +++ b/assets/final/shaders/visual_debug.wgsl @@ -1,7 +1,11 @@ -struct Uniforms { - viewProj : mat4x4<f32>, -} -@group(0) @binding(0) var<uniform> uniforms : Uniforms; +struct GlobalUniforms { + view_proj: mat4x4<f32>, + inv_view_proj: mat4x4<f32>, + camera_pos_time: vec4<f32>, + params: vec4<f32>, + resolution: vec2<f32>, +}; +@group(0) @binding(0) var<uniform> uniforms : GlobalUniforms; struct VertexInput { @location(0) position : vec3<f32>, @@ -16,7 +20,7 @@ struct VertexOutput { @vertex fn vs_main(in : VertexInput) -> VertexOutput { var out : VertexOutput; - out.position = uniforms.viewProj * vec4<f32>(in.position, 1.0); + out.position = uniforms.view_proj * vec4<f32>(in.position, 1.0); out.color = in.color; return out; } diff --git a/src/gpu/demo_effects.cc b/src/gpu/demo_effects.cc index 36fd16e..069d36c 100644 --- a/src/gpu/demo_effects.cc +++ b/src/gpu/demo_effects.cc @@ -3,6 +3,8 @@ // Its content has been split into individual effect files and helper files. #include "gpu/demo_effects.h" +#include "gpu/effects/circle_mask_effect.h" +#include "gpu/effects/rotating_cube_effect.h" // Auto-generated function to populate the timeline void LoadTimeline(MainSequence& main_seq, WGPUDevice device, WGPUQueue queue, diff --git a/src/gpu/demo_effects.h b/src/gpu/demo_effects.h index 82700cd..fabfbd2 100644 --- a/src/gpu/demo_effects.h +++ b/src/gpu/demo_effects.h @@ -9,6 +9,8 @@ #include "gpu/effects/flash_effect.h" // FlashEffect with params support #include "gpu/effects/post_process_helper.h" #include "gpu/effects/shaders.h" +#include "gpu/effects/circle_mask_effect.h" +#include "gpu/effects/rotating_cube_effect.h" #include "gpu/gpu.h" #include "gpu/texture_manager.h" #include "gpu/uniform_helper.h" diff --git a/src/gpu/effects/circle_mask_effect.cc b/src/gpu/effects/circle_mask_effect.cc index 55bcb90..226b603 100644 --- a/src/gpu/effects/circle_mask_effect.cc +++ b/src/gpu/effects/circle_mask_effect.cc @@ -51,7 +51,7 @@ void CircleMaskEffect::init(MainSequence* demo) { WGPUShaderModule compute_module = wgpuDeviceCreateShaderModule(ctx_.device, &compute_desc); const WGPUColorTargetState compute_target = { - .format = WGPUTextureFormat_RGBA8Unorm, + .format = ctx_.format, // Match auxiliary texture format .writeMask = WGPUColorWriteMask_All, }; WGPUFragmentState compute_frag = {}; @@ -60,6 +60,7 @@ void CircleMaskEffect::init(MainSequence* demo) { compute_frag.targetCount = 1; compute_frag.targets = &compute_target; WGPURenderPipelineDescriptor compute_pipeline_desc = {}; + compute_pipeline_desc.label = label_view("CircleMaskEffect_compute"); compute_pipeline_desc.vertex.module = compute_module; compute_pipeline_desc.vertex.entryPoint = str_view("vs_main"); compute_pipeline_desc.primitive.topology = WGPUPrimitiveTopology_TriangleList; @@ -98,11 +99,19 @@ void CircleMaskEffect::init(MainSequence* demo) { render_frag.entryPoint = str_view("fs_main"); render_frag.targetCount = 1; render_frag.targets = &render_target; + const WGPUDepthStencilState depth_stencil = { + .format = WGPUTextureFormat_Depth24Plus, + .depthWriteEnabled = WGPUOptionalBool_False, // Don't write depth + .depthCompare = WGPUCompareFunction_Always, // Always pass + }; + WGPURenderPipelineDescriptor render_pipeline_desc = {}; + render_pipeline_desc.label = label_view("CircleMaskEffect_render"); render_pipeline_desc.vertex.module = render_module; render_pipeline_desc.vertex.entryPoint = str_view("vs_main"); render_pipeline_desc.primitive.topology = WGPUPrimitiveTopology_TriangleList; render_pipeline_desc.primitive.cullMode = WGPUCullMode_None; + render_pipeline_desc.depthStencil = &depth_stencil; render_pipeline_desc.multisample.count = 1; render_pipeline_desc.multisample.mask = 0xFFFFFFFF; render_pipeline_desc.fragment = &render_frag; @@ -139,16 +148,18 @@ void CircleMaskEffect::compute(WGPUCommandEncoder encoder, float time, compute_uniforms_.update(ctx_.queue, uniforms); WGPUTextureView mask_view = demo_->get_auxiliary_view("circle_mask"); - const WGPURenderPassColorAttachment color_attachment = { - .view = mask_view, - .loadOp = WGPULoadOp_Clear, - .storeOp = WGPUStoreOp_Store, - .clearValue = {0.0, 0.0, 0.0, 1.0}, - }; - const WGPURenderPassDescriptor pass_desc = { - .colorAttachmentCount = 1, - .colorAttachments = &color_attachment, - }; + WGPURenderPassColorAttachment color_attachment = {}; + color_attachment.view = mask_view; + color_attachment.loadOp = WGPULoadOp_Clear; + color_attachment.storeOp = WGPUStoreOp_Store; + color_attachment.clearValue = {0.0, 0.0, 0.0, 1.0}; +#if !defined(DEMO_CROSS_COMPILE_WIN32) + color_attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED; +#endif + + WGPURenderPassDescriptor pass_desc = {}; + pass_desc.colorAttachmentCount = 1; + pass_desc.colorAttachments = &color_attachment; WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(encoder, &pass_desc); wgpuRenderPassEncoderSetPipeline(pass, compute_pipeline_); diff --git a/src/gpu/effects/rotating_cube_effect.cc b/src/gpu/effects/rotating_cube_effect.cc index b4f3d3e..7f590c5 100644 --- a/src/gpu/effects/rotating_cube_effect.cc +++ b/src/gpu/effects/rotating_cube_effect.cc @@ -63,12 +63,12 @@ void RotatingCubeEffect::init(MainSequence* demo) { ShaderComposer::CompositionMap composition_map; composition_map["render/scene_query_mode"] = "render/scene_query_linear"; - std::string composed_shader = ShaderComposer::Get().Compose( + composed_shader_ = ShaderComposer::Get().Compose( {}, std::string(shader_code, shader_size), composition_map); WGPUShaderSourceWGSL wgsl_src = {}; wgsl_src.chain.sType = WGPUSType_ShaderSourceWGSL; - wgsl_src.code = str_view(composed_shader.c_str()); + wgsl_src.code = str_view(composed_shader_.c_str()); WGPUShaderModuleDescriptor shader_desc = {}; shader_desc.nextInChain = &wgsl_src.chain; @@ -105,18 +105,16 @@ void RotatingCubeEffect::init(MainSequence* demo) { pipeline_ = wgpuDeviceCreateRenderPipeline(ctx_.device, &pipeline_desc); wgpuShaderModuleRelease(shader_module); - WGPUTextureView dummy_sky = noise_view_; const WGPUBindGroupEntry entries_0[] = { {.binding = 0, .buffer = uniform_buffer_.buffer, .size = sizeof(Uniforms)}, {.binding = 1, .buffer = object_buffer_.buffer, .size = sizeof(ObjectData)}, {.binding = 3, .textureView = noise_view_}, {.binding = 4, .sampler = noise_sampler_}, - {.binding = 5, .textureView = dummy_sky}, }; const WGPUBindGroupDescriptor bg_desc_0 = { .layout = wgpuRenderPipelineGetBindGroupLayout(pipeline_, 0), - .entryCount = 5, + .entryCount = 4, .entries = entries_0, }; bind_group_0_ = wgpuDeviceCreateBindGroup(ctx_.device, &bg_desc_0); diff --git a/src/gpu/effects/rotating_cube_effect.h b/src/gpu/effects/rotating_cube_effect.h index 1ce81b7..89b3fa6 100644 --- a/src/gpu/effects/rotating_cube_effect.h +++ b/src/gpu/effects/rotating_cube_effect.h @@ -8,6 +8,7 @@ #include "gpu/effect.h" #include "gpu/gpu.h" #include "util/mini_math.h" +#include <string> class RotatingCubeEffect : public Effect { public: @@ -46,6 +47,9 @@ class RotatingCubeEffect : public Effect { WGPUSampler noise_sampler_ = nullptr; WGPUSampler mask_sampler_ = nullptr; float rotation_ = 0.0f; + + // Store composed shader to keep it alive for WebGPU + std::string composed_shader_; }; #endif /* ROTATING_CUBE_EFFECT_H_ */ diff --git a/src/tests/test_demo_effects.cc b/src/tests/test_demo_effects.cc index cf77c13..25ada59 100644 --- a/src/tests/test_demo_effects.cc +++ b/src/tests/test_demo_effects.cc @@ -17,8 +17,9 @@ static constexpr int EXPECTED_POST_PROCESS_COUNT = // ChromaAberrationEffect, SolarizeEffect, FadeEffect, // ThemeModulationEffect, VignetteEffect static constexpr int EXPECTED_SCENE_COUNT = - 6; // HeptagonEffect, ParticlesEffect, ParticleSprayEffect, - // MovingEllipseEffect, FlashCubeEffect, Hybrid3DEffect + 8; // HeptagonEffect, ParticlesEffect, ParticleSprayEffect, + // MovingEllipseEffect, FlashCubeEffect, Hybrid3DEffect, + // CircleMaskEffect, RotatingCubeEffect #include "effect_test_helpers.h" #include "gpu/demo_effects.h" @@ -154,6 +155,8 @@ static void test_scene_effects() { std::make_shared<MovingEllipseEffect>(fixture.ctx())}, {"FlashCubeEffect", std::make_shared<FlashCubeEffect>(fixture.ctx())}, {"Hybrid3DEffect", std::make_shared<Hybrid3DEffect>(fixture.ctx())}, + {"CircleMaskEffect", std::make_shared<CircleMaskEffect>(fixture.ctx())}, + {"RotatingCubeEffect", std::make_shared<RotatingCubeEffect>(fixture.ctx())}, }; int passed = 0; @@ -163,9 +166,11 @@ static void test_scene_effects() { assert(!effect->is_post_process() && "Scene effect should return false for is_post_process()"); - // FlashCubeEffect and Hybrid3DEffect require full 3D pipeline (Renderer3D) + // FlashCubeEffect, Hybrid3DEffect, RotatingCubeEffect, and CircleMaskEffect require full 3D pipeline (Renderer3D) or auxiliary textures const bool requires_3d = (strcmp(name, "FlashCubeEffect") == 0 || - strcmp(name, "Hybrid3DEffect") == 0); + strcmp(name, "Hybrid3DEffect") == 0 || + strcmp(name, "RotatingCubeEffect") == 0 || + strcmp(name, "CircleMaskEffect") == 0); const int result = test_effect_smoke(name, effect, &main_seq, requires_3d); if (result == 1) { |
