Skip to content

Texture Mapping

Texture mapping is the step that transforms a projected wall slice into actual surface pixels. After DDA has found the hit and projection has computed the slice height, the renderer turns geometry into texture coordinates.

Per-column mapping

DDA hit data becomes concrete texel coordinates

Texture mapping happens after wall projection. For each screen column, the renderer chooses a texture, computes `tex_x`, advances `tex_y`, then samples the image buffer into the frame buffer.

01
pick the wall texture

The chosen texture depends on DDA side and ray direction sign.

side == 0 && ray_dir_x > 0 WE
side == 0 && ray_dir_x < 0 EA
side == 1 && ray_dir_y > 0 SO
side == 1 && ray_dir_y < 0 NO
02
compute `wall_x` then `tex_x`

The renderer keeps only the fractional hit part on the wall face.

// hit fraction on the wall plane
if (side == 0)
  wall_x = player.y + perp_dist * ray_dir_y;
else
  wall_x = player.x + perp_dist * ray_dir_x;

wall_x -= floor(wall_x);
tex_x = (int)(wall_x * tex_width);

if (side == 0 && ray_dir_x > 0)
  tex_x = tex_width - tex_x - 1;
if (side == 1 && ray_dir_y < 0)
  tex_x = tex_width - tex_x - 1;
03
advance `tex_y` across the slice

The vertical sampler converts projected wall height into texture rows.

step = (double)tex_height / line_height;
tex_pos = (draw_start - screen_h / 2 + line_height / 2) * step;

for (y = draw_start; y < draw_end; y++)
{
  tex_y = (int)tex_pos;
  tex_pos += step;
  // sample + write pixel
}
wall_x

fractional hit position on the wall face after removing the integer tile coordinate

tex_x

horizontal texel column derived from `wall_x * tex_width`, then flipped if needed

step / tex_pos

vertical sampling stride for the projected slice from `draw_start` to `draw_end`

tex_y

current texture row sampled for the current screen pixel

04
interactive column preview

Move across the wall to inspect `wall_x`, `tex_x`, and the selected texture stripe.

move over the preview
sample texture, write frame pixel

Each wall pixel now uses a valid texture address and writes a consistent wall color into the frame buffer.

`tex_x` stays in bounds `tex_y` stays in bounds face orientation stays coherent frame buffer is ready for present

Per screen column, the mapper uses:

  • DDA hit information (side, ray direction, hit tile, perp_dist)
  • projected wall bounds (draw_start, draw_end, line_height)
  • texture dimensions and image buffer metadata
  • player position to recover the hit fraction on the wall face

These values are enough to choose the correct wall texture and the exact texel to sample at each pixel.

Texture mapping

For one screen column, rendering chooses a texture, then a texture column

Raycasting gives a hit side, a distance, and an impact point. Mapping turns those values into tex_x and tex_y before writing pixels into frame.addr.

1Which texture?

`NO`, `SO`, `WE`, or `EA` depending on the hit face.

2Which column?

`wall_x` becomes `tex_x`.

3Which row?

`tex_pos` advances vertically while drawing.

Texture selection

NO: ray travels south, north face, tex_no texture
if (ray.side == 0 && ray.ray_dir_x > 0)
    return (&app->tex_we);
if (ray.side == 0)
    return (&app->tex_ea);
if (ray.ray_dir_y > 0)
    return (&app->tex_no);
return (&app->tex_so);

`wall_x` to `tex_x`

wall_x=0.32 -> tex_x=20 / 64

Copy texture to frame

source texture tex->addr + tex_y * line_len + tex_x * bpp/8
one single `tex_x` column is read vertically
destination frame x = ray.x, y = draw_start..draw_end
wall pixels replace ceiling/floor only inside the slice
tex_x = (int)(wall_x * (double)tex->width);
step = (double)tex->height / (double)ray.line_height;
tex_pos = (ray.draw_start - app->frame.height / 2
    + ray.line_height / 2) * step;

color = sample_texel(tex, tex_x, (int)tex_pos);
put_pixel(&app->frame, ray.x, y, color);

The first decision is which texture family to use.

Base mandatory logic selects directional wall textures:

  • NO
  • SO
  • WE
  • EA

Bonus mode can replace that with additional wall, door, symbol, or animated texture sources, but the decision still depends on the tile hit and the side reported by DDA.

wall_x represents where the ray hit the wall face, expressed as a fractional position in [0, 1).

That fraction becomes:

  • tex_x = (int)(wall_x * tex_width)

Then the code flips the result on some faces so opposite directions do not show mirrored textures incorrectly.

Vertical sampling uses the projected wall height:

  • step = tex_height / line_height
  • tex_pos starts at the first visible pixel of the slice
  • each rendered screen pixel increments tex_pos
  • tex_y comes from the current integer part of that value

This keeps texture sampling stable even when walls are very tall, clipped, or far away.

For each wall pixel:

  1. read the texel from the selected texture image buffer
  2. write the sampled wall color into the frame buffer
  3. let later world-level passes, such as vignette, darken the composed frame

At the end of the loop, the wall column is fully resolved and ready for the final image present call.

After texture mapping:

  • sampled coordinates remain inside valid texture bounds
  • directional orientation remains coherent
  • wall pixels stay aligned with DDA hit geometry
  • the frame buffer contains valid final wall color data
  • srcs/render/raycast_draw.c
  • srcs/render/raycast.c
  • srcs/core/init_mlx.c
  • srcs_bonus/retro/walls_*.c