NOTE: You will be working entirely in world coordinates 👍
NOTE: The position along the ray is vec3 p = o + t * dir;
- vec3 o; is the ray origin (aka camera/renderer origin)
- double t; is the distance along the ray
- vec3 dir; is the direction of the ray
a. (80 pts) software ray tracer
Implement a software ray tracer.
- An attempt will set up the rays properly (pressing 'z' on the keyboard will show them).
- A bare bones solution will also draw the triangles in the right place (say, in blue).
- A better solution will also draw them in the right color (not considering shadows or lighting).
- A solid solution will also get overlap correct.
- A wonderful solution will also have basic shadows.
- A wow solution will also include Phong lighting (or something vaguely similar).
- You can also at that point throw in multiple lights, reflective surfaces, etc.
NOTE: Your grade will be based on how many of these features you include; A bare bones solution will score most of the points.
Recommended development trajectory:
- (math and code in class) set up the rays--origin and direction--properly
- (math in class) intersect the ray with a triangle by solving a 4x4-matrix-vector equation; color pixel appropriate color if inside
- use a for loop to intersect with all the triangles
- get overlap correct by setting the color based on the closest hit
- implement shadows (i moved some code out into a function called cast_ray(...), which i also used to cast shadow rays)
- implement lighting (this actually isn't too bad once you've got the rest)
✨ NOTE: I shipped the code with a little snippet of gl_*(...) code that draws the rays.
You can move this around; edit it; etc. I found it very useful to draw _only the rays that hit_, and to draw them from $\mathbf{o}$ to $\mathbf{p}$.
✨ HINT (iterating over the mesh)
int num_triangles = mesh->num_vertices / 3;
for (int triangle_i = 0; triangle_i < num_triangles; ++triangle_i) {
// (a, b, c) -- triangle vertex positions in world coordinates
// color_* -- vertex colors
// n -- unit vector normal to triangle
vec3 a, b, c;
vec3 color_a, color_b, color_c;
vec3 n;
{
a = mesh->vertex_positions[3 * triangle_i + 0];
b = mesh->vertex_positions[3 * triangle_i + 1];
c = mesh->vertex_positions[3 * triangle_i + 2];
vec3 e1 = b - a;
vec3 e2 = c - a;
n = normalized(cross(e1, e2));
if (mesh->vertex_colors != NULL) {
color_a = mesh->vertex_colors[3 * triangle_i + 0];
color_b = mesh->vertex_colors[3 * triangle_i + 1];
color_c = mesh->vertex_colors[3 * triangle_i + 2];
} else {
vec3 fallback_color = V3(.5, .5, .5) + .5 * n;
color_a = fallback_color;
color_b = fallback_color;
color_c = fallback_color;
}
}
}✨ HINT (ray-triangle intersection)
Consider ray with origin $\mathbf{o}$ and direction $\mathbf{dir}$.
Let's intersect the ray with some triangle $(\mathbf{a}, \mathbf{b}, \mathbf{c})$.
Call the (possible) intersection point $\mathbf{p}$ and get pumped.
Our approach will be to express $\mathbf{p}$ in two different ways and set them equal.
First, we can write down the definition of a ray $$ \begin{cases} \mathbf{p} = \mathbf{o} + t * \mathbf{dir} \end{cases} $$ where we can also write the condition for the $\mathbf{p}$ being in front of the camera (not behind it) $$ \begin{cases} t > 0 \end{cases} $$
Second, we can write down the definition barycentric coordinates $$ \begin{cases} \mathbf{p} = \alpha\mathbf{a} + \beta\mathbf{b} + \gamma\mathbf{c}\\ \alpha + \beta + \gamma = 1 \end{cases} $$ where we can also write the condition for $\mathbf{p}$ being inside the triangle $$ \begin{cases} \alpha,\beta,\gamma > 0 \end{cases} $$
Equating the different equations for $\mathbf{p}$ gives us the following system of equations
$$ \begin{cases} \mathbf{o} + t * \mathbf{dir} = \alpha\mathbf{a} + \beta\mathbf{b} + \gamma\mathbf{c}\\ \alpha + \beta + \gamma = 1 \end{cases} $$
Taking on both conditions (in front of camera; inside of triangle) gives us the condition for a "hit"
$$ \begin{cases} \alpha,\beta,\gamma,t > 0 \end{cases} $$
You can solve the system any way you like, but since we're not so concerned with speed, let's just bash it with a matrix solve.
First, some algebra.
$$ \begin{cases} \alpha\mathbf{a} + \beta\mathbf{b} + \gamma\mathbf{c} - t * \mathbf{dir} = \mathbf{o}\\ \alpha + \beta + \gamma = 1 \end{cases} $$
You are the One, Neo.
$$ \begin{bmatrix} \mathbf{a} & \mathbf{b} & \mathbf{c} & -\mathbf{dir} \\ 1 & 1 & 1 & 0 \end{bmatrix} \begin{bmatrix} \alpha \\ \beta \\ \gamma \\ t \end{bmatrix} = \begin{bmatrix} \mathbf{o} \\ 1 \end{bmatrix} $$
Solving that system of equations gives us $\alpha,\beta,\gamma,t$.
If they pass the condition for a hit, then we have a hit! :)
And in the case of a hit, we can finally compute $\mathbf{p}$ in world coordinates as, for example, $\mathbf{p} = \mathbf{o} + t * \mathbf{dir}$.
(NOTE: You won't need p until you implement shadows)
Wow.
🐄
✨ HINT (closest hit)
foreach (pixel (i, j)) {
bool hit_at_least_one_triangle = false;
double min_t = INFINITY;
vec3 color = {};
{
foreach (triangle) {
...
// calculate alpha, beta, gamma, t
bool hit = ...;
if (hit) {
hit_at_least_one_triangle = true;
if (t < min_t) {
min_t = t; // * don't forget this
color = ...;
}
}
}
}
if (hit_at_least_one_triangle) {
hw8a_set_pixel(i, j, color);
}
}b. (20 pts) ray marching distance shaders (Creative Coding)
Ray march a scene of your own design.
✨ Include a _brief_ writeup of your goal and method in your submission comment on glow.
You may copy and paste code from https://iquilezles.org/articles/distfunctions/.
You may browse shadertoy for inspiration, but you may _not_ copy and paste code (apply the 50 ft rule applies).
If you are heavily inspired by a shader, please link it in your glow comment.
In order to prepare you for the upcoming final project, a bare bones submission will _not_ score full points.
You will be graded on:
- 50% - The clarity (not length) of your glow comment
- 25% - The technical depth of your implementation
- 25% - The creativity of your scene