by William Shoaff with lots of help
Rasterization is the process of converting a vertex representation to a pixel representation; rasterization is also called scan conversion. Included in this definition are geometric objects such as circles where you are given a center and radius. In these notes I will cover:
Scan conversion algorithms use incremental methods that exploit coherence. An incremental method computes a new value quickly from an old value, rather than computing the new value from scratch, which can often be slow. Coherence in space or time is the term used to denote that nearby objects (e.g., pixels) have qualities similar to the current object.
Let
and
be two endpoints of a line
segment.
We will assume that these points are in device space so that the
coordinates
are integers.
The point-intercept form of the equation of the line from p0 to p1 is
Notice that if x is incremented to x+1, then y changes to y+m.
Similarly, if y is incremented to y+1, then x changes to
.
Of course, the slope (or its reciprocal) often won't be integers.
Let's consider the two cases where these increments occur.
Pretend the slope is between minus one and one (
)
With little lost of generality, pretend x0 <x1 so the line
is drawn from left to right.
We know that if
is on the line, then so is
(x+1, y+m),
and we simply iterate on this fact.
The pseudo-code for the algorithm is given below.
The DDA code is presented in ../dda.html
Given end-points
and
,
find the pixels illuminated by the DDA algorithm.
In this case, the slope is m=6/7, which is a shallow line.
Thus, x is incremented by 1 and y by m at each step.
The first point is
.
The next point on the line is
,
which is
rounded to the pixel value
.
Continuing, the plotted pixels are:
| Step | y | pixel |
| 1 | 21/7 | |
| 2 | 27/7 | |
| 3 | 33/7 | |
| 4 | 39/7 | |
| 5 | 45/7 | |
| 6 | 51/7 | |
| 7 | 57/7 | |
| 8 | 63/7 |
The DDA algorithm requires floating point arithmetic and rounding inside the main loop. Bresenham's algorithm works solely with integers and integer arithmetic.
Let
and
be two endpoints of a line segment
(in device coordinate, so
are integers).
The implicit equation of the line from p0 to p1 is
We'll pretend that
Now define an error
e = Ax+By+C which measures
whether or not a point
is on the line or not.
Consider an R move, the error becomes
If
move to right;
if
move to diagonally up.
The sign of the difference
| eR | eD |
|
next pixel |
| + | + | -B > 0 | D |
| + | - | er + eD = 2e+2A+B | don't know |
| - | + | can't happen | |
| - | - | B < 0 | R |
Notice that in the
case we have
Bresenham uses the indeterminate
case, where we don't know which direction to
move, to define a biased error
If eR and eD are both non-negative, then so is g and the above chart shows that a diagonal move is best. If eR and eD are both negative, so is g and so we move to the right as the chart indicates. In the indeterminate case, if g is non-negative we move diagonally, while if g is negative, we move to the right.
Now once a move is made we must update the biased error.
For a diagonal move:
Finally, we need an initial value for the biased error.
But this is easy since
g = 2e+2A +B and the first point
in on the line so the error e is zero:
The initial value of the biased error is
We'll only develop the code for Bresenham's algorithm for
the special case discuss above.
Let
and
be line segment end-points,
pretend the slope of line is between 0 and 1 and let x0 < x1.
The Bresenham line code is presented in ../bresenham.html
Here we have the change in y is A = 9 - 3 = 6 and minus the change in x is B = -(8 - 1) = -7. The initial biased error is g = 2A+B = 5. The biased error update for a diagonal move is diagonalInc = 2(A+B) = -2, and the biased error update for a right move is rightInc = 2A=12.
| Step | old g | pixel | new g |
| 1 | 5 | 3 | |
| 2 | 3 | 1 | |
| 3 | 1 | -1 | |
| 4 | -1 | 11 | |
| 5 | 11 | 9 | |
| 6 | 9 | 7 | |
| 7 | 7 | 5 | |
| 8 | 5 | 3 |
The remaining question is how do we scan convert a line when the slope is not between 0 and 1 or the first x coordinate is not less than the second.
Now let's consider how we can use the ideas developed above to rasterize a circle.
Let
At each step we choose to move either to the right (R): x=x+1,
or diagonally down (D):
.
Define the error to be
| eR | eD |
|
next pixel |
| + | + | 2y-1 > 0 | D |
| + | - | er + eD | don't know |
| - | + | can't happen | |
| - | - | -(2y-1) < 0 | R |
We also need the initial value for g: It is
g = 3 - 2r,
since the initial point
is on the circle..
And we need updates for the biased error for diagonal and right moves.
Note that a diagonal and right moves results in changes to the biased
error:
The Bresenham circle code is presented in ../brescircle.html
Note that symmetricplot(x, y) calls plot with all 8 values
.
We want to develop scan conversion algorithms that generate all pixels in the interior of a polygon. There are two general classes of polygon filling algorithms:
Scan line algorithms typically work only with polygons but fill algorithms work for more general objects Scan line algorithms can be used for hidden surface removal.
Following convention, a raster will be defined as a two dimensional
grid numbers from
in the upper left corner to
in the lower left corner.
To be specific, let's use the HDTV resolution of
which has a 9 to 16 aspect ratio.
Here's a small
raster.
We want to fill a polygon defined by 3 or more vertices.
We will make some assumptions (which are not quite right, but they get us started).
First, we define scan-line order:
Given a polygon P, with edges
- 1.
- Use DDA or Bresenham's algorithm to find each pixel on P's boundary
- 2.
- Sort the pixels in scan-line order, storing them in a list L
- 3.
- Pull off pairs of points
from L and color each pixel
such that
![]()
(Note it must be the case that y1=y=y2)
You've been lied to, scan-lines that hit polygon vertices intersect the region an odd number of times and horizontal edges have infinitely many intersections with a scan-line Corrections are:
For a high resolution device there may be thousands of intersections,
so even an
sort routine may take time
Bucket sorting (a form of hashing) can save time
However, not all pixels need to be save,
we can compute them on the fly.
There are three quantities that need to be stored in a record place into
a scan line list of buckets indexed by the y values of the scan lines (0 to
).
The record looks like
For each non-horizontal edge, let
Starting at the top pixel of the edge
,
when y is incremented by 1,
x will change by
For each non-horizontal edge
to
,
Now, the algorithm to fill the polygon is:
Consider the polygon defined by vertices
The scan line bucket-list then looks like:
| bucket |
|
|
| 0 | 8, 5/7, 0 | 2, 8, 0 |
| 1 | ||
| 2 | 4, 1/4, 8.25 | |
| 3 | ||
| 4 | ||
| 5 | ||
| 6 | 2, -2, 7 | |
| 7 | ||
| 8 | ||
| 9 |
Now we pull off from the top the first non empty list from the scan line buckets.
Active-Edge List:
| y=0 | 8, 5/7, 0 | 2, 8, 0 |
From this active edge list we plot:
.
| y=1 | 7, 5/7, 5/7 | 1, 8, 8 |
| y=2 | 6, 5/7, 10/7 | 0, 8, 16 |
| y=2 | 6, 5/7, 10/7 | 4, 1/4, 8.25 |
| y=3 | 5, 5/7, 15/7 | 3, 1/4, 8.5 |
| y=4 | 4, 5/7, 20/7 | 2, 1/4, 8.75 |
| y=5 | 3, 5/7, 25/7 | 1, 1/4, 9 |
| y=6 | 2, 5/7, 30/7 | 2, -2, 7 |
| y=7 | 1, 5/7, 35/7 | 1, -2, 5 |
One thing to note about this implementation is its dependence on floating
point arithmetic and rounding. It is possible to separate the denominator
and numerator information in
and implement an integer only
algorithm that is similar to Bresenham's line algorithm.
A Java applet that executes a version of scan line fill is available for you to test. http://www.cs.fit.edu/wds/java/html/RasterDriver.html
We can extend the data in the record store for each vertex to implement Gouraud (or Phong) shading as the polygon is filled. For Gouraud shading, this would be done by keeping the red, green, blue (RGB) intensity at each vertex and the change in these values as the edge is traced out. For simplicity, we'll illustrate this with gray scale using only an intensity I at each vertex.
Pretend we are given polygon vertices, as before, with intensity data:
The scan line bucket-list then looks like:
| bucket |
|
|
| 0 | 8, 5/7, 0, 6/7, 2 | 2, 8, 0, 8, 2 |
| 1 | ||
| 2 | 4, 1/4, 8.25,-5/4,8.75 | |
| 3 | ||
| 4 | ||
| 5 | ||
| 6 | 2, -2, 7,3/2,6.5 | |
| 7 | ||
| 8 | ||
| 9 |
Now we pull off from the top the first non empty list from the scan line buckets.
Active-Edge List:
| y=0 | 8, 5/7, 0 , 6/7, 2 | 2, 8, 0,8,2 |
From this active edge list we plot:
with intensity 2.
Update Active-Edge List:
| y=1 | 7, 5/7, 5/7,6/7,20/7 | 1, 8, 8,8,10 |
You must have observed that we now want to interpolate (via constant
increments) from intensity I=20/7 to intensity I=10 as we step across the
scan line. So let's define a double difference (that corresponds to what's
called bilinear interpolation)
Plot the 8 pixels
to
with intensities:
When the active edge list is updated, one record (for edge 1) drops out and an new record (for edge 2) is merged in:
| y=2 | 6, 5/7, 10/7,6/7,26/7 | 4, 1/4, 8.25,-5/4,8.75 |
Note in practice the intensity values would be truncated or rounded to integers.
Now to change directions, we present fill algorithms that you might find in a ``paint'' program.
Assume a seed point inside the region to be filled and empty stack
Pseudo-code for Flood Fill Algorithm
Assume a seed point inside the region to be filled and an algorithm that uses a 4-point topology
Pseudo-code for Flood Fill Algorithm
Filling Using Coherence