Overview
-----
You will create a rasterizer for drawing scenes composed of 2D polygons. This
rasterizer will be extended upon in the next homework assignment wherein you
will write a camera class to project 3D polygons into 2D space and rasterize
them. You will code your own classes using C++ and will have your first exposure
to the Qt code libraries.
Supplied Code
---------
Click here to access the homework's Github repository.
We will provide you with a basic Qt GUI that is capable of displaying QImages.
Make sure you read the code comments that explain how it works. Unlike the
previous assignment, you may make any changes you want to the code we give you.
Please note that the coordinate system of the images has X increasing from left
to right and Y increasing from top to bottom.
Additionally, we have provided a robust library of linear algebra functions
called GLM (OpenGL Mathematics) to use for your transformation needs. The
library you wrote for homework 1 was designed to be very similar in format to
the classes and functions provided by GLM, but there are some differences in
inputs to the transformation matrix functions GLM provides.
You can find the
(admittedly difficult to navigate) documentation for GLM here.
Conceptual Questions (10 points, Due Friday, September 15 at 11:59 PM)
-------------
Before you begin the programming portion of this homework assignment, read and
answer the following conceptual questions. Your answers should be submitted as a
plaintext (`.txt`) file to the dashboard.
* (5 pts) What are the three different configuration cases when determining the
intersection of a pixel row with a triangle edge? In all three cases, what
simple criterion can one use to determine whether the triangle edge overlaps
the pixel row at all?
* (5 pts) How might one use barycentric interpolation to determine whether or not a
given point in space lies within the bounds of a triangle? In rasterization,
would this method be more efficient than row bound checking for determining
which pixels lie within a triangle? Why or why not?
Help Log (5 points)
-------
Maintain a log of all help you receive and resources you use. Make sure the date
and time, the names of everyone you work with or get help from, and every URL
you use, except as noted in the collaboration policy. Also briefly log your
question, bug or the topic you were looking up/discussing. Ideally, you should
also the answer to your question or solution to your bug. This will help you
learn and provide a useful reference for future assignments and exams. This also
helps us know if there is a topic that people are finding difficult.
You may submit your help log as an ASCII (plain) text file or as a PDF. Refer to the Policies section of the course web site for more specifications.
Code Requirements (85 points, Due Wednesday, September 20 at 11:19 PM)
-------
Classes you will edit:
* Rasterizer
* Polygon
Classes you won't need to edit (but may edit if you so choose)
* Triangle
* Vertex
* MainWindow
* tinyobj
The `Rasterizer` class contains a function named `RenderScene()` that returns a
512x512 `QImage` to be displayed in the GUI. The `QImage` should use the `RGB_32` format.
You will have to implement several functions and classes to properly output a
rasterized image. It is important to note that the features listed below do
not necessarily have to be completed in the order they are written. When you
initialize your `QImage`, make sure to populate its pixels with black.
For reference images of expected results, refer to the bottom of this page.
### Convex polygon triangulation (15 points) ###
We have provided you with a `Polygon` class and a function in `MainWindow` that
creates and stores polygons based on an input JSON file. You will have to write
code for the `Polygon` class's `Triangulate()` function that populates the
`Polygon`'s list of `Triangle`s. You may assume that the vertices listed for
`Polygon`s in the input JSON files are always in counter-clockwise order, and
that they always define a convex polygon.
If you wish to test your rendering functionality on triangles without
implementing triangulation, we have provided you with a function that
automatically creates an equilateral triangle Polygon and sets it in the
scene's list of `Polygon`s. You can access this from the Scenes dropdown menu
in the GUI.
### Line segments (20 points) ###
Create a class that represents a 2D line segment, to be used when determining
which pixels on a row a triangle overlaps. You may choose to make it in a new
header file, or add it to an existing one. It should contain the following
member variables:
* Two vectors representing the segment's endpoints. We recommend making these
vectors at least three-dimensional, since the Z coordinate of the lines will
be important.
* A variable (or variables) for storing the slope of the line segment. This will
help you to determine which of the three configuration cases the segment falls
into without having to compute the slope more than once.
Your line segment class should also implement the following functions:
* A constructor that takes in both endpoints of the line segment. The
constructor should make use of an initialization list to assign values to __all__
member variables.
* A function that computes the x-intersection of the line segment with a
horizontal line based on the horizontal line's y-coordinate. This function
should return a boolean indicating whether or not the lines intersect at all,
while using a pointer as a function input to write the x-intersection.
### Bounding boxes (10 points) ###
Create a way to compute and store the 2D axis-aligned bounding box of a
triangle. You will use these bounding boxes to improve the efficiency of
rendering each triangle. Rather than testing every single row of the screen
against a triangle, you will only test the screen rows contained within the
triangle's bounding box.
### Triangle rendering (15 points) ###
The `Rasterizer` class contains a function called `RenderScene()`. Using the
code you wrote for the features listed above, you should be able to draw
triangles on the QImage set up in this function using pixel row overlap testing.
At this point, you may draw the triangles using a single color for testing.
However, once you complete the next requirement you will be able to draw your
shapes properly with interpolated vertex colors.
### Barycentric interpolation (15 points) ###
Implement a function that, for a given `Triangle` and point within the `Triangle`,
returns the barycentric influence that each vertex exerts on any attribute
interpolated at the input point. Note that the return value is really three
distinct values, so returning them as a vector is the most straightforward
method.
You will use this interpolation function to properly color the pixels that lie
within a triangle. Each pixel should have a blended color based on the color of
each vertex of the triangle. The weighting of the blending will be determined by
this barycentric interpolation function.
### Painter's algorithm (10 points) ###
In the scenes we provide you, each polygon has the same Z coordinate for each of
its vertices. Given this feature, use the Painter's Algorithm to sort each
polygon by depth, back to front, then draw the polygons in sorted order.
In computer graphics, we treat lower depth values as being closer to the camera,
so you should sort your polygons from high to low. We recommend you use
`std::sort` to perform this operation, which means you'd have to implement
a comparator function for the `Polygon` or `Triangle` class. A comparator
function takes in two `const` references to the objects it is supposed to
compare and returns a boolean that states whether or not the first of the
two inputs is "less than" the second. Determining the meaning of "less
than" depends on the objects being compared; in the case of two `Polygons`, one
`Polygon` is "less than" another if its Z coordinate is smaller.
Coding Style (10 points)
-------
We will provide you with feedback on the organization and clarity of the code
you have written for this assignment. Please refer to our course style guide as
you implement your rasterizer. When coding this assignment, consider places you
might bundle code into helper functions to improve the human readability of your
program. Also remember: when you find yourself copying and pasting your own code,
think about how you could reduce that code with a loop or helper function.
Extra Credit (Maximum 30 points)
---------
If you implement any extra credit, please document it your `readme.txt`, as
described in the Submission section at the bottom of this page.
### Custom scene (5 points) ###
Write a JSON scene file that, when rendered, displays an interesting scene.
For example, you could create an environment with characters interacting.
### Anti-aliasing (10 - 20 points) ###
You've probably noticed that the diagonal edges of your polygons are jagged in
appearance. This is due to aliasing, which is the lack of sufficient sampling of
a signal (in this case, the "signal" is the polygon being rendered). A simple
way to counteract this aliasing is to sample the image more frequently. In a
rasterizer, this effectively means rendering the image at a higher resolution
and then scaling the image down, treating every block of NxN pixels as a single
pixel in the final image. When downscaling the image, one averages the colors
of each NxN block into a single color. For example, the image below has been
rendered at 4x its actual resolution then scaled down to a 512x512 image.
Remember that the scene files we provide assume the images you render will be
512x512 in size. If you render an image at 1024x1024 to anti-alias it, you will
have to treat each pixel as a 0.5x0.5 square or every polygon as being 2x its
actual size. To earn 10 points, perform anti-aliasing by just rendering your
scene at one higher resolution then scaling it down. To earn 20 points, add
options to your Qt GUI that allow the user to perform anti-aliasing at different
levels of scaling, such as 1x, 4x, 9x, or 16x. If you implement this option,
your code should not have if-else statements to handle the anti-aliasing.
No AA
4x AA
### Line rendering (15 points) ###
Add an option to your GUI (e.g. a dropdown menu choice) that allows the user to
render all the triangles in your scene as wireframes rather than solid faces.
This means only coloring pixels that each edge of the triangle overlaps, and
using linear interpolation to color each pixel on the line. If you're interested
in efficient rendering of lines, you might look into Bresenham's line algorithm.
### Concave polygon triangulation (25 points) ###
Given a custom polygon JSON scene, determine whether or not each polygon is
concave. If a polygon is concave, divide it up into convex subcomponents then
triangulate them. You may use whatever method of division you wish.
Reference images
-------
Below are the expected results of rendering certain scenes. Use these to make
sure you've properly implemented your functions.
If you have some space between the triangles that compose your polygons, try
rounding your left intersections down to the nearest pixel and your right
intersections up to the nearest pixel.
equilateral_triangle.json
two_polygons.json
regular_pentagon.json
pentagons.json
Submission
--------
Make sure your homework compiles in the Moore labs. We will deduct points from
any homework that does not compile, and possibly give a 0 grade if the compile
errors are numerous. __Along with your code, make sure to include a `readme.txt`
file at the topmost level of your repository that includes any notes on your
implementation you'd like us to know, along with any extra credit you've
implemented.__ We will grade the code you have pushed to your GitHub
repository, so make sure that you have correctly committed all of your files!
Once you have pushed your finished project to GitHub, submit a link to your
commit through the course dashboard. If you click on the Commits tab of your
repository on Github, you will be brought to a list of commits you've made.
Simply click on the one you wish for us to grade, then copy and paste the URL
of the page into your text file.