分享

OpenGL坐标系 | Beatrice7's Fancy

 LVADDIE 2015-07-14

坐标系统

想要弄懂几何变换,一定要搞清楚OpenGL中的坐标系统。
从我们构造模型的局部坐标系(Local/Object Space)经过一系列处理最终渲染到屏幕坐标(Screen Space)下,这过程中有6种坐标系。

  • World Coordinates(世界坐标系)
  • Object Coordinates(对象坐标系、模型坐标系、局部坐标系或当前绘图坐标系)
  • Eye Coordinates(眼坐标系或照相机坐标系)
  • Clip Coordinates(裁剪坐标系)
  • Normalized Device Coordinates (NDC) (归一化设备坐标系)
  • Window Coordinates (Screen Coordinates)(屏幕坐标)

绘制管线如图所示:

实际上,并不存在单独的模型变换(Model)和视点变换(View),通常将这两种变换合称为ModelView变换。则OpenGL的顶点变换过程如图所示:

世界坐标系

世界坐标系始终是固定不变的。OpenGL使用右手坐标,这里有一个形象的方法:使用右手定则
X 是你的拇指
Y 是你的食指
Z 是你的中指
如果你把你的拇指指向右边,食指指向天空,那么中指将指向你的背后。我们的观察方向是Z轴负半轴的方向。
进行旋转操作时需要指定的角度θ的方向则由右手法则来决定,即右手握拳,大拇指直向某个坐标轴的正方向,那么其余四指指向的方向即为该坐标轴上的θ角的正方向(即θ角增加的方向),图中用圆弧形箭头标出:

对象坐标系

这是对象在被应用任何变换之前的初始位置和方向所在的坐标系,也就是当前绘图坐标系。该坐标系不是固定的,且仅对该对象适用。在默认情况下,该坐标系与世界坐标系重合。
这里能用到的函数有glTranslatef(),glScalef(), glRotatef(),当用这些函数对当前绘图坐标系进行平移、伸缩、旋转变换之后, 世界坐标系和当前绘图坐标系不再重合。改变以后,再用glVertex3f()等绘图函数绘图时,都是在当前绘图坐标系进行绘图,所有的函数参数也都是相对当前绘图坐标系来讲的。如图则是对物体进行变换后,对象坐标系与世界坐标系的相对位置。

眼坐标系

模型变换:对象坐标系->世界坐标系
视变换:世界坐标系->眼坐标系
GL_MODELVIEW矩阵是模型变换矩阵和视变换矩阵的组合(Mview*Mmodel),前面已经说了,并不存在单独的模型变换(Model)和视点变换(View)。所以使用GL_MODELVIEW矩阵就可以使对象从对象坐标系转换到眼坐标系。

为啥要转换到眼坐标系呢?
可以这样理解,通过前面的MODEVIEW变换,这个世界坐标系中的场景已经绘制好了。这时候我们还不能看到场景哦,因为我们的观察位置还没定呢,而且如果我们眼睛(照相机)的位置不同,那么观察物体的角度则不同,那看到的场景的样子肯定也不同,所以要有这一步,把场景与我们的观察位置对应起来。
默认情况下,眼坐标系与世界坐标系也是重合的。使用函数 gluLookAt()则可以指定眼睛(相机)的位置和眼睛看向的方向。该函数的原型如下:

1
2
3
void gluLookAt(GLdouble eyex, GLdouble eyey, GLdouble eyez,
GLdouble centerx, GLdouble centery, GLdouble centerz,
GLdouble upx, GLdouble upy, GLdouble upz);

函数参数中,点(eyex, eyey, eyez)代表眼睛所在位置;
点(centerx, centery,centerz)代表眼睛看向的位置;
向量(upx, upy, upz)代表视线向上方向,其中视点和参考点的连线与视线向上方向要保持垂直关系
只需控制这三个量,便可定义新的视点。
这里介绍一个可以帮助理解的程序包的下载链接,下面就是运行效果的截图,帮助会很大的哟!

tips

使用glTranslatef(),glScalef(), glRotatef()这些函数是对对象坐标系进行变动;使用void gluLookAt()是对眼坐标系进行变动,两者可以达到相同的变换效果。相当于对象不动移动相机,和相机不动移动对象。比如场景向x轴正方向移动1个单位(相机不动),相当于相机向x轴负方向移动一个单位(对象不动),glTranslatef(1.0, 0.0, 0.0) <=> gluLookAt(-1.0, 0.0, 0.0, …, … )。

裁剪坐标系

眼坐标到裁剪坐标是通过投影完成的。眼坐标通过乘以GL_PROJECTION矩阵变成了裁剪坐标。
这个GL_PROJECTION矩阵定义了视景体( viewing volume),即确定哪些物体位于视野之内,位于视景体外的对象会被剪裁掉。除了视景体,投影变换还定义了顶点是如何投影到屏幕上的,是透视投影(perspective projection)还是正交投影(orthographic projection)。

透视投影类似于日常生活看到的场景,远处物体看起来小,近处看起来大。使用透视投影函数glFrustum()和gluPerspective().

1
2
3
void glFrustum(GLdouble left, GLdouble right,
      GLdouble bottom, GLdouble top,
      GLdouble near, GLdouble far)

far, near是指近裁剪面,远剪裁面离视点的距离(>0),
对角坐标, (left, bottom, -near)和(right, top, -near)定义了近裁剪面的左下角和右上角的(x, y, z)坐标。

1
2
void gluPerspective(GLdouble fovy, GLdouble aspect,
        GLdouble near, GLdouble far)

fovy视角,aspect = w/h

正投影把物体直接映射到屏幕上,不影响它们的相对大小。也就是图像反映物体的实际大小。函数glOrtho()创建一个用于正交投影的平行视景体, 将其与当前矩阵相乘。

1
2
3
void glOrtho(GLdouble left, GLdouble right,
     GLdouble bottom, GLdouble top,
     GLdouble near, GLdouble far);

归一化设备坐标系

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

屏幕坐标系

通常将屏幕上的设备坐标称为屏幕坐标。设备坐标又称为物理坐标,是指输出设备上的坐标。设备坐标用对象距离窗口左上角的水平距离和垂直距离来指定对象的位置,是以像素为单位来表示的,设备坐标的 X 轴向右为正,Y 轴向下为正,坐标原点位于窗口的左上角

投影变换和视点变换一起决定了场景的映射方式,而视口变换指出了场景被映射到什么样的屏幕区域,即指定场景在屏幕上所占据的区域。可以这样认为:视口变换定义处理后照片的大小和位置, 你希望照片像海报那么大还是钱包那么大。

从NDC坐标到屏幕坐标基本上是一个线性映射关系。通过对NDC坐标进行视口变换得到。这时候就要用到函数glViewport(),该函数用来定义渲染区域的矩形,也就是最终图像映射到的区域。

1
void glViewport(0.0, 0.0, (GLsizei) w, (GLsizei) h)

有4个参数,前2个参数描述窗口内部有效屏幕空间的原点(0, 0); 后2个参数描述有效区域的width和height(均以像素为单位)

小结

以上是我对OpenGL坐标系统的学习笔记,主要参考了这篇很详细的博文以及其中的链接博文。

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

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多