栅格化

栅格化（英式英语：rasterisation、美式英语：rasterization），又称光栅化，是将向量图形格式表示的图像转换成位图以用于显示器或者打印机输出的过程。

## 综述

总体上来说，栅格化这个术语可以用于任何将向量图形转换成位图的过程。

在通常的应用中，这个术语用来表示在计算机上显示三维形状的流行渲染算法。栅格化目前是生成实时三维计算机图形最流行的算法。实时应用需要立即响应用户输入，并且通常需要至少每秒 24 帧的速率。

与辐射着色、光线跟踪等其它渲染技术不同，栅格化的速度非常快，但是由于它不是根据光传输的物理规律进行处理的，所以无法正确模拟许多复杂真实光照环境，只能达到足够欺骗人类眼睛的程度。

## 基本实现方法

最基础的栅格化算法将多边形表示的三维场景渲染到二维表面。多边形由三角形的集合表示，三角形由三维空间中的三个顶点表示。在最简单的实现形式中，栅格化工具将顶点数据映射到观察者显示器上对应的二维坐标点，然后对变换出的二维三角形进行合适的填充。

### 变换

通常使用矩阵运算进行变换，另外也可以用四元数运算但那不是本文讨论的范围。在三维顶点中添加一个齐次变量成为四维定点然后左乘一个 4 x 4 的变换矩阵，通过这种方法就可以对三维顶点进行变换。主要的变换有平移、缩放、旋转以及投射。

平移变换是点在三维空间中从一点移动到固定偏移的另外一点的过程，平移可以用下面的矩阵表示：

$$\begin{bmatrix} 1 & 0 & 0 & X \\ 0 & 1 & 0 & Y \\ 0 & 0 & 1 & Z \\ 0 & 0 & 0 & 1 \end{bmatrix}$$

X、Y 与 Z 分别是三维空间中的偏移。

缩放变换通过在顶点位置上乘以一个标量值实现，这样就将顶点相对于原点的位置进行缩放。缩放变换可以用下面的矩阵表示：

$$\begin{bmatrix} X & 0 & 0 & 0 \\ 0 & Y & 0 & 0 \\ 0 & 0 & Z & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}$$

其中 X、Y 与 Z 是三维坐标中每一维所乘的数值。通过使用不同的 X、Y、Z 值可以实现不对称缩放。

旋转变换是绕着一个轴线对每点进行旋转。

绕 X 轴旋转：

$$\begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & \cos{\theta} & - \sin{\theta} & 0 \\ 0 & \sin{\theta} & \cos{\theta} & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}$$

绕 Y 轴旋转：

$$\begin{bmatrix} \cos{\theta} & 0 & \sin{\theta} & 0 \\ 0 & 1 & 0 & 0 \\ - \sin{\theta} & 0 & \cos{\theta} & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}$$

绕 Z 轴旋转：

$$\begin{bmatrix} \cos{\theta} & -\sin{\theta} & 0 & 0 \\ \sin{\theta} & \cos{\theta} & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}$$

其中 θ 表示旋转角度。

逻辑上一系列的平移、缩放、旋转可以表示绝大多数的变换。通常栅格化系统使用变换栈将输入顶点的数据流变换到指定位置，变换栈是保存矩阵的标准堆栈，输入顶点与矩阵栈相乘进行变换。

为了说明如何使用变换栈，我们假设有一个简单场景中只有一个人的模型。这个人在一个特定位置面向任意一个角度竖直站立，头转向另外一个方向。使用一系列的顶点与模型用来表示这个人物。首先，将一个变换矩阵压到堆栈中将模型移到正确的位置；其次，将缩放矩阵压到堆栈中将模型缩放到正确的尺寸；然后，表示身体的顶点数据流送到栅格化工具中；由于头部面向另外一个方向，将旋转矩阵从堆栈弹出，压入一个绕 Y 轴旋转一个不同角度的旋转矩阵；最后，表示头部的顶点数据流送到栅格化工具。

