fix: correct 3d rendering

This commit is contained in:
2026-09-23 18:34:00 +02:00
parent 694f63ab9d
commit 63cee5d8b2
9 changed files with 290 additions and 173 deletions
+67 -20
View File
@@ -19,16 +19,31 @@ fn vs_main(@builtin(vertex_index) idx: u32) -> @builtin(position) vec4<f32> {
return vec4<f32>(fullscreen_triangle_pos(idx), 0.0, 1.0);
}
fn shadow_fragment(pos: vec3<f32>) -> vec4<f32> {
fn shadow_fragment(pos: vec2<f32>) -> vec4<f32> {
let x = i32(pos.x);
let y = i32(pos.y);
let size = textureDimensions(data_tex);
if textureLoad(data_tex, vec2<i32>(x, y), 0).b != 0. {
return vec4<f32>(0.1, 0.1, 0.1, 1.0);
} else {
// Forward differences, except on the last column/row where x+1 / y+1
// is off the texture: fall back to a backward difference, mirrored
// (h0 + (h0 - h[-1])) so the slope keeps the sign normal_from_heights
// expects — plugging h[-1] in directly would flip the normal there.
let h0 = textureLoad(data_tex, vec2<i32>(x, y), 0).g;
let h1 = textureLoad(data_tex, vec2<i32>(x + 1, y), 0).g;
let h2 = textureLoad(data_tex, vec2<i32>(x, y + 1), 0).g;
var h1: f32;
if x + 1 < i32(size.x) {
h1 = textureLoad(data_tex, vec2<i32>(x + 1, y), 0).g;
} else {
h1 = 2.0 * h0 - textureLoad(data_tex, vec2<i32>(x - 1, y), 0).g;
}
var h2: f32;
if y + 1 < i32(size.y) {
h2 = textureLoad(data_tex, vec2<i32>(x, y + 1), 0).g;
} else {
h2 = 2.0 * h0 - textureLoad(data_tex, vec2<i32>(x, y - 1), 0).g;
}
let normal = normal_from_heights(h0, h1, h2);
return vec4<f32>(shadow_color(normal), 1.0);
}
@@ -38,7 +53,7 @@ fn fs_main(@builtin(position) pos: vec4<f32>) -> @location(0) vec4<f32> {
if u.shadow == 2u {
return ray_marching(pos);
} else if u.shadow == 1u {
return shadow_fragment(pos.xyz);
return shadow_fragment(pos.xy);
} else {
let d = textureLoad(data_tex, vec2<i32>(i32(pos.x), i32(pos.y)), 0);
let ci = d.r;
@@ -54,44 +69,76 @@ fn fs_main(@builtin(position) pos: vec4<f32>) -> @location(0) vec4<f32> {
}
fn sdf(pos: vec3<f32>) -> f32 {
let texture_pos = vec2<i32>(i32(pos.x), i32(pos.y));
let aspect_ratio = u.screen_dim.x / u.screen_dim.y;
let size = vec2<f32>(textureDimensions(data_tex));
var texture_pos_f32 = vec2<f32>(pos.x * size.x / aspect_ratio, pos.y * size.y);
var texture_pos = vec2<i32>(i32(texture_pos_f32.x), i32(texture_pos_f32.y));
texture_pos.x = clamp(texture_pos.x, 0, i32(size.x) - 1);
texture_pos.y = clamp(texture_pos.y, 0, i32(size.y) - 1);
let to_texture = max(-min(texture_pos_f32, vec2(0.)), max(texture_pos_f32 - size, vec2(0.)));
let dist_to_texture = length(to_texture) / size.y;
let px = textureLoad(data_tex, texture_pos, 0);
let de = px.g;
let z = pos.z;
let de = (px.g / size.y) * 0.5;
// Height is measured toward -z, the side the camera sits on (it looks
// along +z), so the terrain is solid on +z: interior plateau at z = 0,
// exterior sloping away from the camera as `de` grows.
let signed_z = -pos.z;
let z = max(signed_z, 0.);
var d: f32;
if px.b != 0. {
return 0.;
d = z;
} else {
return sqrt(z * z + (de * de));
d = min(sqrt(z * z + de * de), signed_z + 1. - exp(-de * 5.));
}
// Outside the texture footprint, `d` is the distance from the clamped
// point q on the footprint's edge. The terrain lies over the (convex)
// footprint, so |p - x|² ≥ |q - x|² + |p - q|² for every terrain point x:
// combine in quadrature (not by adding, which overshoots). p can't be in
// the solid out here, so a negative `d` counts as 0.
if dist_to_texture > 0. {
let d_pos = max(d, 0.);
return sqrt(d_pos * d_pos + dist_to_texture * dist_to_texture);
}
return d;
}
fn ray_marching(pos: vec4<f32>) -> vec4<f32> {
let size = vec2<f32>(textureDimensions(data_tex));
let in_texture = vec2<f32>(
pos.x / 1980. * size.x,
pos.y / 1080. * size.y,
(pos.x / size.x) * 2. - 1.,
(pos.y / size.y) * 2. - 1.,
);
var world_pos = u.camera_inv_proj * vec4<f32>(in_texture, pos.z - 10, 1.0);
var world_pos = u.camera_inv_proj * vec4<f32>(in_texture, 0., 1.0);
let ray_origin = world_pos.xyz;
let ray_dir = u.camera_direction;
var p = ray_origin;
let z_intersect = ray_origin.z / ray_dir.z;
var p = ray_origin - ray_dir * z_intersect;
var i = 0u;
var dist = 0.0;
while i < 1000u {
dist = sdf(p);
if dist < 0.0001 {
let dist_threshold = 0.000001;
while i < 100u {
if length(p - ray_origin) > 3. {
break;
}
p += dist * ray_dir;
i += 1u;
dist = sdf(p);
if dist < dist_threshold {
break;
}
p += dist * ray_dir;
i += 1u;
}
if dist < 0.0001 {
return shadow_fragment(p);
if dist < dist_threshold {
let aspect_ratio = u.screen_dim.x / u.screen_dim.y;
let size = vec2<f32>(textureDimensions(data_tex));
let texture_pos_f32 = vec2<f32>(p.x * size.x / aspect_ratio, p.y * size.y);
return shadow_fragment(texture_pos_f32);
}
return vec4<f32>(1., 0., 0., 1.);
}