🚨 please pull a fresh copy of the codebase before starting this homework!--it has the starter code in main.cpp :) 🗣️ 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 - only one of you is required to submit on glow (please add a glow comment saying who your partner is)
goals. - understand rasterization
highly-recommended reading. rasterization let's draw a triangle mesh on a screen; no OpenGL required :) consider a single triangle with model-space vertex positions $(\mathbf{a}^\text{model}, \mathbf{b}^\text{model}, \mathbf{c}^\text{model})$ and corresponding vertex colors $(\mathbf{color}_a, \mathbf{color}_b, \mathbf{color}_c)$ note that our notation here is a little different than you may be used to $\mathbf{a}^\text{model} = \begin{bmatrix}a_x^\text{model} \\ a_y^\text{model} \\ a_z^\text{model}\end{bmatrix}$ is a point $\mathbf{a}$ written in model coordinates $\mathbf{color}_a$ is the vertex color of vertex $\mathbf{a}$ you know how to transform vertex positions into various coordinate systems, e.g., $(\mathbf{a}^\text{world}, \mathbf{b}^\text{world}, \mathbf{c}^\text{world})$, and $(\mathbf{a}^\text{camera}, \mathbf{b}^\text{camera}, \mathbf{c}^\text{camera})$, $(\mathbf{a}^\text{NDC}, \mathbf{b}^\text{NDC}, \mathbf{c}^\text{NDC})$ now let's talk pixels consider the $(i, j)$-th pixel ($i$-th row, $j$-th column) we can convert this pixel's position into NDC, which we denote $\mathbf{p}^\text{NDC}$ if $\mathbf{p}^\text{NDC}$ lies inside the NDC projection of the triangle, then we should consider drawing it!
NDC from pixels
inside test
depth test
screen-space interpolation it is tempting to use these barycentric weights (which were found in NDC) directly, to interpolate both $z^\text{camera}$ and $\mathbf{color}$ $z^\text{camera} = \alpha{a_z^\text{camera}} + \beta{b_z^\text{camera}} + \gamma{c_z^\text{camera}}$ $\mathbf{color} = \alpha{\mathbf{color}_a} + \beta{\mathbf{color}_b} + \gamma{\mathbf{color}_c}$ unfortunately, this approach is incorrect (but feel free to use it on this homework, as for what we're doing it looks okay) here is a derivation of how to interpolate properly, the punchline of which is $z^\text{camera} = \frac{1}{\alpha\frac{1}{a_z^\text{camera}} + \beta\frac{1}{b_z^\text{camera}} + \gamma\frac{1}{c_z^\text{camera}}}$ $\mathbf{color} = \frac{{\alpha\frac{\mathbf{color}_a}{a_z^\text{camera}} + \beta\frac{\mathbf{color}_b}{b_z^\text{camera}} + \gamma\frac{\mathbf{color}_c}{c_z^\text{camera}}}}{\frac{1}{z^\text{camera}}}$
a. software rasterizer note: this homework is hard.
HINT (what to expect)
- a correct solution should be relatively short; my real-time bunny solution torasterize(...)
is only about 60 lines
- 🚨 mind your coordinate systems (what should be in cmaera space, NDC, etc.?)
- ✨ transformPoint(...)
is your friend :)
HINT (possibly useful functions and fields)
inverse(...)
INVERSE_LERP(...)
LINEAR_REMAP(...)
MAX(...)
MIN(...)
real(...) // just a cast
texture_set_pixel(...)
texture_get_pixel(...)
transformPoint(...)
note: we will be working entirely inside of the
rasterize(...)
function
find your way on in there now
note that it is totally empty
ohno D:
on the bright side i've already got the color and depth buffers all set up for you, and everything (matrices, mesh, etc.) is already being passed to rasterize(...)
as an argument :)
0. get familiar with the texture functions
- try calling texture_set_pixel(color_buffer, 20, 5, monokai.orange, 0.5);
inside of the rasterize(...)
function
-- this sets the $(i, j)$-th pixel in the color buffer to be 50%-transparent orange
--- $i$ refers to the $i$-th *row* of pixels (i.e., the pixel's $y$-coordinate in pixel coordinates)
--- $j$ refers to the $j$-th *column* of pixels (i.e., the pixel's $x$-coordinate in pixel coordinates)
--- by zooming in carefully, verify that this pixel is in fact transparent orange
- try calling texture_set_pixel(depth_buffer, 20, 5, 1.0);
inside of the rasterize(...)
function
-- this sets the $(i, j)$-th pixel in the depth buffer to be full red (max depth)
-- verify you can see it (press Z to toggle viewing the color buffer and the depth buffer)
- try calling the following code to get (and print) the depth value you just set
real current_depth;
texture_get_pixel(depth_buffer, 20, 5, ¤t_depth);
printf("%lf\n", current_depth);
i. clear the color buffer and depth buffer (more details in the code)
- NOTE: please don't worry about doing this fast; pixel by pixel is fine :)
- for simplicity, our "screen" is a square (same number of rows and columns of pixels)
-- to get its side length, write, e.g., int side_length_in_pixels = color_buffer->width;
- please run the code to verify you did this correctly
- press Z to toggle viewing the color buffer and the depth buffer
-- a cleared color buffer could be 50%-transparent white
-- a cleared depth buffer should be full red; (min depth is 0.0 (black); max depth is 1.0 (red))
ii. rasterize the triangles without worrying about color (e.g., draw them all blue)
- this would be a good time to refresh yourself on the IndexedTriangleMesh3D
-- note you are passed IndexedTriangleMesh3D *mesh
, which is a pointer to a single mesh
- NOTE: you are welcome to malloc and such, but my solution doesn't happen to
iii. add color (according to mesh->vertex_colors)
iv. implement depth testing (z buffering)
- the near and far clip planes are at $z_\text{near}$ and $z_\text{far}$ respectively
- verify that the overlap in the example mesh is correct
-- again, you can press Z to toggle viewing the color buffer and the depth buffer
- NOTE: accessing the depth buffer is somewhat annoying
// read out an entry of a (1 channel) depth buffer
real current_depth;
texture_get_pixel(depth_buffer, i, j, ¤t_depth);
v. get the bunny drawing (slowly) (more details in the code)
- the bunny mesh doesn't include vertex_colors by default (i.e., (mesh->vertex_colors == NULL)
will be true)
-- instead, please use the formula $(0.5, 0.5, 0.5)^T + 0.5 * \hat{\mathbf{n}}$ to calculate a vertex's color, where $\hat{\mathbf{n}}$ is that vertex's normal in model coordinates
- make sure you take into account the model matrix $\mathbf{M}$ if you ignored it before (for the first example mesh, it was the identity)
vi. make the rasterizer FAST(er)
- real-time should be fairly easy (ish) :)
vii. creative coding (choose 1+ of the following or equivalent)
- add a checkbox to invert the colors of the final image you rasterize into the color buffer
- add a checkbox to grayscale the final image you rasterize into the color buffer (if you want to get fancy, feel free to, e.g., convert to HSV)
☁ (!! please grab a fresh copy of cow before attempting) add a checkbox to blur the final image you rasterize into the color buffer (google "blur kernel"--you may want to make another texture)
🌟 (!! please grab a fresh copy of cow before attempting) add a checkbox to do edge detection (using a kernel) and then composite those edges onto the final image (so the bunny appears *outlined*) :)
✂. clipping
- hack together an example mesh similar to the "external triangle" example in this blog post and verify it breaks our rasterizer :(
- fix it (i.e., add some hacky clipping inside of rasterize(...)
, in whatever coordiante system you like) :)