summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--assets/final/shaders/renderer_3d.wgsl37
-rw-r--r--doc/HANDOFF_2026-02-08.md359
2 files changed, 362 insertions, 34 deletions
diff --git a/assets/final/shaders/renderer_3d.wgsl b/assets/final/shaders/renderer_3d.wgsl
index 1183e3c..d3b0bae 100644
--- a/assets/final/shaders/renderer_3d.wgsl
+++ b/assets/final/shaders/renderer_3d.wgsl
@@ -1,5 +1,6 @@
#include "common_uniforms"
#include "math/common_utils"
+#include "math/sdf_utils"
@group(0) @binding(0) var<uniform> globals: GlobalUniforms;
@group(0) @binding(1) var<storage, read> object_data: ObjectsBuffer;
@@ -135,41 +136,9 @@ fn fs_main(in: VertexOutput) -> FragmentOutput {
let q_hit = ro_local + rd_local * t;
p = (obj.model * vec4<f32>(q_hit, 1.0)).xyz; // Correct world position
- // Calculate normal with bump mapping
- let e = vec2<f32>(0.005, 0.0);
+ // Calculate normal with bump mapping (using utility function)
let disp_strength = 0.05;
-
- let q_x1 = q_hit + e.xyy;
- let uv_x1 = spherical_uv(q_x1);
- let h_x1 = textureSample(noise_tex, noise_sampler, uv_x1).r;
- let d_x1 = get_dist(q_x1, obj.params) - disp_strength * h_x1;
-
- let q_x2 = q_hit - e.xyy;
- let uv_x2 = spherical_uv(q_x2);
- let h_x2 = textureSample(noise_tex, noise_sampler, uv_x2).r;
- let d_x2 = get_dist(q_x2, obj.params) - disp_strength * h_x2;
-
- let q_y1 = q_hit + e.yxy;
- let uv_y1 = spherical_uv(q_y1);
- let h_y1 = textureSample(noise_tex, noise_sampler, uv_y1).r;
- let d_y1 = get_dist(q_y1, obj.params) - disp_strength * h_y1;
-
- let q_y2 = q_hit - e.yxy;
- let uv_y2 = spherical_uv(q_y2);
- let h_y2 = textureSample(noise_tex, noise_sampler, uv_y2).r;
- let d_y2 = get_dist(q_y2, obj.params) - disp_strength * h_y2;
-
- let q_z1 = q_hit + e.yyx;
- let uv_z1 = spherical_uv(q_z1);
- let h_z1 = textureSample(noise_tex, noise_sampler, uv_z1).r;
- let d_z1 = get_dist(q_z1, obj.params) - disp_strength * h_z1;
-
- let q_z2 = q_hit - e.yyx;
- let uv_z2 = spherical_uv(q_z2);
- let h_z2 = textureSample(noise_tex, noise_sampler, uv_z2).r;
- let d_z2 = get_dist(q_z2, obj.params) - disp_strength * h_z2;
-
- let n_local = normalize(vec3<f32>(d_x1 - d_x2, d_y1 - d_y2, d_z1 - d_z2));
+ let n_local = get_normal_bump(q_hit, obj.params, noise_tex, noise_sampler, disp_strength);
normal = transform_normal(obj.inv_model, n_local);
// Apply texture to SDF color
diff --git a/doc/HANDOFF_2026-02-08.md b/doc/HANDOFF_2026-02-08.md
new file mode 100644
index 0000000..f796f05
--- /dev/null
+++ b/doc/HANDOFF_2026-02-08.md
@@ -0,0 +1,359 @@
+# Handoff: Shader Parametrization System (February 8, 2026)
+
+## Summary
+Completed comprehensive shader parametrization system enabling dynamic per-frame parameters for visual effects via uniform buffers and .seq file syntax.
+
+## Work Completed ✅
+
+### Shader Parametrization System (Task #73 Phase 0)
+**Goal**: Enable artists to configure visual effects with parameters (color, intensity, decay rates) via .seq files without touching C++ code.
+
+**Use Cases**: Flash effect with custom colors, chromatic aberration strength control, blur radius adjustment, distortion parameters.
+
+**Implementation**:
+
+#### Phase 1: UniformHelper Template
+- Created `src/gpu/uniform_helper.h` - Generic type-safe wrapper for WebGPU uniform buffers
+- Template class handles buffer creation, updates, and lifetime management
+- Zero-overhead abstraction over `gpu_create_buffer()` and `wgpuQueueWriteBuffer()`
+
+```cpp
+template <typename T>
+class UniformBuffer {
+ void init(WGPUDevice device);
+ void update(WGPUQueue queue, const T& data);
+ GpuBuffer& get();
+};
+```
+
+#### Phase 2: FlashEffect Parameter Structs
+- Added `FlashEffectParams` (constructor-time base parameters):
+ - `color[3]` - RGB flash color (default: white)
+ - `decay_rate` - Flash fade rate per frame (default: 0.98)
+ - `trigger_threshold` - Intensity threshold to trigger flash (default: 0.7)
+
+- Added `FlashUniforms` (GPU buffer layout with WGSL alignment):
+ - `flash_intensity` (4 bytes, offset 0)
+ - `intensity` (4 bytes, offset 4)
+ - `_pad1[2]` (8 bytes, offset 8-15) - **Padding for vec3 alignment**
+ - `color[3]` (12 bytes, offset 16-27) - **Aligned to 16 bytes**
+ - `_pad2` (4 bytes, offset 28-31)
+ - Total: 32 bytes (enforced with `static_assert`)
+
+#### Phase 3: Parameterized WGSL Shader
+- Updated `flash_effect.cc` shader to use `uniforms.flash_color` instead of hardcoded white
+- Shader bindings: sampler (0), texture (1), uniforms (2)
+- Fragment shader mixes input color with parameterized flash color based on flash intensity
+
+```wgsl
+struct Uniforms {
+ flash_intensity: f32,
+ intensity: f32,
+ flash_color: vec3<f32>, // Parameterized color
+ _pad: f32,
+};
+
+fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
+ let color = textureSample(inputTexture, inputSampler, input.uv);
+ var flashed = mix(color.rgb, uniforms.flash_color, uniforms.flash_intensity);
+ return vec4<f32>(flashed, color.a);
+}
+```
+
+#### Phase 4: Per-Frame Parameter Computation
+- Implemented dynamic parameter animation in `FlashEffect::render()` method
+- Parameters computed each frame based on time, beat, and intensity
+- Example: Color modulated by sinusoidal functions of time and beat
+
+```cpp
+// Animate color based on time and beat
+const float r = params_.color[0] * (0.5f + 0.5f * sinf(time * 0.5f));
+const float g = params_.color[1] * (0.5f + 0.5f * cosf(time * 0.7f));
+const float b = params_.color[2] * (1.0f + 0.3f * beat);
+
+const FlashUniforms u = {
+ .flash_intensity = flash_intensity_,
+ .intensity = intensity,
+ ._pad1 = {0.0f, 0.0f},
+ .color = {r, g, b},
+ .pad2 = 0.0f
+};
+uniforms_.update(ctx_.queue, u);
+```
+
+#### Phase 5: .seq Syntax Extension
+- Extended `seq_compiler.cc` to parse `key=value` parameters after effect class name
+- Added `parse_parameters()` function to extract parameter pairs
+- Code generation creates `FlashEffectParams` struct initialization for parameterized effects
+- Backward compatibility: Effects without parameters use default constructor
+
+**Syntax Example**:
+```
+EFFECT + FlashEffect 0 1 color=1.0,0.5,0.5 decay=0.95
+```
+
+**Generated Code**:
+```cpp
+{
+ FlashEffectParams p;
+ p.color[0] = 1.0f;
+ p.color[1] = 0.5f;
+ p.color[2] = 0.5f;
+ p.decay_rate = 0.95f;
+ seq->add_effect(std::make_shared<FlashEffect>(ctx, p), 0.0f, 1.f, 0);
+}
+```
+
+---
+
+### Critical Bugfix: WGSL Alignment
+**Problem**: WebGPU validation error "Buffer is bound with size 24 where the shader expects 32"
+
+**Root Cause**: WGSL alignment rules require `vec3<f32>` to be 16-byte aligned despite having 12-byte size. Initial `FlashUniforms` struct layout was:
+```cpp
+struct FlashUniforms {
+ float flash_intensity; // 0-3
+ float intensity; // 4-7
+ float color[3]; // 8-19 (WRONG - no padding)
+ float _pad; // 20-23
+}; // Total: 24 bytes (WRONG)
+```
+
+**Solution**: Added explicit padding to align `color` array to 16-byte boundary:
+```cpp
+struct FlashUniforms {
+ float flash_intensity; // 0-3
+ float intensity; // 4-7
+ float _pad1[2]; // 8-15 (padding)
+ float color[3]; // 16-27 (aligned to 16 bytes)
+ float _pad2; // 28-31
+}; // Total: 32 bytes (CORRECT)
+static_assert(sizeof(FlashUniforms) == 32, "FlashUniforms must be 32 bytes");
+```
+
+**Impact**: Demo now runs without validation errors, all 32/32 tests pass.
+
+---
+
+## Files Modified
+
+**New Files (3)**:
+- `src/gpu/uniform_helper.h` - UniformBuffer template class
+- `src/tests/test_uniform_helper.cc` - Unit test for template compilation
+- `doc/SHADER_PARAMETRIZATION_PLAN.md` - Complete design document
+
+**Modified Files (11)**:
+- `src/gpu/effects/flash_effect.h` - Added FlashEffectParams, FlashUniforms structs, parameterized constructor
+- `src/gpu/effects/flash_effect.cc` - Implemented per-frame computation, updated shader with parameterized color
+- `src/gpu/demo_effects.h` - Removed duplicate FlashEffect definition, added include
+- `tools/seq_compiler.cc` - Added parameter parsing and code generation
+- `assets/demo.seq` - Added example with parameters
+- `src/generated/timeline.cc` - Generated code with struct initialization
+- `CMakeLists.txt` - Added test_uniform_helper target
+- `TODO.md` - Added Task #73 (extend to other effects), moved completion to summary
+- `PROJECT_CONTEXT.md` - Updated "Recently Completed" section
+- `doc/COMPLETED.md` - Added detailed completion entry
+- `doc/HANDOFF_2026-02-08.md` - This document
+
+---
+
+## Commits Made
+
+**c7d1dd7** - `feat(gpu): Implement shader parametrization system`
+- Phases 1-5 implementation
+- 11 files changed (808+ / 40-)
+- UniformHelper template + FlashEffect parametrization + .seq syntax
+
+**775c0ea** - `fix(gpu): Correct FlashUniforms struct alignment for WGSL`
+- Critical alignment bugfix (24 → 32 bytes)
+- Added static_assert for future safety
+- Fixed validation error
+
+---
+
+## Test Results
+
+**All 32/32 tests passing (100%)**
+
+**Test Coverage**:
+- `test_uniform_helper.cc` - Verifies UniformBuffer template compiles correctly
+- `test_demo_effects.cc` - All post-process and scene effects instantiate successfully
+- Full shader compilation tests pass with new parameterized shaders
+
+**Verification**:
+- Demo runs without WebGPU validation errors
+- Visual effects render with parameterized colors
+- .seq file parsing generates correct parameter initialization code
+- Per-frame animation produces time-dependent color modulation
+
+---
+
+## Current Status
+
+**Completed:**
+- ✅ Full shader parametrization infrastructure
+- ✅ FlashEffect fully parameterized with color/decay control
+- ✅ .seq syntax extension with key=value parsing
+- ✅ Per-frame dynamic parameter computation
+- ✅ Critical WGSL alignment bug fixed
+- ✅ Documentation updated (TODO.md, PROJECT_CONTEXT.md, COMPLETED.md)
+
+**Ready for:**
+- Task #73: Extend parametrization to other effects (ChromaAberrationEffect, GaussianBlurEffect, DistortEffect, SolarizeEffect)
+- Additional parameter types (vec2, vec4, enums)
+- Parameter curve system for keyframe animation (future)
+
+---
+
+## Technical Notes
+
+### WGSL Alignment Rules
+**Critical for future implementations:**
+- `vec3<f32>` has **size = 12 bytes** but **alignment = 16 bytes**
+- Always add padding before vec3 fields to align to 16-byte boundary
+- Use `static_assert(sizeof(YourStruct) == expected_size)` to catch misalignment
+- Reference: [WebGPU WGSL Spec - Alignment and Size](https://www.w3.org/TR/WGSL/#alignment-and-size)
+
+### UniformHelper Pattern
+**Reusable template for any effect parameters:**
+```cpp
+// 1. Define parameter structs
+struct YourEffectParams { /* constructor-time params */ };
+struct YourEffectUniforms { /* GPU buffer layout with padding */ };
+static_assert(sizeof(YourEffectUniforms) == expected_size);
+
+// 2. Add UniformBuffer member
+UniformBuffer<YourEffectUniforms> uniforms_;
+
+// 3. Initialize in constructor
+uniforms_.init(ctx_.device);
+
+// 4. Update in render()
+const YourEffectUniforms u = { /* compute values */ };
+uniforms_.update(ctx_.queue, u);
+
+// 5. Bind in shader
+pipeline_ = create_pipeline(...);
+bind_group_ = create_bind_group(..., uniforms_.get().buffer, ...);
+```
+
+### Backward Compatibility
+**All existing effects continue to work:**
+- Default parameter constructor delegates to parameterized constructor
+- Effects without .seq parameters use default values
+- No changes required to existing timeline code
+
+---
+
+## Design Document
+
+**Complete design**: See `doc/SHADER_PARAMETRIZATION_PLAN.md`
+
+**Key Sections**:
+- Motivation and use cases
+- 5-phase implementation plan
+- WGSL alignment rules and solutions
+- Per-frame vs constructor-time parameters
+- .seq syntax specification
+- Migration path for existing effects
+- Size budget analysis (~400-500 bytes)
+- Alternative approaches considered
+
+---
+
+## Size Impact
+
+**Binary Size**: +400-500 bytes net
+- UniformHelper template code: ~200 bytes
+- FlashEffectParams/Uniforms structs: ~100 bytes
+- Per-effect parameter overhead: ~50-100 bytes each
+- .seq compiler parameter parsing: ~100 bytes
+
+**Trade-off**: Acceptable size increase for significant artist workflow improvement (no C++ recompilation for parameter tweaks).
+
+---
+
+## Next Steps (Recommendations)
+
+### Immediate (Task #73)
+Extend parametrization to other effects using FlashEffect as template:
+
+1. **ChromaAberrationEffect**:
+ - Parameters: `offset` (vec2), `strength` (float)
+ - Syntax: `EFFECT + ChromaAberrationEffect 0 5 offset=0.01,0.02 strength=1.5`
+
+2. **GaussianBlurEffect**:
+ - Parameters: `radius` (float), `sigma` (float)
+ - Syntax: `EFFECT + GaussianBlurEffect 2 8 radius=5.0 sigma=2.0`
+
+3. **DistortEffect** (if exists):
+ - Parameters: `amount` (float), `speed` (float), `scale` (vec2)
+
+4. **SolarizeEffect**:
+ - Parameters: `threshold` (float), `intensity` (float)
+
+### Future Enhancements
+- Parameter curve system (keyframe animation via .seq timeline)
+- vec4/mat4 parameter support (color with alpha, transformation matrices)
+- Enum parameters (blend modes, filter types)
+- Parameter validation and clamping (min/max ranges)
+
+---
+
+## Context for Next Session
+
+### What Works
+- Shader parametrization infrastructure fully functional
+- FlashEffect demonstrates complete implementation pattern
+- .seq syntax parsing robust with backward compatibility
+- All tests passing, demo stable
+
+### Known Limitations
+- Only FlashEffect parametrized (other effects use hardcoded values)
+- No parameter validation or range clamping
+- No keyframe animation support (future feature)
+
+### Migration Pattern for Other Effects
+1. Copy `FlashEffectParams` / `FlashUniforms` pattern
+2. Add `UniformBuffer<YourUniforms>` member
+3. Update shader to use uniform values
+4. Implement per-frame computation in `render()`
+5. Extend `seq_compiler.cc` with effect-specific parameter parsing
+6. Test with .seq file, verify alignment with static_assert
+
+---
+
+## User Feedback Summary
+
+**Session Start**: User requested shader parametrization with use cases (flash colors, aberration strength)
+
+**Key Clarifications**:
+- User confirmed parameters will be "generated in the code (manually, in effect's C++ code)"
+- User emphasized "precision: user change parameter values on a per-frame basis. time-dependent"
+- Critical feedback: Per-frame computation required, not just static constructor params
+
+**Critical Bug Report**: User provided log.txt showing GPU validation error (buffer size mismatch)
+
+**Outcome**: User satisfied, requested commit + documentation update (completed)
+
+---
+
+## Handoff Checklist
+
+- [x] All tests passing (32/32)
+- [x] Working tree clean
+- [x] Documentation updated (TODO.md, PROJECT_CONTEXT.md, COMPLETED.md)
+- [x] Commits created with detailed messages (c7d1dd7, 775c0ea)
+- [x] No known regressions
+- [x] Design document complete (SHADER_PARAMETRIZATION_PLAN.md)
+- [x] Extension task noted (Task #73)
+- [x] Ready for next session
+
+---
+
+**handoff(Claude):** Shader parametrization system complete. FlashEffect fully parameterized with .seq syntax. Critical alignment bug fixed. 32/32 tests passing. Ready for extension to other effects (Task #73).
+
+---
+
+*Generated: February 8, 2026*
+*Claude Sonnet 4.5*