<- previous index next ->
Perspective Viewing, Resize Choices and Transformation Matrices. This lecture covers the technical details and mathematics that makes a 2D screen look line a 3D scene. In the simplest case, computing the i,j screen position of a vertex at world coordinates x,y,z is accomplished by multiplying a 4 by 4 perspective matrix by a 4 by 4 model matrix times the x,y,z,w vertex vector, then placing the i,j in the viewport.Shows the frustum that contains the 3D "World" that is to be presented to the user. Any objects with more negative Z than "near" are clipped, any objects with more negative values of Z than "far" are clipped. Any objects outside of the X or Y of the frustum are clipped. The positive Z axis is considered to come out of the screen toward the viewer. Name the vertices on the diagram as: On the 'near' rectangle, the lower left is 'xmin' 'ymin' the upper left is 'xmin' 'ymax' the lower right is 'xmax' 'ymin' the upper right is 'xmax' 'ymax' The distance from the eye to near is 'near' a positive number The distance from the eye to far is 'far' a positive number The 4 by 4 perspective matrix is |2*near/(xmax-xmin) 0.0 (xmax+xmin)/(xmax-xmin) 0.0 | | | | 0.0 2*near/(ymax-ymin) (ymax+ymin)/(ymax-ymin) 0.0 | | | | 0.0 0.0 -(far+near)/(far-near) -2*far*near/(far-near)| | | | 0.0 0.0 -1.0 0.0 | The OpenGL call to create this perspective matrix is: glFrustum(xmin, xmax, ymin, ymax, near, far); An alternate call uses the vertical the eye position, looking at the center of interest, + on 'far', and Y up=1, X up=0, Z up=0 is: gluLookAt(eyex, eyey, eyez, coix, coiy, coiz, Xup, Yup, Zup); Yet another alternative using the angle, field of view, and w/h aspect ratio is: gluPerspective(angle, w/h, near, far); The model view matrix begins as the identity matrix and is multiplied by the users rotations, scaling and translations. The world coordinates may be in any system of physical units, yet all coordinates must be in the same units. The six degrees of freedom for a solid 3D object are to to translate in three dimensions and rotate about three axis. The translation matrix to translate 0,0,0 to x,y,z is | 1.0 0.0 0.0 x | | 0.0 1.0 0.0 y | unused translations are 0.0 | 0.0 0.0 1.0 z | | 0.0 0.0 0.0 1.0 | glTranslatef(x, y, z); The scaling matrix to scale x by sx, y by sy and z by sz is | sx 0.0 0.0 0.0 | | 0.0 sy 0.0 0.0 | unused scales are 1.0 | 0.0 0.0 sz 0.0 | | 0.0 0.0 0.0 1.0 | glScalef(sx, sy, sz); The rotation matrix by angle a about the X axis is | 1.0 0.0 0.0 0.0 | | 0.0 cos a -sin a 0.0 | | 0.0 sin a cos a 0.0 | | 0.0 0.0 0.0 1.0 | glRotatef(a, 1.0, 0.0, 0.0); The rotation matrix by angle a about the Y axis is | cos a 0.0 sin a 0.0 | | 0.0 1.0 0.0 0.0 | | -sin a 0.0 cos a 0.0 | | 0.0 0.0 0.0 1.0 | glRotatef(a, 0.0, 1.0, 0.0); The rotation matrix by angle a about the Z axis is | cos a -sin a 0.0 0.0 | | sin a cos a 0.0 0.0 | | 0.0 0.0 1.0 0.0 | | 0.0 0.0 0.0 1.0 | glRotatef(a, 0.0, 0.0, 1.0); A user world coordinate vertex p = x, y, z, w (w=1.0) is transformed into pp by perspective matrix times model view matrix times p is pp To get screen coordinates, given the screen width w, and screen height h, screen x = w * ((pp.x/pp.z)-xmin)/(xmax-xmin) screen y = h * ((pp.y/pp.z)-ymin)/(ymax-ymin) Trying to check that the equations are correct, the program demo_mat.c writes out OpenGL matrices. The output is demo_mat.out The equations are coded in check_mat.c The output is check_mat.out It seems that OpenGL stores the matrix column major (Fortran style) while the "C" program stores the matrix row major, causing the printout to appear to be the transpose. The same geometry and same data were used in both programs. The final result from both is essentially the same. output from demo_mat.c OpenGL 0.5, 1.5, 2.5 at win x=25.798641, y=345.927915, z=0.827098 output from check_mat.c x scr, y scr=25.798841, 345.927778 at relative z=0.827098) width, height =300.000000, 400.000000 In OpenGL or equivalently in your code, you can save the present matrix and start with a new identity matrix, do transformations, cause actions, then revert back the the prior matrix. glPushMatrix(); glLoadIdentity(); glRotatef(theta[0], 1.0, 0.0, 0.0); glRotatef(theta[1], 0.0, 1.0, 0.0); glRotatef(theta[2], 0.0, 0.0, 1.0); glTranslatef(pos[0], pos[1], pos[2]); /* use the Model view matrix to do something */ glPopMatrix(); /* a possible Reshape, it happens on first expose and every change */ glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(w <= h) /* e.g size = 2.0, x in -2.0 .. 2.0 */ glOrtho(-size, size, /* xmin, xmax */ -size*(GLfloat)h/(GLfloat)w, size*(GLfloat)h/(GLfloat)w, /* ymin, ymax */ -10.0, 10.0); /* near in real Z value, far as real Z value */ else glOrtho(-size*(GLfloat)w/(GLfloat)h, size*(GLfloat)w/(GLfloat)h, -size, size, /* Y is size, w/h for X */ -10.0, 10.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); /* go do Display or something */
<- previous index next ->