分享

OpenGL学习脚印: OpenGL 坐标变换

 方海龙的书馆 2014-10-28

OpenGL学习脚印: OpenGL 坐标变换

写在前面

               本节内容翻译和整理自http://www. songho的博客《OpenGL Transformation》内容,以供自己和初学者熟悉OpenGL中坐标变换的整个过程。

通过本节,你可以了解到:

  •  OpenGL坐标变换过程
  •  理解OpenGL矩阵计算

概览

几何数据例如顶点位置和法向量在光栅化操作之前,都要通过Vertex Operation   和 Primitive Assembly OpenGL流水线操作(在OpenGL pipeline节描述)。



OpenGL 顶点变换

Object Coordinates(对象坐标系或模型坐标系)

       这是对象的局部坐标系统,是对象在被应用任何变换之前的初始位置和方向所在的坐标系。要对对象实行变换,可以使用glRotatef(), glTranslatef(), glScalef()等函数。

Eye Coordinates(眼坐标系或照相机坐标系)

         由GL_MODELVIEW矩阵和模型坐标系中坐标相乘的结果。在OpenGL中使用GL_MODELVIEW矩阵来使对象从模型坐标系转换到眼坐标系。

          GL_MODELVIEW矩阵是模型变换和视变换矩阵的组合(Mview*Mmodel)。模型变换从对象坐标系转换到世界坐标系,而视变换从世界坐标系转换到眼坐标系。


注意:OpenGL中并没有单独的视变换矩阵。因此,要想模拟变换照相机或者进行视变换,那么场景(3D对象和光照)必须以视变换矩阵的逆矩阵进行变换。换言之,OpenGL将照相机定义在位于眼坐标系下朝向-Z轴,位于点(0,0,0)的位置,而不能进行变换。

法向量同样从对象坐标系变换到眼坐标系来用于光照计算。

注意:法向量的转换方式和顶点不同。它用法向量乘以GL_MODELVIEW矩阵的逆矩阵的转置矩阵。请参考Normal Vector Transformation获取更多细节。



Clip Coordinates(裁剪坐标系)

         眼坐标通过乘以GL_PROJECTION变成了裁剪坐标。这个GL_PROJECTION矩阵定义了视见体( viewing volume,frustum),顶点式如何投影到屏幕上的(透视投影还是正交投影perspective or orthogonal)。称作裁剪坐标系的是因为,经过变换后的顶点(x,y,z)将与 ±w相比较来进行裁剪。请参考下面Projection Matrix部分获取更多细节。

   

Normalized Device Coordinates (NDC) (归一化设备坐标系)

       由裁剪坐标系下通过除以W分量得到。这个操作称为透视除法。NDC坐标很像屏幕坐标,但是还没有经过平移和缩放到屏幕像素。现在3个轴上的值范围均为[-1,1]。


Window Coordinates (Screen Coordinates)(屏幕坐标)

        通过对NDC坐标进行视口变换得到。NDC坐标通过平移和缩放来适应渲染的屏幕。屏幕坐标最终传递给绘制流水线中的光栅化处理部分来变成片元。glViewport()        用来定义渲染区域的矩形,这是最终图像映射到的区域。另外,glDepthRange()用来确定屏幕坐标中的Z值。屏幕坐标通过上面两个函数的给定参数来进行计算:

glViewport(x, y, w, h);
glDepthRange(n, f);

这个公式由NDC坐标和屏幕坐标之间的线性关系来获得的:


OpenGL变换矩阵

     OpenGL使用4x4矩阵来进行变换。注意,这16个元素的矩阵,实际上按列主序的方式以1D形式存储。如下图所示:

      如果想当做通常的行主序格式使用,你需要将其转置。

     OpenGL当中,有四类型的矩阵: GL_MODELVIEW,GL_PROJECTION, GL_TEXTURE,   和 GL_COLOR

    可以通过glMatrixMode()       函数来指定当前矩阵类型,例如使用模视矩阵,则可以选择GL_MODELVIEW,调用     glMatrixMode(GL_MODELVIEW)。


Model-View Matrix (GL_MODELVIEW) 模视矩阵

       GL_MODELVIEW矩阵将模型变换矩阵和视变换矩阵组合成一个矩阵。为了转换相机,你需要对整个场景执行相反的变换。gluLookAt()函数专门用来设定视变换。矩阵最后一列元素(m12,m13, m14)       用于执行平移变换,glTranslatef()

元素m15是齐次坐标系下坐标,特别用于投影变换。

(m0, m1, m2), (m4, m5, m6) and (m8,m9, m10)这3个元素集,用于欧几里得和仿射变换,例如glRotatef()用于旋转,glScalef()用于缩放。注意: 这三个元素集实际上代表3个正交坐标轴:

  • (m0, m1,m2)   : +X axis, left vector, (1, 0, 0) by default
  • (m4, m5,m6)   : +Y axis, up vector, (0, 1, 0) by default
  • (m8, m9,m10) : +Z axis, forward vector, (0, 0, 1) by default

如下图所示:



 
   我们可以从角度和lookat向量来直接构造GL_MODELVIEW,而不是用OpenGL来操作4列的GL_MODELVIEW矩阵。这里有一些有用的代码来构造GL_MODELVIEW矩阵:

