From f45ce44d41930391354c5086a833d63e8791c0bd Mon Sep 17 00:00:00 2001 From: tslil clingman <> Date: Sat, 4 Sep 2021 11:53:39 -0400 Subject: *linear* interpolation on direction vectors seems fine!? --- src/raycast.zig | 67 ++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 27 deletions(-) (limited to 'src/raycast.zig') diff --git a/src/raycast.zig b/src/raycast.zig index aca7db0..3df8cef 100644 --- a/src/raycast.zig +++ b/src/raycast.zig @@ -107,22 +107,31 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type { map: Map, renderWall: RenderWallFunction, ) void { - const floor = std.math.floor; + // This is a TERRIBLE hack: for whatever reason *linearly* + // interpolating on the direction vectors gives + // perspective-correct-seeming walls! + const cos_first = std.math.cos(self.ang + 0.5 * FOV); + const cos_last = std.math.cos(self.ang - 0.5 * FOV); + const sin_first = std.math.sin(self.ang + 0.5 * FOV); + const sin_last = std.math.sin(self.ang - 0.5 * FOV); + + const cos_step = (cos_last - cos_first) / PlaneWidth; + const sin_step = (sin_last - sin_first) / PlaneWidth; var col: i32 = 0; - var ra: f32 = 0.5 * FOV + self.ang; - const ra_step = FOV / PlaneWidth; + var cosra = cos_first; + var sinra = sin_first; while (col < PlaneWidth) : ({ col += 1; - ra -= ra_step; + cosra += cos_step; + sinra += sin_step; }) { - const cosra = std.math.cos(ra); - const sinra = std.math.sin(ra); - // Observe that sqrt(1+tan^2) = abs(1/cos) sqrt(cos^2+sin^2) = - // abs(1/cos). Similarly so for cot, hence we obtain the following - // lengths for the hypotenuses assuming that x (respectively y) are - // unit length and the angle is ra. + // abs(1/cos). Similarly so for cot, hence we obtain the + // following lengths for the hypotenuses assuming that x + // (respectively y) are unit length and the angle is ra. This + // for whatever reasons still works when we linearly interpolate + // on cos and sin! const dy_for_x_step = std.math.fabs(1 / cosra); const dx_for_y_step = std.math.fabs(1 / sinra); @@ -132,8 +141,8 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type { var dist_x: f32 = undefined; var dist_y: f32 = undefined; - var ipos_x: i32 = @floatToInt(i32, floor(self.pos_x)); - var ipos_y: i32 = @floatToInt(i32, floor(self.pos_y)); + var ipos_x: i32 = @floatToInt(i32, std.math.floor(self.pos_x)); + var ipos_y: i32 = @floatToInt(i32, std.math.floor(self.pos_y)); // looking right if (cosra >= 0) { @@ -171,12 +180,8 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type { }) { var cell = map.lookup(ipos_x, ipos_y); - // the correct distance is the shortest distance from the plane - // of projection to the point, that is, perpendicular distance - const perp_distance = distance * std.math.cos(self.ang - ra); - // project the top of the wall - const top = PlaneHeight / 2 + PlaneDist * (cell.height - self.height) / perp_distance; + const top = PlaneHeight / 2 + PlaneDist * (cell.height - self.height) / distance; // We have a wall to draw if it protrudes above what we have so far drawn if (top > highest_point) { @@ -185,7 +190,7 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type { if (top > PlaneHeight) still_drawing = false; // compute the height of this wall - const total_length = PlaneDist * cell.height / perp_distance; + const total_length = PlaneDist * cell.height / distance; // as well as the fraction we'll be drawing const draw_length = top - highest_point; @@ -207,7 +212,7 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type { // var y = @floatToInt(i32, top); // while (y > @floatToInt(i32, highest_point)) : (y -= 1) { // const index = @intCast(usize, col * @floatToInt(i32, PlaneHeight) + y); - // self.z_buffer.set(index, perp_distance); + // self.z_buffer.set(index, distance); // } highest_point = top; @@ -219,23 +224,31 @@ pub fn Player(PlaneWidth: f32, PlaneHeight: f32) type { pub fn renderFloorsToTexture(self: @This(), floors_image: Image, rendered_floors_texture: Texture, map: Map) !void { var pixels = [_]Colour{Colour.Black} ** (PlaneWidth * PlaneHeight / 2); - const ang_step = FOV / PlaneWidth; + // Again, another TERRIBLE hack: we do the same nasty linear + // interpolation trick and for whatever reason the floors look fine. + const cos_first = std.math.cos(self.ang + 0.5*FOV); + const sin_first = std.math.sin(self.ang + 0.5*FOV); + const cos_last = std.math.cos(self.ang - 0.5*FOV); + const sin_last = std.math.sin(self.ang - 0.5*FOV); + var row: usize = 0; while (row < PlaneHeight / 2) : (row += 1) { const row_dist = self.height * PlaneDist / @intToFloat(f32, row + 1); + const dx_step = row_dist * (cos_last - cos_first) / PlaneWidth; + const dy_step = row_dist * (sin_last - sin_first) / PlaneWidth; + var col: usize = 0; - var ang = 0.5 * FOV + self.ang; - var ang_diff: f32 = 0.5 * FOV; + var dx = row_dist * cos_first; + var dy = row_dist * sin_first; while (col < PlaneWidth) : ({ col += 1; - ang -= ang_step; - ang_diff -= ang_step; + dx += dx_step; + dy += dy_step; }) { - const perp_dist = row_dist / std.math.cos(ang_diff); - const x = self.pos_x + perp_dist * std.math.cos(ang); - const y = self.pos_y + perp_dist * std.math.sin(ang); + const x = self.pos_x + dx; + const y = self.pos_y + dy; const sx = std.math.modf(x); const sy = std.math.modf(y); -- cgit v1.3.1