OpenGL ES 2.0中的3D透视变换

在之前的Camera Live Filter中,我们讨论了2D下OpenGL ES的使用。当我们把内容扩展到3D的世界时,需要稍微学习一下透视变换(以及坐标系变换)的基本知识和相关的函数使用。

在讨论这些变换之前,我们先讨论几个之后会用到的坐标空间。注意,这些空间的名字是我们自己起的,引入它们是为了帮助更好地理解OpeinGL中3D渲染,变换过程:

space_convert

GL空间(CVV空间)

这是最后被OpenGL绘制出来的空间。它是一个正立方体,范围是x[-1,1], y[-1,1], z[-1,1]。当它被绘制出来的时候,假定观察者站在Z轴的正无穷处,向负方向看去。

在这个坐标空间中,OpenGL不提供任何透视变换(因此观察者站在z=2,z=5,z=无穷 都是一样的)。只是简单地把x[-1,1]映射到Viewport的x方向的(0,X.Max),把y[-1,1]映射到y方向的(y.Max, 0)。如果Depth Test未被激活的话,则z轴的信息被完全丢弃,OpenGL根据绘制的先后顺序决定最后显示的内容。如果Depth Test被激活的话,则根据z从小到大优先选择(距离观察着从近到远)显示的图形(先不考虑alpha),由于不提供透视,用户可以想象一下,以z=-1为正面,向上;用力把一个盒子压扁,看到的内容就是投射到屏幕上的内容。

对于2D内容的绘制来说,一般我们把定点固定在相同的z轴位置上(当然是-1到1之间,0比较合适),在x(-1,1),y(-1,1)之间作画。但是对于3D图形来说,我们需要根据观察者所在的位置进行透视变换,因此这里引入了额外的坐标系帮助理解。

可视空间

在可视空间中,我们已经有了观察者位置。观察着站在原点(0,0,0)处,向这Z轴的负方向看去。因此,在这个空间中,所有落在Z轴正空间以内的顶点都是不可见的。如果程序员没有额外地应用观察者位置的话(绘制空间即是可视坐标),则必须保证所有绘制的内容都落在Z轴小于0的空间。

通过应用透视变换矩阵,我们将可视空间转换到CVV空间(实际上,透视变换的过程就是根据观察者和物体间的相对距离,重新设置物体在投影面上x,y的位置,同时,部分保持z轴的信息用来进行深度测试)

perspective

在Android上,Google提供了Matrix::frustumM()来建立透视变换矩阵。

frustumM(float[] m, int offset, float left, float right, float bottom, float top, float near, float far)

在这个函数中,将近裁剪平面作为投影平面。

  • near: 是近裁剪平面到原点的距离,必须大于0
  • far: 是远裁剪平面到原点的距离,必须大于near
  • left,right: 定义了近裁剪平面的横轴位置和宽度
  • top, bottom: 定义了近裁剪平面的纵轴位置和高度

CSDN上的这篇文章详细介绍了根据这些参数如何计算出透视转换矩阵。

绘制空间

这个坐标系是用户绘制三维图像的坐标系,我们为vertex所设置的(x,y,z)坐标都落在这个坐标系中。在这个空间中,不需要考虑观察者位置,程序可以在广阔三维坐标中的任意位置绘制。通过应用观察者位置(LookAt Matrix), 可以将绘制空间转换成可视空间。

在WebGL上验证

为了验证以上的坐标系转换,在WebGL上写了一个小的测试程序来验证该转换的正确性。放在个人网站上。这个联系包括两个部分:

  • t3d.js是一个迷你的3D渲染引擎,根据上述算法实现了物体定义(位置,表面纹理),物体运动(平移,旋转,缩放),观察者视角变换,透视变换等。
  • 主程序加载了t3d.js,定义了两个旋转的立方体

webgl_screenshot

Leave a Reply

Your email address will not be published. Required fields are marked *