本系列教程是自己学习OpenGL时的一些笔记,分享出来希望对大家有所帮助。因为本人是学数学出身,内容侧重于理论推导。
1. 坐标变换序列
对于OpenGL来说,从物体的世界坐标系到显示器的屏幕坐标,需要经过四个变换阶段,如下图所示。
首先通过Model-View Matrix将Obect Coordinates变换为Eye Coordinates(以观察者为原点,观察方向为Z轴正方向所定义的坐标系),然后通过Projection Matrix将Eye Coordinates变换为Clip Coordinates(这一步是投影变换,定义了观察者所能看到的范围),接着通过Perspective Division将Clip Coordinates变换为Normalized Device Coordinates(这一步是对坐标进行标准化处理,一般自动完成),最后通过Viewport Transformation将Normalized Device Coordinates变换为Window Coordinates(将投影平面变换为显示器上的窗口坐标,这样就可以直接显示图形了)。
在计算机的内部,点采用齐次坐标的形式来表示。这样就可以把一个点的坐标变换表示为变换矩阵和点的坐标向量的乘法。例如,假设点P的坐标为(x, y, z, w)',变换矩阵为M,则变换后的点P'的坐标(x', y', z', w')' = M * (x, y, z, w)'。(注意:点的坐标一般用列向量表示,v'表示向量v的转置。)
2. 指定后序矩阵的类型
#define GL_MODELVIEW 0x1700
#define GL_PROJECTION 0x1701
#define GL_TEXTURE 0x1702
glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, &maxModelViewStackDepth);
glGetIntegerv(GL_MAX_PROJECTION_STACK_DEPTH, &maxProjectionStackDepth);
glGetIntegerv(GL_MAX_TEXTURE_STACK_DEPTH, &maxTextureStackDepth);
glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &curModelViewStackDepth);
glGetIntegerv(GL_PROJECTION_STACK_DEPTH, &curProjectionStackDepth);
glGetIntegerv(GL_TEXTURE_STACK_DEPTH, &curTextureStackDepth);
(4) void glRotated (GLdouble angle, GLdouble x, GLdouble y, GLdouble z);
void glRotatef (GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
对模型按照指定的旋转轴逆时针旋转一定角度,也就是将当前变换矩阵乘以一个旋转矩阵。angle表示旋转角度,(x, y, z)表示旋转轴(以原点为起点,以(x, y, z)为终点的向量)。可按照以下步骤来构造该旋转矩阵。
第一种方法是OpenGL官方文档上给出的公式。
第二种方法是Rodriguez公式,这两种表示方式是完全等价的。
(5) void glTranslated (GLdouble x, GLdouble y, GLdouble z);
void glTranslatef (GLfloat x, GLfloat y, GLfloat z);
对模型按照指定的偏移量进行平移,也就是将当前变换矩阵乘以一个旋转矩阵。x,y和z表示各个坐标方向的偏移量。平移矩阵具体形式如下。
(6) void glScaled (GLdouble x, GLdouble y, GLdouble z);
void glScalef (GLfloat x, GLfloat y, GLfloat z);
对模型按照指定的因子进行缩放,也就是将当前变换矩阵乘以一个缩放矩阵。x,y和z表示各个坐标方向的缩放因子。缩放矩阵具体形式如下。
(7) void gluLookAt (GLdouble eyex, GLdouble eyey, GLdouble eyez, GLdouble centerx, GLdouble centery, GLdouble centerz, GLdouble upx, GLdouble upy, GLdouble upz);
指定观察者坐标系,可将Object Coordinates变换为Eye Coordinates。该函数定义在glu.h文件中。
该函数所完成的功能等价于下面两条语句。
glMultMatrixd(M);
glTranslated(-eyex, -eyey, -eyez);
其中,矩阵M的定义如下所示。
4. 投影变换(Projection Transformation)
投影变换包括透视投影和正交投影,OpenGL提供如下函数来实现投影变换。
(1) void glFrustum (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);
定义了透视投影矩阵P,将点(left, bottom, -zNear, 1)变换为(-zNear, -zNear, -zNear, zNear),将点(right, top, -zNear, 1)变换为(-zNear, -zNear, -zNear, zNear)。投影矩阵P的具体形式如下。
(2) void gluPerspective (GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar);
定义了一种对称透视投影四棱锥,是glFrustum的一种特殊类型。
(3) void glOrtho (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdoublezNear, GLdouble zFar);
定义了三维平行投影矩阵P,将点(left, bottom, -zNear, 1) 变换为(-1, -1, -1, 1) ,将点(right, top, -zNear, 1) 变换为(1, 1, -1, 1)。投影矩阵P的具体形式如下。
(4) void gluOrtho2D (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top);
定义了二维平行投影,是glOrtho的一种特殊类型,即默认zNear = -1, zFar = 1。
5. 透视除法(Perspective Division)
对投影坐标做归一化处理,由OpenGL内部自动完成,没有提供用可供操作的函数。
6. 视口变换(Viewport Transformation)
将标准化的设备坐标转化为显示器上的窗口坐标,OpenGL提供了glViewport函数完成该功能。
z坐标分量主要用来做深度对比,即判断一个物体是否被另一个物体遮挡。zNear默认为0,zFar默认为1,也可以通过函数glDepthRange来指定。