4.1 坐标空间和转换

[TOC]

五种坐标空间

  • 局部空间(Local Space)
  • 世界空间(World Space)
  • 观察空间(View Space)
  • 裁剪空间(Clip Space)
  • 屏幕空间(Screen Space)

一种通俗的解释

我们先来简略地了解一下图中各个过程:

  • 首先,一个3D对象的模型被创建(使用某种建模软件)出来之后,是以本地坐标(local coordinates)来表达的,坐标原点(0, 0, 0)一般位于3D对象的中心。不同的3D对象对应各自不同的本地坐标系(local space)。
  • 3D对象的本地坐标经过一个model变换,就变换到成了世界坐标(world coordinates)。不同的对象经过各自的model变换之后,就都位于同一个世界坐标系(world space)中了,它们的世界坐标就能表达各自的相对位置。一般来说,model变换又包含三种可能的变换:缩放(scaling)、旋转(rotation)、平移(translation)。在计算机图形学中,一个变换通常使用矩阵乘法来计算完成,因此这里的model变换相当于给本地坐标左乘一个model矩阵,就得到了世界坐标。后边将要介绍的view变换和投影变换,也都对应着一个矩阵乘法。
  • 在同一个世界坐标系内的各个3D对象共同组成了一个场景(scene),对于这个场景,我们可以从不同的角度去观察。当观察角度不同的时候,我们眼中看到的也不同。为了表达这个观察视角,我们会再建立一个相机坐标系,英文可以称为camera space, 或eye space, 或view space。从世界坐标系到相机坐标系的转换,我们称之为view变换。当我们用相机这个词的时候,相机相当于眼睛,执行一个view变换,就相当于我们把眼睛调整到了我们想要的一个观察视角上。
  • 对相机坐标执行一个投影变换(projection),就变换成了裁剪坐标(clip coordinates)。在裁剪坐标系(clip space)下,x、y、z各个坐标轴上会指定一个可见范围,坐标超过可见范围的顶点(vertex)就会被裁剪掉,这样,3D场景中超出指定范围的部分最终就不会被绘制,我们也就看不到这些部分了。这个投影变换,是从3D变换到2D的关键步骤。之所以会有这么一步,是因为我们总是通过一个屏幕来观察3D场景(类似于透过一扇窗户观察窗外的景色),屏幕(窗户)不是无限大的,因此一定存在某些观察视角,我们看不到场景的全部。看不到的场景部分,就是通过这一步被裁剪掉的,这也是「裁剪」这一词的来历;另一方面,把3D场景投射到2D屏幕上,也主要是由这一步起的作用。另外值得注意的是,经过裁剪变换,3D对象的顶点个数不一定总是减少,还有可能被裁剪后反而增多了。这个细节我们留在后面再讨论。

  • 裁剪坐标(clip coordinates)经过一个特殊的perspective division的过程,就变换成了NDC坐标(Normalized Device Coordinates)。这个perspective division的过程,跟齐次坐标有关,我们留在后面再讨论它的细节。由于这个过程在OpenGL ES中是自动进行的,我们不需要针对它来编程,因此我们经常把它和投影变换放在一起来理解。我们可以不太严谨地暂且认为,相机坐标经过了一个投影变换,就直接得到NDC了。NDC是什么呢?它才是真正的由OpenGL ES来定义的坐标。在NDC的定义中,x、y、z各个坐标都在[-1,1]之间。因此,NDC定义了一个边长为2的立方体,每个边从-1到1,NDC中的每个坐标都位于这个立方体内(落在立方体外的顶点在前一步已经被裁剪掉了)。值得注意的是,虽然NDC包含x、y、z三个坐标轴,但它主要表达了顶点在xOy平面内的位置,x和y坐标它们最终会对应到屏幕的像素位置上去。而z坐标只是为了表明深度关系,谁在前谁在后(前面的挡住后面的),因此z坐标只是相对大小有意义,z的绝对数值是多大并不具有现实的意义。

  • NDC坐标每个维度的取值范围都是[-1,1],但屏幕坐标并不是这样,而是大小不一。以分辨率720x1280的屏幕为例,它的x取值范围是[0, 720],y的取值范围是[0,1280]。这样NDC坐标就需要一个变换,才能变换到屏幕坐标(screen coordinates),这个变换被称为视口变换(viewport transform)。在OpenGL ES中,这个变换也是自动完成的,但需要我们通过glViewport接口来指定绘制视口(屏幕)的大小。这里还需要注意的一点是,屏幕坐标(screen coordinates)与屏幕的像素(pixel)还不一样。屏幕坐标(screen coordinates)是屏幕上任意一个点的精确位置,简单来说就是可以是任意小数,但像素的位置只能是整数了。这里的视口变换是从NDC坐标变换到屏幕坐标,还没有到最终的像素位置。再从屏幕坐标对应到像素位置,是后面的光栅化完成的(光栅化的细节不在本文的讨论范围)。

