<- back
hw05 help hours | codebase | docs | notes | glow

🚨 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 to rasterize(...) 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, &current_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, &current_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) :)