From 5dbdb0a359d51355e4031d2e5dd576796f422d0a Mon Sep 17 00:00:00 2001 From: skal Date: Mon, 2 Feb 2026 16:25:01 +0100 Subject: fix(3d): Resolve missing shadows on floor plane - Switched floor from rasterized CUBE to SDF PLANE for consistent shadow mapping. - Implemented skip_idx in map_scene to properly prevent self-shadowing. - Standardized soft shadow logic with improved bias and step size. - Increased object elevation and adjusted light direction for more visible shadows. - Enabled debug boxes in test_3d_render. --- src/3d/renderer.cc | 89 +++++++++++++++------------------------------ src/gpu/gpu.h | 3 ++ src/tests/test_3d_render.cc | 20 ++++++---- 3 files changed, 44 insertions(+), 68 deletions(-) (limited to 'src') diff --git a/src/3d/renderer.cc b/src/3d/renderer.cc index 505cd31..db50a35 100644 --- a/src/3d/renderer.cc +++ b/src/3d/renderer.cc @@ -101,11 +101,12 @@ fn get_dist(p: vec3, obj_type: f32) -> f32 { return 100.0; } -fn map_scene(p: vec3) -> f32 { +fn map_scene(p: vec3, skip_idx: u32) -> f32 { var d = 1000.0; let count = u32(globals.params.x); for (var i = 0u; i < count; i = i + 1u) { + if (i == skip_idx) { continue; } let obj = object_data.objects[i]; let obj_type = obj.params.x; if (obj_type <= 0.0) { continue; } @@ -121,16 +122,16 @@ fn map_scene(p: vec3) -> f32 { return d; } -fn calc_shadow(ro: vec3, rd: vec3, tmin: f32, tmax: f32) -> f32 { +fn calc_shadow(ro: vec3, rd: vec3, tmin: f32, tmax: f32, skip_idx: u32) -> f32 { var res = 1.0; var t = tmin; - if (t < 0.02) { t = 0.02; } + if (t < 0.05) { t = 0.05; } // Avoid self-shadowing for (var i = 0; i < 32; i = i + 1) { - let h = map_scene(ro + rd * t); + let h = map_scene(ro + rd * t, skip_idx); if (h < 0.001) { return 0.0; } - res = min(res, 32.0 * h / t); // Harder shadows k=32 - t = t + clamp(h, 0.01, 0.5); + res = min(res, 16.0 * h / t); // Soft shadow k=16 + t = t + clamp(h, 0.02, 0.5); if (t > tmax) { break; } } return clamp(res, 0.0, 1.0); @@ -154,84 +155,52 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4 { var p: vec3; var normal: vec3; var base_color = in.color.rgb; - let light_dir = normalize(vec3(0.2, 1.0, 0.2)); // More vertical light for easier shadow debugging + // Tilted light for longer, more visible shadows + let light_dir = normalize(vec3(1.0, 1.0, 1.0)); - if (obj_type == 0.0) { // Rasterized object + if (obj_type <= 0.0) { // Rasterized object (legacy path) p = in.world_pos; let local_normal = normalize(cross(dpdx(in.local_pos), dpdy(in.local_pos))); - - // Correct normal transformation using inverse transpose let mat3_it = mat3x3(obj.model_inv_tr[0].xyz, obj.model_inv_tr[1].xyz, obj.model_inv_tr[2].xyz); normal = normalize(mat3_it * local_normal); - - // Apply grid texture for color to floor - let uv = p.xz * 0.1; - let grid_val = textureSample(noise_tex, noise_sampler, uv).r; - base_color = base_color * (0.3 + 0.7 * grid_val); } else { // SDF object let center = vec3(obj.model[3].x, obj.model[3].y, obj.model[3].z); let scale = length(vec3(obj.model[0].x, obj.model[0].y, obj.model[0].z)); let ro = globals.camera_pos_time.xyz; let rd = normalize(in.world_pos - ro); - var t = length(in.world_pos - ro); - p = ro + rd * t; + var t_hit = length(in.world_pos - ro); + p = ro + rd * t_hit; let mat3 = mat3x3(obj.model[0].xyz/scale, obj.model[1].xyz/scale, obj.model[2].xyz/scale); var hit = false; - for (var i = 0; i < 40; i = i + 1) { + // Raymarch into the proxy box to find the SDF surface + for (var i = 0; i < 64; i = i + 1) { let q = transpose(mat3) * (p - center) / scale; - let d_world = get_dist(q, obj_type) * scale; + let d_local = get_dist(q, obj_type); + let d_world = d_local * scale; if (d_world < 0.001) { hit = true; break; } - if (d_world > 3.0 * scale) { break; } + if (d_world > 3.0 * scale) { break; } // Out of proxy box bounds p = p + rd * d_world; } if (!hit) { discard; } let q_hit = transpose(mat3) * (p - center) / scale; - - let e = vec2(0.005, 0.0); - let disp_strength = 0.05; - - // Calculate normal with bump mapping - let q_x1 = q_hit + e.xyy; - let uv_x1 = vec2(atan2(q_x1.x, q_x1.z) / 6.28 + 0.5, acos(clamp(q_x1.y / length(q_x1), -1.0, 1.0)) / 3.14); - let h_x1 = textureSample(noise_tex, noise_sampler, uv_x1).r; - let d_x1 = get_dist(q_x1, obj_type) - disp_strength * h_x1; - - let q_x2 = q_hit - e.xyy; - let uv_x2 = vec2(atan2(q_x2.x, q_x2.z) / 6.28 + 0.5, acos(clamp(q_x2.y / length(q_x2), -1.0, 1.0)) / 3.14); - let h_x2 = textureSample(noise_tex, noise_sampler, uv_x2).r; - let d_x2 = get_dist(q_x2, obj_type) - disp_strength * h_x2; - - let q_y1 = q_hit + e.yxy; - let uv_y1 = vec2(atan2(q_y1.x, q_y1.z) / 6.28 + 0.5, acos(clamp(q_y1.y / length(q_y1), -1.0, 1.0)) / 3.14); - let h_y1 = textureSample(noise_tex, noise_sampler, uv_y1).r; - let d_y1 = get_dist(q_y1, obj_type) - disp_strength * h_y1; - - let q_y2 = q_hit - e.yxy; - let uv_y2 = vec2(atan2(q_y2.x, q_y2.z) / 6.28 + 0.5, acos(clamp(q_y2.y / length(q_y2), -1.0, 1.0)) / 3.14); - let h_y2 = textureSample(noise_tex, noise_sampler, uv_y2).r; - let d_y2 = get_dist(q_y2, obj_type) - disp_strength * h_y2; - - let q_z1 = q_hit + e.yyx; - let uv_z1 = vec2(atan2(q_z1.x, q_z1.z) / 6.28 + 0.5, acos(clamp(q_z1.y / length(q_z1), -1.0, 1.0)) / 3.14); - let h_z1 = textureSample(noise_tex, noise_sampler, uv_z1).r; - let d_z1 = get_dist(q_z1, obj_type) - disp_strength * h_z1; - - let q_z2 = q_hit - e.yyx; - let uv_z2 = vec2(atan2(q_z2.x, q_z2.z) / 6.28 + 0.5, acos(clamp(q_z2.y / length(q_z2), -1.0, 1.0)) / 3.14); - let h_z2 = textureSample(noise_tex, noise_sampler, uv_z2).r; - let d_z2 = get_dist(q_z2, obj_type) - disp_strength * h_z2; - - let n_local = normalize(vec3(d_x1 - d_x2, d_y1 - d_y2, d_z1 - d_z2)); - - normal = normalize(mat3 * n_local); + normal = normalize(mat3 * get_normal(q_hit, obj_type)); + + // Optional: procedural grid for all objects, but mainly for the floor + if (obj_type == 4.0) { // Plane + let uv = p.xz * 0.2; + let grid = 0.5 + 0.5 * sin(uv.x * 3.14 * 2.0) * sin(uv.y * 3.14 * 2.0); + base_color = base_color * (0.6 + 0.4 * smoothstep(0.45, 0.55, grid)); + } } - let shadow = calc_shadow(p + normal * 0.01, light_dir, 0.0, 20.0); - let lighting = (max(dot(normal, light_dir), 0.0) * shadow) + 0.1; + // Shadow ray start from hit point p + let shadow = calc_shadow(p, light_dir, 0.1, 20.0, in.instance_index); + // Darken shadowed areas significantly + let lighting = (max(dot(normal, light_dir), 0.0) * (0.2 + 0.8 * shadow)) + 0.1; return vec4(base_color * lighting, 1.0); } diff --git a/src/gpu/gpu.h b/src/gpu/gpu.h index f47355c..3aff729 100644 --- a/src/gpu/gpu.h +++ b/src/gpu/gpu.h @@ -101,6 +101,9 @@ struct RenderPass { void gpu_init(PlatformState* platform_state); void gpu_draw(float audio_peak, float aspect_ratio, float time, float beat); void gpu_resize(int width, int height); +#if !defined(STRIP_ALL) +void gpu_simulate_until(float time); +#endif void gpu_shutdown(); // Helper functions (exposed for internal/future use) diff --git a/src/tests/test_3d_render.cc b/src/tests/test_3d_render.cc index 3084edc..141b1b7 100644 --- a/src/tests/test_3d_render.cc +++ b/src/tests/test_3d_render.cc @@ -137,18 +137,18 @@ void setup_scene() { g_scene.clear(); srand(12345); // Fixed seed - // Large floor, use CUBE type to exclude from SDF calculations - Object3D floor(ObjectType::CUBE); + // Large floor, use PLANE type + Object3D floor(ObjectType::PLANE); floor.position = vec3(0, -2.0f, 0); - floor.scale = vec3(20.0f, 0.5f, 20.0f); + floor.scale = vec3(100.0f, 1.0f, 100.0f); floor.color = vec4(0.9f, 0.9f, 0.9f, 1.0f); // Brighter white for better shadow contrast g_scene.add_object(floor); // Center object Object3D center(ObjectType::TORUS); - center.position = vec3(0, 0, 0); - center.scale = vec3(1.5f, 1.5f, 1.5f); + center.position = vec3(0, 1.0f, 0); // Higher + center.scale = vec3(2.5f, 2.5f, 2.5f); // Larger center.color = vec4(1, 0, 0, 1); g_scene.add_object(center); @@ -163,12 +163,12 @@ void setup_scene() { Object3D obj(type); float angle = (rand() % 360) * 0.01745f; - float dist = 3.0f + (rand() % 100) * 0.05f; - float height = -1.0f + (rand() % 100) * 0.04f; + float dist = 4.0f + (rand() % 100) * 0.06f; + float height = 0.5f + (rand() % 100) * 0.04f; // Higher obj.position = vec3(std::cos(angle) * dist, height, std::sin(angle) * dist); - float s = 0.3f + (rand() % 100) * 0.005f; + float s = 0.5f + (rand() % 100) * 0.005f; obj.scale = vec3(s, s, s); obj.color = vec4((rand() % 100) / 100.0f, (rand() % 100) / 100.0f, @@ -242,6 +242,10 @@ int main(int argc, char** argv) { g_scene.objects[i].position.y = std::sin(time * 3.0f + i) * 1.5f; } +#if !defined(STRIP_ALL) + Renderer3D::SetDebugEnabled(true); +#endif + WGPUSurfaceTexture surface_tex; wgpuSurfaceGetCurrentTexture(g_surface, &surface_tex); if (surface_tex.status == -- cgit v1.2.3