In this project I implemented Triangle Rasterization to render triangles to the screen, antialiasing through Supersampling, Pixel and Level Sampling, transformation matrices to rotate shapes, and Barycentric Interpolation to interpolate colors and texture coordinates used in Pixel and Level Sampling. As a whole, I managed to implement parts of a graphics stack such that I can render textured shapes and objects from svg files while using antialiasing to smooth out jagged edges. This was an interesting project which helped me gain a better understanding of the topics from lecture as well as giving me experience in going from math to code. I learned about how antialiasing can be done in various ways and how different sampling techniques can change the final image output in different and sometimes subtle ways. I also learned about how easy a bug in my code can cause strange artifacts in the final image output and of course I gained more debugging experience by debugging the various bugs I created and encountered in this project.
To rasterize triangles, I first calculated the points of the bounding box that surrounds the triangle by finding the minimum and maximum x, y values from the three triangle points. Next, I iterated over each pixel in the bounding box from x_min to x_max and from y_min to y_max and added 0.5 to both the current x and y coordinates to center the test point in the middle of the pixel. Then, I called my line_test function on that test point for each of the three lines of the triangle checking to see if all the calls returned all true or all false which indicates if the test pixel is inside or on the edge of the triangle regardless of the winding order of the triangle. If the pixel is found to be inside or on the edge of the triangle, the pixel is colored in the framebuffer to make it show up on the display.
My algorithm is no worse than one that checks each sample inside the bounding box of the triangle because that is exactly what my algorithm is doing according to the steps I described above.
To supersample, I changed my rasterize_triangle function to sample multiple times within each pixel according to the sample rate and to write to the supersample buffer instead of the framebuffer. I also modified the fill_pixel function so that it writes to the supersample buffer and uses the same color for all samples of the pixels of a point or line since those did not need to be supersampled. I also made sure to resize the supersample buffer when the sampling rate is changed and when the window is resized by modifying the functions set_sample_rate and set_framebuffer_target. For pipeline modifications, I added clearing the supersample buffer to clear_buffers and modified resolve_to_framebuffer to do color averaging over the samples of each pixel to form each final pixel to be finally written to the framebuffer.
Supersampling is useful to help reduce jaggies along the sharp edges of triangles and also to reduce discontinuities because it adds averaged colored pixels along edges and inside discontinuities which creates a much smoother look to the human eye at the cost of increased computational requirements which nowadays is not a problem with modern GPUs.
|
|
|
I rotated the robot's arms up by adding rotation transforms to both arms in the svg file.
Barycentric Coordinates can be used to smoothly interpolate quantities such as colors, texture coordinates, and normal vectors to name a few over a triangle defined by three points with a starting quantity at each point. In the example above, each vertex of the triangle has an associated color and the Barycentric Coordinates are calculated for each x, y point in the triangle using proportional distance equations \(\alpha = \frac{L_{BC}(x,y)}{L_{BC}(x_A, y_A)}\), \(\beta = \frac{L_{AC}(x,y)}{L_{AC}(x_B, y_B)}\), and \(\gamma = 1 - \alpha - \beta\) where \(L\) is a function that gives the shortest distance between the point x, y and the line formed by a pair of triangle vertices and \(x_A, y_A\) and \(x_B, y_B\) are two triangle vertices. One important constraint is that the Barycentric Coordinates must add up to one. Finally, the interpolated color at a particular point on the triangle is computed by using \(C = \alpha C_A + \beta C_B + \gamma C_C\) where \(C_A\), \(C_B\), and \(C_C\) are the starting color vectors at the vertices of the triangle.
In general, pixel sampling involves sampling pixels from a texture to draw on triangles. I implemented the sampling process by calculating Barycentric Coordinates with the three given uv values mapped to each vertex of a triangle. The interpolated uv coordinate is then scaled up depending on the width and height of the texture used. Since it is possible for the interpolated uv coordinate to fall in between whole texels which makes it unclear which texel should be used, Nearest Neighbor Sampling or Bilinear Sampling is used to determine the pixel color value to be drawn on the triangle. Nearest Neighbor Sampling simply chooses the color of the closest neighboring texel to the desired texel coordinate by rounding the desired texel coordinate to the nearest whole numbers. On the other hand, Bilinear Sampling uses linear interpolation to average the colors of the four neighbors down to one color depending on the distances of the desired texel coordinate from its neighboring texels.
|
|
|
|
One large difference observed is in the comparison in between Nearest Neighbor sampling with no supersampling and Bilinear Sampling with no supersampling shows that Bilinear Sampling helps with smoothing out jaggies which is most noticeable on the edges of letters, numbers, and the lines in the center of the seal. The differences are much smaller in the 16x supersampling case between Nearest Neighbor sampling and Bilinear Sampling which can be observed in the pixel inspector which is showing how the edges of the lines in the center of the seal are smoother in the Bilinear case.
Based on the above, there will be a large difference between Nearest Neighbor and Bilinear Sampling when there are sharp edges in the texture that is being sampled. This is due to Nearest Neighbor Sampling only using one sample which leads to aliasing while Bilinear Sampling uses four samples which helps with reducing aliasing.
I implemented level sampling by calculating the continuous mipmap level D using the equation from the lecture slides. For Nearest Level Sampling, I rounded the continuous level D to the closest integer and sampled colors from that mipmap level. For Linear Level Sampling, I sampled a color from mipmap level floor(D) and mipmap level ceil(D) respectively and I calculated how far the continuous level D is from floor(D). Then the distance is input to the lerp function to average the two colors from the two different mipmap levels which has the effect of weighting the floor mipmap or ceiling mipmap color more in the final color if the continuous level D is closer to the floor mipmap level or the ceiling mipmap level.
Speed: Pixel Sampling and Level Sampling will be faster compared to Supersampling with high number of samples per pixel because Pixel Sampling and Level Sampling use less and a constant number of samples while supersampling becomes slower and slower as the number of samples per pixel increases.
Memory Usage: Pixel Sampling uses less memory because it is only sampling from one texture. Level Sampling uses more memory because mipmaps have to be generated which have an upper bound memory usage of 4/3x the original texture size according to comments on the lecture slides. Supersampling could use less or more memory depending on if a supersampling buffer is used and the size of the supersampling buffer. The size of the supersampling buffer is proportional to how many samples are desired and the framebuffer size.
Antialiasing Power: Pixel Sampling from what I observed seems to have the least amount of antialiasing power due to using a constant number of samples (One for Nearest Neighbor and four for Bilinear Sampling). Level Sampling has higher antialiasing power because its goal is to sample from a texture whose size is suitable for the size of the object being rendered on the screen. Supersampling could have the highest antialiasing power if a large enough amount of samples are used but it would take a large amount of time to render the scene.
|
|
|
|
Wall texture from https://www.publicdomainpictures.net/en/view-image.php?image=24533&picture=stone-wall-texture