From 8fd3eda0ed069b1a817261f8f4d6a35c565b3fe4 Mon Sep 17 00:00:00 2001 From: skal Date: Sun, 22 Mar 2026 23:17:50 +0100 Subject: fix(cnn_v3): shadow pass — 5 bugs fixed, labels in gbuf_view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Camera Y-inversion: proj.m[5] = -proj.m[5] in upload_scene_data + WGPUFrontFace_CCW on raster pipeline. 2. Shadow formula: replace shadowWithStoredDistance with 64-step IQ soft shadow (8*d/t, unbounded). 3. Local→world SDF scale: d *= length(obj.model[0].xyz). 4. Shadow bias: use rasterized normal from normal_mat_tex (binding 4) instead of light direction — fixes terminator self-shadow on spheres. 5. ShaderComposer: GBufViewEffect now resolves #include via ShaderComposer::Get().Compose(). Also: per-tile channel labels in gbuf_view.wgsl via debug_str. Scene simplified to 1 cube + 1 sphere for debugging (restore TODO). Scale propagation for pulsating sphere confirmed correct end-to-end. handoff(Gemini): shadow validated. Next: restore full scene in GBufferEffect::set_scene() (20 cubes + 4 spheres, 2 lights), then run training pass per cnn_v3/docs/HOWTO.md §3. --- cnn_v3/src/gbuffer_effect.cc | 78 +++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 45 deletions(-) (limited to 'cnn_v3/src/gbuffer_effect.cc') diff --git a/cnn_v3/src/gbuffer_effect.cc b/cnn_v3/src/gbuffer_effect.cc index c49ab88..b059915 100644 --- a/cnn_v3/src/gbuffer_effect.cc +++ b/cnn_v3/src/gbuffer_effect.cc @@ -109,50 +109,29 @@ void GBufferEffect::set_scene() { }; auto rrange = [&](float lo, float hi) { return lo + rnd() * (hi - lo); }; - // 20 small cubes scattered in a [-2,2]×[-1.5,1.5]×[-1.5,1.5] volume. - static const int kNumCubes = 20; - for (int i = 0; i < kNumCubes; ++i) { + // 2 large cubes. + // 2 large static cubes for shadow debugging. + { Object3D obj(ObjectType::CUBE); - obj.position = vec3(rrange(-2.0f, 2.0f), - rrange(-1.5f, 1.5f), - rrange(-1.5f, 1.5f)); - const float s = rrange(0.10f, 0.25f); - obj.scale = vec3(s, s, s); - obj.color = vec4(rrange(0.4f, 1.0f), - rrange(0.4f, 1.0f), - rrange(0.4f, 1.0f), 1.0f); - - // Random rotation axis (avoid degenerate zero-length axis). - vec3 axis = vec3(rrange(-1.0f, 1.0f), - rrange(-1.0f, 1.0f), - rrange(-1.0f, 1.0f)); - if (axis.len() < 0.01f) axis = vec3(0.0f, 1.0f, 0.0f); - axis = axis.normalize(); - const float speed = rrange(0.3f, 1.5f) * (rnd() > 0.5f ? 1.0f : -1.0f); - + obj.position = vec3(-1.0f, 0.0f, 0.0f); + obj.scale = vec3(0.6f, 0.6f, 0.6f); + obj.color = vec4(0.9f, 0.5f, 0.3f, 1.0f); scene_.add_object(obj); - cube_anims_.push_back({axis, speed}); + cube_anims_.push_back({{0.0f, 1.0f, 0.0f}, 0.0f}); } - - // 4 pumping spheres at fixed positions; radius modulated by audio_intensity. - static const vec3 kSpherePos[4] = { - { 0.0f, 0.0f, 0.0f}, - { 1.5f, 0.5f, -0.5f}, - {-1.5f, -0.5f, 0.5f}, - { 0.0f, 1.0f, 1.0f}, - }; - static const float kBaseSphereRadius[4] = {0.35f, 0.28f, 0.30f, 0.25f}; - for (int i = 0; i < 4; ++i) { + { Object3D obj(ObjectType::SPHERE); - obj.position = kSpherePos[i]; - const float r = kBaseSphereRadius[i]; - obj.scale = vec3(r, r, r); - obj.color = vec4(0.85f, 0.60f, 0.95f, 1.0f); + obj.position = vec3(1.0f, 0.0f, 0.0f); + const float r = 0.9f; + obj.scale = vec3(r, r, r); + obj.color = vec4(0.3f, 0.6f, 0.9f, 1.0f); const int idx = (int)scene_.objects.size(); scene_.add_object(obj); sphere_anims_.push_back({idx, r}); } + // (sphere removed for shadow debugging) + // Camera: above and in front of the scene, looking at origin. camera_.set_look_at(vec3(0.0f, 2.5f, 6.0f), vec3(0.0f, 0.0f, 0.0f), @@ -213,13 +192,13 @@ void GBufferEffect::render(WGPUCommandEncoder encoder, // Upload two directional lights. { GBufLightsUniforms lu = {}; - lu.params = vec4(2.0f, 0.0f, 0.0f, 0.0f); + lu.params = vec4(1.0f, 0.0f, 0.0f, 0.0f); // Key: warm sun, upper-right-front. lu.lights[0].direction = vec4(0.408f, 0.816f, 0.408f, 0.0f); // norm(1,2,1) lu.lights[0].color = vec4(1.00f, 0.92f, 0.78f, 1.0f); - // Fill: cool sky, upper-left-back. - lu.lights[1].direction = vec4(-0.577f, 0.577f, -0.577f, 0.0f); // norm(-1,1,-1) - lu.lights[1].color = vec4(0.40f, 0.45f, 0.80f, 0.4f); + // Fill: cool sky, upper-left-back. (disabled for debugging) + // lu.lights[1].direction = vec4(-0.577f, 0.577f, -0.577f, 0.0f); + // lu.lights[1].color = vec4(0.40f, 0.45f, 0.80f, 0.4f); lights_uniform_.update(ctx_.queue, lu); } @@ -301,7 +280,7 @@ void GBufferEffect::render(WGPUCommandEncoder encoder, // --- Pass 2: SDF shadow raymarching --- if (shadow_pipeline_.get() != nullptr) { - WGPUBindGroupEntry shadow_entries[4] = {}; + WGPUBindGroupEntry shadow_entries[5] = {}; shadow_entries[0].binding = 0; shadow_entries[0].buffer = global_uniforms_buf_.buffer; shadow_entries[0].size = sizeof(GBufGlobalUniforms); @@ -317,12 +296,15 @@ void GBufferEffect::render(WGPUCommandEncoder encoder, shadow_entries[3].buffer = lights_uniform_.get().buffer; shadow_entries[3].size = sizeof(GBufLightsUniforms); + shadow_entries[4].binding = 4; + shadow_entries[4].textureView = normal_mat_view; + WGPUBindGroupLayout shadow_bgl = wgpuRenderPipelineGetBindGroupLayout(shadow_pipeline_.get(), 0); WGPUBindGroupDescriptor shadow_bg_desc = {}; shadow_bg_desc.layout = shadow_bgl; - shadow_bg_desc.entryCount = 4; + shadow_bg_desc.entryCount = 5; shadow_bg_desc.entries = shadow_entries; WGPUBindGroup shadow_bg = @@ -448,7 +430,8 @@ void GBufferEffect::upload_scene_data(const Scene& scene, : (size_t)kGBufMaxObjects); const mat4 view = camera.get_view_matrix(); - const mat4 proj = camera.get_projection_matrix(); + mat4 proj = camera.get_projection_matrix(); + proj.m[5] = -proj.m[5]; // undo post-process Y flip: G-buffer uses integer reads const mat4 vp = proj * view; GBufGlobalUniforms gu = {}; @@ -554,7 +537,7 @@ void GBufferEffect::create_raster_pipeline() { pipe_desc.depthStencil = &ds; pipe_desc.primitive.topology = WGPUPrimitiveTopology_TriangleList; pipe_desc.primitive.cullMode = WGPUCullMode_Back; - pipe_desc.primitive.frontFace = WGPUFrontFace_CW; // Y-flipped perspective + pipe_desc.primitive.frontFace = WGPUFrontFace_CCW; // standard (no Y flip) pipe_desc.multisample.count = 1; pipe_desc.multisample.mask = 0xFFFFFFFF; @@ -584,7 +567,7 @@ void GBufferEffect::create_shadow_pipeline() { WGPUShaderModule shader = wgpuDeviceCreateShaderModule(ctx_.device, &shader_desc); // BGL: B0=GlobalUniforms, B1=ObjectsBuffer, B2=texture_depth_2d, B3=GBufLightsUniforms - WGPUBindGroupLayoutEntry bgl_entries[4] = {}; + WGPUBindGroupLayoutEntry bgl_entries[5] = {}; bgl_entries[0].binding = 0; bgl_entries[0].visibility = @@ -607,8 +590,13 @@ void GBufferEffect::create_shadow_pipeline() { bgl_entries[3].buffer.type = WGPUBufferBindingType_Uniform; bgl_entries[3].buffer.minBindingSize = sizeof(GBufLightsUniforms); + bgl_entries[4].binding = 4; + bgl_entries[4].visibility = WGPUShaderStage_Fragment; + bgl_entries[4].texture.sampleType = WGPUTextureSampleType_Float; + bgl_entries[4].texture.viewDimension = WGPUTextureViewDimension_2D; + WGPUBindGroupLayoutDescriptor bgl_desc = {}; - bgl_desc.entryCount = 4; + bgl_desc.entryCount = 5; bgl_desc.entries = bgl_entries; WGPUBindGroupLayout bgl = wgpuDeviceCreateBindGroupLayout(ctx_.device, &bgl_desc); -- cgit v1.2.3