实现渲染最核心的组件是图像渲染管线,也简单的称为管线。它最主要的功能是在指定的虚拟摄像头,3d对象,光源,阴影方程式,纹理等里,生成或渲染出一张2d图片。在这张生成图里,所有对象的坐标和形状都会受到它们自身几何形状,摄像头摆放的位置,以及周围环境的影响。而它们的外观则受到材质属性,光源,纹理和阴影模型的影响。这章将主要介绍渲染管线在不同阶段的功能。
一.架构
渲染管线主要分为3个阶段:1应用阶段,几何阶段,光栅化阶段。
二.应用阶段
这个阶段是开发者可以完全掌控的阶段,因为都是在cpu下执行的。但在这个阶段的改动仍然会影响到后面的阶段。例如,在应用阶段的算法或者设置可能会增加最终被渲染的三角形个数。
在这个阶段结束时,会把即将要渲染的几何体传给下一个阶段。这就是应用阶段最重要的任务。
这个阶段,需要处理来自用户的输入,例如键盘,鼠标之类的。也需要处理纹理动画的变换,碰撞检测,加速算法,视锥剔除等等。为了提高表现,这个阶段通常使用多核处理。具体内容之后章节会讨论。
三.几何阶段
几何阶段的主要职责是预多边形和预顶点的操作。这个阶段将功能性的分为几个子阶段
1模型视图的转换。
为了能够显示在屏幕上,一个模型要在几个不同的空间或坐标系之间进行转换。最初,模型存放在自己的模型空间,该空间记录着它本身的位置和方向。每一个模式是可以关联几个模型转换的。这意味着在没有基础几何体复制的条件下,在同一个场景中,是可以同时存在着一个模型的多个有着不同位置,方向,大小的模型备份的。
通过模型转换(model transform),模型内的所有顶点和模型法线都发生了转换。物体的坐标即所谓的模型坐标,在经过模型转换后,模型就存在在世界坐标系或称为世界空间中。这个世界空间是唯一的,当所有的模型在各自的模型转换之后,会存在于同一个世界空间。
正如之前所说的那样,只有存在于摄像机内的模型才会渲染出来。在世界坐标中,摄像机有位置和方向,是用来定位摄像头的。为了有助于投影(projection)和剪切(clipping·),所有的模型和坐标都要经过视图转换(view transform)。视图转换的目的是使摄像机定位在原点,同时方向指向z轴的负方向(上),此时,x轴以右为正方向,y轴以垂直于平面向上(如下图右)。
figure:左图是用户希望的摄像机位置和方向,右图则是经过视图转换后摄像机的位置和方向。经过操作后使得剪切和投影的运算更简单快速。同时,对于不同的投影都可以使用相似的技术。
右图描绘的空间被称为摄像机空间(camera space),或眼睛空间(eye space)。模型变换和视图变换都使用到4*4的矩阵。以后会详细讨论。
2顶点着色
为了生成一个真实的场景,不仅要渲染出物体的形状和位置,它们的外观也必须被模型化。这意味着包括物体自身的材质,以及任意对物体产生影响的光照。有很多方式模型化材质和光照,有易有难。这些对光照和材质操作一般称之为 着色(shading)。它涉及到对物体上大多数的点进行着色方程式计算。通常,这样的操作会发生在几何阶段的模型顶点,以及光栅化阶段的预像素(候选像素 pre-pixel)。大量数据存在于顶点(vertex)中,类似于点的位置,法线,颜色,以及其他数字化后的用于着色方程的信息。顶点着色的结果将会传递给光栅化阶段用于插值。
着色运算通常建议发生在世界空间。因为这样方便把相关的元素(摄像头,光源等)转换到其他空间(模型空间,眼睛空间)并进行运算。这样有效是因为---存在于同一空间中的所有的在着色计算中的元素进行转换,它们之间的光照,摄像机,模型等相关关系将会被保存。
3投影
在着色之后,渲染系统将运行投影,即把视景体(view volume)转换成每个坐标系的点都只存在于(-1,-1,-1)和(1,1,1)之间的单元立方体中。这个单元立方体(unit cube)也被称为正规化视景体。通常,有两种投影形式,一种叫正交投影(orthographic projection),另一种叫透视投影(perspective projection)。
正交投影得到的视景体通常是一个矩形盒子。它最主要的性质是,视景体经过转换后,平行的线仍然保持平行。这个转换是一系列位移和缩放的集合。
透视投影相对来说更复杂一些。经过它的转换以后,距离摄像机越远的对象就越小。它模拟了我们在眼睛看到实物的方式。转换后,得到的视景体也被称为视锥体(frustum)。

