Raycasting

Raycasting is used to create a 3D projection of a 2D scene. Raycasting can be seen in games like Wolfenstein 3D, and was used because it wasn't possible to run 3D engines in real time, when computers were slower. Raycasting saves computing power by only casting one ray per vertical strip on the screen.

In this project the map of the game consists of walls, a floor, and ceiling. This is represented by a 2-dimensional array of integers. Each integer represents a cube section of a wall. e.g. 0 would indicate no wall, 1 would indicate a red wall And the player position, direction are defined using vectors. And either a field of view (a floating point number) or projection plan (a vector) is defined.

Using this information, a ray (a ray starts at one point and goes off in a direction for infinity) for each vertical section of the screen is cast out until it hits a wall. The correct vertical section of the wall is then drawn to the screen and scaled down depending on how far it is from the player.

For more information and tutorials see links at the bottom.

360 Panorama of Raycasting Raycasting code (for Processing)

for (int x = 0; x < width; x++) {
//gives a value from -1 to 1 to each x coordinate
float planePosition = (2 * x / (float)width - 1) * -1;
//direction vector of ray
Vector2D ray = new Vector2D(direction.x * cos(fov * planePosition) - direction.y * sin(fov * planePosition), direction.x * sin(fov * planePosition) + direction.y * cos(fov * planePosition));

//position in array
int mapX = (int)position.x;
int mapY = (int)position.y;

//DDA algorithm (used to find the wall)
Vector2D distanceToNextSquare = new Vector2D(0, 0);
Vector2D nextSquareIncrement = new Vector2D(sqrt(1 + (ray.y * ray.y) / (ray.x * ray.x)), sqrt(1 + (ray.x * ray.x) / (ray.y * ray.y)));
Vector2D mapStep = new Vector2D(0, 0);
boolean XY = false;

if (ray.x < 0)
{
mapStep.x = -1;
distanceToNextSquare.x = (position.x - mapX) * nextSquareIncrement.x;
}
else
{
mapStep.x = 1;
distanceToNextSquare.x = (mapX + 1.0 - position.x) * nextSquareIncrement.x;
}
if (ray.y < 0)
{
mapStep.y = -1;
distanceToNextSquare.y = (position.y - mapY) * nextSquareIncrement.y;
}
else
{
mapStep.y = 1;
distanceToNextSquare.y = (mapY + 1.0 - position.y) * nextSquareIncrement.y;
}
while (true) {
if (distanceToNextSquare.x < distanceToNextSquare.y)
{
distanceToNextSquare.x += nextSquareIncrement.x;
mapX += mapStep.x;
XY = false;
}
else
{
distanceToNextSquare.y += nextSquareIncrement.y;
mapY += mapStep.y;
XY = true;
}

if (mapX < 0 || mapY < 0 || mapX >= mapSize || mapY >= mapSize) {
//ray now outside of array
break;
} else if (map[mapX][mapY] > 0) {
//wall found

//calculate distance
float distance = 0;
if (XY) {
distance = mapY - position.y;
if (ray.y < 0)
distance++;
distance /= ray.y;
} else {
distance = mapX - position.x;
if (ray.x < 0)
distance++;
distance /= ray.x;
}

//fisheye correction
distance = fishEyeCorrection ? distance * abs(cos(fov * planePosition)) : distance;

//wall segment size
int drawStart = -(int)(height / distance) / 2 + height / 2;
int drawEnd = (int)(height / distance) / 2 + height / 2;

//get color and darken color for back faces to create shading
if (XY) {
color c;
if (map[mapX][mapY] > colors.length || map[mapX][mapY] <= 0)
c = color(#0AFFD4);
else
c = colors[map[mapX][mapY] - 1];

float r = red(c);
float b = blue(c);
float g = green(c);

stroke(color(r / 2, g / 2, b / 2));

} else
if (map[mapX][mapY] > colors.length || map[mapX][mapY] <= 0)
stroke(color(#0AFFD4));
else
stroke(colors[map[mapX][mapY] - 1]);

//draw line
line(x, drawStart, x, drawEnd);
break;
}
}
}

Video

Fish Eye effect

The fish eye effect is created when the distance the ray has to travel to get to a wall is farther than the actual distance to the wall from the player.

You can see how when uncorrected the walls bulge in the center. When correction is turned on the wall becomes straight again. However the sides of the screen become distorted the larger the field of view is and does not work past 90 degrees field of view.