From 5354b5dc0b67f775fe173f2968f2e01023e45e21 Mon Sep 17 00:00:00 2001 From: skal Date: Mon, 2 Feb 2026 16:39:15 +0100 Subject: fix(3d): Unify all objects to SDF path for consistent shadows - Updated ObjectData to include inv_model for reliable world-to-local mapping. - Enabled SDF raymarching path for all objects in test_3d_render (floor is now a large SDF BOX). - Implemented robust normal calculation using SDF gradient for all objects. - Standardized lighting (light_dir = 1,1,1) and diffuse+ambient model. - Refined calc_shadow with instance-based skip_idx and robust bias. - Fixed non-uniform scale handling in shader by extracting min scale from model matrix. --- src/3d/renderer.cc | 73 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 32 deletions(-) (limited to 'src/3d/renderer.cc') diff --git a/src/3d/renderer.cc b/src/3d/renderer.cc index 9f43382..80d0160 100644 --- a/src/3d/renderer.cc +++ b/src/3d/renderer.cc @@ -20,7 +20,7 @@ struct GlobalUniforms { struct ObjectData { model: mat4x4, - model_inv_tr: mat4x4, + inv_model: mat4x4, color: vec4, params: vec4, }; @@ -94,9 +94,9 @@ fn sdPlane(p: vec3, n: vec3, h: f32) -> f32 { } fn get_dist(p: vec3, obj_type: f32) -> f32 { - if (obj_type == 1.0) { return sdSphere(p, 0.9); } - if (obj_type == 2.0) { return sdBox(p, vec3(0.7)); } - if (obj_type == 3.0) { return sdTorus(p, vec2(0.6, 0.25)); } + if (obj_type == 1.0) { return length(p) - 1.0; } // Unit Sphere + if (obj_type == 2.0) { return sdBox(p, vec3(1.0)); } // Unit Box + if (obj_type == 3.0) { return sdTorus(p, vec2(1.0, 0.4)); } // Unit Torus if (obj_type == 4.0) { return sdPlane(p, vec3(0.0, 1.0, 0.0), 0.0); } return 100.0; } @@ -104,16 +104,22 @@ fn get_dist(p: vec3, obj_type: f32) -> 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; } - 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 mat3 = mat3x3(obj.model[0].xyz/scale, obj.model[1].xyz/scale, obj.model[2].xyz/scale); - let q = transpose(mat3) * (p - center) / scale; - d = min(d, get_dist(q, obj_type) * scale); + + let q = (obj.inv_model * vec4(p, 1.0)).xyz; + + // Extraction of approx min scale for distance normalization + let scale_x = length(obj.model[0].xyz); + let scale_y = length(obj.model[1].xyz); + let scale_z = length(obj.model[2].xyz); + let s = min(scale_x, min(scale_y, scale_z)); + + d = min(d, get_dist(q, obj_type) * s); } return d; } @@ -121,11 +127,13 @@ fn map_scene(p: vec3, skip_idx: u32) -> 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.1) { t = 0.1; } // Robust bias to avoid self-shadowing + for (var i = 0; i < 32; i = i + 1) { let h = map_scene(ro + rd * t, skip_idx); if (h < 0.001) { return 0.0; } res = min(res, 16.0 * h / t); // Standard k=16 - t = t + clamp(h, 0.01, 0.5); + t = t + clamp(h, 0.02, 0.5); if (t > tmax) { break; } } return clamp(res, 0.0, 1.0); @@ -151,40 +159,42 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4 { var base_color = in.color.rgb; let light_dir = normalize(vec3(1.0, 1.0, 1.0)); - if (obj_type <= 0.0) { // Legacy raster path + if (obj_type <= 0.0) { // Raster path p = in.world_pos; let local_normal = normalize(cross(dpdx(in.local_pos), dpdy(in.local_pos))); - 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); + // Normal transformation: multiply by transpose of inverse + let normal_matrix = mat3x3(obj.inv_model[0].xyz, obj.inv_model[1].xyz, obj.inv_model[2].xyz); + normal = normalize(transpose(normal_matrix) * local_normal); } else { // SDF path - 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); - // Raymarch from camera to find exact SDF hit - var t = length(in.world_pos - ro) * 0.9; // Start slightly before the proxy hull + var t = length(in.world_pos - ro) * 0.9; for (var i = 0; i < 64; i = i + 1) { p = ro + rd * t; - let mat3 = mat3x3(obj.model[0].xyz/scale, obj.model[1].xyz/scale, obj.model[2].xyz/scale); - let q = transpose(mat3) * (p - center) / scale; - let d = get_dist(q, obj_type) * scale; - if (d < 0.001) { break; } - t = t + d; + let q = (obj.inv_model * vec4(p, 1.0)).xyz; + let d_local = get_dist(q, obj_type); + + let scale_x = length(obj.model[0].xyz); + let scale_y = length(obj.model[1].xyz); + let scale_z = length(obj.model[2].xyz); + let s = min(scale_x, min(scale_y, scale_z)); + let d_world = d_local * s; + + if (d_world < 0.001) { break; } + t = t + d_world; if (t > length(in.world_pos - ro) * 1.5) { discard; } } - let mat3 = mat3x3(obj.model[0].xyz/scale, obj.model[1].xyz/scale, obj.model[2].xyz/scale); - let q_hit = transpose(mat3) * (p - center) / scale; - normal = normalize(mat3 * get_normal(q_hit, obj_type)); + let q_hit = (obj.inv_model * vec4(p, 1.0)).xyz; + let n_local = get_normal(q_hit, obj_type); + let normal_matrix = mat3x3(obj.inv_model[0].xyz, obj.inv_model[1].xyz, obj.inv_model[2].xyz); + normal = normalize(transpose(normal_matrix) * n_local); } - // Shadow ray: start from surface, march towards light - // Use instance skipping to avoid self-shadowing artifacts let shadow = calc_shadow(p, light_dir, 0.05, 20.0, in.instance_index); - let diffuse = max(dot(normal, light_dir), 0.0); - let lighting = diffuse * (0.1 + 0.9 * shadow) + 0.1; // Ambient + Shadowed Diffuse + let lighting = diffuse * (0.2 + 0.8 * shadow) + 0.15; return vec4(base_color * lighting, 1.0); } @@ -387,9 +397,8 @@ void Renderer3D::update_uniforms(const Scene& scene, const Camera& camera, ObjectData data; data.model = obj.get_model_matrix(); - // Calculate Inverse Transpose for correct normal transformation - mat4 inverse = data.model.inverse(); - data.model_inverse_transpose = mat4::transpose(inverse); + // Calculate Inverse for point transformation + data.inv_model = data.model.inverse(); data.color = obj.color; float type_id = 0.0f; -- cgit v1.2.3