diff options
Diffstat (limited to 'doc/SDF_EFFECT_GUIDE.md')
| -rw-r--r-- | doc/SDF_EFFECT_GUIDE.md | 217 |
1 files changed, 126 insertions, 91 deletions
diff --git a/doc/SDF_EFFECT_GUIDE.md b/doc/SDF_EFFECT_GUIDE.md index fba80e7..06511e5 100644 --- a/doc/SDF_EFFECT_GUIDE.md +++ b/doc/SDF_EFFECT_GUIDE.md @@ -1,86 +1,144 @@ # SDF Effect Guide -Streamlined workflow for SDF raymarching effects using the `SDFEffect` base class. +Workflow for SDF raymarching effects. Canonical example: `Scene1`. --- -## Quick Start +## C++ Pattern + +Inherit from `Effect`. Add `UniformBuffer<CameraParams> camera_params_` as a member, +passed as `effect_params` to `pp_update_bind_group`. ```cpp // src/effects/my_sdf_effect.h -class MySDFEffect : public SDFEffect { - MySDFEffect(const GpuContext& ctx); - void render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) override; - RenderPass pass_; +#pragma once +#include "gpu/camera_params.h" +#include "gpu/effect.h" +#include "gpu/uniform_helper.h" +#include "gpu/wgpu_resource.h" + +class MySdfEffect : public Effect { + public: + MySdfEffect(const GpuContext& ctx, const std::vector<std::string>& inputs, + const std::vector<std::string>& outputs, float start_time, + float end_time); + + void render(WGPUCommandEncoder encoder, const UniformsSequenceParams& params, + NodeRegistry& nodes) override; + + private: + RenderPipeline pipeline_; + BindGroup bind_group_; + UniformBuffer<CameraParams> camera_params_; }; ``` ```cpp // src/effects/my_sdf_effect.cc #include "effects/my_sdf_effect.h" +#include "effects/shaders.h" #include "gpu/gpu.h" -#include "gpu/shaders.h" +#include "gpu/post_process_helper.h" +#include "util/mini_math.h" + +MySdfEffect::MySdfEffect(const GpuContext& ctx, + const std::vector<std::string>& inputs, + const std::vector<std::string>& outputs, + float start_time, float end_time) + : Effect(ctx, inputs, outputs, start_time, end_time) { + HEADLESS_RETURN_IF_NULL(ctx_.device); + + create_nearest_sampler(); + create_dummy_scene_texture(); + + camera_params_.init(ctx_.device); -MySDFEffect::MySDFEffect(const GpuContext& ctx) : SDFEffect(ctx) { - ResourceBinding bindings[] = { - {uniforms_.get(), WGPUBufferBindingType_Uniform}, - {camera_params_.get(), WGPUBufferBindingType_Uniform}}; - pass_ = gpu_create_render_pass(ctx_.device, ctx_.format, - my_sdf_shader_wgsl, bindings, 2); - pass_.vertex_count = 3; + pipeline_.set(create_post_process_pipeline( + ctx_.device, WGPUTextureFormat_RGBA8Unorm, my_sdf_shader_wgsl)); } -void MySDFEffect::render(WGPURenderPassEncoder pass, - const CommonPostProcessUniforms& uniforms) { - uniforms_.update(ctx_.queue, uniforms); +void MySdfEffect::render(WGPUCommandEncoder encoder, + const UniformsSequenceParams& params, + NodeRegistry& nodes) { + // Update camera (orbiting example) + CameraParams cam; + const float R = 6.0f; + const vec3 ro(R * sin(params.time * 0.5f), 2.0f, R * cos(params.time * 0.5f)); + cam.inv_view = mat4::look_at(ro, vec3(0, 0, 0), vec3(0, 1, 0)).inverse(); + cam.fov = 1.0472f; // 60 degrees + cam.near_plane = 0.1f; + cam.far_plane = 100.0f; + cam.aspect_ratio = 1.0f; // aspect handled in shader + camera_params_.update(ctx_.queue, cam); - // Orbiting camera - vec3 cam_pos(std::cos(uniforms.time * 0.5f) * 5.0f, 2.0f, - std::sin(uniforms.time * 0.5f) * 5.0f); - update_camera(cam_pos, vec3(0, 0, 0), vec3(0, 1, 0), 0.785398f, 0.1f, 100.0f, - uniforms.aspect_ratio); + WGPUTextureView output_view = nodes.get_view(output_nodes_[0]); - wgpuRenderPassEncoderSetPipeline(pass, pass_.pipeline); - wgpuRenderPassEncoderSetBindGroup(pass, 0, pass_.bind_group, 0, nullptr); - wgpuRenderPassEncoderDraw(pass, pass_.vertex_count, 1, 0, 0); + pp_update_bind_group(ctx_.device, pipeline_.get(), bind_group_.get_address(), + dummy_texture_view_.get(), uniforms_buffer_.get(), + camera_params_.get()); + + WGPURenderPassColorAttachment color_attachment = {}; + gpu_init_color_attachment(color_attachment, output_view); + + WGPURenderPassDescriptor pass_desc = {}; + pass_desc.colorAttachmentCount = 1; + pass_desc.colorAttachments = &color_attachment; + + WGPURenderPassEncoder pass = + wgpuCommandEncoderBeginRenderPass(encoder, &pass_desc); + wgpuRenderPassEncoderSetPipeline(pass, pipeline_.get()); + wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_.get(), 0, nullptr); + wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0); // Fullscreen triangle + wgpuRenderPassEncoderEnd(pass); + wgpuRenderPassEncoderRelease(pass); } ``` +--- + +## WGSL Pattern + +Binding layout (standard post-process): +- `@binding(0)` — sampler (unused for SDF, but layout requires it) +- `@binding(1)` — input texture (unused for SDF) +- `@binding(2)` — `UniformsSequenceParams` +- `@binding(3)` — `CameraParams` + ```wgsl // workspaces/main/shaders/my_sdf.wgsl -#include "common_uniforms" +#include "sequence_uniforms" #include "camera_common" #include "math/sdf_shapes" #include "render/raymarching" -@group(0) @binding(0) var<uniform> uniforms: CommonUniforms; -@group(0) @binding(1) var<uniform> camera: CameraParams; +@group(0) @binding(2) var<uniform> uniforms: UniformsSequenceParams; +@group(0) @binding(3) var<uniform> camera: CameraParams; -fn df(p: vec3<f32>) -> f32 { +fn df(p: vec3f) -> f32 { return sdSphere(p, 1.0); } @vertex -fn vs_main(@builtin(vertex_index) vid: u32) -> @builtin(position) vec4<f32> { +fn vs_main(@builtin(vertex_index) vid: u32) -> @builtin(position) vec4f { let x = f32((vid & 1u) << 2u) - 1.0; let y = f32((vid & 2u) << 1u) - 1.0; - return vec4<f32>(x, y, 0.0, 1.0); + return vec4f(x, y, 0.0, 1.0); } @fragment -fn fs_main(@builtin(position) pos: vec4<f32>) -> @location(0) vec4<f32> { - let uv = (pos.xy / uniforms.resolution - 0.5) * 2.0; +fn fs_main(@builtin(position) pos: vec4f) -> @location(0) vec4f { + let uv = (pos.xy / uniforms.resolution - 0.5) * 2.0 + * vec2f(uniforms.aspect_ratio, 1.0); let ray = getCameraRay(camera, uv); let t = rayMarch(ray.origin, ray.direction, 0.0); - var col = vec3<f32>(0.1); + var col = vec3f(0.1); if (t < MAX_RAY_LENGTH) { let hit_pos = ray.origin + ray.direction * t; let n = normal(hit_pos); - col = vec3<f32>(n * 0.5 + 0.5); + col = n * 0.5 + 0.5; } - return vec4<f32>(col, 1.0); + return vec4f(col, 1.0); } ``` @@ -88,77 +146,54 @@ fn fs_main(@builtin(position) pos: vec4<f32>) -> @location(0) vec4<f32> { ## Available Uniforms -### CommonUniforms (binding 0) -- `resolution`: vec2 (screen size) -- `time`: float (physical seconds) -- `beat_time`: float (musical beats) -- `beat_phase`: float (0-1 within beat) -- `audio_intensity`: float (peak) -- `aspect_ratio`: float +### UniformsSequenceParams (binding 2) +- `resolution`: vec2f +- `time`: f32 (physical seconds) +- `beat_time`: f32 (musical beats) +- `beat_phase`: f32 (0–1 within beat) +- `audio_intensity`: f32 +- `aspect_ratio`: f32 -### CameraParams (binding 1) -- `inv_view`: mat4x4 (inverse view matrix) -- `fov`: float (vertical FOV in radians) -- `near_plane`, `far_plane`: float -- `aspect_ratio`: float +### CameraParams (binding 3) +- `inv_view`: mat4x4f (inverse view matrix) +- `fov`: f32 (vertical FOV, radians) +- `near_plane`, `far_plane`: f32 +- `aspect_ratio`: f32 --- ## WGSL Helpers -From `camera_common.wgsl`: - +### `camera_common` ```wgsl -fn getCameraRay(cam: CameraParams, uv: vec2<f32>) -> Ray; -fn getCameraPosition(cam: CameraParams) -> vec3<f32>; -fn getCameraForward(cam: CameraParams) -> vec3<f32>; -fn getCameraUp(cam: CameraParams) -> vec3<f32>; -fn getCameraRight(cam: CameraParams) -> vec3<f32>; +fn getCameraRay(cam: CameraParams, uv: vec2f) -> Ray; +fn getCameraPosition(cam: CameraParams) -> vec3f; +fn getCameraForward(cam: CameraParams) -> vec3f; ``` -From `render/raymarching.wgsl`: - +### `render/raymarching` ```wgsl -fn rayMarch(ro: vec3<f32>, rd: vec3<f32>, initt: f32) -> f32; -fn normal(pos: vec3<f32>) -> vec3<f32>; -fn shadow(lp: vec3<f32>, ld: vec3<f32>, mint: f32, maxt: f32) -> f32; +fn rayMarch(ro: vec3f, rd: vec3f, initt: f32) -> f32; +fn normal(pos: vec3f) -> vec3f; +fn shadow(lp: vec3f, ld: vec3f, mint: f32, maxt: f32) -> f32; ``` -From `math/sdf_shapes.wgsl`: - +### `math/sdf_shapes` ```wgsl -fn sdSphere(p: vec3<f32>, r: float) -> f32; -fn sdBox(p: vec3<f32>, b: vec3<f32>) -> f32; -fn sdTorus(p: vec3<f32>, t: vec2<f32>) -> f32; -fn sdPlane(p: vec3<f32>, n: vec3<f32>, h: f32) -> f32; -``` - ---- - -## Camera Control - -```cpp -// Method 1: Manual values -update_camera(position, target, up, fov, near, far, aspect); - -// Method 2: Camera object -Camera cam; -cam.position = vec3(0, 5, 10); -cam.target = vec3(0, 0, 0); -update_camera(cam, uniforms.aspect_ratio); +fn sdSphere(p: vec3f, r: f32) -> f32; +fn sdBox(p: vec3f, b: vec3f) -> f32; +fn sdTorus(p: vec3f, t: vec2f) -> f32; +fn sdPlane(p: vec3f, n: vec3f, h: f32) -> f32; ``` --- ## Registration Checklist +Follow `doc/EFFECT_WORKFLOW.md`. SDF-specific notes: 1. Add shader to `workspaces/main/assets.txt` -2. Add extern declaration to `src/gpu/shaders.h` -3. Add definition to `src/gpu/shaders.cc` -4. Add `.cc` to `cmake/DemoSourceLists.cmake` (both headless & normal) -5. Include header in `src/gpu/demo_effects.h` -6. Add to `src/tests/gpu/test_demo_effects.cc` - ---- - -## Example: workspaces/main/shaders/sdf_test.wgsl +2. Add `.cc` to `CMakeLists.txt` GPU_SOURCES (**both** headless and normal sections) +3. Include header in `src/gpu/demo_effects.h` +4. Add to `workspaces/main/timeline.seq` with priority modifier (`+`, `=`, or `-`) +5. Add to `src/tests/gpu/test_demo_effects.cc` under `scene_effects` +6. Build: `cmake --build build -j4 && cd build && ./test_demo_effects` |
