diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-02 16:39:15 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-02 16:39:15 +0100 |
| commit | 5354b5dc0b67f775fe173f2968f2e01023e45e21 (patch) | |
| tree | 27bee9b512d46fade2d5313178e5e219679c5ffa /src/3d/renderer.cc | |
| parent | fcf1cbe0e520137d729152239c2aed68d9952cc5 (diff) | |
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.
Diffstat (limited to 'src/3d/renderer.cc')
| -rw-r--r-- | src/3d/renderer.cc | 73 |
1 files changed, 41 insertions, 32 deletions
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<f32>, - model_inv_tr: mat4x4<f32>, + inv_model: mat4x4<f32>, color: vec4<f32>, params: vec4<f32>, }; @@ -94,9 +94,9 @@ fn sdPlane(p: vec3<f32>, n: vec3<f32>, h: f32) -> f32 { } fn get_dist(p: vec3<f32>, obj_type: f32) -> f32 { - if (obj_type == 1.0) { return sdSphere(p, 0.9); } - if (obj_type == 2.0) { return sdBox(p, vec3<f32>(0.7)); } - if (obj_type == 3.0) { return sdTorus(p, vec2<f32>(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<f32>(1.0)); } // Unit Box + if (obj_type == 3.0) { return sdTorus(p, vec2<f32>(1.0, 0.4)); } // Unit Torus if (obj_type == 4.0) { return sdPlane(p, vec3<f32>(0.0, 1.0, 0.0), 0.0); } return 100.0; } @@ -104,16 +104,22 @@ fn get_dist(p: vec3<f32>, obj_type: f32) -> f32 { fn map_scene(p: vec3<f32>, 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<f32>(obj.model[3].x, obj.model[3].y, obj.model[3].z); - let scale = length(vec3<f32>(obj.model[0].x, obj.model[0].y, obj.model[0].z)); - let mat3 = mat3x3<f32>(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<f32>(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<f32>, skip_idx: u32) -> f32 { fn calc_shadow(ro: vec3<f32>, rd: vec3<f32>, 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<f32> { var base_color = in.color.rgb; let light_dir = normalize(vec3<f32>(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<f32>(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<f32>(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<f32>(obj.model[3].x, obj.model[3].y, obj.model[3].z); - let scale = length(vec3<f32>(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<f32>(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<f32>(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<f32>(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<f32>(p, 1.0)).xyz; + let n_local = get_normal(q_hit, obj_type); + let normal_matrix = mat3x3<f32>(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<f32>(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; |
