Triangles are rasterized by finding all of the integer-coordinate points inside the three edges of the triangle1. For each such point, we also wish to interpolate an arbitrary number of other values to that point. We can both find these points and interpolate to them very efficiently using either the DDA algorithm or its rational-number cousin the Bresenham algorithm. As both are similar and you’re likely more familiar with working in real numbers than in rational numbers, we’ll discuss DDA here.
The DDA algorithm takes an -dimensional line segment, specified by its two the -vector end points, and finds all points on that line segment that have an integer coordinate in one particular dimension.
DDA
Officially, DDA stands for digital differential analyzer
but
I’ve never heard that name used except in explanatory texts like this
one. It’s always called DDA
, the DDA algorithm,
or the
DDA line-drawing algorithm.
Setup:
Find the first potential point:
Find all points:
Working backwards,
Given a point on the line segment with integer , is the next point on the line segment with integer as long as both is aligned with the segment and . So once we have one and , we just loop through adding to until we leave the segment (8).
To get we start with a vector aligned with the line segment by simple subtraction (3) and then get by dividing the entire vector by whatever it’s coordinate is (4).
Any time we have division, we have to consider division by zero. In this case division by zero would happen if the line has no extent in the direction, meaning there are either or points on the line with integer ; in either case, we can simply return as there are no meaningful points to find (1).
To get the initial we need to figure out how far the starting end of the segment is from an integer coordinate (, 5) make a vector offset that will move us to that point ( with , 6) and add that offset to the starting end of the segment (7).
All of this assumes we were stepping in the positive direction, so we swap the order of the segment endpoints if needed to make sure that’s the case (2).
The scanline algorithm uses DDA to find the pixels in a triangle by first using DDA in (i.e., finding points with integer values) on the three edge line segments; then using DDA in between pairs of points found this way that share the same value.
Scanline
Scanline rendering is a generic term for any algorithm that draws
triangles or other surfaces one horizontal row of pixels or
scanline
at a time. Originally designed to render on raster-path
cathode ray tubes without requiring a separate frame buffer, they
survived the transition to framebuffer rasterizers in part because of
their cache locality.
Setup:
Let be the top point of (i.e. with the smallest value)
Let be the bottom point of (i.e. with the biggest value)
Let be the remaining point of (i.e. with the middle value)
Run DDA steps 1–7 to setup for the line to with being the axis. This is for the long edge ( to ) so we’ll label its vectors and .
If it returns in DDA step 1, return (no points found)
Find points in the top half of the triangle:
Run DDA steps 1–7 to setup for the line to with being the axis to find and for one edge of the top-half triangle.
Do the DDA loop (DDA step 8) for both and at the same time:
While repeat:
Find points in the bottom half of the triangle:
Run DDA steps 1–7 to setup for the line to with being the axis to find and for one edge of the bottom-half triangle.
Do the DDA loop (DDA step 8) for both and at the same time:
While repeat:
We’re finding all the pixels inside a triangle. We do this with nested DDA: we DDA in one direction to find points along the edges of the triangle, then DDA in another direction between those edge points to find points inside the triangle.
Because of memory layout, it is fastest if the innermost loop is in , so the edge loop will be in and the fill loop in .
First, we’ll sort the points in (1–3).
We’ll use this order to consider the triangle in two parts: the pixels that lie above the middle point and thus between the top-to-middle edge and the top-to-bottom edge; and those that lie below it the middle point. and thus between the middle-to-bottom edge and the top-to-bottom edge.
Because the top-to-bottom edge is needed for both parts, we set up DDA for that edge (4). We’ll also be setting up DDA for other edges so we’ll add primes to the and for this edge to distinguish them from the other edges’ and .
Now we do almost exactly the same work twice, just using a different other edge. First we find and for the top-to-middle edge (5) and DDA the top-half edges (6); then we find and for the middle-to-bottom edge (7) and DDA the top-half edges (8). In both cases we augment the usual DDA loop to update the top-to-bottom edge in lock-step with the other edges (ii–iii), ensuring that and always have the same value.
For each same- pair of edge points and , we DDA in between them (i) to find the pixels in the triangle. Because DDA produces integer coordinates in the dimension it is stepping in, these pixels always have integer and .
Bresenham’s line algorithm achieves the same results as DDA, but using only integer values. Because it uses only integers, it is both faster (integer arithmetic requires less hardware than floating-point hardware) and more precise (no rounding error) than DDA when implemented in hardware. It works on integer, rational, and fixed-point endpoints and outputs, not the floating-point numbers common in CPUs. Because of its efficiency and precision, it is commonly used in graphics hardware.
Bresenham’s line algorithm can be derived by writing out DDA using rational numbers and tracking the integral and fractional parts of each number separately. The result is less intuitive and more complicated code than DDA, but simpler and smaller hardware because the individual components require many fewer transistors.
Jack Elton Bresenham derived this algorithm not as a form of DDA, but rather by considering the implicit equation for a line, and also showed how it could draw other polynomial curves such as circles. The polynomial generalizations are suitable for hardware implementation, but are not commonly implemented in hardware today.
A naive implementation of DDA or Bresenham performs linear interpolation of values across a line or triangle. That means in particular that if one end of a line is at and has a red value of and the other end is at and has then at the interpolated will be .
While linear interpolation may seem intuitive, it is incorrect when paired with projection. To see this, consider the following two images:
If we use linear interpolation in 2D screen space, everything looking 2D and flat. This can be fixed by interpolating in hyperbolic coordinates instead.
In 1992 Jim Blinn wrote a paper explaining how interpolation could be made perspective-correct using something called hyperbolic interpolation; all graphics hardware now uses his approach.
Hyperbolic interpolation assumes that three components of the vector of values at each vertex of a triangle are special. and are special because they correspond to pixel locations; and is special because in homogeneous coordinates they are used to create frustums and perspective projection. It is traditional to also treat as special because it gives better [depth buffer] precision for up-close objects where errors would be more visible.
Suppose we have a supplied vertex coordinate . We first perform perspective projection in and by dividing by ; we also divide everyithing else by while we’re at it, getting . Note that is replaced by not . We use these divided coordinates in DDA to get a pixel coordinate . The and are the correct pixel and and the is the depth we use in the depth buffer, but the rest of the coordinates had a spurious division by which we now undo to get .
Hyperbolic interpolation
Consider the two points and .
Division by gives and .
Interpolation to the midpoint gives .
Divistion of the post- coordinated by the interpolated and inversion of gives .
When integrated with DDA, this means we
It is common to wish to provide color and other material properties
at a different resolution than geometry. The usual way to realize this
is by (a) interpolating texture coordinates
to each fragment and
(b) at each fragment looking up the color and so on at those coordinates
in an image or array called a texture
. We have a separate page on using Textures in WebGL and
one on various applications of textures;
this section covers only basic texture lookups.
decal, adjusting the color only for a part of the triangle, and the alpha is composited on top of the interpolated object color.