4 ‐ Ray Casting - shoganaix/42Cub3d GitHub Wiki
Ray Casting
Rays are cast and traced in groups based on some geometric constraints.
For instance: on a 320×200 display resolution, a ray-caster traces only 320 rays (the number 320 comes from the fact that the display has 320 horizontal pixel resolution, hence 320 vertical column).
- Faster but inexact.
- Small storage.
- Render on the fly.
Vs. RAY-TRACING
RAY-TRACING: each ray is traced separately, so that every point (usually a pixel) on the display is traced by one ray.
For instance: on a 320×200 display resolution, a ray-tracer needs to trace 320×200 (64,000) rays. (That is roughly 200 times slower than ray-casting.).
- Slower, but exact.
- Usually some renders are cached
Creating our world
Variables
- cube size CxCxC (The larger the size of the cube, the blockier the world will look like, but smaller cube will make the rendering slower)
- map as a grid
Projection attributes
- player
- height - C/2
- FOV - field of view (60 deg)
- POV: position (x, y) + orientation ( ALPHA deg)
- Projection plane's dimension - the window's size: W * H = n_rays * H
- Relationship between player and projection plane
- angle between subsequent rays: ray_angle = FOV / n_rays
- distance between the player and the projection plane. dist_to_plane = (n_rays/2) / tan(FOV /2)
INDEX
Based on the viewing angle, subtract 30 degrees (half of the FOV)
For each column (there's a ray for each column):
- From the player, trace the ray until it hits a wall. - FIND WALL COLLISION POINT
- Record the distance to the wall (the distance is equal to the length of the ray). - FIND DISTANCE TO WALL
- find projection wall height using the distance to the wall, distance to the projection plane and wall height. - FIND PROJECTION WALL
- Find p1 y p2 that define the wall slice in the projection slice.- DEFINE UPPER AND BOTTOM SLICE
- Map projection to wall texture : image_slice receives image and proportion
- Draw Slice (loop)-> draw slice on buffer: ceiling, image_slice, floor
STEPS
Starting from column 0, Suppose FOV = 60
A. Cast a ray.
B. Trace the ray until it hits a wall. (FINDING WALLS)
C. Record the distance to the wall (the distance is equal to the length of the ray). (FINDING DISTANCE TO WALLS + DRAWING WALLS + TEXTURE MAPPED WALLS)
D. Add the angle increment so that the ray moves to the right (we know from Figure 10 that the value of the angle increment is 60/320 degrees)
E. Repeat step 2 and 3 for each subsequent column until all 320 rays are cast.
#1. Based on the viewing angle, subtract 30 degrees (half of the FOV).
orientation = 90 # N=90, W=180, ..
ray_angle = orientation - FOV/2
For each ray_angle : # nrays = ncols = W
# # DRAW WALL SLICE : sky | texture | floor -------
# 1. FIND WALL COLLISION POINT
wall_collision_point = ray_collides_wall(...)
# 2. FIND DISTANCE TO WALL
ray_len = dist_player_to_point(wall_collision_point)
# 3. FIND PROJ WALL HEIGHT
projected_slice_height = get_proj_height()
# 4. FIND DIVIDING POINTS
get_wall_slice_points = ...
# 5. MAP PROJECTION TO WALL TEXTURE
image_slice = # receives image and proportion
# 6. DRAW SLICE ON IMAGE. draw slice on buffer: ceiling, image_slice, floor
img ....
angle ++
1. FINDING WALLS
process vertically and horizontally separated, get the closest one
dist_h, int_h = horizontal_intersection(...)
dist_v, int_v = vertical_intersection(...)
wall_inters = min(int_h, int_v, key=dist) # choose the closest one
2. FINDING DISTANCE TO WALLS
calculate real distance and refine
CAREFUL with “fishbowl effect” Fishbowl effect happens because ray-casting implementation mixes polar coordinate and Cartesian coordinate together. Therefore, using the above formula on wall slices that are not directly in front of the viewer will gives a longer distance. This is not what we want because it will cause a viewing distortion such as illustrated below.
How do we do refine:
The resulting distance obtained must be multiplied by cos(BETA)
where BETA is the angle of the ray that is being cast relative to the viewing angle
ALPHA is 90 degrees because the player is facing N. Because we have 60 degrees FOV, BETA is 30 degrees for the leftmost ray and it is -30 degrees for the rightmost ray
3. DRAWING WALLS
We draw each wall slice at a time. We use function draw_slice that receives an offset, the direction and the upper and bottom points(double pointer)
void draw_slice(t_game *game, int p_wall[2][2], t_card cardinal, int offset)
we know
d = the distance of the player to the wall slice (ray length) so we can calculate the projected slice height.
proj_slice_H = C / d * 277
dist_to_plane = 277
Slice
[--------[||||||||+||||||||]---------]
[ window height ]
[ wall/2 + wall/2 ]
centre
4. TEXTURE MAPPED WALLS
What is texture mapping?
Texture mapping refers to painting a bitmap/texture onto a surface.
For our cub3d world, we CAN use bitmaps that have the size of C by C pixels. It is possible to use different size bitmaps, but using the same size simplifies the process. To draw this on screen, is just a matter of scaling a slice (or column) of bitmap
The offset is the position of the ray relative to the grid. This offset can be found and used to determine which column of the bitmap is to be drawn as the wall slice.
if the slice is on a vertical collision
OFFSET = y % C
on horizontal collisions
OFFSET = x % 64