Jordan Savant # Software Engineer

Ascii FPS Ratracing


This example will use an ascii terminal to render the ratracing algorithm for 2D projected into 3D.

I studied this (Wolfenstein) logic while trying to recreate the Doom engine in Python.

I have removed the portions for how to render to a console directly, cell by cell.

pX = 8
pY = 8
pAngle = 0
pFoV = pi/4

mapW = 16
mapH = 16
map = [...] # walls are hashes '#', and floor is '.'

screenW = 120
screenH = 40

maxDepth = 16

while gameLoop

    # pseudo input stuff i can do later
    if turnLeft
        pAngle -= turnAngle
    if turnRight

    foreach xcol on screen:

        # - half fov on left + half vov on right
        rayAngle = (pAngle - pFoV / 2) + (xcol / screenW) * pFoV

        # this is expensive and where the raytacing occurs
        distToWall = 0

        # unit vector to repesent way player is looking
        eyeX = sin(rayAngle)
        eyeY = cos(rayAngle)

        while !hitwall and distToWall < maxDepth:
            distToWall += .01

            testX = pX + eyeX * distToWall # you can cast these to int to test against a tile in world space
            testY = pY + eyeY * distToWall # ..

            # test ray is out of bounds of map
            if testX < 0 or testX > mapW or testY < 0 or testY > mapH:
                hitWall = true
                distToWAll = depth

            # ray in bounds test to see if at wall
                # if our cell is a wall
                if map[ testY * mapW + testX] == '#':
                    hitWall = true

        # calculate distance to ceiling and floor (vertical scren height)
        # as distance get larger, subtraction gets smaller so it gets taller
        # using screenH / distToWall means that when the distance is equal
        # to screenHeight it will be on unit (pixel or character depending
        # on how you display)
        # This is a bit arbitrary but I would think we would want to define
        # a different vanishing point and get our ratio of wallDistance to
        # vanishingPoint, then set that ratio against the screenHeight
        ceiling = (screenH / 2) - screenH / distToWall
        floor = screenH - ceiling

        # draw into screen column
        for y in screenH

            # if we are above our ceiling draw nothing
            if y < ceiling
                draw(xCol, y, ' ')
            # if we are between ciling and floor draw wall
            else if y < ceiling and y > floor

                # draw the wall
                draw(xCol, y, 'W')

                # VERSION 2 - shade based on distance
                shadeChar = getShadeBasedOnDist(distToWall) # basically if elses based on rules on how far
                draw(xCol, y, shadeChar)

            # if below floor so draw nothing
                draw(xCol, y, ' ')

In regards to our Doom engine we are not raytracing from screen X coords to distant walls

Instead we have divided the game into lines and use a BSP to figure out which walls to render

Those walls are chosen based on your position and don't care about camera rotation or FoV etc

Because of this we need to be able to determine if a wall is within the view of the camera

If it is then we need to determine where our view intersects with that wall and render it

If we know we are rendering a wall facing the camera

  • We get the left intersection
  • This intersection point will render at some X coordinate on the screen based on the X and Y of the position in the camra
  • This is also where I get tripped up because that X coord wants to be projected with linear algebra
  • Because in this raytracing example we already know which X coord we are rendering and finding the wall segment with a ray
  • In the doom one we are trying to reverse this by taking a wall position and reverse finding the X coord
  • If the camera was facing directly up or down (in 2d) then it would be a straight X world to X screen coord
  • But as the camera rotates the Y and X must be combined to map to a screen X coord

I think this tutorial can span that knowledge gap:

It talks about projection of wall coords to screen coords and I think it can be redone