🚨 please pull a fresh copy of the codebase before starting this homework! 🍏 if this homework runs slow, please reduce your window size to speed things up 🗣️ you are highly encouraged to collaborate on homework provided you follow the spirit of the 50 ft rule 👥 additionally, for this lab you may optionally choose to work with a partner 🦍 this homework is tough 📺 sample solution
goals. - get a first taste of ray tracing - get a first taste of ray marching distance shaders
optional. math review here is a quick linear algebra crash course
lecture summary. a ray a ray is parameterized by the equation $\mathbf{p}(t) = \mathbf{o} + t\mathbf{d},$ • $\mathbf{o}$ is the ray's origin (e.g, the camera position/origin) • $\mathbf{d}$ is a ray's direction (a vector pointing from, e.g., the camera origin to a particular pixel) • $t$ is "distance," i.e. how far we've travelled along the ray; note that for a ray, $t > 0$
lecture summary. basic ray tracing for each given pixel, a primary ray is shot (cast, traced, etc.) from the camera origin toward the pixel - please see the lecture slides for help calculating the ray's direction $\mathbf{d}$ in code if the ray hits the mesh, then a shadow ray is shot from the position of the first hit to the position of the light source - if the shadow ray makes it to the light source without hitting anything else, then the surface patch at the hit position is lit - otherwise it is in shadow lit surface patches get the full Blinn-Phong lighting model surface patches in shadow get only the ambient term - for this assignment, the ambient color should be something like
0.4 * color
, where color
is the barycentric interpolation of the hit triangle's vertex colors (NOT, e.g., vec3(0.4)
like we used in the shader assignment)
-- note: everything is in world coordinates, so $\mathbf{color} = \alpha{\mathbf{color}_a} + \beta{\mathbf{color}_b} + \gamma{\mathbf{color}_c}$ is perfectly correct
lecture summary. ray-triangle intersection Consider ray with origin $\mathbf{o}$ and direction $\mathbf{d}$. Let's intersect the ray with some triangle $(\mathbf{a}, \mathbf{b}, \mathbf{c})$. For simplicity, say all these quantities are in world coordinates. Call the (possible) intersection point $\mathbf{p}$ and get pumped. Our approach is 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{d} \end{cases},$$and 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 of barycentric coordinates $$\begin{cases} \mathbf{p} = \alpha\mathbf{a} + \beta\mathbf{b} + \gamma\mathbf{c}\\ \alpha + \beta + \gamma = 1,\end{cases}$$and 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{d} = \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}$$ All that remains is to find $\alpha,\beta,\gamma,t$. Rearranging the system of equations yields $$\begin{cases} \alpha\mathbf{a} + \beta\mathbf{b} + \gamma\mathbf{c} - t\mathbf{d} = \mathbf{o}\\ \alpha + \beta + \gamma = 1 \end{cases}. \tag{1}$$ We will solve Equation 1 for $\alpha,\beta,\gamma,t.$ For a change, let's bash with a matrix solve :) Using our handy dandy linear algebra trick, the first equation in Equation 1 becomes $$\begin{bmatrix} \mathbf{a} & \mathbf{b} & \mathbf{c} & -\mathbf{d}\end{bmatrix} \begin{bmatrix} \alpha \\ \beta \\ \gamma \\ t \end{bmatrix} = \begin{bmatrix} \mathbf{o} \end{bmatrix},$$onto which we tack on the second equation to yield our final system $$\begin{bmatrix} \mathbf{a} & \mathbf{b} & \mathbf{c} & -\mathbf{d} \\ 1 & 1 & 1 & 0 \end{bmatrix} \begin{bmatrix} \alpha \\ \beta \\ \gamma \\ t \end{bmatrix} = \begin{bmatrix} \mathbf{o} \\ 1 \end{bmatrix}.$$You are the One, Neo
a. implement a software raytracer - this hw is written to follow a similar structure to the rasterization homework :) - initially, we will be working entirely inside of the
raytrace(...)
function
-- eventually, you will want to add your own function immediately above it that casts a single ray
i. set up the rays properly
- add a checkbox to draw them using eso_begin(PV_for_debug_drawing_rays, SOUP_LINES, 1.0)
-- once you're confident the rays are set up right, you're welcome to delete this functionality
(hint / spoilers) drawing rays with eso
Here's a way to do it with only a single call toeso_begin(...)
and eso_end(...)
.
vec3 o = ...
eso_begin(PV_for_debug_drawing_rays, SOUP_LINES);
eso_color(monokai.white);
for (int i = 0; i < side_length_in_pixels; ++i) {
for (int j = 0; j < side_length_in_pixels; ++j) {
vec3 d = ...
eso_vertex(o);
eso_vertex(o + d);
}
}
eso_end();
(readme) sample code to iterate over the mesh
for (int tri = 0; tri < mesh->num_triangles; ++tri) {
vec3 a = mesh->vertex_positions[mesh->triangle_indices[tri][0]];
vec3 b = mesh->vertex_positions[mesh->triangle_indices[tri][1]];
vec3 c = mesh->vertex_positions[mesh->triangle_indices[tri][2]];
...
}
(0.5 * color)
if the pixel is in shadow
-- a pixel is in shadow if its shadow raw hits a triangle OR its normal is facing away from the light
--- (two vectors face away from each other if their dot product is negative)
--- the "its normal is facing away from the light" check will no longer being necessary if you properly implement lighting in vi.
- your code should NOT contain large amounts of repetition (i.e., no copy and pasting 50 lines)
-- first, make it work; then, let a function (and a struct) spring forth organically! 🌱
(hint / spoilers) my ray casting API
struct CastRayHit {
real t; // t at the hit point; recall ray equation is p(t) = o + (t * d)
int tri; // index of triangle that was hit; -1 if no triangles were hit
bool hit_any_triangles() { return (tri != -1); }
vec3 p; // position of hitpoint
vec3 n; // normal at hitpoint
vec3 color; // color at hitpoint
// barycentric coordinates of hit
real alpha;
real beta;
real gamma;
};
CastRayHit cast_ray(IndexedTriangleMesh3D *mesh, vec3 o, vec3 d) {
CastRayHit hit = {};
hit.t = INFINITY;
hit.tri = -1;
for (int tri = 0; tri < mesh->num_triangles; ++tri) {
...
}
return hit;
}
if ((tri == mesh->num_triangles - 2) || (tri == mesh->num_triangles - 1)) { ... }
-- (these are the floor for the teapot scene)
- you'll need to pick a color for the sky
b. (Creative Coding) ray march something nice - initially, we will be working entirely inside of the
march(...)
function inside of hw9b.frag
-- i put the shader in a separate file to make your IDE happy :)
- references!
-- you may copy and paste code from https://iquilezles.org/articles/distfunctions/.
-- optional reading: https://michaelwalczyk.com/blog-ray-marching.html
-- you may browse shadertoy for inspiration, but you may _not_ copy and paste code (the 50 ft rule applies)
--- if you are heavily inspired by a shader, please link it in your glow comment
- please include a one-two sentence writeup explaining what you did in your glow comment
😎. add blinn-phong lighting (or something similar)
- you will need to google around for how to approximate the surface normal using finite differences ohwowsofun :D