Raycasting
A way to draw a first-person view of a tile map by casting one ray per screen column across the grid and sizing that column's wall slice by the distance to the hit.
On this page
A raycaster draws a first-person view one screen column at a time. For each column it casts a ray from the viewer across a map of square tiles, stops at the first solid tile, and draws a wall slice whose height falls with the distance to the hit.
Wolfenstein 3D casts one ray per column of the view, each at its own offset from the facing, in 3,600 angles to a turn1,1. A ray crosses the tile map one grid line at a time, testing the tile beyond each crossing2. The slice’s height comes from the hit point’s distance measured along the facing rather than along the ray, which keeps a flat wall straight3. Its source’s release notes, signed by John Carmack, say “a BSP based texture mapper could go faster, but ray casting was a lot simpler to do at the time”4.
- Grid raycastWolfenstein 3D (1992). One ray per view column steps across a 64 by 64 tile map, and each wall slice's height comes from the hit's distance along the facing.1,2,3,4
Games
| Game | Year | How | Note |
|---|---|---|---|
| Wolfenstein 3D | 1992 | Grid raycast | one ray per view column steps across a 64 by 64 tile map, and each wall slice's height comes from the hit's distance along the facing |
Every claim about this mechanic, by game (4)
References
- ^ abcWolfenstein 3D finds its walls with one ray per column of the view:
AsmRefreshstepspixxfrom 0 up toviewwidth, aiming each ray at the view angle pluspixelangle[pixx], in angles of 3,600 to a turn (FINEANGLES), andCalcProjectionsets each column's offset to the arctangent of its whole-pixel distance from the center (scaled byVIEWGLOBAL/viewwidth) overfacedist, the focal length plusMINDIST, so the rays cross a flat projection plane at even spacing rather than fanning out at even angles (the last part computed). The two middle columns both get an offset of 0, though the loop's comment says it starts "1/2 pixel over".Codeid-Software/wolf3drevisedHow we know
7FINEANGLES = 3600WOLFSRC/WL_DR_A.ASM line 7 at 0516778 · upstream ↗ 207pixxloop: 208 mov ax,SEG finetangent 209 mov es,ax 210 mov cx,[midangle] ; center of view area 211 mov bx,[pixx] 212 shl bx,1 213 add cx,[pixelangle+bx] ; delta for this pixel
WOLFSRC/WL_DR_A.ASM lines 207–213 at 0516778 · upstream ↗ 517nextpix: 518 mov ax,[pixx] 519 inc ax 520 mov [pixx],ax 521 cmp ax,[viewwidth] 522 jge done 523 jmp pixxloop
WOLFSRC/WL_DR_A.ASM lines 517–523 at 0516778 · upstream ↗ 653 focallength = focal; 654 facedist = focal+MINDIST;
WOLFSRC/WL_MAIN.C lines 653–654 at 0516778 · upstream ↗ 674 for (i=0;i<halfview;i++) ⋮ 676 // start 1/2 pixel over, so viewangle bisects two middle pixels 677 tang = (long)i*VIEWGLOBAL/viewwidth/facedist; 678 angle = atan(tang); 679 intang = angle*radtoint; 680 pixelangle[halfview-1-i] = intang; 681 pixelangle[halfview+i] = -intang;
WOLFSRC/WL_MAIN.C lines 674–681 at 0516778 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Revised after a check by a second AI model, 2026-10-22.
- ^ abEach Wolfenstein 3D ray walks the tile map, 64 by 64 tiles at most (
MAPSIZE), one grid line at a time:AsmRefreshalternates between the next vertical and the next horizontal grid line, taking whichever the ray crosses first, and steps by adding a tangent fromfinetangent(ysteporxstep) to the other coordinate's intercept; at each crossing it tests the tile beyond intilemap, marking an empty one inspotvis, and the first nonzero tile ends the ray with a call toHitVertWallorHitHorizWall, or the door and pushwall code when the tile's top bit is set.Codeid-Software/wolf3dHow we know
122#define MAPSIZE 64 // maps are 64*64 maxWOLFSRC/WL_DEF.H line 122 at 0516778 · upstream ↗ 232entry90: 233 mov [xtilestep],1 ; xtilestep = 1 234 mov [ytilestep],-1 ; ytilestep = -1 235 mov [BYTE cs:horizop],OP_JGE ; patch a jge in 236 mov [BYTE cs:vertop],OP_JLE ; patch a jle in 237 mov bx,DEG90-1 238 sub bx,cx 239 shl bx,2 240 mov ax,[es:bx] 241 mov dx,[es:bx+2] 242 mov [WORD xstep],ax 243 mov [WORD xstep+2],dx ; xstep = finetangent[DEG90-1-angle] ⋮
WOLFSRC/WL_DR_A.ASM lines 232–252 at 0516778 · upstream ↗ 438vertcheck: 439 cmp dx,bp 440vertop: ; 0x7e = jle (ytilestep==-1) 441 jle horizentry ; 0x7d = jge (ytilestep==1) 442vertentry: 443 test [BYTE tilemap+si],0ffh ; tilehit = *((byte *)tilemap+xspot); 444 jnz hitvert 445passvert: 446 mov [BYTE spotvis+si],1 ; *((byte *)spotvis+xspot) = true; 447 add bx,[xtilestep] ; xtile+=xtilestep 448 mov ax,[WORD ystep] 449 add [WORD yintercept],ax ; yintercept += ystep ⋮
WOLFSRC/WL_DR_A.ASM lines 438–469 at 0516778 · upstream ↗ 479horizcheck: 480 cmp cx,bx 481horizop: ; 0x7e = jle (xtilestep==-1) 482 jle vertentry ; 0x7d = jge (xtilestep==1) 483horizentry: 484 test [BYTE tilemap+di],0ffh ; tilehit = *((byte *)tilemap+yspot); 485 jnz hithoriz 486passhoriz: 487 mov [BYTE spotvis+di],1 ; *((byte *)spotvis+yspot) = true; 488 add bp,[ytilestep] ; ytile+=ytilestep 489 mov ax,[WORD xstep] 490 add [WORD xintercept],ax ; xintercept += xstep ⋮
WOLFSRC/WL_DR_A.ASM lines 479–508 at 0516778 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^ abWolfenstein 3D sizes each wall column by the hit point's distance along the facing, not along the ray:
CalcHeighttakes the hit point's offset from the view point, whichWallRefreshsetsfocallengthbehind the player, projects it onto the view direction withviewcosandviewsin(nx = gxt-gyt), raises it to at leastmindist, and dividesheightnumeratorbynx>>8, which the code's comment calls the "perspective ratio". Measuring along the facing is what keeps a flat wall straight on screen instead of bowed (fisheye), the atlas's reading.Codeid-Software/wolf3dHow we know
366 gx = xintercept-viewx; 367 gxt = FixedByFrac(gx,viewcos); ⋮ 369 gy = yintercept-viewy; 370 gyt = FixedByFrac(gy,viewsin); ⋮ 372 nx = gxt-gyt; ⋮ 375 // calculate perspective ratio (heightnumerator/(nx>>8)) ⋮ 377 if (nx<mindist) 378 nx=mindist; // don't let divide overflow ⋮ 380 asm mov ax,[WORD PTR heightnumerator] 381 asm mov dx,[WORD PTR heightnumerator+2] 382 asm idiv [WORD PTR nx+1] // nx>>8
WOLFSRC/WL_DRAW.C lines 366–382 at 0516778 · upstream ↗ 488 wallheight[pixx] = CalcHeight();WOLFSRC/WL_DRAW.C line 488 at 0516778 · upstream ↗ 1307 viewx = player->x - FixedByFrac(focallength,viewcos); 1308 viewy = player->y + FixedByFrac(focallength,viewsin);
WOLFSRC/WL_DRAW.C lines 1307–1308 at 0516778 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^ abJohn Carmack's notes to the Wolfenstein 3D source release say the game's "ray casting refresh architecture is still reasonably appropriate for the game", and that "a BSP based texture mapper could go faster, but ray casting was a lot simpler to do at the time".Developerid-Software/wolf3d
How we know
20The ray casting refresh architecture is still reasonably 21appropriate for the game. A BSP based texture mapper could 22go faster, but ray casting was a lot simpler to do at the time.
WOLFSRC/README/RELEASE.TXT lines 20–22 at 0516778 · upstream ↗ 51John Carmack 52Technical Director
WOLFSRC/README/RELEASE.TXT lines 51–52 at 0516778 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
Sources
Code
- Software, id. 2012. “Wolfenstein 3D: the source release (id-Software/wolf3d).” Commit 0516778. id-Software/wolf3d @ 0516778 · licence, all uses