GPU流水线

当GPU从CPU那里得到渲染命令后,就会进行一系列流水操作,最终把图元渲染到屏幕上。

从图中可以看出,GPU的渲染流水线接收顶点数据作为输入。这些顶点数据是由应用阶段加载到显存中,再由Draw Call指定的。这些数据随后被传递给顶点着色器。

GPU流水线

顶点着色器(Vertex Shader)是完全可编程的,它通常用于实现顶点的空间变换、顶点着色等功能。

曲面细分着色器(Tessellation Shader)是一个可选的着色器,它用于细分图元。

几何着色器(Geometry Shader)同样是一个可选的着色器,它可以被用于执行逐图元(Per-Primitive)的着色操作,或者被用于产生更多的图元。

裁剪(Clipping),这一阶段的目的是将那些不在摄像机视野内的顶点裁减掉,并剔除某些三角图元的面片,是可配置的。

屏幕映射(Screen Mapping),不可配置和编程的,它负责把每个图元的坐标转换到屏幕坐标系中。

三角形设置(Triangle Setup)和三角形遍历(Triangle Traversal)阶段都是固定函数(Fixed-Function)的阶段。

片元着色器(Fragment Shader),是完全可编程的,它用于实现逐片元(Per-Fragment)的着色操作。

逐片元操作(Per-Fragment Operations)阶段负责执行很多重要的操作,例如修改颜色、深度缓冲、进行混合等,它不是可编程的,但具有很高的可配置性。

1. 顶点着色器

顶点着色器(Vertex Shader)是流水线的第一个阶段,它的输入来自于CPU。顶点着色器的处理单位是顶点,也就是说,输入进来的每个顶点都会调用一次顶点着色器。顶点着色器本身不可以创建或者销毁任何顶点,而且无法得到顶点与顶点之间的关系。但正是因为这样的独立性,GPU可以利用本身的特性并行化处理每一个顶点,这意味着这一阶段的处理速度会很快。

顶点着色器需要完成的工作主要有:坐标变换和逐顶点光照(计算顶点的颜色),还可以输出后续阶段所需的数据。

·坐标变换,把顶点坐标从模型空间转换到齐次裁剪空间,如以下代码:
o.pos = mul(UNITY_MVP,v.position);

GPU流水线

注意,图中给出的坐标范围是OpenGL同时也是Unity使用的NDC,它的z分量范围在[-1,1]之间,而在DirectX中,NDC的z分量范围是[0,1]。顶点着色器可以有不同的输出方式。最常见的输出路径是经光栅化后交给片元着色器进行处理。

2. 屏幕映射

屏幕映射(Screen Mapping)的任务是把每个图元的x和y坐标转换到屏幕坐标系(Screen Coordinates)下。屏幕坐标系是一个二维坐标系,它和用于显示画面的分辨率有很大关系。

需要注意的是,屏幕坐标系在OpenGL和DirectX之间的差异问题。OpenGL把屏幕的左下角当成最小的窗口坐标值,而DirectX则定义了屏幕的左上角为最小的窗口坐标值。

GPU流水线

3. 三角形设置

由这一步开始就进入了光栅化阶段。

从上一个阶段输出的信息是屏幕坐标系下的顶点位置以及和它们相关的额外信息,如深度值(z坐标)、法线方向、视角方向等。

光栅化阶段有两个最重要的目标:计算每个图元覆盖了哪些像素,以及为这些像素计算他们的颜色。

光栅化的第一个流水线阶段是三角形设置(Triangle Setup),这个阶段会计算光栅化一个三角网格所需的信息。

具体来说,上一个阶段输出的都是三角网格的顶点,即我们得到的是三角网格每条边的两个端点。但如果要得到整个三角网格对像素的覆盖情况,我们就必须计算每条边上的像素坐标。为了能够计算边界像素的坐标信息,我们就需要得到三角形边界的表示方式。这样的一个计算三角形网格表示数据的过程就叫做三角形设置。

4. 三角形遍历

三角形遍历(Triangle Traversal)阶段将会检查每个像素是否被一个三角网格所覆盖。如果被覆盖的话,就会生成一个片元(fragment)。而这样一个找到哪些像素被三角网格覆盖的过程就是三角形遍历,也被称为扫描变换(Scan Conversion)。

GPU流水线

5. 片元着色器

片元着色器(Fragment Shader)的输入是上一个阶段对顶点信息插值得到的结果,更具体来说,是根据那些从顶点着色器输出的数据插值得到的。而它的输出是一个或者多个颜色值。

6. 逐片元操作

逐片元操作(Per-Fragment Operations)是OpenGL中的说法,在DirectX中,这一阶段被称为输出合并阶段(Output-Merger)。

主要任务:
(1)决定每个片元的可见性。这涉及了很多测试工作,例如深度测试、模板测试等。
(2)如果一个片元通过了所有的测试,就需要把这个片元的颜色值和已经存储在颜色缓冲区中的颜色进行合并,或者说是混合。

逐片元操作阶段是高度可配置性的,即我们可以设置每一步的操作细节。

为什么需要合并呢?我们要知道,这里所讨论的渲染过程是一个物体接着一个物体画到屏幕上的。而每个像素的颜色信息被存储在一个名为颜色缓冲的地方。因此,当我们执行这次渲染时,颜色缓冲中往往已经有了上次渲染之后的颜色结果,那么我们是使用这次渲染得到的颜色完全覆盖之前的结果,还是进行其他处理?这就是合并需要解决的问题。

对于不透明物体,开发者可以关闭混合(blend)操作。这样片元着色器计算得到的颜色值就会直接覆盖掉颜色缓冲区中的像素值。但对于半透明物体,我么就需要使用混合操作来让这个物体看起来是透明的。

GPU流水线

本文转自:博客园 - 前进的yoyocool ,转载此文目的在于传递更多信息,版权归原作者所有。