两种投影都是用4*4的矩阵构建的(详见后章)。模型被转换后的坐标都会是归一化设备坐标(normalized device coordinates) 即坐标取值只能在[-1,1]区间。
模型最终是需要由3d转换为2d,z轴的坐标不会记录在生成的图片中(但会保存在z-buffer中)。
4剪切
只有位置全部或部分存在在视景体中的图元(点,线,三角形)才会传递到光栅化阶段,也只有这些才有机会被渲染到屏幕上。那些全部不在视景体的图元就直接被抛弃(因为不需要渲染出来),而图元部分在外面,部分在里面的将需要剪切(clipping)。
图片:在经过了投影转换后,只有存在于单元立方体里面的图元才会得到进一步的处理。经过剪切之后,那些在外面的图元的顶点将会被删除,而新的顶点也会随之生成。注:上图显示的只有一面,但单元立方体是有6面的。
5屏幕映射
经过剪切之后的图元进入了这个阶段。它们的坐标仍然是3维的,x和y会被转为屏幕坐标(screen coordinates)。而z坐标被称为窗口坐标(window coordinates)。假设一个场景要被渲染到一个最小值为(x1,y1)最大值为(x2,y2)的窗口。那么图元的坐标需要通过一定缩放操作使得它们的值是在[x1,x2]和[y1,y2]之间。经过操作后的新的x,y值就是所谓的屏幕坐标。而z值会被压缩到[-1,1]之间,传递到光栅化阶段。

四.光栅化阶段
光栅化的目标是利用 几何阶段获得的所有经过转换和投影的顶点 计算出每个像素的颜色。
光栅化阶段也可按功能性分为几个阶段:
1三角形的建立
这是一个硬件自动操作的环节。三角形表面的差异和其他数据都会被计算。这些数据将会用来三角形遍历。同时大量用于插值的着色数据也在这产生。
2三角形遍历
这阶段三角形上的每一个像素都会被检测,并生成片段(fragment)。查询每一个存在于三角形内部的像素,被定义为三角形遍历。使用在3个三角形顶点之间数据插值的生成每个三角形片段的属性。这些属性包括了片段的深度值,以及几何阶段的着色数据。
3像素着色(pixel shading)
这阶段使用插值渲染的数据(上阶段获得的每个三角形的片段)作为输入,每一个可能成为最终图片上的像素都会被调用到。最终的结果是一个或多个颜色会传递到下一个阶段(成为最终那张图上的像素颜色)。这是一个可被gpu编译的阶段,大量的技术都可以在这个阶段使用。最重要的一项是纹理贴图。
4合并
每个像素的信息都会被存在颜色缓存(color buffer)中。这个阶段意味着前面阶段每个三角形的片段颜色都将合并到唯一一个颜色缓存中。这个阶段是不可编程的,但是可以通过配置的方式开启一些特殊效果(深度检测,模板检测等等)。
这阶段的任务是解决可见性,这意味着当整个场景被渲染时,颜色缓存里面应该拥有的是哪些图元的颜色。对于大多数的硬件来说,是通过深度缓存(depth buffer)算法来控制的。深度缓存的大小,形状和颜色缓存一模一样。对于每一个像素,深度缓存保存深度值的都是来自于最近的图元。这也就是说,当一个图元的像素准备被渲染时,该像素的z值将被计算,并且会和在深度缓存里面相同位置的值做对比。如果新的值小于深度缓存里面的值,就表明在这个像素的位置上,该图元比之前的图元更靠近于摄像机。因此,该像素的颜色的值和深度都需要更新为新的值。值大于原来的时候,颜色值和z值将原封不动。深度缓存的算法非常简单,平均效率为O(n)(n为所有被渲染的图元)。这个算法运行大部分的图元在任意条件下都能渲染,这也是它为什么这么流行的原因。但是,部分透明的图元必须在所有不透明的图元渲染后才能渲染(5.7章)。这也是深度缓存最大的缺点之一。
除了深度缓存和颜色缓存保存像素数据以外,还有一些其他的通道和缓存用来过滤片段信息。透明度通道(alpha channel)就是用来保存像素透明度的。在深度测试(depth test)之前,可以开启透明度测试(alpha test)来测试片段。片段的alpha值将会一个指定的值进行比较(大小,或等于)。如果片段没有通过测试,将停止进行之后的操作。这个测试通常用于确保那些全透明的片段不影响深度缓存(6.6章)。
模板缓存(stencil buffer)是一个用来记录图元的位置的缓存。它通常每个像素包含8个位。图元可以通过多种方式渲染进入模板缓存。模板缓存中的内容会用来控制颜色缓存和深度缓存。例如,有一个全部填充的圆形被画入模板缓存,它可以控制其他图元的颜色只在圆形存在的地方显示。模板缓存是一种非常强大的生成各种特效的工具。
所有的这些在管线最后的功能方法都被称为光栅操作或混合操作。
帧缓存通常包括了所有的缓存,但有时也只包括颜色缓存和深度缓存。
当图元到达光栅化阶段,它们将会显示到屏幕。为了防止人眼能识别出这些图元正在渲染到屏幕,双缓冲计算就出现了。它意味着场景的渲染不是发生在屏幕的,而是先渲染到后缓存(back buffer),一旦场景在后缓存渲染完成,它的内容就交换到前缓存(front buffer)。
本章描述了实时渲染的管线。它不是唯一一种渲染管线。还有几种离线渲染的管线。渲染影视作品的通常叫micropolygon pipeline。学院研究和前瞻渲染应用通常使用光线追踪渲染器(ray tracing renderers)。
过去的软件开发者只能使用固定渲染管线的api。因为以前的硬件是不支持编程的,可以控制的只有几个阶段(例如深度测试的开关),但是不能用代码来控制那些阶段的。可编程的gpu让一切变得可能。
To Be Continued.