图形管线(Graphics Pipeline)
这一节将对前面的所有内容做一个总结,因为图形管线就是由前面的所有内容组成的一个图形渲染流水线,空间中的点经过图形管线就被渲染到屏幕上成为图形。本节以概述形式介绍图形管线,随着学习的深入,会对图形管线有更深入的理解,到时候会对图形渲染管线进行全面详尽的总结。
图形管线是基于物体顺序渲染的一系列绘制操作的序列。相对于基于物体顺序渲染还有基于图像顺序渲染,会在之后会讲到。基于物体顺序渲染由于其极高的运行效率而统治了图形学的渲染方法,从早期人们提出了软件图形管线后,图形管线的流程思路又被硬件优化内置到显卡中成为硬件图形管线,这两个管线后来呈现出并行关系,负责不同的事务。
图形管线的主要流程如下:
1 顶点数据处理
输入图形管线的是三维空间中的顶点,这些输入的顶点数据不止有坐标,还包括纹理坐标、顶点法线和顶点颜色等各种属性,顶点数据处理就是对这些属性数据进行处理,首先把顶点连成空间中的一个个面,可以是三角形也可以是四边形,这里我们就用常用的三角形,空间中的三角形称为图元。
之后就是经过各种矩阵变换把三维映射到二维屏幕上,在变换的时候还会进行一步重要的操作,叫做视体裁剪,视体就是指透视投影近平面和远平面组成的四棱台。这需要回顾透视投影部分的内容。
透视投影有一个重要的性质就是它会将直线映射为直线,平面映射为平面,线段映射为线段,并且保留了所有点的排序正确,这保证了三角形正确映射为三角形。
但是经过透视投影矩阵,原本的 $z$ 将会被映射为 $z’=n+f-nf/z$。于是我们就会发现在这个映射中,正的 $z$ 值会被映射到负 $z$ 上,负的 $z$ 值被映射到正 $z$ 上,当我们要渲染的物体都在视体内时自然还能正确投影到屏幕上,但是一旦出现了跨越 $z=0$ 的线段,线段就会有一部分被映射到正负无穷因而被撕裂。
详细来看其实是下面的图所导致的。下图所表示经过透视投影处理后的 $z$ 值情况,可以看到由于视体标准化的原因 $n+f$ 是真正意义上的 0 点,$z$ 值在 $n+f$ 的位置发生的翻转,这个翻转导致了图的下半部分的三角形 $c$ 点经过透视投影后出现在了另一个方向,这将会导致光栅化的时候绘制出错误三角形。
观察上面的图我们还可以发现在视体中的部分(也就是z=n到z=f的区域),经过透视投影后也会保持原来的顺序和位置,因而解决这个问题的方法就很直观了,在透视投影前就先将视体之外的面片裁剪掉,这就是视体裁剪。视体裁剪除了防止撕裂外还有一个显而易见的好处就是减少了后面进行投影变换时需要处理的顶点数量,而且减少掉的内容本来就在视体之外所以不会影响到最终的画面。
具体如何进行裁剪,我们可以根据视体的顶点坐标计算出六个平面的方程,然后将空间中的点带入视体面方程,但平面方程计算比较复杂,更多关于裁剪的内容可以查看本篇的参考内容。
2 曲面细分
曲面细分是利用镶嵌化处理技术对三角面进行细分,以此来增加物体表面的三角面的数量,是渲染管线一个可选的阶段。它由外壳着色器(Hull Shader)、镶嵌器(Tessellator)和域着色器(Domain Shader)构成,其中外壳着色器和域着色器是可编程的,而镶嵌器是由硬件管理的。我们可以借助曲面细分的技术实现细节层次(Level-of-Detail)的机制,使得离摄像机越近的物体具有更加丰富的细节,而远离摄像机的物体具有较少的细节。
3 光栅化
经过前面的处理我们得到了许多图元,并且最后通过视口变换变换到了屏幕坐标系下,然后就是进行光栅化,光栅化阶段会把图元变成屏幕坐标系中的离散的片元,片元可以理解为像素,但它与像素的区别在于片元拥有比像素更多的信息,例如深度值,法线,纹理坐标等信息。因此片元只是尺度和像素相同,但片元还不能直接显示到屏幕,一个像素会有很多个候选片元,这些片元还要经过一系列计算才能决定哪些片元显示在屏幕上。
4 片元处理
片元处理阶段会对各个片元计算深度、颜色等属性信息,这里用到的就是三角形重心坐标插值,计算颜色还要用到纹理映射,因为片元相当于一个像素,所以这里的着色频率相当于是对每一个像素计算着色,也就是Phong Shading。有的图形管线也会在顶点处理时进行着色,也就是进行Gouraud Shading,但效果肯定不如逐片元着色好。
5 混合测试
经过片元处理我们就得到了所有片元的颜色,最后就是要选择哪些片元能够显示在屏幕上,这里要经过一系列的混合测试,比如深度测试,也就是要用到之前说过的Z-Buffer,选择深度小的片元进行显示,当然除了深度测试之外还有切测试、Alpha测试、模板测试等等。
6 Shader
刚才说到过,在图形管线中我们可以选择在顶点处理阶段进行着色或者在片元处理阶段进行着色,而且我们还可以对着色进行专门的编程,控制GPU对对一个着色点执行什么样的着色操作,这就是Shader。
Shader(着色器)是指一组供计算机图形资源在执行渲染任务时使用的指令,用于计算图像的颜色或明暗。通俗地说,着色器告诉电脑如何用特有的一种方法去绘制物体。
关于Shader,后面会有专门的文章详细的讲解。