前面几节分别介绍了模型变换和视变换,本节从全局把握一遍OpenGL坐标转换的过程,从整体上认识坐标变换过程。
坐标变换的全局图
OpenGL中的坐标处理过程包括模型变换、视变换、投影变换、视口变换等过程,如下图所示:
在上面的图中,注意,OpenGL只定义了裁剪坐标系、规范化设备坐标系和屏幕坐标系,而局部坐标系(模型坐标系)、世界坐标系和照相机坐标系都是为了方便用户设计而自定义的坐标系,它们的关系如下图所示:
图中左边的过程包括模型变换、视变换,投影变换,这些变换可以由用户根据需要自行指定,这些内容在顶点着色器中完成;
而图中右边的两个步骤,包括透视除法、视口变换,这两个步骤是OpenGL自动执行的,在顶点着色器处理后的阶段完成。
各个变换阶段的理解
下面分别对每个阶段的变换做一个总结,以帮助理解。
1.模型变换——从模型坐标系到世界坐标系
局部坐标系(模型坐标系)是为了方便构造模型而设立的坐标系,建立模型时我们无需关心最终对象显示在屏幕哪个位置。模型的原点定位也可以有所不同,例如下面在模型坐标系定义的模型:
模型变换主要完成从模型坐标系到世界坐标系的转换
模型变换的主要目的是通过变换使得用顶点属性定义或者3d建模软件构造的模型,能够按照需要,通过缩小、平移等操作放置到场景中合适的位置。通过模型变换后,物体放置在一个全局的世界坐标系中,世界坐标系是所有物体交互的一个公共坐标系。例如下面的图中在模型坐标系定义的茶壶模型(来自World, View and Projection Transformation Matrices):
茶壶通过模型变换,转换到世界坐标系中:
模型变换包括:旋转、平移、缩放、错切等内容。例如将物体从一个位置 p=(x,y,z) ,移动到另一个位置 p′=(x′,y′,z′) 的过程
应用多个模型变换时,注意变换执行的顺序影响变换的结果,一般按照缩放–>旋转—>平移的顺序执行;另外,注意旋转和缩放变换的不动点问题。这些内容在 模型变换一节已经介绍了,这里不再赘述。利用 GLM数学库实现模型变换,例如平移变换示例代码为:
- 1
- 2
2.视变换——从世界坐标系到相机坐标系
视变换完成从世界坐标系到相机坐标系的转换过程,在这个坐标系中相机是个假想的概念,是为了便于计算而引入的。相机坐标系中的坐标,就是从相机的角度来解释世界坐标系中位置。相机和场景的示意图如下所示(来自World, View and Projection Transformation Matrices):
OpenGL中相机始终位于原点,指向-Z轴,而以相反的方式来调整场景中物体,从而达到相同的观察效果。例如要观察-z轴方向的一个立方体的右侧面,可以有两种方式:
- 立方体不动,让相机绕着+y轴,旋转+90度,此时相机镜头朝向立方体的右侧面,实现目的。完成这一旋转的矩阵记作 Ry(π2)
- 相机不动,让立方体绕着+y轴,旋转-90度,此时也能实现同样的目的。注意这时相机没有转动。完成这一旋转的矩阵记作 Ry(−π2)
OpenGL中采用方式2的观点来解释视变换。再举一个例子,比如,一个物体中心位于原点,照相机也位于初始位置原点,方向指向-Z轴。为了对物体的+Z面成像,那么必须将照相机从原点移走,如果照相机仍然指向-Z轴,需要将照相机沿着+Z轴方向后退。假若照相机不移动,我们可以通过将物体沿着-Z轴后退d个单位
通过在世界坐标系中指定相机的位置,指向的目标位置,以及viewUp向量来构造一个相机坐标系,通过视变换矩阵将物体坐标由世界坐标系转换到相机坐标系,视变换矩阵的推导已经在视变换一节介绍,感兴趣地可以去参考。利用GLM数学库实现视变换的代码为:
- 1
- 2
- 3
3.投影变换——从世界坐标系到裁剪坐标系
OpenGL最终的渲染设备是2D的,我们需要将3D表示的场景转换为最终的2D形式,前面使用模型变换和视变换将物体坐标转换到照相机坐标系后,需要进行投影变换,将坐标从相机-->裁剪坐标系,经过透视除法后,变换到规范化设备坐标系(NDC),最后进行视口变换后,3D坐标才变换到屏幕上的2D坐标
投影变换通过指定视见体(viewing frustum)来决定场景中哪些物体将可能会呈现在屏幕上。在视见体中的物体会出现在投影平面上,而在视见体之外的物体不会出现在投影平面上投影方式决定以何种方式成像,投影方式有很多种,OpenGL中主要使用两种方式,即透视投影(perspective projection)和正交投影( orthographic projection)。
1.正交投影是平行投影的一种特殊情形,正交投影的投影线垂直于观察平面。平行投影的投影线相互平行,投影的结果与原物体的大小相等,因此广泛地应用于工程制图等方面。
2.透视投影的投影线相交于一点,因此投影的结果与原物体的实际大小并不一致,而是会近大远小。因此透视投影更接近于真实世界的投影方式。
两者的示意图如下:
在OpenGL中成像时的效果如下所示(图片来自Modern OpenGL):
上面的图中,红色和黄色球在视见体内,因而呈现在投影平面上,而绿色球在视见体外,没有在投影平面上成像。指定视见体通过(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble nearVal, GLdouble farVal)6个参数来指定。注意在相机坐标系下,相机指向-z轴,nearVal和farVal表示的剪裁平面分别为:近裁剪平面 z=−nearVal ,以及远裁剪平面 z=−farVal 。
a.使用下面API指定正交投影:
glOrtho(xleft, xright, ybottom, ytop, znear, zfar);
或者类似API指定正交投影,参数意义形象表示为下图所示(来自World, View and Projection Transformation Matrices):
b.使用下面API指定透视投影:
void glFrustum(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble nearVal, GLdouble farVal);
void gluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar);
或者类似的API指定透视投影的视见体,其参数含义如下图所示(来自World, View and Projection Transformation Matrices):
关于投影矩阵的推导,可以不用深究。利用GLM数学库实现视透视投影变换的代码示例为:
- 1
- 2
经过投影变换后,物体坐标变换到了裁剪坐标系,经过OpenGL自动执行的透视除法后,变换到规范化设备坐标系中。透视除法就是将裁剪坐标系中坐标都除以 wc 成分的过程。
使用Fov指定的透视投影
另外一种经常使用 的方式是通过视角(Fov),宽高比(Aspect)来指定透视投影,例如旧版中函数gluPerspective,参数形式为:
API void gluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar);
其中指定fovy指定视角,aspect指定宽高比,zNear和zFar指定剪裁平面。fovy的理解如下图所示(来自opengl 投影):
4.视口变换——从NDC(规范化设备坐标系)到屏幕坐标系
视变换是将规范化设备坐标(NDC)转换为屏幕坐标的过程,如下图所示:
视口变化通过函数:
glViewport(GLint
sx
, GLint
sy
, GLsizei
ws
, GLsizei
hs
);
glDepthRangef(GLclampf
ns
, GLclampf
fs
);
两个函数来指定。其中( sx , sy )表示窗口的左下角, ns 和 fs 指定远近剪裁平面到屏幕坐标的映射关系。视口变换矩阵的推导可以不用深究。用上面的glViewport和glDepthRangef函数指定参数后,视口变换由OpenGL自动执行,不需要额外代码。
5.坐标变换的计算过程
上述过程从坐标计算角度来看,如下图所示:
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
透视除法(Derspective Divison)
透视除法的思路是:将视野(平截头体)内所有的东西都变形,然后挤压到一个小立方体里面。就像一个黑洞,当你掉进一个针孔般大的黑洞的时候,你会被挤压得比针孔还要小很多
好了,用什么数学方法去挤压它呢?答案就是除法。投影变换损失了一个维度(损失的是深度z维)。弄丢了深度信息,这使得深度测试和裁剪都非常难以执行。于是我们想了一个办法让深度信息得以保留,这就是透视除法。透视除法将裁减坐标系的坐标除以深度,形成归一化的空间
坐标变换的程序实现
在程序中,我们需要在两个部分做处理。
第一,编写顶点着色器程序如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
第二,在主程序中传递变换矩阵到顶点着色器,并绘制场景中物体,代码如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
例如利用圆形坐标系指定相机位置,绘制的立方体如下图所示:
参考资料
1.World, View and Projection Transformation Matrices
2.GLSL Programming/Vertex Transformations
相关资源
1.OpenGL 101: Matrices - projection, view, model
2.songho OpenGL Transformation
3.The Perspective and Orthographic Projection Matrix
4.songho OpenGL Projection Matrix
5. glOrtho
6. glFrustum
7. gluPerspective
8. gluLookAt