OpenGL 坐标系统(Coordinate System)

Reading time ~1 minute

过渡坐标系

OpenGL希望在所有顶点着色器运行后,所有我们可见的顶点都变为标准化设备坐标(Normalized Device Coordinate, NDC)。我们通常会自己设定一个坐标的范围,之后再在顶点着色器中将这些坐标转换为NDC。然后将这些NDC传入光栅器(Rasterizer),再将他们转换为屏幕上的二维坐标或像素。

这一过程通常是分步实现的,在流水线里面我们会将对象转换到多个坐标系统,即过渡坐标系(Intermediate Coordinate System)。将对象的坐标转换到几个过渡坐标系的优点在于,在这些特定的坐标系统中进行一些操作或运算更加方便和容易。比较重要的总共有5个不同的坐标系统:

  • 局部空间(Local Space,或者称为物体空间(Object Space))
  • 世界空间(World Space)
  • 观察空间(View Space,或者称为视觉空间(Eye Space))
  • 裁剪空间(Clip Space)
  • 屏幕空间(Screen Space)

这些就是我们将所有顶点转换为片段之前,顶点需要处于的不同的状态。

概述

为了将坐标从一个坐标系转换到另一个坐标系,我们需要用到几个转换矩阵,最重要的几个分别是模型(Model)、视图(View)、投影(Projection)三个矩阵。下面的图示显示了整个流程及各个转换过程做了什么:

之所以将顶点转换到各个不同的空间的原因,是有些操作在特定的坐标系统中才有意义且更方便。例如,当修改对象时,如果在局部空间中则是有意义的;当对对象做相对于其它对象的位置的操作时,在世界坐标系中则是有意义的。我们本可以定义一个直接从局部空间到裁剪空间的转换矩阵,但那样会失去灵活性。接下来我们将要更仔细地讨论各个坐标系。

局部空间(Local Space)

局部空间是指对象所在的坐标空间,如对象最开始所在的地方。你的模型的所有顶点都是在局部空间:他们相对于你的对象来说都是局部的。

世界空间(World Space)

世界空间中的坐标是指顶点相对于(游戏)世界的坐标。物体变换到的最终空间就是世界坐标系,物体会在世界坐标中分散摆放。对象的坐标将会从局部坐标转换到世界坐标,该转换是由模型矩阵(Model Matrix)实现的。

模型矩阵是一种转换矩阵,它能通过对对象进行平移、缩放、旋转来将它置于它本应该在的位置或方向。如导入一个房子的模型,通过对它进行缩放、平移、旋转等转换将其置于 正好位于游戏场景中马路边街区的位置。

观察空间(View Space)

观察空间经常称为OpenGL的摄像机(Camera),所以有时也称为摄像机空间(Camera Space)或视觉空间(Eye Space)。 观察空间就是将对象的世界空间的坐标转换为观察者视野(摄像机)前面的坐标。而这通常是由一系列转换组合来平移和旋转场景从而使得特定的对象被转换到摄像机前面。这些组合在一起的转换通常存储在一个观察矩阵(View Matrix)里,用来将世界坐标转换到观察空间。

裁剪空间(Clip Space)

在一个顶点着色器运行的最后,OpenGL期望所有的坐标都能落在一个给定的范围内,且任何在这个范围之外的点都应该被裁剪(Clipped)

因为将所有可见的坐标都放置在-1.0到1.0的范围内不是很直观,所以我们会指定自己的坐标集(Coordinate Set)并将它转换回标准化设备坐标系,就像OpenGL期望它做的那样。

为了将顶点坐标从观察空间转换到裁剪空间,我们需要定义一个投影矩阵(Projection Matrix),它指定了坐标的范围。投影矩阵接着会将在它指定的范围内的坐标转换到标准化设备坐标系中(-1.0,1.0)。

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

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

在这一阶段之后,坐标经过转换的结果将会被映射到屏幕空间(由glViewport设置)且被转换成片段。

投影矩阵将观察坐标转换为裁剪坐标的过程采用两种不同的方式,每种方式分别定义自己的平截头体。我们可以创建一个正射投影矩阵(Orthographic Projection Matrix)或一个透视投影矩阵(Perspective Projection Matrix)

  • 正射投影(Orthographic Projection) 正射投影矩阵定义了一个类似立方体的平截头体。创建一个正射投影矩阵需要指定可见平截头体的宽、高和长度。它的平截头体看起来像一个容器:

    上面的平截头体定义了由宽、高、近平面和远平面决定的可视的坐标系。 正视平截头体直接将平截头体内部的顶点映射到NDC中,因为每个向量的w分量都是不变的;如果w分量等于1.0,则透视划分不会改变坐标的值。

    正射投影矩阵直接将坐标映射到屏幕的二维平面内,但这会产生不真实的结果,因为这个投影没有将透视(Perspective)考虑进去。所以我们需要透视投影矩阵来解决这个问题。

  • 透视投影(Perspective Projection) 我们都知道近大远小的现象,这个效果我们称之为透视。透视的效果在我们看一条无限长的高速公路或铁路时尤其明显,正如下面图片显示的那样:

    透视效果

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

    每个顶点坐标的分量都会除以它的w分量,得到一个距离观察者的较小的顶点坐标。这是也是另一个w分量很重要的原因,因为它能够帮助我们进行透射投影。最后的结果坐标就是处于标准化设备空间内的。

    透视平截头体的如下所示:

    fov表示的是视野(Field of View),设置了观察空间的大小。对于一个真实的观察效果,它的值经常设置为45.0。所有在近平面和远平面的顶点且处于平截头体内的顶点都会被渲染。

把它们都组合到一起

我们为上述的每一个步骤都创建了一个转换矩阵:模型矩阵、观察矩阵和投影矩阵。一个顶点的坐标将会根据以下过程被转换到裁剪坐标:

注意每个矩阵被运算的顺序是相反的(需要从右往左乘上每个矩阵)。最后的顶点应该被赋予顶点着色器中的gl_Position且OpenGL将会自动进行透视划分和裁剪。


参考: LearnOpenGL-CN 坐标系统(Coordinate System)

Scriptable Objects 及 游戏架构

Scriptable Objects 相关介绍,及基于其的游戏架构技术 Continue reading

AssetBundle 最佳实践

Published on January 29, 2019

AssetBundle 基础总结

Published on January 27, 2019