1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
/* * 3D Shapes */ #include <GLUT/glut.h> /* Global variables */ char title[] = "3D Shapes"; /* Initialize OpenGL Graphics */ void initGL() { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set background color to black and opaque glClearDepth(1.0f); // Set background depth to farthest glEnable(GL_DEPTH_TEST); // Enable depth testing for z-culling glDepthFunc(GL_LEQUAL); // Set the type of depth-test glShadeModel(GL_SMOOTH); // Enable smooth shading glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Nice perspective corrections } /* Handler for window-repaint event. */ void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear color and depth buffers glMatrixMode(GL_MODELVIEW); // To operate on model-view matrix // Render a color-cube consisting of 6 quads with different colors glLoadIdentity(); // Reset the model-view matrix glTranslatef(1.5f, 0.0f, -7.0f); // Move right and into the screen glBegin(GL_QUADS); // Begin drawing the color cube with 6 quads // Top face (y = 1.0f) // Define vertices in counter-clockwise (CCW) order with normal pointing out glColor3f(0.0f, 1.0f, 0.0f); // Green glVertex3f( 1.0f, 1.0f, -1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Bottom face (y = -1.0f) glColor3f(1.0f, 0.5f, 0.0f); // Orange glVertex3f( 1.0f, -1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // Front face (z = 1.0f) glColor3f(1.0f, 0.0f, 0.0f); // Red glVertex3f( 1.0f, 1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // Back face (z = -1.0f) glColor3f(1.0f, 1.0f, 0.0f); // Yellow glVertex3f( 1.0f, -1.0f, -1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); // Left face (x = -1.0f) glColor3f(0.0f, 0.0f, 1.0f); // Blue glVertex3f(-1.0f, 1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // Right face (x = 1.0f) glColor3f(1.0f, 0.0f, 1.0f); // Magenta glVertex3f(1.0f, 1.0f, -1.0f); glVertex3f(1.0f, 1.0f, 1.0f); glVertex3f(1.0f, -1.0f, 1.0f); glVertex3f(1.0f, -1.0f, -1.0f); glEnd(); // End of drawing color-cube // Render a pyramid consists of 4 triangles glLoadIdentity(); // Reset the model-view matrix glTranslatef(-1.5f, 0.0f, -6.0f); // Move left and into the screen glBegin(GL_TRIANGLES); // Begin drawing the pyramid with 4 triangles // Front glColor3f(1.0f, 0.0f, 0.0f); // Red glVertex3f( 0.0f, 1.0f, 0.0f); glColor3f(0.0f, 1.0f, 0.0f); // Green glVertex3f(-1.0f, -1.0f, 1.0f); glColor3f(0.0f, 0.0f, 1.0f); // Blue glVertex3f(1.0f, -1.0f, 1.0f); // Right glColor3f(1.0f, 0.0f, 0.0f); // Red glVertex3f(0.0f, 1.0f, 0.0f); glColor3f(0.0f, 0.0f, 1.0f); // Blue glVertex3f(1.0f, -1.0f, 1.0f); glColor3f(0.0f, 1.0f, 0.0f); // Green glVertex3f(1.0f, -1.0f, -1.0f); // Back glColor3f(1.0f, 0.0f, 0.0f); // Red glVertex3f(0.0f, 1.0f, 0.0f); glColor3f(0.0f, 1.0f, 0.0f); // Green glVertex3f(1.0f, -1.0f, -1.0f); glColor3f(0.0f, 0.0f, 1.0f); // Blue glVertex3f(-1.0f, -1.0f, -1.0f); // Left glColor3f(1.0f,0.0f,0.0f); // Red glVertex3f( 0.0f, 1.0f, 0.0f); glColor3f(0.0f,0.0f,1.0f); // Blue glVertex3f(-1.0f,-1.0f,-1.0f); glColor3f(0.0f,1.0f,0.0f); // Green glVertex3f(-1.0f,-1.0f, 1.0f); glEnd(); // Done drawing the pyramid glutSwapBuffers(); // Swap the front and back frame buffers (double buffering) } /* Handler for window re-size event. Called back when the window first appears and whenever the window is re-sized with its new width and height */ void reshape(GLsizei width, GLsizei height) { // GLsizei for non-negative integer // Compute aspect ratio of the new window if (height == 0) height = 1; // To prevent divide by 0 GLfloat aspect = (GLfloat)width / (GLfloat)height; // Set the viewport to cover the new window glViewport(0, 0, width, height); // Set the aspect ratio of the clipping volume to match the viewport glMatrixMode(GL_PROJECTION); // To operate on the Projection matrix glLoadIdentity(); // Reset // Enable perspective projection with fovy, aspect, zNear and zFar gluPerspective(45.0f, aspect, 0.1f, 100.0f); } /* Main function: GLUT runs as a console application starting at main() */ int main(int argc, char** argv) { glutInit(&argc, argv); // Initialize GLUT glutInitDisplayMode(GLUT_DOUBLE|GLUT_DEPTH); // Enable double buffered mode glutInitWindowSize(640, 480); // Set the window's initial width & height glutInitWindowPosition(50, 50); // Position the window's initial top-left corner glutCreateWindow(title); // Create window with the given title glutDisplayFunc(display); // Register callback handler for window re-paint event glutReshapeFunc(reshape); // Register callback handler for window re-size event initGL(); // Our own OpenGL initialization glutMainLoop(); // Enter the infinite event-processing loop return 0; } |
GLUT Setup - main()
The program contains a initGL()
, display()
and reshape()
functions.
The main()
program:
- glutInit(&argc,
argv);
Initializes the GLUT. - glutInitWindowSize(640,
480);
glutInitWindowPosition(50, 50);
glutCreateWindow(title);
Creates a window with a title, initial width and height positioned at initial top-left corner. - glutDisplayFunc(display);
Registersdisplay()
as the re-paint event handler. That is, the graphics sub-system calls backdisplay()
when the window first appears and whenever there is a re-paint request. - glutReshapeFunc(reshape);
Registersreshape()
as the re-sized event handler. That is, the graphics sub-system calls backreshape()
when the window first appears and whenever the window is re-sized. - glutInitDisplayMode(GLUT_DOUBLE|GLUT_DEPTH););
Enables double buffering. Indisplay()
, we useglutSwapBuffers()
to signal to the GPU to swap the front-buffer and back-buffer during the next VSync (Vertical Synchronization). - initGL();
Invokes theinitGL()
once to perform all one-time initialization tasks. - glutMainLoop();
Finally, enters the event-processing loop.
One-Time Initialization Operations - initGL()
The initGL()
function performs the one-time
initialization tasks. It is invoked from main()
once.
glClearColor(0.0f,
0.0f, 0.0f, 1.0f); // Set
background color to black and opaque
glClearDepth(1.0f); // Set
background depth to farthest
// In display()
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
Set the clearing (background) color to black (R=0, G=0, B=0)
and opaque (A=1), and the clearing (background) depth to the
farthest (Z=1). In display()
, we invoke glClear()
to clear the color and depth buffer, with the clearing color
and depth, before rendering the graphics.
glEnable(GL_DEPTH_TEST);
// Enable depth testing for
z-culling
glDepthFunc(GL_LEQUAL); //
Set the type of depth-test
We need to enable depth-test to remove the hidden surface,
and set the function used for the depth test.
glShadeModel(GL_SMOOTH);
// Enable smooth shading
We enable smooth shading in color transition. The
alternative is GL_FLAT
.
glHint(GL_PERSPECTIVE_CORRECTION_HINT,
GL_NICEST); // Nice
perspective corrections
In graphics rendering, there is often a trade-off between
processing speed and visual quality. We can use glHint()
to decide on the trade-off. In this case, we ask for the
best perspective correction, which may involve more
processing. The default is GL_DONT_CARE
.
Defining the Color-cube and Pyramid
The color-cube is made up of 6 quads. Each quad is made up of 4 vertices, defined in counter-clockwise (CCW) order, so that the normal vector is pointing out, indicating the front face. All 4 vertices have the same color. The color-cube is defined in its local space (called model space) with origin at the center of the cube with sides of 2 units.
Similarly, the pyramid is made up of 4 triangles (without the base). Each triangle is made up of 3 vertices, defined in CCW order. The 5 vertices of the pyramid are assigned different colors. The color of the triangles are interpolated (and blend smoothly) from its 3 vertices. Again, the pyramid is defined in its local space with origin at the center of the pyramid.
Model Transform
The objects are defined in their local spaces (model spaces). We need to transform them to the common world space, known as model transform.
To perform model transform, we need to operate on the so-called model-view matrix (OpenGL has a few transformation matrices), by setting the current matrix mode to model-view matrix:
glMatrixMode(GL_MODELVIEW); // To operate on model-view matrix
We perform translations on cube and pyramid, respectively, to position them on the world space:
// Color-cube
glLoadIdentity(); // Reset
model-view matrix
glTranslatef(1.5f, 0.0f, -7.0f); // Move right and into the screen
// Pyramid
glLoadIdentity();
glTranslatef(-1.5f, 0.0f, -6.0f); // Move left and into the screen
View Transform
The default camera position is:
gluLookAt(0.0, 0.0, 0.0, 0.0, 0.0, -100.0, 0.0, 1.0, 0.0)
That is, EYE=(0,0,0)
at the origin, AT=(0,0,-100)
pointing at negative-z axis (into the screen), and UP=(0,1,0)
corresponds to y-axis.
OpenGL graphics rendering pipeline performs so-called view
transform to bring the world space to
the camera's view space. In the case of the
default camera position, no transform is needed.
Try using the gluLookAt function, change the eye position, where the camera is looking and the up vector. Explain what happens. Try setting the up vector to (0.0, -1.0, 0.0). This should be done in the reshape callback after the gluPerspective call.
Try moving around the two
objects using only the gluLookAt function. The
movement should be in the z=0 plane. Try to look at
the back face of each object. Then the right and left
sides of the cube.
Did you get it to work?
Viewport Transform
void reshape(GLsizei
width, GLsizei height) {
glViewport(0, 0, width, height);
The graphics sub-system calls reshape()
when
the window first appears and whenever the window is resized,
given the new window's width
and height
,
in pixels. We set our application viewport to cover the
entire window, top-left corner at (0, 0) of width
and height
, with default minZ
of
0 and maxZ
of 1. We also use the same aspect
ratio of the viewport for the projection view frustum to
prevent distortion. In the viewport, a pixel has (x, y)
value as well as z-value for depth processing.
Projection Transform
GLfloat aspect =
(GLfloat)width / (GLfloat)height; // Compute aspect ratio of window
glMatrixMode(GL_PROJECTION); //
To operate on the Projection matrix
glLoadIdentity(); // Reset
gluPerspective(45.0f, aspect, 0.1f, 100.0f); // Perspective projection: fovy,
aspect, near, far
A camera has limited field of view. The projection
models the view captured by the camera. There are two types
of projection: perspective projection and orthographic
projection. In perspective projection, object further to the
camera appears smaller compared with object of the same size
nearer to the camera. In orthographic projection, the
objects appear the same regardless of the z-value.
Orthographic projection is a special case of perspective
projection where the camera is placed very far away. We
shall discuss the orthographic projection in the later
example.
To set the projection, we need to operate on the projection matrix. (Recall that we operated on the model-view matrix in model transform.)
We set the matrix mode to projection matrix and reset the
matrix. We use the gluPerspective()
to enable
perspective projection, and set the fovy (view angle from
the bottom-plane to the top-plane), aspect ratio
(width/height), zNear and zFar of the View Frustum
(truncated pyramid). In this example, we set the fovy to
45°. We use the same aspect ratio as the viewport to avoid
distortion. We set the zNear to 0.1 and zFar to 100
(z=-100). Take that note the color-cube (1.5, 0, -7) and the
pyramid (-1.5, 0, -6) are contained within the View Frustum.
The projection transform transforms the view
frustum to a 2x2x2 cuboid clipping-volume
centered on the plane (z=0). The subsequent viewport
transform transforms the clipping-volume to
the viewport in screen space. The viewport is set
earlier via the glViewport()
function.
Try changing the parameters of
the gluProjection function. This should be done in the
reshape callback.
Try a fovy of 60 degrees, then 90 degrees, what about
120 degrees? Explain what happens. Try playing with
the near and far clipping plane values. List the
values you tried and explain what happened. Try
modifying the aspect ratio, divide by 2.0 multiply by
2.0. What happens?
Do you understand how the
perspective function works?
2. Example 2 - This is to slow down animations
using the GLUT Timer function, instead of the idle callback.
Let's modify the previous example to carry out animation (rotating the cube and pyramid).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
/* * 3D Shapes with animation */ #include <GLUT/glut.h> // GLUT, include glu.h and gl.h /* Global variables */ char title[] = "3D Shapes with animation"; GLfloat anglePyramid = 0.0f; // Rotational angle for pyramid [NEW] GLfloat angleCube = 0.0f; // Rotational angle for cube [NEW] int refreshMills = 15; // refresh interval in milliseconds [NEW] /* Initialize OpenGL Graphics */ void initGL() { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set background color to black and opaque glClearDepth(1.0f); // Set background depth to farthest glEnable(GL_DEPTH_TEST); // Enable depth testing for z-culling glDepthFunc(GL_LEQUAL); // Set the type of depth-test glShadeModel(GL_SMOOTH); // Enable smooth shading glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Nice perspective corrections } /* Handler for window-repaint event. Called back when the window first appears and whenever the window needs to be re-painted. */ void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear color and depth buffers glMatrixMode(GL_MODELVIEW); // To operate on model-view matrix // Render a color-cube consisting of 6 quads with different colors glLoadIdentity(); // Reset the model-view matrix glTranslatef(1.5f, 0.0f, -7.0f); // Move right and into the screen glRotatef(angleCube, 1.0f, 1.0f, 1.0f); // Rotate about (1,1,1)-axis [NEW] glBegin(GL_QUADS); // Begin drawing the color cube with 6 quads // Top face (y = 1.0f) // Define vertices in counter-clockwise (CCW) order with normal pointing out glColor3f(0.0f, 1.0f, 0.0f); // Green glVertex3f( 1.0f, 1.0f, -1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Bottom face (y = -1.0f) glColor3f(1.0f, 0.5f, 0.0f); // Orange glVertex3f( 1.0f, -1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // Front face (z = 1.0f) glColor3f(1.0f, 0.0f, 0.0f); // Red glVertex3f( 1.0f, 1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // Back face (z = -1.0f) glColor3f(1.0f, 1.0f, 0.0f); // Yellow glVertex3f( 1.0f, -1.0f, -1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); // Left face (x = -1.0f) glColor3f(0.0f, 0.0f, 1.0f); // Blue glVertex3f(-1.0f, 1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // Right face (x = 1.0f) glColor3f(1.0f, 0.0f, 1.0f); // Magenta glVertex3f(1.0f, 1.0f, -1.0f); glVertex3f(1.0f, 1.0f, 1.0f); glVertex3f(1.0f, -1.0f, 1.0f); glVertex3f(1.0f, -1.0f, -1.0f); glEnd(); // End of drawing color-cube // Render a pyramid consists of 4 triangles glLoadIdentity(); // Reset the model-view matrix glTranslatef(-1.5f, 0.0f, -6.0f); // Move left and into the screen glRotatef(anglePyramid, 1.0f, 1.0f, 0.0f); // Rotate about the (1,1,0)-axis [NEW] glBegin(GL_TRIANGLES); // Begin drawing the pyramid with 4 triangles // Front glColor3f(1.0f, 0.0f, 0.0f); // Red glVertex3f( 0.0f, 1.0f, 0.0f); glColor3f(0.0f, 1.0f, 0.0f); // Green glVertex3f(-1.0f, -1.0f, 1.0f); glColor3f(0.0f, 0.0f, 1.0f); // Blue glVertex3f(1.0f, -1.0f, 1.0f); // Right glColor3f(1.0f, 0.0f, 0.0f); // Red glVertex3f(0.0f, 1.0f, 0.0f); glColor3f(0.0f, 0.0f, 1.0f); // Blue glVertex3f(1.0f, -1.0f, 1.0f); glColor3f(0.0f, 1.0f, 0.0f); // Green glVertex3f(1.0f, -1.0f, -1.0f); // Back glColor3f(1.0f, 0.0f, 0.0f); // Red glVertex3f(0.0f, 1.0f, 0.0f); glColor3f(0.0f, 1.0f, 0.0f); // Green glVertex3f(1.0f, -1.0f, -1.0f); glColor3f(0.0f, 0.0f, 1.0f); // Blue glVertex3f(-1.0f, -1.0f, -1.0f); // Left glColor3f(1.0f,0.0f,0.0f); // Red glVertex3f( 0.0f, 1.0f, 0.0f); glColor3f(0.0f,0.0f,1.0f); // Blue glVertex3f(-1.0f,-1.0f,-1.0f); glColor3f(0.0f,1.0f,0.0f); // Green glVertex3f(-1.0f,-1.0f, 1.0f); glEnd(); // Done drawing the pyramid glutSwapBuffers(); // Swap the front and back frame buffers (double buffering) // Update the rotational angle after each refresh [NEW] anglePyramid += 0.2f; angleCube -= 0.15f; } /* Called back when timer expired [NEW] */ void timer(int value) { glutPostRedisplay(); // Post re-paint request to activate display() glutTimerFunc(refreshMills, timer, 0); // next timer call milliseconds later } /* Handler for window re-size event. Called back when the window first appears and whenever the window is re-sized with its new width and height */ void reshape(GLsizei width, GLsizei height) { // GLsizei for non-negative integer // Compute aspect ratio of the new window if (height == 0) height = 1; // To prevent divide by 0 GLfloat aspect = (GLfloat)width / (GLfloat)height; // Set the viewport to cover the new window glViewport(0, 0, width, height); // Set the aspect ratio of the clipping volume to match the viewport glMatrixMode(GL_PROJECTION); // To operate on the Projection matrix glLoadIdentity(); // Reset // Enable perspective projection with fovy, aspect, zNear and zFar gluPerspective(45.0f, aspect, 0.1f, 100.0f); } /* Main function: GLUT runs as a console application starting at main() */ int main(int argc, char** argv) { glutInit(&argc, argv); // Initialize GLUT glutInitDisplayMode(GLUT_DOUBLE|GLUT_DEPTH); // Enable double buffered mode glutInitWindowSize(640, 480); // Set the window's initial width & height glutInitWindowPosition(50, 50); // Position the window's initial top-left corner glutCreateWindow(title); // Create window with the given title glutDisplayFunc(display); // Register callback handler for window re-paint event glutReshapeFunc(reshape); // Register callback handler for window re-size event initGL(); // Our own OpenGL initialization glutTimerFunc(0, timer, 0); // First timer call immediately [NEW] glutMainLoop(); // Enter the infinite event-processing loop return 0; } |
The new code is:
GLfloat anglePyramid =
0.0f; // Rotational angle for
pyramid [NEW]
GLfloat angleCube = 0.0f; //
Rotational angle for cube [NEW]
int refreshMills = 15; //
refresh interval in milliseconds [NEW]
We define two global variables to keep track of the
current rotational angles of the cube and pyramid. We also
define the refresh period as 15 msec (66 frames per second).
void timer(int value) {
glutPostRedisplay(); // Post re-paint request to
activate display()
glutTimerFunc(refreshMills, timer, 0);
// next timer call
milliseconds later
}
To perform animation, we define a function called timer()
,
which posts a re-paint request to activate display()
when the timer expires, and then runs the timer again. In main()
,
we perform the first timer()
call via glutTimerFunc(0,
timer, 0)
.
glRotatef(angleCube,
1.0f, 1.0f, 1.0f); // Rotate
the cube about (1,1,1)-axis [NEW]
......
glRotatef(anglePyramid, 1.0f, 1.0f, 0.0f); // Rotate about the (1,1,0)-axis
[NEW]
......
anglePyramid += 0.2f; //
update pyramid's angle
angleCube -= 0.15f; // update
cube's angle
In display()
, we rotate the cube and
pyramid based on their rotational angles, and update the
angles after each refresh.