注意:如果多个变换应用到一个顶点时,OpenGL以逆序的方式执行多个相乘操作。举例来说,如果顶点先由MA      变换,再由MB变换,那么OpenGL先执行MB x MA       操作,再乘以顶点。因此,在代码中,后执行的变换先出现,而先执行的变换后出现:

  1. // Note that the object will be translated first then rotated  
  2. glRotatef(angle, 1, 0, 0);   // rotate object angle degree around X-axis  
  3. glTranslatef(x, y, z);       // move object to (x, y, z)  
  4. drawObject();  
  5. <span style="font-size:14px;"><code class="codeblock" style="margin-left:30px;"></code></span>  

Projection Matrix (GL_PROJECTION) 投影矩阵

GL_PROJECTION用于定义视锥。视锥决定了哪些对象以及对象的哪些部分会被裁减掉。 同时,它也决定了3D场景是如何被投影到屏幕上的。

OpenGL使用两个函数来进行投影变换。glFrustum()用于进行透视投影,glOrtho() 用于进行正交(平行)投影。这两个函数都需要6各参数来指定裁剪平面的:left,right, bottom, top, near and far planes。两个函数原型如下:

void glFrustum(GLdouble  left,  GLdouble  right,  GLdouble  bottom,  GLdouble  top,  GLdouble  nearVal,  GLdouble  farVal);

void glOrtho(GLdouble  left,  GLdouble  right,  GLdouble  bottom,  GLdouble  top,  GLdouble  nearVal,  GLdouble  farVal);

8个顶点如下图所示:


         远裁剪面(far)的顶点可以通过相似三角形的比例计算出来(补充: 实际上通过函数gluFrustum指定时,参数都是指的近裁面的l,r,t,b,远裁剪面可以计算出来),例如,远裁剪面的left参数可以如下计算:

        对于正交投影,这个比例是1,因此远裁剪面的 left, right, bottomtop   和近裁剪面的一样,如下图所示:

       


gluPerspective() and gluOrtho2D()函数使用时需要更少的参数。gluPerspective()只需要四个参数,FOV视角,宽高比,以及远近裁剪面的距离。函数原型为:

void gluPerspective(GLdouble  fovy,  GLdouble  aspect,  GLdouble  zNear,  GLdouble  zFar);

从gluPerspective()转换为同等的glFrustum()的代码如下:

  1. // This creates a symmetric frustum.  
  2. // It converts to 6 params (l, r, b, t, n, f) for glFrustum()  
  3. // from given 4 params (fovy, aspect, near, far)  
  4. void makeFrustum(double fovY, double aspectRatio, double front, double back)  
  5. {  
  6.     const double DEG2RAD = 3.14159265 / 180;  
  7.   
  8.     double tangent = tan(fovY/2 * DEG2RAD);   // tangent of half fovY  
  9.     double height = front * tangent;          // half height of near plane  
  10.     double width = height * aspectRatio;      // half width of near plane  
  11.   
  12.     // params: left, right, bottom, top, near, far  
  13.     glFrustum(-width, width, -height, height, front, back);  
  14. }  

补充关于gluPerspective函数计算视锥

利用gluPerspective函数指定视锥如下图所示:

注意到视锥的对称性,利用三角形的相似性,可以推算如下图所示:


其中  top : n = tan(fov/2) , bottom= -top,  left = -right

===>    top= height/2 =  = n*tan(fov/2)    height为近裁剪面高度

===>    right=width/2 =  aspect*height/2=aspect*n*tan(fov/2)  width为近裁剪面宽度

注意上述代码中width,height均为实际值的一半。


 注意:这里使用gluPerspective函数构造的是一个对称的视锥,如果你想要构造非对称视锥必须直接使用

glFrustum()函数。例如,你想要把一个宽场景绘制到两个相连的屏幕上,你可以将视锥分割为左右两个非对称的视锥,然后在每个视锥中渲染场景,如下图所示:


Texture Matrix (GL_TEXTURE)纹理矩阵

        纹理坐标(s,t,r,q)在进行任何纹理映射前乘以GL_TEXTURE。默认情况下,它是一个单位阵,因此纹理会被映射到物体的位置,那个你指定纹理坐标的位置。通过修改GL_TEXTURE,你可以滑动、旋转、拉伸以及收缩纹理。

  1. // rotate texture around X-axis   
  2. glMatrixMode(GL_TEXTURE);   
  3. glRotatef(angle, 1, 0, 0)   

Color Matrix (GL_COLOR) 颜色矩阵

         颜色分量(r,g,b,a)乘以GL_COLOR矩阵。可以用于颜色空间转换和颜色分量交换。GL_COLOR矩阵被经常使用,并且需要GL_ARB_imaging拓展。

其他的矩阵操作

glPushMatrix()
push the current matrix into the current matrix stack.
glPopMatrix()
pop the current matrix from the current matrix stack.
glLoadIdentity()
set the current matrix to the identity matrix.
glLoadMatrix{fd}(m)
replace the current matrix with the matrixm.
glLoadTransposeMatrix{fd}(m)
replace the current matrix with the row-major ordered matrixm.
glMultMatrix{fd}(m)
multiply the current matrix by the matrixm, and update the result to the current matrix.
glMultTransposeMatrix{fd}(m)
multiply the current matrix by the row-major ordered matrixm, and update the result to the current matrix.
glGetFloatv(GL_MODELVIEW_MATRIX,m)
return 16 values of GL_MODELVIEW matrix tom.



正文到此结束,关于文中提到的两个经典帮助理解的例子,可以通过原文下载,这里不再介绍。


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多