Raycasting is the algorithm that turns the 2D map into the first-person world
view. In this project, the core traversal uses DDA: a grid-walking method that
advances from one cell boundary to the next until a blocking tile is reached.
Grid traversal
One column, one ray, one deterministic first hit
DDA is the core grid-walking algorithm behind `cub3D`. For each screen column, the engine builds a ray, walks cell crossings until a solid tile is hit, then projects the corresponding wall slice.
01
per-column DDA sequence
The same four stages repeat for every screen column in the render loop.
01build ray direction from camera_x
each screen column gets a different camera-space coordinate and therefore a different ray direction
Switch between top-down ray view, projected wall result, and DDA step trace.
move over the map view to inspect a column
03
DDA invariants
The traversal stays deterministic because each column is independent and state-local.
first hit only
the loop stops on the first blocking grid cell reached by the ray
guarded traversal
a maximum step count avoids infinite loops on malformed runtime state
stack-local ray data
`t_ray` lives per column and does not share mutable state with other rays
✓
`O(W * K)` and column-independent
Width `W` controls the number of rays, while `K` is the average number of grid crossings before a hit. That keeps the algorithm simple, local, and predictable.
deterministic first-hit traversalper-column independenceprojection from `perp_dist`ready for texture mapping
initialize map cell, deltas, steps, and side distances
walk the grid with DDA
stop on the first solid hit
compute perp_dist
project the wall slice for rendering
That makes the whole wall rendering pipeline column-based and deterministic.
raycast_scene
The 3D scene is a column loop
`raycast_scene()` does not draw a whole wall at once. It repeats the same work for every frame column x: cast a ray, find distance, compute height, draw one slice.
column0
perp_dist-
line_height-
waiting frame
Real function
int raycast_scene(t_app *app)
{
int x;
t_ray ray;
x = 0;
while (x < app->frame.width)
{
ray = cast_ray(app, x);
draw_wall_column(app, ray);
bonus_sprites_set_depth(app, x, ray.perp_dist);
x++;
}
return (0);
}
Distance to height
`line_height = frame.height / perp_dist`.
The larger the distance, the smaller the vertical slice becomes.
After the hit, the engine uses perpendicular distance instead of raw Euclidean
distance from the player.
That matters because:
it removes fish-eye distortion
it produces correct wall slice height
it matches the camera plane projection model
From there, line_height, draw_start, and draw_end are derived for the current column.
Fish-eye - and how to correct it
euclidean distance vs perpendicular distance
1. First - understand the problem
Imagine you are the player. You look at a straight wall in front of you. The wall is flat, at the same projected distance everywhere. But with Euclidean distance, the rays near the edges of the field of view travel farther.
2. What it does on screen
Reminder: line_height = WIN_H / distance. The larger the distance, the smaller the column. So if edge rays are longer, their columns get smaller, and the wall looks curved.
Euclidean distance - curved wall (fish-eye)
Euclidean distances - larger near the edges:
edge colcenter coledge col
3. How your code corrects fish-eye
Your code never computes sqrt(dx² + dy²) for projection. It directly uses the accumulated DDA distances, which gives the perpendicular distance automatically.
// × NEVER this in your code:
dist = sqrt(dx*dx + dy*dy); // euclidean distance = fish-eye// ✓ what your code does:
if (r.side == 0)
r.perp_dist = r.side_x - r.delta_x; // perpendicular distance
else
r.perp_dist = r.side_y - r.delta_y; // perpendicular distance
Why does side_x - delta_x give the perpendicular distance?
side_x = accumulated distance to the next vertical border
delta_x = distance between two consecutive vertical borders
side_x - delta_x = distance to the previous border = distance to the wall
This value is projected on the camera axis. It is perpendicular to the camera plane, not diagonal. That is why it is the same for all rays that hit the same flat wall.