在所有点都已经变换到相对于观察者的合适三维空间位置之后，就需要将它们变换到二维空间了。最简单的投影方法正投影简单地从变换的三维顶点中扔掉 Z 分量。正投影的特点是三维空间中的平行线在二维表示中仍然平行。但是，真实世界中的图像都是透视图像，离观察者较远的点之间的距离看起来要比较近点之间的距离近，这些点都要进行透视投影变换。

从概念上来讲就是将透视体转变成正视体。透视体是平截头体，即被截去头部的金字塔体。正视体是一个矩形盒，其中远近两个观察面都与图像平面平行。

透视投影变换可以用下面的矩阵表示：

$$\begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & (N+F)/N & -F \\ 0 & 0 & 1/N & 0 \end{bmatrix}$$

其中 F 与 N 分别是观察面的远近距离。四个结果向量是一个齐次变量不是 1 的向量。对向量进行齐次变换，或者乘以齐次变量的逆，齐次变量变为单位矩阵，这就是最终二维位置的 x 与 y 坐标。

### 裁剪

一旦三角形顶点转换到正确的二维位置之后，这些位置可能位于观察窗口之外，也可能位于屏幕之内。裁剪就是对三角形进行处理以适合显示区域的过程。

最常用的技术是Sutherland-Hodgeman裁剪算法。在这种方法中，每次测试每个图像平面的四条边，对于每个边测试每个待渲染的点。如果该点位于边界之外，就剔除该点。对于与图像平的面边相交的三角形边，即边的一个顶点位于图像内部一个位于外部，那么就在交叉点插入一个点并且移除外部的点。

### 扫描变换

传统的栅格化过程的最后一步就是填充图像平面中的二维三角形，这个过程就是“扫描变换”。

第一个需要考虑的问题就是是否需要绘制给定的像素。一个需要渲染的像素必须位于三角形内部、必须未被裁掉，并且必须未被其它像素遮挡。有许多算法可以用于在三角形内进行填充，其中最流行的方法是扫描线算法。

由于很难确定栅格化引擎是否会从前到后绘制所有像素，因此必须要有一些方法来确保离观察者较近的像素不会被较远的像素所覆盖。最为常用的一种方法是深度缓存，深度缓存是一个与图像平面对应的保存每个像素深度的二维数组。每个像素进行绘制的时候都要更新深度缓存中的深度值，每个新像素在绘制之前都要检查深度缓存中的深度值，距离观察者较近的像素就会绘制，而距离较远的都被舍弃。

为了确定像素颜色，需要进行纹理或者浓淡效果计算。纹理图是用于定义三角形显示外观的位图。每个三角形顶点除了位置坐标之外都与纹理以及二维纹理坐标 (u,v) 发生关联。每次渲染三角形中的像素的时候，都必须在纹理中找到对应的纹素，这是根据在屏幕上像素与顶点的距离在与纹理坐标相关联的三角形顶点之间插值完成的。在透视投影中，插值是在根据顶点深度分开的纹理坐标上进行的，这样做就可以避免透视缩减（perspective foreshortening）问题。

在确定像素最终颜色之前，必须根据场景中的所有光源计算像素上的光照。在场景中通常有三种类型的光源。“定向光”是在场景中按照一个固定方向传输并且强度保持不变的光。在现实生活中，由于太阳距离遥远所以在地球上的观察者看来是平行光线并且其衰减微乎其微，所以太阳光可以看作是定向光。“点光源”是从空间中明确位置向所有方向发射光线的光源。在远距离的物体上的入射光线会有衰减。最后一种是“聚光灯”，如同现实生活中的聚光灯一样，它有一个明确的空间位置、方向以及光锥的角度。另外，经常在光照计算完成之后添加一个环境光值以补偿光栅化无法正确计算的全局照明效果。

