Opengl编程指南第三章:视图


1、概述

对象的三维坐标需要经过一系列的计算才能编程屏幕上的像素:

坐标变换:通过矩阵乘法实现,包括模型变换,视角变换,投影变换。涉及的操作有,平移、旋转、缩放、透视投影、正交投影;
剪切:由于整个场景最终被绘制到一个矩形窗口上,位置在窗口外的图元必需被剪切掉;
视口变换:将变换后的坐标对应到屏幕像素。

拿照相来做比喻,照相可能需要以下几个步骤:
1、放置好三脚架和相机(视角变换)
2、把场景中的物体摆好位置(模型变换)
3、调整焦距(投影变换)
4、确定最终照片的大小(视口变换)

所有的坐标变换是通过一个4x4的矩阵来实现的,v' = v*M。

 

2、矩阵操作API

glMatrixMode(GLenum mode)指定当前的矩阵,mode的值为GL_MODELVIEW, GL_PROJECTION
glLoadIdentity()把当前矩阵重置为单位矩阵
glLoadMatrix*(const TYPE *m)把当前矩阵置为参数指定的矩阵
glMultMatrix*(const TYPE *m)将当前矩阵与参数矩阵想乘



3、视角、模型变换

视角变换和模型变换是相对的。以上一节的照相机比方为例,把物体沿某个方移动,等价于把照相机沿相反的方向移动。所以在opengl里面,这个两个变换被合成用一个矩阵表示。在考虑具体场景的时候,到底聪视角变换的角度出发,还是从模型变换的角度出发,就看哪个更加自然,更加符合你的思维习惯。

变换的顺序
假设一个物体中心处于原点位置,先沿Z轴逆时针旋转45度再沿X轴平移一段距离,此时物体处于X轴上;加入将这两个变换的顺序反过来,物体则处于直线y=x上。
“一次变换”相当于当前的变换矩阵乘以一个新的变换举证。假设当前的矩阵是C,接着两个变换M,N,则最终的矩阵变为CMN。顶点v经过该矩阵的变换得到v'=CMNv=C(M(Nv)),相当于顶点依次经历变换N,M,C。由此可见,顶点实际的变换顺序和代码所写的变换顺序实际上是相反的。
上面的分析是把物体放在一个固定的、大的坐标系中来考虑的。如果想象每个物体有一个固有的局部坐标系,所有的变换都是相对这个局部坐标系发生,那么变换的顺序就和代码一致了:移动时,物体和局部坐标系移动相对于当前局部坐标系移动,旋转时类似。


模型变换API:glScale;glTranslate;glRotate;
视角变换API: api相同(模型变换和视角变换是相对的),比如glScale相当于把场景中所有物体的大小进行缩放。视角变换在代码中一般处在模型变换之前,从上边的分析可以,实际上作用于模型变换之后,这是符合编程者设计场景的一般流程的。gluLookAt是辅助库的函数,通过组合上述api而实现。


4、投影变换

投影变换的目的是定义一个视体空间(或视见体,view volume),它决定了顶点如何被投影到屏幕上,同时决定了那些物体或物体部分应该被剪切掉。
透视投影(Perspective Projection):透视投影的特点是远离视点的物体会显得小,而近处的物体显得大。这是因为透视投影的视见体是一个“截头椎体”(frustum),里面的物体都向视点一侧的椎体顶点投影。glFrustum(GLdouble left, GLdouble right,GLdouble bottom, GLdouble top,GLdouble near, GLdouble far)或void gluPerspective(GLdouble fovy, GLdouble aspect,GLdouble near, GLdouble far),具体解释请参考原书。
正投影(Orthographic Projection):正投影的视见体是一个矩形体,无论近处的还是远处的物体都会平行地投影到屏幕上。API,glOrtho*。
可见投影变换定义了6个剪切面(Clipping pane),场景的物体会被这个6个面剪切。


5、视口变换

视口变换定义了用户显示最终画面的窗口矩形区域。注意:此时物体已经经过视角模型变换、投影变换,经过了剪切。glViewport(GLint x, GLint y, GLsizei width, GLsizei height)。

深度坐标
在视口变换的过程中,深度坐标(depth(z) coordinate)会被编码。可以同通过glDepthRange设定一个范围,使z值被缩放至这个范围内。glDepthRange(GLclampd near, GLclampd far);定义depth buffer中存储的最大和最小值,默认是0,1,输入参数会被现在[0,1]以内。受透视除法(perspective division)的影响,frustum的远端的depth坐标的精度弱于近端。


6、其他

矩阵栈
模型视角矩阵和投影矩阵实际上有各自的栈,你操作的是栈顶的矩阵,矩阵栈对于构建层次化、复杂对象很有用。
glPushMatrix() 拷贝当前矩阵并推入栈顶
glPopMatrix() 弹出栈顶矩阵


额外的剪切面
除了视见体所定义的六个剪切面之外,你还可以定义额外的剪切面。|
一个面由方程Ax+By+Cz+D=0来定义。
glClipPlane(GLenum plane, const GLdouble *equation);pane是GL_CLIP_PLANEi(i=0...),需要先调用glEnable(GL_CLIP_PLANEi)激活;*equation则是(A,B,C,D)四个变量组成的向量buffer。
满足(A B C D)M-1(xe ye ze we)T>=0的点被保留,否则被剪切掉,(xe,ye,ze,we)是眼坐标。实际上就是向量(A,B,C)所指向的平面一侧被保留。


逆向坐标变换
有时,你需要逆向地进行坐标变换,比如通过鼠标在屏幕上点击一个点,如何找出与这个点对应的世界坐标系中的点呢?
int gluUnProject(GLdouble winx, GLdouble winy, GLdouble winz,
const GLdouble modelMatrix[16],
const GLdouble projMatrix[16],
const GLint viewport[4],
GLdouble *objx, GLdouble *objy, GLdouble *objz);
winx, winy是屏幕坐标,winz则是depth坐标,也即glDepthRange定义的值;
modelMatrix是模型视角矩阵
projMatrix是投影矩阵
viewport是视口范围
objx,objy,objz存储返回的坐标值


int gluProject(GLdouble objx, GLdouble objy, GLdouble objz,
const GLdouble modelMatrix[16],
const GLdouble projMatrix[16],
const GLint viewport[4],
GLdouble *winx, GLdouble *winy, GLdouble *winz);
与gluUnProject相反,从世界坐标系映射到屏幕坐标系。

 

关于变换的一些更基础知识可以参考博客:http://blog.csdn.net/popy007/#


原文地址:https://www.cnblogs.com/longhuihu/p/10423341.html