//****************************************************************************//
//************* Intro to Rasterization - January 30th, 2019 *****************//
//**************************************************************************//

- So, waaaaaaaaaaaaaay back before the non-snowday (the *shudder* before times), we were talking about how to write out equations of line, implicitly and parametrically

- Now, let's see some code for actually drawing a line:

    void line(x0, y0, x1, y1):
        dx = x1 - x0
        dy = y1 - y0
        // this is the maximum number of pixel "steps" we have to take to get to
        // the end, not the actual line's length
        length = max(float_abs(dx), float_abs(dy))
        xinc = (float)dx / length
        yinc = (float)dy / length

        x = x0
        y = y0
        for (i = 0; i < length; i++):
            //since we can't put a pixel in a fractional location, just round
            // to the nearest pixel
            gtWritePixel(round(x), round(y), someColor)
            x += xinc
            y += yinc

    - So, if we run this code on (say) a 6x6 grid (the smallest monitor in the world), and we try to draw a line from (1,3) to (5,5):
        - We end up with the following initial values:
            - dx = 5 - 1 = 4
            - dy = 5 - 3 = 2
            - length = 4
            - xinc = 4/4 = 1
            - yinc = 2/4 = 0.5
        - This leads to a sort of stair-stepping line
            - "If we only have black and white pixels to deal with, this line will look pretty jaggedy, or ALIASED - fortunately, almost all displays these days let us have a variety of shades, which means we can soften this line to look smoother using ANTI-ALIASING techniques"
                - We might not get to this in class, but it's in the textbook if you're interested
- So, what kind of line equation is this function using? It's parametric!
    - ...but where's the "t" value here? Well, it's "i"! It's a bit hidden, but we're basically using our incremented i value in place of t
    - There are more complicated line drawing algorithms if you want to use them, but we won't really bother with them in this class

- Now, here's some context for where we're going in this class:
    - On the screen right now is a "shutterbug" image of 3 different orthographic views of a wireframe scene (lines only) - it looks pretty primitive, and is similar to what we're trying to do in class right now
    - If we jump to perspective projection, it'll look a bit more jumbled (since it's only lines, and we can see through them!), but the perspective is clearly more lifelike now
        - If we jump to the next image, we see that there are now "hidden surfaces" in the rendering - objects that are behind another object are no longer drawn!
        - Then, the next image has surfaces on the objects - there's COLOR on the objects, but there's no shading, so the image looks pretty flat
        - In the next image, there's some basic shading, and now it looks like the objects have depths!
            - Still further on, there are multiple light sources in the scene, and then objects casting shadows and having reflections, and then objects having textures and images!
- "So, that's where we're headed in the not-too-distant future for this class - but for now, let's get back to the present and keep working towards this stuff!"

- So, let's talk about our next step: POLYGON RASTERIZATION!
    - Let's start off with the humble rectangle: how do we fill in a rectangle with a solid color on our screen?
        - Let's assume we already know where the corners of the rectangle are going to be drawn on our screen; (xmin, ymin) is the lower-left corner, (xmax, ymax) is 1 pixel BEFORE the top-right row/column
            - Why stop 1 pixel early? Because if 2 objects were right next to each other, they would overlap and "fight" over who fills in the column/row of pixels where they're touching; if we do it this way, then who "owns" which row is now well-defined (it's just taken by the left/bottom row of the object to the right/top)
                - This isn't a big problem now, but it becomes a much bigger issue when dealing with transparency, or techniques like "XOR drawing"
        - For the time being, let's also assume our rectangle isn't rotated, and is lying flat against the screen
    - So, with that knowledge, filling in the rectangle on our grid would look something like this:

        for (y = ymin; y < ymax; y++):
            for (x = xmin; x < xmax; x++):
                gtWritePixel(x, y, color)

- So, doing this with rectangles isn't too difficult - but what about for general polygons?
    - As I'm sure you remember, a POLYGON is just some region of space that's enclosed by a set of straight lines
        - These can be CONVEX (i.e. any infinitely-long line can intersect the shape at at most 2 points) or CONCAVE (there's a "cave"/inset in the object where they can be hit multiple times)
            - "Concave polygons tend to be the more ugl-...well, I shouldn't say ugly. They're, um, different."
        - Some graphics libraries will let you draw polygons with "features," i.e. holes (like a triangle with a star-shaped hole in the middle) - these are tricky to deal with if you have to write the drawing functions from scratch
            - This isn't really a focus of this course, but it's something to be aware of out there in the wild 
    - To rasterize ANY polygon, we're going to fill in one "scanline" (i.e. row of pixels) of the polygon at a time, from bottom to top, maxing sure to fill between INTERSECTIONS
        - What intersections? We'll get there in a second, but it's basically where the lines of the polygon intersect the edges of our pixels in the grid
        - As for bottom-to-top, it's just an arbitrary convention that's become pretty standard
    - So, if we had some rotated triangle overlaid on our tiny monitor grid again:
        - We find the points in the row where the lines are intersecting with the pixel grid
        - Fill in all the pixels in the row that're in-between those points
        - Move up to the next row
    - In pseudocode:

        for (y = ymin; y < ymax; y++):
            find x intersections with polygon edges
            sort intersections on x-values
            fill between pairs of intersections (from left-to-right)

    - ...of course, there're some unanswered questions here, including a pretty big one: how do we find these intersection points quickly?
        - Fortunately, it's pretty easy once we have the first intersection point
            - For now, suppose we know where the leftmost intersection point in the row is
            - We know where the closest point of the POLYGON (not the intersection) to our left is, and the next point of the polygon to the right; knowing this, we can make a right triangle (3rd point is just (x1, y0)
                - If we then scale that triangle so its height is 1 pixel, we'll have a similar triangle whose base's length is how much we need to step to the right for every scanrow (i.e. 1 pixel) we go up to stay on the polygon's edge!
                - So, if we know where 1 point is, we can figure out where the edges of the polygon are really easily/quickly! Go efficiency!

- We'll keep talking about rasterization on Friday - 'til then, keep working on your projects, etc.