局部空间(Local Space)

模型的坐标参考点都是自己,这些坐标所在的坐标空间就是局部空间。

世界空间(World Space)

多个模型绘制在更大的一个空间,通过模型矩阵,将不同模型进行缩放、位移、旋转,这样,不用考虑局部空间,而多个模型组成一个世界空间。

观察空间(View Space)

当物体在世界空间中就位了,接下来就是要考虑从哪个方向和角度来观察物体了。

在观察空间里,坐标原点不再是世界空间的坐标原点了,而是以摄像机的视角作为场景原点,最终建立了一个以摄像机位置为原点的坐标系。

视图矩阵(View Matrix)

裁剪空间(Clip Space)

当物体坐标都位于观察空间后,接下来要做的就是裁剪。根据我们的需要来裁剪一定范围内的物体,而在这个范围之外的坐标就会被忽略掉。

从观察空间到裁剪空间,需要用到:投影矩阵(Projection Matrix)

投影矩阵会指定一个坐标范围,这个范围内的坐标将变换为归一化设备坐标(NDC) ,不在这个范围内的坐标就会被裁剪掉。

观察空间中的坐标经过投影矩阵的变换之后称为投影坐标,又叫做裁剪坐标。

  • 正交投影
  • 透视投影

Normalized device coordinates(归一化设备坐标系)

当坐标经过投影矩阵的变换到裁剪空间之后,紧接着就会进行透视除法的操作。

经过裁剪之后,再进行透视除法。就是将 x、y、z 坐标分别除以 w 分量,得到新的 x、y、z 坐标。由于 x、y、z 坐标的绝对值都小于 w 的绝对值,所以得到新的坐标值都是位于 [−1,1] 的区间内的。此时得到的坐标,也就是归一化设备坐标。

归一化设备坐标是独立于屏幕的,而且它的坐标系用的是左手坐标系。

屏幕空间(Screen Space)

OpenGL 会使用 glViewPort 函数来将归一化设备坐标映射到屏幕坐标,每个坐标都关联了屏幕上的一个点,这个过程称为视口变换。这一步操作不再需要变换矩阵了。

坐标空间转换

  • 模型矩阵(Model Matrix)
    • 发生在世界空间
  • 视图矩阵(View Matrix)
    • 发生在观察空间
  • 投影矩阵(Projection Matrix)
    • 发生在裁剪空间


模型矩阵(Model Matrix)

视图矩阵(View Matrix)

投影矩阵(Projection Matrix)

平截头体(Frustum)

由投影矩阵创建的观察区域(Viewing Box)被称为平截头体(Frustum),也可以叫做视景体,且每个出现在平截头体范围内的坐标都会最终出现在用户的屏幕上。将一定范围内的坐标转化到标准化设备坐标系的过程(而且它很容易被映射到2D观察空间坐标)被称之为投影(Projection),因为使用投影矩阵能将3维坐标投影(Project)到很容易映射的2D标准化设备坐标系中。

正交投影

正射投影(Orthographic Projection)矩阵定义了一个类似立方体的平截头体,指定了一个裁剪空间,每一个在这空间外面的顶点都会被裁剪。创建一个正射投影矩阵需要指定可见平截头体的宽、高和长度。所有在使用正射投影矩阵转换到裁剪空间后如果还处于这个平截头体里面的坐标就不会被裁剪。它的平截头体看起来像一个容器:

上面的平截头体定义了由宽、高、近平面和远平面决定的可视的坐标系。任何出现在近平面前面或远平面后面的坐标都会被裁剪掉。正视平截头体直接将平截头体内部的顶点映射到标准化设备坐标系中,因为每个向量的w分量都是不变的;如果w分量等于1.0,则透视划分不会改变坐标的值。

正射投影矩阵直接将坐标映射到屏幕的二维平面内,但实际上一个直接的投影矩阵将会产生不真实的结果,

透视投影

由于透视的原因,平行线似乎在很远的地方看起来会相交。这正是透视投影(Perspective Projection)想要模仿的效果,这个投影矩阵不仅将给定的平截头体范围映射到裁剪空间,同样还修改了每个顶点坐标的w值,从而使得离观察者越远的顶点坐标w分量越大。被转换到裁剪空间的坐标都会在-w到w的范围之间(任何大于这个范围的对象都会被裁剪掉)。


左侧fov是90度,右侧是45度,可见fov越大,可见的范围越广。

透视划分(Perspective Division)

一旦所有顶点被转换到裁剪空间,最终的操作——透视划分(Perspective Division)将会执行,在这个过程中我们将位置向量的x,y,z分量分别除以向量的齐次w分量;透视划分是将4维裁剪空间坐标转换为3维标准化设备坐标。这一步会在每一个顶点着色器运行的最后被自动执行。

可参考