OpenGL 坐标变换(1)

本文深入解析OpenGL中坐标变换过程,包括对象坐标系、照相机坐标系、裁剪坐标系及归一化设备坐标系的转换原理,以及OpenGL变换矩阵的使用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

OpenGL学习脚印: OpenGL 坐标变换

写在前面

               本节内容翻译和整理自http://www.songho.ca 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_PROJECTIONGL_TEXTURE,   和 GL_COLOR

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


Model-View Matrix (GL_MODELVIEW) 模视矩阵

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

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

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

  • (m0m1,m2)   : +X axis, left vector, (1, 0, 0) by default
  • (m4m5,m6)   : +Y axis, up vector, (0, 1, 0) by default
  • (m8m9,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,rightbottomtopnear 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,因此远裁剪面的 leftrightbottomtop   和近裁剪面的一样,如下图所示:

        


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 matrix m.
glLoadTransposeMatrix{fd}(m) : 
replace the current matrix with the row-major ordered matrix m.
glMultMatrix{fd}(m) : 
multiply the current matrix by the matrix m, and update the result to the current matrix.
glMultTransposeMatrix{fd}(m) : 
multiply the current matrix by the row-major ordered matrix m, and update the result to the current matrix.
glGetFloatv(GL_MODELVIEW_MATRIX,m) : 
return 16 values of GL_MODELVIEW matrix to m.



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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值