我们这一篇文章将二维世界扩展到三维,通过上一篇文章我们已经明白了,几何变换和观察变换本质是一样的,所以暂且忽视三维几何变换,直接进入三维观察的世界。
三维观察流水线包括五个步骤:建模变换、观察变换、投影变换、规范化变换和裁剪、视口变换。物体从模型坐标系转换到世界坐标系的过程叫做建模变换,观察变换是将世界坐标系中建好的场景模型转换到选择的观察坐标系。观察坐标系定义了观察参数,包括投影平面的位置和方向。然后再投影平面上定义与照相机镜头对应的二维裁剪窗口,并建立三维裁剪区域。该裁剪区域称为观察体,投影变换将场景的观察坐标描述转换为投影平面的坐标位置。之后,对象映射到规范化坐标系,所有在观察体外的部分被裁剪掉。裁剪操作可以在所有与设备无关的坐标变换。与在二维观察中一样,视口边界可以在规范化坐标系或设备坐标系中指定。最后一步是视口变换,将观察坐标系映射到设备坐标系的指定显示窗口中。
话不多说,我们直接来看最直观的示例。
#include <GL/glut.h>
GLint winWidth = 600, winHeight = 600;
GLfloat x0 = 100.0, y0 = 50.0, z0 = 50.0;//观察点位置
GLfloat xref = 50.0, yref = 50.0, zref = 0.0;//注视点位置
GLfloat Vx = 0.0, Vy = 1.0, Vz = 0.0;//向上向量
GLfloat xwMin = -40.0, ywMin = -60.0, xwMax = 40.0, ywMax = 60.0;//裁剪窗口坐标
GLfloat dnear = 25.0, dfar = 125.0;//裁剪平面
void init(void)
{
glClearColor(1.0, 1.0, 1.0, 0.0);
glMatrixMode(GL_MODELVIEW);
gluLookAt(x0, y0, z0, xref, yref, zref, Vx, Vy, Vz);//观察函数
glMatrixMode(GL_PROJECTION);
glFrustum(xwMin, xwMax, ywMin, ywMax, dnear, dfar);//视锥体
}
void displayFcn(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.0, 1.0, 0.0);
glPolygonMode(GL_FRONT, GL_FILL);
glPolygonMode(GL_BACK, GL_LINE);
glBegin(GL_QUADS);//绘制正方形
glVertex3f(0.0, 0.0, 0.0);
glVertex3f(100.0, 0.0, 0.0);
glVertex3f(100.0, 100.0, 0.0);
glVertex3f(0.0, 100.0, 0.0);
glEnd();
glFlush();
}
void reshapeFcn(GLint newWidth, GLint newHeight)
{
glViewport(0, 0, newWidth, newHeight);//活动视口
winWidth = newWidth;
winHeight = newHeight;
}
void main(int argc, char**argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RED);
glutInitWindowPosition(50, 50);
glutInitWindowSize(winWidth, winHeight);
glutCreateWindow("Perspective view of A Square");
init();
glutDisplayFunc(displayFcn);
glutReshapeFunc(reshapeFcn);
glutMainLoop();
}
示例中我们在三维世界绘制了一个正方形,但是由于我们采用透视投影模式,最后显示的是一个绿色的梯形。那好,我们下面就给大家解析一下实现该效果的函数。
对可用glMatrixMode选择的四种模式(建模观察、投影、纹理和颜色)中的每一种,OpenGL维护一个矩阵栈。开始每一个栈仅仅包含单位矩阵,栈顶的矩阵称为该模式的“当前矩阵”。在指定观察和几何变换后,建模观察栈顶是一个应用于场景的观察变换和各种几何变换的4*4复合矩阵。
OpenGL有两个函数用来处理栈中的矩阵:
glPushMatrix();//复制活动栈顶的当前矩阵并将其存入第二个栈位置
glPopMatrix();//破坏栈顶矩阵,栈的第二个矩阵成为当前矩阵
该示例首先指定了建模观察模式,并调用函数gluLookAt(x0, y0, z0, xref, yref, zref, Vx, Vy, Vz)计算建模观察矩阵。该函数前三个参数指定观察参考系原点(观察点位置),之后三个参数指定参考点位置(视点位置),后三个参数指定向上向量(人头的方向)。
之后,该段代码指定了投影模式,并调用glFrustum(xwMin, xwMax, ywMin, ywMax, dnear, dfar)计算投影矩阵。该函数指定了一个对称棱台观察体或一个斜棱台观察体。该函数前四个参数设定近平面上裁剪窗口的坐标,而后两个参数指定从坐标系原点沿负z轴到近和远裁剪平面的距离。
除了这个函数外,正交投影参数用下列参数选择:
glOrtho(xwmin,xwmax,ywmin,ywmax,dnear,dfar);
该函数生成一个垂直于观察平面(近裁剪平面)的平行投影。
对称透视投影棱台观察体用下列GLU函数建立:
gluPerspective(theta,aspect,dnear,dfar);
该函数前两个参数定义近裁剪平面上的裁剪窗口尺寸和位置,后两个参数指定观察点到近和远裁剪平面的距离。参数theta表示视场角,即上下裁剪平面间的夹角,参数aspect赋以裁剪窗口纵横比。
glFrustum和gluPerspective函数都用于生成透视投影视图,对于透视投影来说,透视参考点都是观察坐标系原点且近裁剪平面是观察平面。