有许多可以用于光栅化的浓淡算法。所有的浓淡处理算法都必须考虑与光源的距离以及遮蔽物体法向量与光照入射角。最快的算法让三角形中的所有像素使用同样的亮度，但是这种方法无法生成平滑效果的表面。另外也可以单独计算顶点的亮度，然后绘制内部像素的时候对顶点亮度进行插值。速度最慢也最为真实的实现方法是单独计算每点的亮度。常用的浓淡模型有 Gouraud着色法 和 Phong着色法。

## 加速技术

为了在任何栅格化引擎中获得最大的性能，只能往渲染工具中发送最少数量的多边形。人们已经开发出了一些加速技术以剔除无法看到的物体。

### 后向剔除

最简单的剔除多边形之方法就是剔除所有背离观察者的多边形，这就是后向剔除。由于大多数三维物体都是封闭的，所以除非观察者位于物体内部，背离观察者的多边形都会被面向观察者的多边形所遮挡。多边形的方向由它的旋绕方向（winding）或者送到渲染工具的顶点顺序所确定。一旦多边形变换到屏幕空间之后，就可以检查它是否位于相反的方向，一旦如此就丢弃这个多边形。当然，后向剔除不适合于简并的不封闭立体。

### 空间数据结构

许多先进的技术使用数据结构剔除观察物体之外的物体或者被其它物体遮挡的物体，最为常用的数据结构有二元空间分割、八叉树以及“Cell and Portal Culling”。

## 进一步改进

尽管基本的栅格化过程已经出现了数十年，许多当今的应用仍然在优化、增加栅格化渲染引擎的应用范围。

### 纹理映射

纹理是在特定的分辨率生成的，但是由于纹理覆盖的表面与观察者之间可能是任意的距离，所以纹理也可能最后图像上有任意的尺寸。因此，屏幕上的一个像素通常并不直接对应于一个纹素，而是需要使用一些纹理滤波技术来生成任意距离的清晰图像。有许多在图像质量与计算的复杂性进行不同这种考虑的方法可以完成这项工作。

### 环境映射

环境映射是纹理坐标与观察点相关的纹理映射形式。例如，其中一个常用的应用程序就是用来模拟镜面反射，我们可以将整个房间内部环境映射到房间内的一个金属杯上，当观察者沿着杯子移动的时候，杯子顶点的纹理坐标也随之变化，这样就得到反射效果。

### 凸凹纹理映射

凸凹纹理映射是改变像素深度而不是颜色的另外一种纹理映射形式。尤其是与最新的阴影工具一起使用的时候，凸凹纹理映射使得表面显现出与光照有关的凸凹不平，从而大幅度地提高真实感。

### 细节层次

在许多当今的应用中，任何场景中的多边形数目都是非常大的，但是场景中的观察者只能区分近距物体的细节。细节层次算法根据物体与观察者的距离改变几何图形的复杂性。正对着观察者的物体需要进行非常复杂的渲染，而距离远的物体可以动态地简化，甚至可以完全适用二维 sprite 替代。

### 阴影

传统栅格化过程的光照计算没有考虑物体遮挡的因素。阴影贴图与阴影体是当今两种生成阴影的普通技术。

## 硬件加速

从1990年代开始，用于消费型桌面电脑的硬件加速开始变得普通。图形程序员早期依赖于手工编写汇编程序以改善程序的运行速度，现在大多数的程序已经使用现有的图形 API 驱动专用的图形处理器。

目前的图形处理器已普遍带有几乎完全是可程式化的 绘图管线，包含对各个顶点及片段进行处理，并有多种图形API来帮助开法者使用这些管线，如 OpenGL 和 Vulkan 等。

## 外部链接

- Michael Abrash’s articles on computer graphics
- 微软 DirectX API
- OpenGL API
- Matrices (including transformation matrices) from MathWorld
- OpenGL对于其所提供图形管线API的说明