This project is 42 project that aimes to introduce us to the beautiful world of Raytracing
In 3D computer graphics, ray tracing is a technique for modeling light transport for use in a wide variety of rendering algorithms for generating digital images.
Clone this repository:
git clone https://github.com/YassineEddyb/miniRT_42 && cd miniRT_42 && make
Then run the program with a file as an argument from the scenes folder:
./miniRT ./scenes/1337.rt
You can also create your own scene, Just create a file with .rt extention and configure it.
There is three conifg object that are mandatory to have:
- Ambient
- Light
- Camera
And four basic shapes that are (optional, will not entirely cause if you leave it empty you will only see a black window):
- Plane
- Sphere
- Cylinder
- Cube
Position: 20,3,0 => x,y,z coordinates
Oreintation: 0,1,0 => 3d normalized orientation vector. In range [-1,1] for each x,y,z axis
Color: 255,0,255 => R,G,B colors in range [0,255]
Shininess: 200 => how much the surface shine in range [0,200]
Reflection: 1 => how much the surface reflect in range [0,1]
in case you're wondering how all this works? just loop over all pixels and generate a ray for each one, then see if intersects with any object and calculate the color for this pixel, sounds simple isn't it, well it's not.
- Introduction to Ray Tracing: a Simple Method for Creating 3D Images
- The cherno ray tracing in youtube
- Ray Tracing in One Weekend
- The The Ray Tracer Challenge
- Ray Tracer series youtube
-
First of all you should be familiar with matrixes and vectors, Check this article from scratchapixel geometry.
-
Structures needed for this lesson
ray {
vector origin # start of the ray
vector dir # direction of the ray
}
sphere {
vector pos # position of the sphere
matrix transform # transformation matrix of the sphere
}
- in this lesson well draw a sphere and the same method go for other basic shapes.
- Intersection of the sphere will be in object space then we will transform it to world space see Object space vs World space.
In orther to generate rays in real life you would have to implement a camera but we'll keep thing simple here.
General configurations
Width = 150
Height = 100
ray.pos = point(0, 0, -5)
sphere.pos = point(0,0,0)
sphere.transform = identity_matrix;
fov = PI/2
We'll implement a function that calculates the direction of the ray
fucntion get_ray_direction(Width, Height, x, y, fov)
# get the aspect ratio
if Widht > Height
AspectRatio = Width / Height;
else
AspectRatio = Width / Height;
# compute the world y coordinate
world_x = (2 * ((x + 0.5) / Width) - 1) * tan(fov / 2 * M_PI / 180) * AspectRatio;
# compute the world x coordinate
world_y = (1 - 2 * ((y + 0.5) / Height) * tan(fov / 2 * M_PI / 180);
# describe the point that the ray will target
vector position = point(world_x, world_y, 0)
# get ray direction
return normalize(ray_dir - ray_origin);
end function
After generating rays we'll see if they intersect with a sphere.
We know the equation of a line:
P = O + tD
Where :
- P: a point on the line
- O: the origin on the line
- t: the scaler just think of it a number
- D: the direction of the line
We also know the equation of a sphere:
(x - a)2 + (y - b)2 + (z - c)2 = r2
Where:
- a,b,c: the position of the sphere
- x,y,z: a point from the sphere
- r: the raduis of the shpere
Assuming that the sphere postion is on 0,0,0.
(x - 0)2 + (y - 0)2 + (z - 0)2 = r2 x2 + y2 + z2 = r2 x2 + y2 + z2 - r2 = 0
Remember that x,y,z are the cordinates of a point in the sphere so we can write it as
P2 - r2 = 0
If we replace P with it's value from the equation of a line we get:
|O + tD|2 - r2 = 0
When we expand this equation we get:
O2 + (Dt)2 + 2ODt − r2 = 0 D2t2 + 2ODt + O2 - r2 = 0
It looks like:
ax2 + bx + c = 0
Where:
- a => D2
- b => 2OD
- c => O2 - r2
So we can solve it using the Quadratic Formula
t1,2 = (-b ± √Δ) / 2a
Where:
Δ = b2 - 4ac
- if
Δ > 0
=> the ray intersects the sphere in two points - if
Δ = 0
=> the ray intersects the sphere in one points - if
Δ < 0
=> the ray does not intersects with the sphre
Now we can implement the ray sphere intersection function In pseudocode
function ray_sphere_intersection(sphere, ray) # first transform the ray by the inverse of transform matrix transformed_ray.origin = ray.pos * sphere.transform transformed_ray.dir = ray.dir * sphere.transform # convert the origin of the ray to a vector origin = trasformed_ray.origin - sphere.pos # D2 a = dot(transformed_ray.dir, transformed_ray.dir) # 2OD b = 2 * dot(orgin, transformed_ray.dir) # O2 - r2 // r = 1 c = dot(origin, origin) - 1 # b2 - 4ac discriminant = b * b - 4 * a * c if (discriminant < 0) return false else return true end if end function
finaly we can draw something by merging what we learnd so far
# for each row of pixels in the canvas
for y = 0 to Width - 1
# for each pixel in the row
for x = 0 to Height - 1
ray.dir = get_ray_direction(Width, Hight, x, y, fov)
if ray_sphere_intersection(sphere, ray)
put_pixel_to_window(x, y, red)
end for
end for
You will get something like this:
Working on it...