!!html_class cgfs !!html_title Shaded Triangles - Computer Graphics from Scratch
In the previous chapter, we developed an algorithm to draw a triangle filled with a solid color. Our goal for this chapter is to draw a shaded triangle---that is, a triangle filled with a color gradient.
We want to fill the triangle with different shades of a single color. It will look like Figure 8-1.
We need a more formal definition of what we're trying to draw. To do this, we'll assign a real value
To compute the exact color shade of a pixel given the base color of the triangle
In order to draw a shaded triangle, all we need to do is compute a value of
At this point, however, we only know the values of
Let's start with the edges of the triangle. Consider the edge
More formally, we have a function
This is suspiciously similar to the situation in the previous chapter: we had a linear function Interpolate
with y as the independent variable (the values we know) and h as the dependent variable (the values we want):
x01 = Interpolate(y0, x0, y1, x1)
h01 = Interpolate(y0, h0, y1, h1)
x12 = Interpolate(y1, x1, y2, x2)
h12 = Interpolate(y1, h1, y2, h2)
x02 = Interpolate(y0, x0, y2, x2)
h02 = Interpolate(y0, h0, y2, h2)
Next, we concatenated the x02
and x012
was x_left
and which was x_right
. Again, we can do something very similar here for the
However, we will always use the
We can code this as follows:
// Concatenate the short sides
remove_last(x01)
x012 = x01 + x12
remove_last(h01)
h012 = h01 + h12
// Determine which is left and which is right
m = floor(x012.length / 2)
if x02[m] < x012[m] {
x_left = x02
h_left = h02
x_right = x012
h_right = h012
} else {
x_left = x012
h_left = h012
x_right = x02
h_right = h02
}
This is very similar to the relevant section of the code in the previous chapter (Listing 7-1), except that every time we do something with an x
vector, we do the same with the corresponding h
vector.
The last step is drawing the actual horizontal segments. For each segment, we know x_left
and x_right
, as in the previous chapter; now we also know h_left
and h_right
. But this time we can't just iterate from left to right and draw every pixel with the base color: we need to compute a value of
Again, we can assume Interpolate
to compute these values. In this case, the independent variable is x_left
value to the x_right
value of the specific horizontal segment we're shading; the dependent variable is x_left
and x_right
are h_left
and h_right
for that segment:
x_left_this_y = x_left[y - y0]
h_left_this_y = h_left[y - y0]
x_right_this_y = x_right[y - y0]
h_right_this_y = h_right[y - y0]
h_segment = Interpolate(x_left_this_y, h_left_this_y,
x_right_this_y, h_right_this_y)
Or, expressed in a more compact way:
h_segment = Interpolate(x_left[y - y0], h_left[y - y0],
x_right[y - y0], h_right[y - y0])
Now it's just a matter of computing the color for each pixel and painting it! Listing 8-1 shows the complete pseudocode for DrawShadedTriangle
.
DrawShadedTriangle (P0, P1, P2, color) {
❶// Sort the points so that y0 <= y1 <= y2
if y1 < y0 { swap(P1, P0) }
if y2 < y0 { swap(P2, P0) }
if y2 < y1 { swap(P2, P1) }
// Compute the x coordinates and h values of the triangle edges
x01 = Interpolate(y0, x0, y1, x1)
h01 = Interpolate(y0, h0, y1, h1)
x12 = Interpolate(y1, x1, y2, x2)
h12 = Interpolate(y1, h1, y2, h2)
x02 = Interpolate(y0, x0, y2, x2)
h02 = Interpolate(y0, h0, y2, h2)
// Concatenate the short sides
remove_last(x01)
x012 = x01 + x12
remove_last(h01)
h012 = h01 + h12
// Determine which is left and which is right
m = floor(x012.length / 2)
if x02[m] < x012[m] {
x_left = x02
h_left = h02
x_right = x012
h_right = h012
} else {
x_left = x012
h_left = h012
x_right = x02
h_right = h02
}
// Draw the horizontal segments
❷for y = y0 to y2 {
x_l = x_left[y - y0]
x_r = x_right[y - y0]
❸h_segment = Interpolate(x_l, h_left[y - y0], x_r, h_right[y - y0])
for x = x_l to x_r {
❹shaded_color = color * h_segment[x - x_l]
canvas.PutPixel(x, y, shaded_color)
}
}
}
The pseudocode for this function is very similar to that for the function developed in the previous chapter (Listing 7-1). Before the horizontal segment loop ❷, we manipulate the Interpolate
❸ to compute the
Note that we're sorting the triangle vertices as before ❶. However, we now consider these vertices and their attributes, such as the intensity value
In this chapter, we've extended the triangle-drawing code developed in the previous chapter to support smoothly shaded triangles. Note that we can still use it to draw single color triangles by using 1.0 as the value of
The idea behind this algorithm is actually more general than it seems. The fact that PutPixel
. This means we could use this algorithm to compute the value of any attribute of the vertices of the triangle, for every pixel of the triangle, as long as we assume this value varies linearly on the screen.
We will indeed use this algorithm to improve the visual appearance of our triangles in the upcoming chapters. For this reason, it's a good idea to make sure you really understand this algorithm before proceeding further.
In the next chapter, however, we take a small detour. Having mastered the drawing of triangles on a 2D canvas, we will turn our attention to the third dimension.