<- 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 positive 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 */
The simplest equations for orthographic projection are given by:
Xs, Ys are 2D screen coordinates. Assume 0,0 at lower left.
X,Y,Z are 3D world coordinates. Assume 0,0,0 at lower left.
Xs = X + cos(theta)*Z
Ys = Y + sin(theta)*Z
Scaling and offsets may be provided as required.
Theta is the angle up from the Xp axis to the where the 3D Z axis is drawn
Doing your own 3D projection may be easiest as orthographic.
For example, in this simple 3D shape entry program, the entry is
in XY (front view), XZ (top view), or YZ (side view) plane then
shown in three views and as orthographic.
The source is draw3D3.java
with output:
<- previous index next ->