As I mentioned in class on Wednesday, in order to render cool shaded versions of our animated shapes, we are going to need to paint each triangle into the image, while interpolating color and perspective z.
I know that some of your polygons have four vertices. Fortunately you can always convert a four sided polygon to two triangles at the moment when you need to render it:
(a,b,c,d) → (a,b,c) and (a,c,d)The most computationally efficient way to do shading is to only apply a shading algorithm (such as the Phong algorithm that Professor Zorin discussed) at the vertices. This is call vertex shading.
The more flexible and powerful (although slower) way to do shading is to apply a shading algorithm at each pixel. This is called pixel shading.
In practice, renderers tend to use a combination of vertex shaders and pixel shaders. For the next week or so we'll just be doing vertex shading, because it is simple and it runs fast. Later we will add pixel shading so you can do things like texturing.
When you do vertex shading, you first do the shading algorithm at each vertex of your polygon - so you already have colors at each vertex, and now you just need to interpolate them. This means that by the time you do scan conversion, the data you have at each vertex is:
[ p_{x} , p_{y} , p_{z} , red , green , blue ]You are going to use the
p_{x}
and
p_{y}
to figure out which pixel the vertex falls on in the image -
That is just the viewport computation.
You are going to interpolate the three vertex
[ red , green , blue , p_{z}]
values
over all the image pixels covered by the triangle
in order to figure out
which triangle is the frontmost one
at each pixel of the image,
and what color that triangle should be at that pixel.
The algorithm to do this is known as
the Z-buffer algorithm.
Next week we are going to do the complete Z-buffer algorithm, and tie it together with your wonderful 3D shape modeling/animating software. This week I just want you to work on the core part of the scan conversion: Interpolating vertex values across image pixels.
As we discussed in class, this scan-conversion step is the key place in your rendering pipeline where things are either going to be fast enough to do interactive graphics, or not. There are two things that are necessary in order to make this algorithm go fast:
To find the interpolated values at any given pixel (x,y), you first find the interpolated values at the left and right edges of the scan line, and then you interpolate those two values to get the value at the pixel. In other words:
// COMPUTE TOP-TO-BOTTOM INTERPOLATION FRACTION FOR THIS SCAN LINE t_{y} = (y - TL_{y}) / (BL_{y} - TL_{y}) // LINEARLY INTERPOLATE TO GET VALUES AT LEFT AND RIGHT EDGES OF THIS SCAN LINE L = lerp(t_{y}, TL, BL) R = lerp(t_{y}, TR, BR) // COMPUTE LEFT-TO-RIGHT INTERPOLATION FRACTION OF THE PIXEL t_{x} = (x - L_{x}) / (R_{x} - L_{x}) // LINEARLY INTERPOLATE TO GET VALUE AT PIXEL [r,g,b,p_{z}]_{x,y} = lerp(t_{x}, L, R)where
L
and
R
are vectors containing values for r,g,b and p_{z}.
Homework due Wednesday March 12
For your homework, which is due next Wednesday, March 12, you should at least demonstrate that you can split a triangle into two trapezoids, and then apply the above math to interpolate [r,g,b,p_{z}] values down to the pixel level. To do this, I suggest you write a Java applet that extends MISApplet, and show that it correctly interpolages given any choices of locations [x,y] and colors [r,g,b] at each of the three triangle vertices.
Once you get that much working, try implementing the speed-ups we discussed in class. As I said above, in order to make this scan-conversion really fast, you can do two things:
// COMPUTE TOTAL NUMBER OF SCAN LINES IN TRAPEZOID int n = BL_{y} - TL_{y} dL_dy = (BL - TL) / n dR_dy = (BR - TL) / n L = TL R = TR for (int y = TL_{y} ; y < BL_{y} ; y++) { // INCREMENTALLY UPDATE VALUES ALONG LEFT AND RIGHT EDGES L += dL_dy R += dR_dy // COMPUTE TOTAL NUMBER OF PIXELS ACROSS THIS SCAN LINE int m = R_{x} - L_{x} d_dx = (R - L) / m [r,g,b,p_{z}] = L for (int x = L_{x} ; x < R_{x} ; x++) { // INCREMENTALLY UPDATE VALUE AT PIXEL [r,g,b,p_{z}] += d_dx } }
If you implement both of the above speed-ups, you'll see a very dramatic improvement in running time.