Project 2: Rasterizing
The required functionality in this project is to rasterize triangles, lines, and pixels with linearly-interpolated color. It is worth 60 points.
The remaining 40 points will come from elective functionality, which may include rasterizing other shapes such as circles, fat lines, and concave polygons, as well as transparency, and anti-aliasing.
Concepts applying to Projects 2, 3 and 4
You may want to access the OpenGL man pages from time to time. There are many versions of them online, including versions edited for most non-C languages.
Projects 2, 3, and 4 will be duplicating portions of the OpenGL pipeline.
You will have the OpenGL built-in functionality render the display shown when you type 1 and fill in an array of pixels that will be shown when you type 2 . Most of that skeleton was provided in Project 1; there are just a few things to keep in mind to get it to work:
Because you will need to call OpenGL and your version as well,
you will need some sort of namespace clobbering.
Then in your versions of each OpenGL method, call the built-in OpenGL method so that display 1 will render.
Then do whatever bookkeeping and rendering you need to to get the same functionality in display 2 .
For example, if I decided to prepend byu_ in front of all GL calls I made and was writing in C, then some simple code might look like:
// my version, using byu_ namespace clobbering
void byu_glVertex2i(GLint x, GLint y) {
// first, forward the call to OpenGL to make it appear on screen 1
glVeretx2i(x,y);
// Also, do my own drawing to the raster array for screen 2
if (myMode == GL_POINTS) {
raster [ ((y*640) + x)*3 + 0 ] = red;
raster [ ((y*640) + x)*3 + 1 ] = green;
raster [ ((y*640) + x)*3 + 2 ] = blue;
}
}
void draw() {
/* whatever code I already had */
byu_glBegin(GL_POINTS);
byu_glVertex4f(100,200,3,4);
byu_glVertex4f(200,100,-1,1);
byu_glEnd();
/* any other code I already had */
// Draw my array (raster) on top of what OpenGL already drew, so that
// only my values show up
if (drawmode == 2) {
// Save the old state
GLint oldmatrixmode;
GLboolean depthWasEnabled = glIsEnabled(GL_DEPTH_TEST);
glDisable(GL_DEPTH_TEST);
glGetIntegerv(GL_MATRIX_MODE,&oldmatrixmode);
glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity();
glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity();
// Draw the array of pixels (raster)
glRasterPos2f(-1,-1);
glDrawPixels(640,480,GL_RGB,GL_FLOAT,raster);
//Set the state back to what it was
glPopMatrix();
glMatrixMode(GL_MODELVIEW); glPopMatrix();
glMatrixMode(oldmatrixmode);
if (depthWasEnabled)
glEnable(GL_DEPTH_TEST);
}
glFlush();
}
More about raster
In the first project you created an array of floats called raster . This is the array of pixels on the screen. To make the pixel x pixels from the left side of the screen and y from the top of the screen turn orange (r=1.0, g=0.5, b=0.0), bearing in mind that our canvas is 640 pixels wide, do
raster[((y*640) + x)*3 + 0] = 1.0;
raster[((y*640) + x)*3 + 1] = 0.5;
raster[((y*640) + x)*3 + 2] = 0.0;
In later projects the size of the screen might change. In that case, simply replace 640 with the new width.
Getting OpenGL to draw
This project ignores the 3D projection process. To simulate the same functionality with OpenGL, go to where you wrote:
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
in project 1 and follow it by:
glOrtho(0,640, 0,480, -1,1); // only for project 2; delete thereafter!
Required Functionality
In order to receive any credit for this lab, you must implement the following portions of the OpenGL API. Completing the required functionality is worth 60 points.
These methods will involve implementing:
- glClearColor(r,g,b,a)
Sets the clear color.
- glClear(GLint)
Clears the entire screen to the clear color.
- glBegin(GLenum) with parameters GL_POINTS, GL_LINES, and GL_TRIANGLES
This tells you how to interpret points, as will be explained more below.
- glVertex2i(int, int), plus possibly other glVertex calls
glVertex specifies a point for drawing, though how it is drawn depends on the mode specified by glBegin. glVertex2i(x,y) specifies the 4-vector point (x,y,0,1).
- glEnd()
There must be one glEnd for every glBegin. glVertex only works between Begin & End pairs.
- glColor3f(r,g,b)
There is one current color (a 4-vector) at all times in OpenGL. Initially, it is (1,1,1,1) (white). Calling glColor3f(r,g,b) sets it to (r,g,b,1).
| Functionality |
Clear the screen |
| Description |
Each time glClear(GLbitfield mode) is called and (mode & GL_COLOR_BUFFER_BIT) != 0, fill every pixel on the screen with the color last specified by glClearColor(r,g,b,a). Ignore the a coordinate of the clear color.
OpenGL uses a bit-vector as the parameter for glClear, hence the need to check (mode & GL_COLOR_BUFFER_BIT) != 0 . Later on you will also need to check (mode & GL_DEPTH_BUFFER_BIT) != 0 and possibly a few other bits.
|
| Example |
This should override the gradient we created in project 1. |
| Credit |
Required |
| References |
OpenGL man pages |
| Related electives |
Plot larger points, Anti-Aliasing |
| Functionality |
Plot single-pixel points |
| Description |
Each time glVertex2i is called between glBegin(GL_POINTS) and glEnd(), fill in the pixel specified in the current color (as specified by the most recent call to glColor3f). |
| Example |
glBegin(GL_POINTS) glColor3f(1,.5,0) glVertex2i(20,50) glEnd()
Should set a pixel near the bottom left of the screen to orange. |
| Credit |
Required |
| References |
OpenGL man pages |
| Related electives |
Plot larger points, Anti-Aliasing |
| Functionality |
Plot single-pixel-width lines |
| Description |
For each pair of calls to glVertex2i between glBegin(GL_LINES) and glEnd(), draw a straight Bresenham or DDA line between the two points. The color at the first point should be the value of the current color when glVertex2i was called the first time, the color at the second point should be the value of the current color when glVertex2i was called the second time, and the color should linearly interpolate between these two colors.
Note that color can be interpolated the same way as the off-dimension of the line; that is, if you are walking in x, you can interpolate y and the color with essentially the same code. |
| Example |
glBegin(GL_LINES)
for i from 0 to 8
glColor3f(1,0,0)
glVertex2i(200,200) glVertex2i(200 + 10*i, 280)
glColor3f(0,1,0)
glVertex2i(200,200) glColor3f(0,1,1) glVertex2i(200 - 10*i, 280)
glVertex2i(200,200) glVertex2i(280, 200 + 10*i)
glVertex2i(200,200) glVertex2i(280, 200 - 10*i)
glEnd()
A variety of different lines. |
| Credit |
Required |
| References |
pp. 94-99 |
| Related electives |
Plot fatter lines, Anti-Aliasing, other modes for glBegin |
| Functionality |
Plot smooth-shaded polygons using a scanline algorithm |
| Description |
For each triple of calls to glVertex2i between glBegin(GL_TRIANGLES) and glEnd(), fill in a triangle with the given vertex locations. Each vertex should be the color which was active when it was generated by glVertex2i, and the color should be linearly interpolated across the triangle.
Many people have difficulty with boundary cases in the scan filled polygon. For this reason, I suggest the following as one of the methods that seems to work pretty well for most people (there are other methods that work—this is not the method I used myself, but it does work well)
- Scan all the edges of the polygon into some kind of data structure using the same basic code as your line algorithm. When this stage is done, you will have a list of two pairs
(xcoord,color) per y value.
Scan each edge from the bottom vertex to the pixel below the top vertex to avoid double-scanning internal vertices.
- Fill between the pairs of vertices on each row, interpolating color as you go.
|
| Example |
glBegin(GL_TRIANGLES)
glColor3f(1,0,0)
glVertex2i(300,300)
glColor3f(0,1,0)
glVertex2i(500,300)
glColor3f(0,0,1)
glVertex2i(400,350)
glEnd()
A single triangle. |
| Credit |
Required |
| References |
pp. 196-200, 593-594 |
| Related electives |
Pixel-perfect polygons, Alpha blending, Anti-Aliasing, other modes for glBegin |
Elective Functionality
To earn the remaining 40 points required for full credit on this lab, choose among the following details to implement.
These methods will involve implementing:
- glEnable(GLenum) and glDisable(GLenum), which are ways of changing how OpenGL draws.
- glVertex2f(float, float), or glVertex2d(double,double), which are exactly like glVertex2i(int, int).
- glColor4f(r,g,b,a) sets the active color to (r,g,b,a), which is useful for transparency.
- Other glBegin parameters.
- glBlendFunc(GLenum, GLenum), which controls transparency style.
- glLineWidth(double) and glPointSize(double).
Note that OpenGL only lets you call glVertex and glColor between glBegin and glEnd; glEnable, etc, are ignored there.
| Functionality |
Pixel-perfect Polygons |
| Description |
For each polygon, fill all those pixels that lie inside the polygon specified, and only those points. In 2D this seems a pretty petty requirement—who cares about being a pixel off here or there? In 3D, however, it becomes key to having a nice image, as without it you can see the polygon borders.
|
| Example |
glBegin(GL_TRIANGLES)
glVertex2i(50,50) glVertex2i(50,60) glVertex2i(65,60)
glVertex2i(50,50) glVertex2i(65,60) glVertex2i(65,50)
glEnd()
should look like a solid square.
glBegin(GL_TRIANGLES)
glVertex2i(50,50) glVertex2i(50,60) glVertex2i(65,60)
glVertex2i(51,50) glVertex2i(66,60) glVertex2i(66,50)
glEnd()
should look like a solid square with a 1-pixel wide line missing through the diagonal.
Note: Your polygons should look exactly like OpenGL's to get credit. This is hard to do. If you don't have the time, try some of the other electives.
|
| Credit |
5 for integer endpoints; 10 for floating-point endpoints |
| References |
pp. 196-200 and Hugo Elias's tutorial |
| Functionality |
The other modes for glBegin |
| Description |
Add support for additional rendering modes. Note that most hardware implementations of OpenGL treat GL_POLYGON the same as GL_TRIANGLE_FAN; you may do that too.
The additional modes are:
GL_LINE_STRIP: Draws one line segment for every call to glVertex2i except the first; vertices n and n + 1 define line segment n.
GL_LINE_LOOP: Exactly like GL_LINE_STRIP except one additional line is draw between the first and last calls to glVertex2i when glEnd is called.
GL_TRIANGLE_STRIP: Draws a connected group of triangles. One triangle is defined for each vertex presented after the first two vertices. For odd n, vertices n, n + 1, and n + 2 define triangle n. For even n, vertices n + 1, n, and n + 2 define triangle n. The alternating order becomes important later when we add backface culling.
GL_TRIANGLE_FAN: Draws a connected group of triangles. One triangle is defined for each vertex presented after the first two vertices. Vertices 1, n + 1, and n + 2 define triangle n.
GL_QUADS: A quadrilateral is defined for every four vertices 4n-3, 4n-2, 4n-1, 4n; each quadrilateral can be rendered directly or split into two triangles. You can assume the quads are convex.
GL_QUAD_STRIP: A connected group of quadrilaterals, with one defined for every two vertices beyond the first two (if the first point is called 1, not 0, then for all n, the points 2n-1, 2n, 2n+2, 2n+1 make a quad); each quadrilateral can be rendered directly or split into two triangles. You can assume the quads are convex. Note that the order in which vertices are used to construct a quadrilateral from strip data is different from that used with independent data.
GL_POLYGON: see GL_TRIANGLE_FAN.
All modes should make each vertex the color that was active when it was called and smoothly interpolate colors, as in the required functionality. |
| Example |
glBegin(some GLenum), followed by some glVertex calls, then glEnd().
|
| Credit |
4 points per additional mode supported, except that GL_POLYGON and GL_TRIANGLE_FAN together count as only one mode. 6 additional points are given if all 6 modes are supported. |
| References |
OpenGL man pages |
| Functionality |
Plot larger points |
| Description |
Each time glVertex2i is called between glBegin(GL_POINTS) and glEnd(), fill in a square or circle of the current point size (as specified by the most recent call to glPointSize) centered on the specified pixel in the current color (as specified by the most recent call to glColor3f). Circles should use the Bresenham (aka Midpoint) algorithm. |
| Example |
call glPointSize(10) before glBegin(GL_POINTS).
|
| Credit |
5 for squares; 10 for circles. To get credit for both, have circles drawn only when GL_POINT_SMOOTH is enabled. |
| References |
pp. 105-109 |
| Functionality |
Plot fatter lines |
| Description |
Draw lines with the current line width (as specified by the most recent call to glLineWidth) instead of just single-pixel lines. You can do this either by extending the Bresenham/DDA algorithms to draw several pixels at each step or by drawing narrow quads instead of lines. Endcap shapes are not specified, but make sure that the width is orientation-independent. |
| Example |
call glLineWidth(10) before glBegin(GL_LINES).
|
| Credit |
10 |
| References |
pp. 105-109 |
| Functionality |
Non-convex Polygons |
| Description |
Create an implementation of the GL_POLYGON mode that is correct for self-intersecting and concave polygons, unlike the hardware OpenGL implementations. You can decide how to interpolate color, or ignore it if you prefer.
You will probably find it useful to either store the points until glEnd is called and then use an active edge list approach, or to implement GL_LINE_LOOP first and make GL_POLYGON like GL_LINE_LOOP, but interpolate the lines into a data structure instead of onto the screen and then scan-fill after glEnd is called.
OpenGL does not do this. Your version should look different than theirs.
|
| Example |
A five pointed star with a hollow center via the points
(50,300), (75,350), (10,320), (90,320), (25,350)
Try making each vertex a different color.
|
| Credit |
15 if color is not interpolated, 20 if color is interpolated smoothly |
| References |
pp. 196-200 |
| Functionality |
Alpha Blending |
| Description |
In OpenGL colors are four values: red, blue, green, and alpha. Alpha is the opacity of a color, with 1.0 being fully opaque and 0.0 fully transparent. For this function, you will need add a glColor4f method and implement alpha blending. Note that there are a lot of kinds of alpha blending in the OpenGL spec; you need to implement the equivalent of glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA), which is to say a pixel of RGB color ct and alpha a drawn on top of a pixel of RGB color cb will have color ct * a + cb * (1 - a).
Alpha blending can be enabled by calling glEnable(GL_BLEND), which defaults to being disabled, and disabled by calling glDisable(GL_BLEND).
Some of the elective functionality refers to glBlendColor, which is not included in all versions of the OpenGL man pages and might not be supported on some graphics cards. You can find it documented here and its role in glBlendFunc calls discussed here. If your graphics card doesn't support it you can still implement it, but not forward the calls on to the hardware renderer.
|
| Example |
Draw two triangles overlapping with non-1 alphas.
|
| Credit |
5 for basic functionality 10 if you also implement glBlendFunc and support parameters GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, and GL_ZERO.
20 if you also implement glBlendColor and support all OpenGL blend functions.
|
| References |
pp. 179-180 |
| Functionality |
Anti-Aliasing |
| Description |
Anti-aliasing is the process of making pixelated lines look smooth by adding half-tone pixels to their edges. This is turned on and off with the *_SMOOTH enable and disable calls. In OpenGL, smoothing only works if GL_BLEND is enabled because the half-tone pixels are actually made transparent; you can choose to implement it without that if you wish.
|
| Credit |
If you use fast but approximate methods based on the decision variable of the Bresenham algorithms or the mantissa of the interpolated DDA coordinate, you get the sum of
- 5 for unit-width lines.
- 5 for fatter lines.
- 5 for unit-sized (and square, if implemented) points with non-integer coordinates.
- 5 for circular points with integer coordinates; an extra 5 if they work for floating-point coordinates, too.
- 10 for polygon edges.
If you use a slower but more accurate super-sampling technique, you will get half as many points. If you implement both slow and fast and add a glHint implementation to switch between them, you get one and a half times as many points. |
| References |
pp. 214-222 |
E-mail: leemhoward@gmail.com |