diff options
Diffstat (limited to 'cnn_v3')
| -rw-r--r-- | cnn_v3/shaders/gbuf_shadow.wgsl | 35 | ||||
| -rw-r--r-- | cnn_v3/shaders/gbuf_view.wgsl | 32 | ||||
| -rw-r--r-- | cnn_v3/src/gbuf_view_effect.cc | 4 | ||||
| -rw-r--r-- | cnn_v3/src/gbuffer_effect.cc | 78 |
4 files changed, 94 insertions, 55 deletions
diff --git a/cnn_v3/shaders/gbuf_shadow.wgsl b/cnn_v3/shaders/gbuf_shadow.wgsl index 0f5f8b4..735e47c 100644 --- a/cnn_v3/shaders/gbuf_shadow.wgsl +++ b/cnn_v3/shaders/gbuf_shadow.wgsl @@ -5,11 +5,13 @@ #include "common_uniforms" #include "camera_common" #include "math/sdf_shapes" +#include "math/normal" #include "render/raymarching_id" @group(0) @binding(0) var<uniform> globals: GlobalUniforms; @group(0) @binding(1) var<storage, read> object_data: ObjectsBuffer; @group(0) @binding(2) var depth_tex: texture_depth_2d; +@group(0) @binding(4) var normal_mat_tex: texture_2d<f32>; struct GBufLight { direction: vec4f, // xyz = toward light (world space, normalized) @@ -38,12 +40,14 @@ fn dfWithID(p: vec3f) -> RayMarchResult { let obj = object_data.objects[i]; let lp = (obj.inv_model * vec4f(p, 1.0)).xyz; let obj_type = u32(obj.params.x); + // Scale factor: convert local-space SDF to world-space distance. + let scale = length(obj.model[0].xyz); var d: f32; switch obj_type { - case 1u: { d = sdSphere(lp, 1.0); } // SPHERE - case 2u: { d = sdPlane(lp, vec3f(0.0, 1.0, 0.0), obj.params.y); } // PLANE - case 3u: { d = sdTorus(lp, vec2f(0.8, 0.2)); } // TORUS - default: { d = sdBox(lp, vec3f(1.0)); } // CUBE (0) + fallback + case 1u: { d = sdSphere(lp, 1.0) * scale; } // SPHERE + case 2u: { d = sdPlane(lp, vec3f(0.0, 1.0, 0.0), obj.params.y); } // PLANE + case 3u: { d = sdTorus(lp, vec2f(0.8, 0.2)) * scale; } // TORUS + default: { d = sdBox(lp, vec3f(1.0)) * scale; } // CUBE (0) + fallback } if (d < res.distance) { res.distance = d; @@ -53,6 +57,20 @@ fn dfWithID(p: vec3f) -> RayMarchResult { return res; } +// Soft shadow march (IQ formula). Returns 1=lit, 0=shadow. +// No dmin/dmax bounds: in open space d grows large so 8*d/t >> 1, res stays 1 naturally. +fn soft_shadow(ro: vec3f, rd: vec3f) -> f32 { + var t = 0.001; + var res = 1.0; + for (var i = 0; i < 64; i++) { + let d = dfWithID(ro + rd * t).distance; + if (d < 0.0005) { return 0.0; } + res = min(res, 8.0 * d / t); + t += d; + } + return clamp(res, 0.0, 1.0); +} + // ---- Vertex: fullscreen triangle ---- @vertex @@ -82,16 +100,17 @@ fn fs_main(@builtin(position) pos: vec4f) -> @location(0) vec4f { let clip = globals.inv_view_proj * vec4f(ndc, depth, 1.0); let world = clip.xyz / clip.w; - // Surface normal estimated from SDF gradient. - let nor = normalWithID(world); - let bias_pos = world + nor * 0.02; + // Use rasterized surface normal for bias — correct for sphere impostors. + let nm = textureLoad(normal_mat_tex, vec2i(pos.xy), 0); + let nor = oct_decode_unorm(nm.rg); + let bias_pos = world + nor * 0.05; // March shadow rays toward each light; take the darkest value. var shadow_val = 1.0; let num_lights = u32(lights.params.x); for (var i = 0u; i < num_lights; i++) { let ld = lights.lights[i].direction.xyz; - let s = shadowWithStoredDistance(bias_pos, ld, MAX_RAY_LENGTH); + let s = soft_shadow(bias_pos, ld); shadow_val = min(shadow_val, s); } diff --git a/cnn_v3/shaders/gbuf_view.wgsl b/cnn_v3/shaders/gbuf_view.wgsl index a5e6c91..3e7d1ff 100644 --- a/cnn_v3/shaders/gbuf_view.wgsl +++ b/cnn_v3/shaders/gbuf_view.wgsl @@ -9,6 +9,8 @@ // Row 3: ch12(m1.r) ch13(m1.g) ch14(m1.b) ch15(m2.r) // Row 4: ch16(m2.g) ch17(m2.b) ch18(shdw) ch19(trns) +#include "debug/debug_print" + struct GBufViewUniforms { resolution: vec2f } @group(0) @binding(0) var feat0: texture_2d<u32>; @@ -93,5 +95,33 @@ fn fs_main(@builtin(position) pos: vec4f) -> @location(0) vec4f { disp = clamp(v, 0.0, 1.0); } - return vec4f(disp, disp, disp, 1.0); + var out = vec4f(disp, disp, disp, 1.0); + + // Label at top-left of each tile + let tile_w = u.resolution.x / 4.0; + let tile_h = u.resolution.y / 5.0; + let origin = vec2f(f32(col) * tile_w + 4.0, f32(row) * tile_h + 4.0); + switch ch { + case 0u: { out = debug_str(out, pos.xy, origin, vec4u(0x616C622Eu, 0x72000000u, 0u, 0u), 5u); } // alb.r + case 1u: { out = debug_str(out, pos.xy, origin, vec4u(0x616C622Eu, 0x67000000u, 0u, 0u), 5u); } // alb.g + case 2u: { out = debug_str(out, pos.xy, origin, vec4u(0x616C622Eu, 0x62000000u, 0u, 0u), 5u); } // alb.b + case 3u: { out = debug_str(out, pos.xy, origin, vec4u(0x6E726D2Eu, 0x78000000u, 0u, 0u), 5u); } // nrm.x + case 4u: { out = debug_str(out, pos.xy, origin, vec4u(0x6E726D2Eu, 0x79000000u, 0u, 0u), 5u); } // nrm.y + case 5u: { out = debug_str(out, pos.xy, origin, vec4u(0x64657074u, 0x68000000u, 0u, 0u), 5u); } // depth + case 6u: { out = debug_str(out, pos.xy, origin, vec4u(0x647A6478u, 0u, 0u, 0u), 4u); } // dzdx + case 7u: { out = debug_str(out, pos.xy, origin, vec4u(0x647A6479u, 0u, 0u, 0u), 4u); } // dzdy + case 8u: { out = debug_str(out, pos.xy, origin, vec4u(0x6D617469u, 0x64000000u, 0u, 0u), 5u); } // matid + case 9u: { out = debug_str(out, pos.xy, origin, vec4u(0x7072762Eu, 0x72000000u, 0u, 0u), 5u); } // prv.r + case 10u: { out = debug_str(out, pos.xy, origin, vec4u(0x7072762Eu, 0x67000000u, 0u, 0u), 5u); } // prv.g + case 11u: { out = debug_str(out, pos.xy, origin, vec4u(0x7072762Eu, 0x62000000u, 0u, 0u), 5u); } // prv.b + case 12u: { out = debug_str(out, pos.xy, origin, vec4u(0x6D312E72u, 0u, 0u, 0u), 4u); } // m1.r + case 13u: { out = debug_str(out, pos.xy, origin, vec4u(0x6D312E67u, 0u, 0u, 0u), 4u); } // m1.g + case 14u: { out = debug_str(out, pos.xy, origin, vec4u(0x6D312E62u, 0u, 0u, 0u), 4u); } // m1.b + case 15u: { out = debug_str(out, pos.xy, origin, vec4u(0x6D322E72u, 0u, 0u, 0u), 4u); } // m2.r + case 16u: { out = debug_str(out, pos.xy, origin, vec4u(0x6D322E67u, 0u, 0u, 0u), 4u); } // m2.g + case 17u: { out = debug_str(out, pos.xy, origin, vec4u(0x6D322E62u, 0u, 0u, 0u), 4u); } // m2.b + case 18u: { out = debug_str(out, pos.xy, origin, vec4u(0x73686477u, 0u, 0u, 0u), 4u); } // shdw + default: { out = debug_str(out, pos.xy, origin, vec4u(0x74726E73u, 0u, 0u, 0u), 4u); } // trns + } + return out; } diff --git a/cnn_v3/src/gbuf_view_effect.cc b/cnn_v3/src/gbuf_view_effect.cc index 180919d..ccf80b0 100644 --- a/cnn_v3/src/gbuf_view_effect.cc +++ b/cnn_v3/src/gbuf_view_effect.cc @@ -10,6 +10,7 @@ #endif #include "gpu/gpu.h" +#include "gpu/shader_composer.h" #include "util/asset_manager.h" #include "util/fatal_error.h" @@ -63,7 +64,8 @@ GBufViewEffect::GBufViewEffect(const GpuContext& ctx, // Shader module WGPUShaderSourceWGSL wgsl_src = {}; wgsl_src.chain.sType = WGPUSType_ShaderSourceWGSL; - wgsl_src.code = str_view(gbuf_view_wgsl); + const std::string composed = ShaderComposer::Get().Compose({}, gbuf_view_wgsl); + wgsl_src.code = str_view(composed.c_str()); WGPUShaderModuleDescriptor shader_desc = {}; shader_desc.nextInChain = &wgsl_src.chain; WGPUShaderModule shader = 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); |
