On this page
  1. Related
  2. Games
  3. References
  4. Sources

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

GameYearHowNote
Wolfenstein 3D1992Grid raycastone 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)
Wolfenstein 3D 1992John 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".Developer
Wolfenstein 3D finds its walls with one ray per column of the view: AsmRefresh steps pixx from 0 up to viewwidth, aiming each ray at the view angle plus pixelangle[pixx], in angles of 3,600 to a turn (FINEANGLES), and CalcProjection sets each column's offset to the arctangent of its whole-pixel distance from the center (scaled by VIEWGLOBAL/viewwidth) over facedist, the focal length plus MINDIST, 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".Code
Each Wolfenstein 3D ray walks the tile map, 64 by 64 tiles at most (MAPSIZE), one grid line at a time: AsmRefresh alternates between the next vertical and the next horizontal grid line, taking whichever the ray crosses first, and steps by adding a tangent from finetangent (ystep or xstep) to the other coordinate's intercept; at each crossing it tests the tile beyond in tilemap, marking an empty one in spotvis, and the first nonzero tile ends the ray with a call to HitVertWall or HitHorizWall, or the door and pushwall code when the tile's top bit is set.Code
Wolfenstein 3D sizes each wall column by the hit point's distance along the facing, not along the ray: CalcHeight takes the hit point's offset from the view point, which WallRefresh sets focallength behind the player, projects it onto the view direction with viewcos and viewsin (nx = gxt-gyt), raises it to at least mindist, and divides heightnumerator by nx>>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.Code

References

  1. ^ abcWolfenstein 3D finds its walls with one ray per column of the view: AsmRefresh steps pixx from 0 up to viewwidth, aiming each ray at the view angle plus pixelangle[pixx], in angles of 3,600 to a turn (FINEANGLES), and CalcProjection sets each column's offset to the arctangent of its whole-pixel distance from the center (scaled by VIEWGLOBAL/viewwidth) over facedist, the focal length plus MINDIST, 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/wolf3drevised
    How we know
    7FINEANGLES	=	3600
    
    WOLFSRC/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.

    In the claim ledger

  2. ^ abEach Wolfenstein 3D ray walks the tile map, 64 by 64 tiles at most (MAPSIZE), one grid line at a time: AsmRefresh alternates between the next vertical and the next horizontal grid line, taking whichever the ray crosses first, and steps by adding a tangent from finetangent (ystep or xstep) to the other coordinate's intercept; at each crossing it tests the tile beyond in tilemap, marking an empty one in spotvis, and the first nonzero tile ends the ray with a call to HitVertWall or HitHorizWall, or the door and pushwall code when the tile's top bit is set.Codeid-Software/wolf3d
    How we know
    122#define MAPSIZE		64					// maps are 64*64 max
    
    WOLFSRC/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.

    In the claim ledger

  3. ^ abWolfenstein 3D sizes each wall column by the hit point's distance along the facing, not along the ray: CalcHeight takes the hit point's offset from the view point, which WallRefresh sets focallength behind the player, projects it onto the view direction with viewcos and viewsin (nx = gxt-gyt), raises it to at least mindist, and divides heightnumerator by nx>>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/wolf3d
    How 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.

    In the claim ledger

  4. ^ 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.

    In the claim ledger

Sources

Code