2.1 综述
要学会怎么使用Shader,我们首先要了解Shader是怎么工作的。实际上,Shader仅仅是渲染流水线中的一个环节,想要让我们的Shader发挥出它的作用,我们就需要知道它在渲染流水线中扮演了怎样的角色。而本节会给出简化后的渲染流水线的工作流程。
2.1.1 什么是流水线
我们先来看一下真实生活中的流水线是什么。在工业上,流水线被广泛应用在装配线上。
我们来举一个例子。假设,老王有一个生产洋娃娃的工厂,一个洋娃娃的生产流程可以分为4个步骤:第1步,制作洋娃娃的躯干;第2步,缝上眼睛和嘴巴;第3步,添加头发;第4步,给洋娃娃进行最后的产品包装。
在流水线出现之前,只有在每个洋娃娃完成了所有这4个工序后才能开始制作下一个洋娃娃。如果说每个步骤需要的时间是1小时的话,那么每4个小时才能生产一个洋娃娃。
但后来人们发现了一个更加有效的方法,即使用流水线。老王把流水线引入工厂之后,工厂发生了很大的变化。虽然制作一个洋娃娃仍然需要4个步骤,但不需要从头到尾完成全部步骤,而是每个步骤由专人来完成,所有步骤并行进行。也就是说,当工序1完成了制作躯干的任务并把其交给工序2时,工序1又开始进行下一个洋娃娃的制作了。
使用流水线的好处在于可以提高单位时间的生产量。在洋娃娃的例子中,使用了流水线技术后每1个小时就可以生产一个洋娃娃。图2.1显示了使用流水线前后生产效率的变化。
▲图2.1 真实生活中的流水线
可以发现,流水线系统中决定最后生产速度的是最慢的工序所需的时间。例如,如果生产洋娃娃的第二道工序需要的是两个小时,其他工序仍然需要1个小时的话,那么平均每两个小时才能生产出一个洋娃娃。即工序2是性能的瓶颈(bottleneck)。
理想情况下,如果把一个非流水线系统分成n个流水线阶段,且每个阶段耗费时间相同的话,会使整个系统得到n倍的速度提升。
2.1.2 什么是渲染流水线
上面的关于流水线的概念同样适用于计算机的图像渲染中。渲染流水线的工作任务在于由一个三维场景出发、生成(或者说渲染)一张二维图像。换句话说,计算机需要从一系列的顶点数据、纹理等信息出发,把这些信息最终转换成一张人眼可以看到的图像。而这个工作通常是由CPU和GPU共同完成的。
《Render-Time Rendering, Third Edition》[1]一书中将一个渲染流程分成3个阶段:应用阶段(Application Stage)、几何阶段(Geometry Stage)、光栅化阶段(Rasterizer Stage)。
注意,这里仅仅是概念性阶段,每个阶段本身通常也是一个流水线系统,即包含了子流水线阶段。图2.2显示了3个概念阶段之间的联系。
▲图2.2 渲染流水线中的3个概念阶段
·应用阶段
从名字我们可以看出,这个阶段是由我们的应用主导的,因此通常由CPU负责实现。换句话说,我们这些开发者具有这个阶段的绝对控制权。
在这一阶段中,开发者有3个主要任务:首先,我们需要准备好场景数据,例如摄像机的位置、视锥体、场景中包含了哪些模型、使用了哪些光源等等;其次,为了提高渲染性能,我们往往需要做一个粗粒度剔除(culling)工作,以把那些不可见的物体剔除出去,这样就不需要再移交给几何阶段进行处理;最后,我们需要设置好每个模型的渲染状态。这些渲染状态包括但不限于它使用的材质(漫反射颜色、高光反射颜色)、使用的纹理、使用的Shader等。这一阶段最重要的输出是渲染所需的几何信息,即渲染图元(rendering primitives)。通俗来讲,渲染图元可以是点、线、三角面等。这些渲染图元将会被传递给下一个阶段——几何阶段。
由于是由开发者主导这一阶段,因此应用阶段的流水线化是由开发者决定的。这不在本书的范畴内,有兴趣的读者可以参考本章的扩展阅读部分。
·几何阶段
几何阶段用于处理所有和我们要绘制的几何相关的事情。例如,决定需要绘制的图元是什么,怎样绘制它们,在哪里绘制它们。这一阶段通常在GPU上进行。
几何阶段负责和每个渲染图元打交道,进行逐顶点、逐多边形的操作。这个阶段可以进一步分成更小的流水线阶段,这在下一章中会讲到。几何阶段的一个重要任务就是把顶点坐标变换到屏幕空间中,再交给光栅器进行处理。通过对输入的渲染图元进行多步处理后,这一阶段将会输出屏幕空间的二维顶点坐标、每个顶点对应的深度值、着色等相关信息,并传递给下一个阶段。
·光栅化阶段
这一阶段将会使用上个阶段传递的数据来产生屏幕上的像素,并渲染出最终的图像。这一阶段也是在GPU上运行。光栅化的任务主要是决定每个渲染图元中的哪些像素应该被绘制在屏幕上。它需要对上一个阶段得到的逐顶点数据(例如纹理坐标、顶点颜色等)进行插值,然后再进行逐像素处理。
和上一个阶段类似,光栅化阶段也可以分成更小的流水线阶段。
提示
读者需要把上面的3个流水线阶段和我们将要讲到的GPU流水线阶段区分开来。这里的流水线均是概念流水线,是我们为了给一个渲染流程进行基本的功能划分而提出来的。下面要介绍的GPU流水线,则是硬件真正用于实现上述概念的流水线。