Advanced Graphics Algorithms Ying Zhu Georgia State University Lecture 07 View & Projection
Outline Camera and view transformation View volume and projection transformation Orthographic projection Perspective projection View port transformation
Outline
3D Scenes and Camera In computer graphics, we often use terms that are analog to theatre or photography A virtual world is a “scene” Objects in the scene are “actors” There is a “camera” to specify viewing position and viewing parameters. Viewing and projection is about how to position the camera and project the 3D scene to a 2D screen.
Camera in computer graphics Computer graphics uses the pinhole camera model This results in perfectly sharp images No depth of field or motion blur Real cameras use lenses with variable aperture sizes This causes depth-of-field: out-of-focus objects appear blurry
Depth of field Depth of view is an important part of the storytelling process Direct viewer’s eyes to a certain area of the scene Depth of view can be faked in CG, but needs extra work
Motion Blur Camera in computer graphics does not generate motion blur either Again, it can be faked in CG but performance may suffer
Aspects of Viewing There are three aspects of the viewing process Positioning the camera Setting the model-view matrix Selecting a lens Setting the projection matrix Clipping Setting the view volume
Positioning the camera Each camera has a location and a direction The purpose of viewing transformation is to position the camera. In OpenGL, by default the camera is located at origin and points in the negative z direction
Positioning the camera What if your camera is not at the origin, facing –Z axis? You use gluLookAt() function call to specify camera location and orientation http://pyopengl.sourceforge.net/documentation/ma nual/gluLookAt.3G.html Note the need for setting an up direction Example: glMatrixMode(GL_MODELVIEW): glLoadIdentity(); gluLookAt(1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0., 1.0. 0.0);
Viewing Transformation in OpenGL gluLookAt(eyex, eyey, eyez, atx, aty, atz, upx, upy, upz) atx, aty, atz will be mapped to the center of the 2D window
Moving the Camera No matter where the camera is initially, eventually it needs to be transformed to the origin, facing –Z axis in order to make the subsequent stages in the graphics pipeline very efficient But the picture taken by the (virtual) camera should be the same before and after the camera transformation How to achieve this? Construct a view transformation matrix to transform the camera to the origin, facing -Z Apply the view transformation matrix to every object in the virtual scene This view transformation is not visible Internally gluLookAt() creates such a matrix
What does gluLookAt() do? gluLookAt(eyex, eyey, eyez, atx, aty, atz, upx, upy, upz) is equivalent to glMultMatrixf(M); // post-multiply M with current model-view matrix glTranslated(-eyex, -eyey, -eyez); Where M = u, n, v are unit vectors.
What does gluLookAt() do? Internally OpenGL creates a view matrix and post- multiplies it with the current model-view matrix (normally an identity matrix) void reshape (int w, int h) { glViewport (0, 0, (GLsizei) w, (GLsizei) h); glMatrixMode (GL_PROJECTION); glLoadIdentity (); gluPerspective(65.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0., 1.0. 0.0); } Current_MV_matrix current_MV_matrix * view_matrix
gluLookAt() gluLookAt() should be called before any OpenGL model transformation function calls That’s why gluLookAt() is normally placed in reshape() because reshape() is called before display() Normally model transformation calls are placed in display() function If you place transforms calls in other functions, pay attention to the call sequence
gluLookAt() Why? Because the view transformation matrix needs to be applied to every object (vertices) in the virtual scene All the model transformations have to be finished before the view transformation. Otherwise the realism is lost
Projection Transformation Viewing transformation transform the camera to the origin and look-at direction with negative Z axis. Before one can actually render a scene, all relevant objects in the scene must be projected to some kind of plane or into some kind of simple volume After that, clipping and rendering are performed Projection transformation maps all 3D objects onto the viewing plane to create a 2D image.
Projection transformation
View volume (frustum) We need to define a view volume for projection Everything inside the view volume will be projected to the 2D plane Everything outside the view volume will be “clipped” and ignored The job of projection transformation is to transform the view volume (and everything in it) to a canonical view volume Canonical view volume: a unit cube centered at origin It has minimum corner of (-1, -1, -1) and maximum corner of (1, 1, 1)
View volume (frustum)
Canonical view volume The coordinates in this volume is called Normalized Device Coordinates (NDC) The reason for transforming into the canonical view volume is that clipping is more efficiently performed there especially in hardware implementation The canonical view volume can also be conveniently mapped to 2D window by view port transformation
Projection transformation Two basic projections in OpenGL: Orthographic projections Useful in applications where relative length judgment are important Can also yield simplifications where perspective would be too expensive, e.g. in some medical visualization applications Perspective projections Used in most interactive 3D applications
Orthographic Projection Projection lines are orthogonal to projection surface
Orthographic Projection Projection plane parallel to principal face Usually form front, top, side views isometric (not multiview orthographic view) front in CAD and architecture, we often display three multiviews plus isometric side top
Orthographic Projection Preserves both distances and angles Shapes preserved Can be used for measurements Building plans Manuals Cannot see what object really looks like because many surfaces hidden from view Often we add the isometric view
Orthographic Projection in OpenGL Specify an orthographic view volume in OpenGL glOrtho(left,right,bottom,top,near,far) Anything outside the viewing frustum is clipped. near and far are distances from camera
Orthographic projection The matrix created by glOrtho() will transform the orthographic view volume to canonical view volume A cube that extends from -1 to 1 in x, y, z. This is often called the clip space. The next steps are clipping, perspective division, and view port transformation We’ll discuss clipping later
Perspective Projection Projectors converge at center of projection
Advantages & Disadvantages Objects further from viewer are projected smaller than the same sized objects closer to the viewer Make images look realistic Equal distances along a line are not projected into equal distances Angles preserved only in planes parallel to the projection plane More difficult to construct by hand than orthographic projection (but not more difficult by computer)
Perspective Projection in OpenGL We define the perspective view volume by calling glFrustum(left,right,bottom,top,near,far) Again, near and far are distances from camera However, with glFrustum it is often difficult to get the desired view
Perspective Projection in OpenGL gluPerpective(fovy, aspect, near, far) often provides a better interface However, it assumes that viewing window is centered about the negative Z axis aspect = w/h
Perspective Projection The job of perspective projection is to transform this view volume (and everything in it) to a canonical view volume Everything outside of this volume will be clipped and ignored
What does gluPerspective() do? Internally OpenGL creates the following matrix The current projection matrix is multiplied by this matrix and the result replaces the current matrix
Perspective Projection in OpenGL There is a current projection matrix in OpenGL Before calling gluPerspective(), you need to tell OpenGL that you are modifying the projection matrix The following code would normally be part of the initialization routine glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(fovy, aspect, near, far); glMatrixMode(GL_MODELVIEW);
Aspect Ratio The aspect ratio in gluPerspective should match the aspect ratio of the associated viewport Normally, gluPerspective(fov, w/h, near, far) W is the width of your window, h is the height of your window You want the aspect ratio of your perspective view volume to match the aspect ratio of your window Otherwise, your final image will look distorted E.g. if you set aspect ratio to be a constant value, then when you resize your window, your image will look distorted
Perspective projection The matrix created by glFrustum() or gluPerspective() will transform the perspective view volume to a canonical view volume A cube that extends from -1 to 1 in x, y, z. This is often called the clip space. The next steps are clipping, perspective division, and view port transformation
The journey of a vertex so far The vertex will be transformed first by the model-view matrix and then by the projection matrix p’ = P x V x M x p E.g. glTranslatef(); glRotatef(); … E.g. gluLookAt() E.g. gluPerspective()
After the perspective projection As the result of the these transformations, vertex p is transformed to p’ After perspective projection, the 4th element of the homogenous coordinates of p’ may not be 1 any more We have to homogenize it to get the real coordinates Divide every element by the 4th element (called w) Normalized Device Coordinates After perspective transformation
Perspective division This operation is also called perspective division The homogenized coordinates are called Normalized Device Coordinates This is the coordinates for point p in the canonical view volume Now we are ready to map this point to the window
View port transformation
View port transformation View port transformation transforms x and y from normalized device coordinates to window coordinates Note that z value of the normalized device coordinates are not transformed in this stage Because z axis is orthogonal to the window (2D plane) and Z values have no effect in the mapping Remember our eye is looking down negative Z But this z value is not lost. It will be passed down to the rasterizer stage Will be used in scan conversion and depth buffer test
View port transformation in OpenGL glViewport(GLint x, GLint y, GLsizei width, GLsizei height) x, y: Specify the lower left corner of the viewport rectangle, in pixels. The initial value is (0,0). width, height: Specify the width and height of the viewport.
What does glViewport() do? Let (xnd, ynd) be normalized device coordinates. Then the window coordinates (xw, yw) are computed as follows: xw = (xnd + 1)(width / 2) + x yw = (ynd + 1)(height / 2) + y It’s a matrix multiplication too. Now we know where to place this particular vertex in the final 2D image
glViewport() glViewport(GLint x, GLint y, GLsizei width, GLsizei height) X and y are normally set to (0, 0) Width and height are normally set to window width and height This means your 2D image size matches your window size You can make your image smaller or bigger than the window by adjusting those values E.g. put multiple images in one window
The journey of a vertex
Why do we spend so much time on transformations? Because that’s where many people find OpenGL programs hard to understand You need to know those low level details when you write shader programs Your vertex shader, not OpenGL, are supposed to handle the model, view, and projection transformations In your vertex shader, you’ll have to deal directly with transformation matrices and matrix multiplications
Example /* planet.c*/ #include <GL/glut.h> static int year = 0, day = 0; void init(void) { // Specify the color (black) used to clear the frame buffer (but not actually do it) glClearColor (0.0, 0.0, 0.0, 0.0); glShadeModel (GL_FLAT); // choose shading model }
Example void display(void) { glClear (GL_COLOR_BUFFER_BIT); // Actually clear the frame buffer glColor3f (1.0, 1.0, 1.0); // set the color for each vertex glPushMatrix(); // current modelview matrix is the view matrix glutWireSphere(1.0, 20, 16); // draw sun, internally call glVertex3f() glRotatef ((GLfloat) year, 0.0, 1.0, 0.0); // Multiply modelview matrix with a // rotation matrix glTranslatef (2.0, 0.0, 0.0); // Multiply current modelview matrix with a // translation matrix glRotatef ((GLfloat) day, 0.0, 1.0, 0.0); // Multiply current modelview matrix // with a rotation matrix glutWireSphere(0.2, 10, 8); // draw smaller planet, internally call // glVertex3f() glPopMatrix(); // current modelview matrix is view matrix again glutSwapBuffers(); // swap double buffers }
Example void reshape (int w, int h) { glViewport (0, 0, (GLsizei) w, (GLsizei) h); // Define view port transformation glMatrixMode (GL_PROJECTION); // We’re going to modify current projection matrix glLoadIdentity (); // initialize projection matrix to the identity matrix // create perspective projection matrix and post-multiply it with the current // projection matrix gluPerspective(60.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0); glMatrixMode(GL_MODELVIEW); // We’re going to modify current model-view matrix glLoadIdentity(); // initialize modelview matrix to identity matrix // place camera, internally generates a view matrix and // then post-multiply the current modelview matrix with the view matrix gluLookAt (0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); }
Example void keyboard (unsigned char key, int x, int y) { switch (key) { case 'd': day = (day + 10) % 360; glutPostRedisplay(); // force a call to display() break; case 'D': day = (day - 10) % 360; glutPostRedisplay(); case 'y': year = (year + 5) % 360; case 'Y': year = (year - 5) % 360; case 27: exit(0); default: }
Example int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB); glutInitWindowSize (500, 500); glutInitWindowPosition (100, 100); glutCreateWindow (argv[0]); init (); // clear frame buffer and select shading model glutDisplayFunc(display); // register event handlers glutReshapeFunc(reshape); glutKeyboardFunc(keyboard); glutMainLoop(); // Start the event loop return 0; }
Summary Viewing and projection is about how to position the camera and project the 3D scene to a 2D screen. In OpenGL, view transformation is conducted by manipulating modelview matrix. Projection transformation is conducted by manipulating projection matrix. Viewport transformation is conducted by glViewport()
Readings OpenGL Programming Guide: chapter 3 OpenGL FAQ on Viewing http://www.opengl.org/resources/faq/technical/vi ewing.htm
Next Lecture Lighting and shading Now we know where to place the vertices in the final 2D image, the next step is to calculate their colors