渲染流水线

学习目标:

  1. 了解用于在2D图像中表现出场景立体感和空间深度感等真实效果的关键因素
  2. 探索如何用Direct3D表示3D对象
  3. 学习如何建立虚拟摄像机
  4. 理解渲染流水线,根据给定的3D场景的几何描述,生成其2D图像的流程

5.1 3D视觉即错觉?

1、从视觉观察效果来看,平行线最终会相交于一点(消失点,又称为灭点),因此我们可以得出结论:随着深度(z方向)的增加,物体会显得越来越小、(dx是左手坐标系,OpenGL是右手坐标系)。

2、我们都知道物体重叠,这是一个重要的概念,即不透明的物体可以遮挡住其后侧物体的局部或整体,它传达了不同物体在场景中的深度顺序关系

3、光照和阴影的处理在刻画3D物体的实体形状和立体感中扮演着至关重要的角色,其中阴影担负着两个重要的任务,即暗示了光源在场景中的相对位置和物体相对于某物的大概位置。

5.2 模型的表示

实际上,实体3D对象是借助三角形网格来近似表示的,三角形是3D物体建模的基石,我们可以用三角形网格模拟出任何真实世界中的3D物体。当然,点和线也是必不可少的,例如,我们可以用一系列宽度为1像素的线段绘制出一条近似曲线。

在游戏开发的过程中,我们一般都不会自己手动列出三角形来模拟3D物体,这样太累了,一般来说,除了最简单的模型,我们大部分模型都是在3D建模工具(3D )中完成。在游戏开发中流行的建模软件有:3D Studio Max、LightWave 3D、Maya、Softimage和Blender。

5.3 计算机色彩基础

计算机显示器中每一个像素发出的颜色都是红绿蓝三色混合光,当混合的光线进入观察者眼中,照射到视网膜的特定区域时,视锥细胞便受到刺激产生神经冲动,并通过视神经传到大脑,大脑继而解释传来的信号并感知颜色。因为混合光的变化各异,所以细胞受到的刺激也不尽相同,所以我们就能感知到不同颜色间的差异了

每款显示器能发出的红绿蓝三种颜色的强度是有限的,为了便于描述光的强度,我们通常将它量化为范围在0-1归一化区间中的值,0代表无强度,1代表最强的强度,由此我们可以使用3D向量(r,g,b)来表示颜色,这三个分量分别表示红绿蓝三色光在混合光中的强度

5.3.1 颜色运算

5.3.2 128位颜色

事实上,我们通常会使用到一种名为alpha分量(alpha component)的颜色分量,alpha分量常用于表示颜色的不透明度(0.0表示完全透明,1.0表示完全不透明),它在混合技术中将会起到至关重要的作用。加上这个分量之后,我们便可以使用4D向量(r,g,b,a)来表示每一种颜色

为了使用128位数据来表示一种颜色,每一个分量都要使用浮点值。由于每一种颜色都可以使用4D向量来表示,所以我们在代码中可以使用XMVECTIR类型来描述他们,然后通过DriectXMath向量函数来进行颜色运算。因此我们也可以借助SIMD技术加快数据的处理速度。

5.3.3 32位颜色

为了用32位数据表示一种颜色,每一个分量尽可以分配到一个字节,因此每一个占用8位字节的颜色分量就可以分别描述256中不同的颜色强度(0代表无强度,256代表最强强度),这样四个颜色分量一共可以产生255的四次方种颜色,在DirectXMath库中提供了XMCOLOR结构体来存储32位颜色。

128位颜色和32位颜色之间是可以相互转换的,只要将32位颜色向量的每一个分量值/255就可以得到对应的128位颜色向量。由于在XMCOLOR中通常将4个8位颜色分量封装成一个32位整数值(例如一个unsigned int类型的值),因此在32位颜色和128位颜色之间的转换通常要进行一些额外的运算,对此,DirectXMath库提供了一个获取XMCOLOR类型实例并且返回对应的XMVECTOR类型值的函数:

XMVECTOR XM_CALLONV PackedVector::XMLoadColor(const XMCOLOR* pSource);

除此之外,DirectXMath库还提供了一个可以将XMVECTOR类型值转换成XMCOLOR类型值的函数:

void XM_CALLCONV PackedVector::XMStoreColor(XMCOLOR pDestination, FXMVECTOR V);

128位颜色通常用于高精度的颜色运算,例如位于像素着色器内的各种运算。不过最终存储在后台缓冲区中的颜色一般都是32位颜色。

5.4 渲染流水线概述

渲染流水线:若给出某一个3D场景的几何描述,并在其中放置一台具有确定位置和朝向的虚拟摄像机,那么渲染流水线则是以此摄像机为观察视角而生成的2D图像的一系列步骤

先上图:

blender 线段建模_数据

图片为手机拍摄,望见谅(图片来源:DirectX 12 3D 游戏开发实战)

上图中左侧表示的是组成渲染流水线的所有阶段,右侧则是显存资源,从资源内存池指向渲染目标流水线阶段的箭头表示该阶段可以读取资源并可以以资源作为输入,从渲染流水线指向资源内存池的箭头则表示该阶段可以向GPU资源写入数据。接下来我们将详细介绍渲染流水线每一个阶段。如我们所见,大多数阶段可以进行读取资源的操作,但是只有少部分阶段才可以对GPU资源进行写操作。(渲染流水线中每一个阶段所输出的数据往往都是下一个阶段的输入)

  1. 输入装配器阶段(从显存读取资源,然后将资源装配成几何图元)
  2. 顶点着色器阶段(处理装配完毕的图元的顶点数据)
  3. 外壳着色器阶段(略)
  4. 曲面细分阶段(增加三角形网格数量,该阶段为可选阶段)
  5. 域着色器阶段(略)
  6. 几何着色器阶段(后续再进行介绍,该阶段为可选阶段)
  7. 流输出阶段(略)
  8. 光栅化阶段(计算三角形对应的像素颜色)
  9. 像素着色器阶段(根据光栅化阶段计算的结果(即顶点属性插值)为每一个像素添加颜色)
  10. 输出合并阶段(对像素片段进行筛选,然后把像素片段写入后台缓冲区)

5.5 输入装配阶段

输入装配器会从显存中读取几何数据(顶点和索引(vertex and index)),再将它们装配成几何图元。这些概念我们将会在后面陆续进行介绍

5.5.1 顶点

在数学上,三角形的顶点是两条边的交点,线段的顶点是它的两个端点,对于单个的点来说,它本身就是一个顶点

在Direct3D中,顶点不仅可以用来表示位置信息,还可以包含其他的信息。例如:我们将在第八章为顶点添加法向量依次来实现光照效果,在第九章中我们会为顶点添加纹理坐标从而实现纹理贴图。Direct3D为用户自定义顶点格式提供了很高的灵活性,在第六章我们会讲解一些和顶点有关的代码

5.5.2 图元拓扑

在Direct3D中,我们要通过一种名为顶点缓冲区的特殊数据结构来将顶点和渲染流水安绑定在一起,顶点缓冲区利用连续的内存来存储一系列顶点,但是仅凭顶点缓冲区是无法说明这些顶点将如何组成几何图元,因此我们需要指定图元拓扑(primitive topology)来告知Direct3D要以何种方式来表示几何图元。

void ID3D12GraphicsCommandList::IASetPrimitiveTopology{
D3D_PRIMITIVE_TOPOLOGY PrimitiveTopology
};

typedef enum D3D_PRIMITIVE_TOPOLOGY {
    D3D_PRIMITIVE_TOPOLOGY_UNDEFINED = 0,
    D3D_PRIMITIVE_TOPOLOGY_POINTLIST = 1,
    D3D_PRIMITIVE_TOPOLOGY_LINELIST = 2,
    D3D_PRIMITIVE_TOPOLOGY_LINESTRIP = 3,
    D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP = 4,
    D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP = 5,
    D3D_PRIMITIVE_TOPOLOGY_LINELIST_ADJ = 10,
    D3D_PRIMITIVE_TOPOLOGY_LINESTRIP_ADJ = 11,
    D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST = 12,
    D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP = 13,
    .
    .
    .
    D3D_PRIMITIVE_TOPOLOGY_32_CONTROL_POINT_PATCHLIST = 64,
}D3D_PRIMITIVE_TOPOLOGY;

在用户通过命令列表修改图元拓扑之前,所有的绘制调用都会沿用当前设置的图元拓扑,下面将举一个通过命令列表对图元拓扑进行修改的代码示例:

//通过三角形列表的方式来绘制对象
mCommandList->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

5.2.2.1 点列表

通过枚举项D3D_PRIMITIVE_TOPOLOGY_POINTLIST来指定点列表,当使用该图元拓扑时,所有的顶点都将在绘制过程中绘制成一个单独的点

5.2.2.2 线条带

通过枚举项D3D_PRIMITIVE_TOPOLOGY_LINESTRIP来指定线条带,当使用该图元拓扑时,顶点将会在绘制调用的过程中被绘制成一系列连续的线段,所以在这种模式下,n + 1个顶点就会生成n条线段

5.2.2.3 线列表

通过枚举项D3D_PRIMITIVE_TOPOLOGY_LINELIST来指定线列表,当使用该图元拓扑时,顶点在绘制调用时会被绘制成一系列单独的线段,所以在这种模式下,2n个顶点就会生成n条线段

5.2.2.4 三角形带

通过枚举项D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP来指定三角形带,当使用该图元拓扑时,顶点在绘制调用时会被绘制成一系列连续的三角形,所以在这种模式下,n个顶点可以生成n - 2个三角形(三角形带的绕序为为顺时针方向)

5.2.2.5 三角形列表

通过枚举项D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST来指定三角形列表,当使用该图元拓扑时,顶点在绘制调用时会被绘制成一系列独立的三角形,所以在这种模式下,3n个顶点就会生成n个三角形

5.2.2.6 具有邻接数据的图元拓扑

5.2.2.7 控制点面片列表

D3D_PRIMITIVE_TOPOLOGY_N_CONTROL_POINT_PATCHLIST拓扑类型表示:将顶点数据解释为具有N个控制点(control point)的面片列表,(此图元常用与渲染流水线的曲面细分阶段,此阶段为可选阶段),这种图元拓扑类型我们将会在第十四章再次进行讨论

5.5.3 索引

在前面我们提到过,三角形是3D实体对象的基本组成部分,所以为三角形指定顶点顺序是十分重要的工作,我们把这个顺序称之为绕序。

构成3D物体的不同三角形之间会共用许多顶点,比如一个四边形,从数学上来看它只有四个顶点,但是在Direct3D中,绘制一个四边形必须要使用两个三角形,即有六个顶点,如果不重复利用顶点数据,那么我们需要创建六个顶点(其中有两个顶点数据是重复的)来绘制一个四边形,这明显不是我们希望看到的结果,所以我们一般都会借助三角形带或者是索引来解决这个问题

三角形带:借助三角形带可以改善顶点数据的重复创建问题,因为三角形带图元拓扑可以使顶点在绘制调用的过程中绘制成一系列连续的三角形。但如果想使用三角形带改善这个问题,前提是这些几何体必须是能够组织成带状的

索引:这是我们推荐的解决方法,整个工作流程是这样的:

  1. 先创建一个顶点列表和索引列表
  2. 在顶点列表中收录一份所有独立的顶点,并在索引列表中存储顶点列表的索引值
  3. 这些索引定义了顶点列表中的顶点是如何组合在一起的,从而构成三角形

通过以上的操作,我们就把“复用的顶点数据”转换为索引列表了,这样便可以对一个顶点数据进行多次调用。

5.6 顶点着色器阶段

待图元被装配完毕之后,其顶点会被送到顶点着色器阶段(vertex shader state),我们可以把顶点着色器看成一种输入数据和输出数据都是单个顶点的函数,每一个要被绘制的顶点都必须要经过顶点着色器的处理之后才可以送往后续阶段。事实上,我们可以认为在硬件中执行的是下列处理操作

for (UINT i = 0; i < numVertices; ++i)
{
    outputVertex[i] = VertexShader(intputVertex[i]);
}

其中的顶点着色器函数(VertexShader)就是我们要实现的部分,由于这个阶段的操作是由GPU完成的,所以一般速度都会很快

在后续的章节中,我们会看到各种不同的顶点着色器示例,所以在学习完本书之后,我们应该要对顶点着色器可以时间的具体功能有一个深刻的认识。接下来,我们将介绍几种常用的空间变换

5.6.1 局部空间和世界空间

设想:如果我们正在拍一部电影,我们所在的团队需要为一些特效镜头打造一个和火车有关的微缩场景,其中我们的具体任务是制作一架袖珍小桥,当然,我们不会把小桥直接搭建在场景之中,否则我们便需要在一个极其复杂的环境中小心翼翼的工作,以防止破坏场景中的其他物体,一旦失手便会功亏一篑,相对来讲,我们更加愿意在远离场景的工作室内制作这个袖珍小桥,等制作完成之后在将它以恰当的角度放在场景中合适的位置

3D美工在工作的时候也和上述设想一样,他们不会直接在世界坐标系(世界空间)中直接构建物体的几何形状,而是选择在相对于局部坐标系(局部空间)来创建物体。只要在局部空间定义了3D模型的各顶点,我们就能将它变换到世界空间中,为了做到这一点,我们必须要定义局部空间和世界空间两者之间的联系。具体的做法是:根据物体的位置和朝向,指定其局部空间坐标系的原点和每个坐标轴相对于世界坐标系的坐标,再运用坐标变换即可将物体从局部空间转换到世界空间了。将局部坐标系内的坐标转换到世界坐标系中的过程称为世界变换,所使用的矩阵称为世界矩阵,由于场景中,每一个物体的朝向和位置都可以各不相同,因此它们都会有属于自己特定的世界矩阵。

在每一个3D模型各自的局部坐标系中的优点如下:
1、易于使用,在局部坐标系中定义物体可以很轻松的确定各个顶点的坐标

2、物体应该要可以跨越多个场景重复使用,如果将物体坐标相对于某一个特定场景进行硬编码则会多出很多麻烦

3、我们有时候需要在同一场景中绘制多个同一物体,但是它们的位置、方向和大小不一样,将物体绘制在局部空间可以解决这个问题。(我们通常将存储一份几何体相对于其局部空间的副本,接着按需求次数来绘制该物体,并使用不同的世界矩阵来指定物体在世界空间中的位置、大小和方向,这种方法称为实例化)

一般来说,为了构建一个世界矩阵,我们必须要弄清局部空间中原点和各坐标轴相对于世界空间的坐标关系,但实际上,获取这个关系并不容易而且也不直观,所以我们一般采用一种更直观的方式:定义一系列的变换组合W,即W = SRT;

S:缩放矩阵,将物体缩放到世界空间

R:旋转矩阵,用来定义物体在局部空间相对于世界空间的朝向

T:平移矩阵,定义的是物体在局部空间相对于世界空间的位置

通过定义一系列的变换组合W,便可以在不指明局部空间的原点以及各坐标轴相对于世界空间的齐次坐标的情况下,直接通过复合一系列简单的变换来建立世界矩阵

5.6.2 观察空间

为了构建场景的2D图像,我们必须在场景中架设一台虚拟摄像机,该摄像机确定了观察者可以见到的视野,也就是生成2D图像所需要的场景空间范围,所以我们要为该摄像机赋予一个局部坐标系,该坐标系称为观察坐标系,也称为观察空间。虚拟摄像机会位于观察坐标系的原点,并且朝z轴的正方向进行观察,y轴位于摄像机的上方,x轴位于摄像机的右侧(dx使用左手坐标系,OpenGL使用右手坐标系)。观察空间用于在渲染流水线后续阶段描述这些顶点相对于观察坐标系的位置,由世界空间到观察空间的坐标变换称之为观察变换(视图变换),所使用的矩阵称为观察矩阵

假设矩阵w为物体从观察空间转换到世界空间的变换矩阵,则从观察空间转换到世界空间的变换矩阵V = W的逆矩阵。

接下来我们将介绍一种构建观察矩阵的直观方法:

假设Q为摄像机的位置,T为被观察的目标点的坐标,j表示世界空间y轴方向的单位向量,则

观察坐标系的z轴方向的单位向量w为:(T - Q) / ||T - Q||

观察坐标系的x轴方向的单位向量u为:(j x w) / ||j x w||

观察坐标系的y轴方向的单位向量v为:w x v

综上所述,只有给定摄像机的位置,观察目标点以及世界空间中y轴方向的向量,就可以构建出该世界空间对应的观察空间的观察矩阵,DirectXMath库针对上述计算观察矩阵的方法提过了一个函数:

//输出对应的观察矩阵
XMMATRIX XM_CALLCONV XMMatrixLookAtLH(
    FXMVECTOR EyePosition,              //输入虚拟摄像机的位置
    FXMVECTOR ForcusPosition,           //输入观察目标点的位置
    FXMVECTOR UpDriecttion              //输入世界空间向上方向的向量
)

5.6.3 投影和齐次裁剪空间

前一节我们介绍了摄像机在世界空间的位置和朝向,除此之外,虚拟摄像机还有一个重要的组成要素,那就是摄像机可以观测到的空间体积(volume of space),此范围可以用一个由四棱锥截取的平截头体(四棱台)表示。所以我们的下一个任务便是将平截头体内的3D几何体投影到2D投影窗口中,根据前文的透视投影的原理可知,投影必定会随着众平行线汇聚于消失点,其投影的尺寸也将会随着物体3D深度的增加而变小。

blender 线段建模_3D_02

图片为手机拍摄,望见谅(图片来源:DirectX 12 3D 游戏开发实战)

5.6.3.1定义平截头体

在观察空间中,我们可以通过近平面n(near plane)、元平面f(far plane)、垂直视场角a(vertical field of view Angle)以及投影窗口的纵横比r这四个参数来定义一个以原点为中心,并且沿z轴正方向进行观察的平截头体。接下来我们将介绍这四个参数的意义:

blender 线段建模_3D_03

图片为手机拍摄,望见谅(图片来源:DirectX 12 3D 游戏开发实战)

1、近平面n:如图

2、远平面f:如图

3、垂直视场角a:如图

4、投影窗口的纵横比r:投影窗口实际是就是观察空间中场景的2D图像,由于该图像最终将会被映射到后台缓冲区中,因此,我们希望投影窗口和后台缓冲区两者的纵横比保持一致,所以我们通常把投影窗口的纵横比指定为后台缓冲区的纵横比。假如后台缓冲区的大小为800 x 600,则投影窗口的纵横比为800 / 600。

下面还有很多内容:略

5.6.3.2 投影顶点

5.6.3.3 规格化设备坐标

5.6.3.4用矩阵来表示投影公式

5.6.3.5 归一化深度值

XMMatrixPerspectiveFovLH函数

我们可以利用DirectXMath库里的XMMatrixPerSpectiveFovLH函数构建对应的投影矩阵

//构建投影矩阵
XMMATRIX XM_CALLCONV XMMatrixPerspectiveFovLH(
    float FovAngleY,            //用弧度制表示的垂直视场角
    float Aspect,               //投影窗口的纵横比(一般都与后台缓冲区的纵横比相等)
    float NearZ,                //虚拟摄像机的位置(观察点)到近平面的距离
    float FarZ                  //虚拟摄像机的位置(观察点)到远平面的距离
);

下面的代码片段为XMMatrixPerspectiveFovLH函数构建一个对应垂直视场角为45度,近平面位于z = 1.0f,远平面位于z = 1000.0f的平截头体的投影矩阵的用法:

XMMATRIX p = XMMatrixPerspectiveFovLH(0.25*XM_PI, AspectRatio(), 1.0f, 1000.0f);

纵横比采用的是我们窗口的宽高比:

float D3DApp::AspectRatio()const
{
    return static_cast<float>(mClientWidth / mClientHeight);
}

5.7 曲面细分阶段

曲面细分阶段(tessellation stage)是利用镶嵌化处理技术对网格中的三角形进行细分,以此来增加物体表面的三角形数量。然后将这些三角形偏移到合适的位置,就可以使网格展现出更加细腻的细节。使用曲面细分的优点主要有以下几方面:

  1. 借此实现一种细节层次机制。我们可以使距离虚拟摄像机较近的三角形进行镶嵌化处理,距离虚拟摄像机较远的三角形则不进行处理
  2. 我们可以在内存中仅维护低模(低精度模型)网格,再根据需求为它动态添加三角形,以此节省内存资源
  3. 我们在处理动画和物理模拟的时候可以使用简单的低模网格,而仅在渲染的过程中使用经镶嵌化处理的高模网格

曲面细分是一个可选的渲染阶段,我们将在第十四章对此阶段进行详细的讲解。

5.8几何着色器阶段

几何着色器阶段是一个可选的渲染阶段,由于我们在第十二章才会用到它,所以我们这里只对它进行简单的介绍。

  1. 几何着色器接受的输入必须是完整的图元,假设我们正在绘制三角形列表,那么向几何着色器输入的一定是定义三角形的三个顶点(这三个顶点已经被顶点着色器处理过的)
  2. 几何着色器的主要优点是可以销毁和创建几何体,比如说,我们可以利用几何着色器将输入的图元扩展称为一个或多个图元,或者是根据一些条件而选择不输出任何图元
  3. 顶点着色器与几何着色器相比,顶点着色器不能创建顶点,它只能接受单个顶点的输入,然后将处理后的单个顶点输出。几何着色器的拿手好戏是将一个点或者一条线扩展成一个四边形

5.9 裁剪

完全位于视椎体(用户在3D空间内的可视范围,形状类似于平截头体,视椎体也被称为视平截头体)之外的几何体需要被完全抛弃,而处于视平截头体交界的几何体也要接受此裁剪的操作。因此,只有在视平截头体之内的物体对象才会被完全保留下来。

由于裁剪操作是由硬件负责完成的,我们在这里并不会进行过多的讲解,如果对裁剪过程有兴趣的话,可以了解一下苏泽兰-霍启曼裁剪算法,这个算法的整体思路是找到平面和多边形的所有交点,然后将这些顶点按顺序组织成新的裁剪多边形。

5.10 光栅化阶段

光栅化阶段(rasterization stage)的主要任务是为投影到主屏幕上的3D三角形计算出相应的像素颜色

5.10.1 视口变换

当裁剪操作完成之后,硬件会通过透视除法将物体从齐次裁剪空间变换到规格化设备坐标(NDC),一旦物体的顶点位于NDC空间之内,构成2D图像的2D顶点x,y坐标就会被变换到后台缓冲区中称为视口的矩形之中,此变换完成会后,这些x,y坐标都会以像素为单位进行表示。(一般来说,由于z坐标经常在深度缓冲技术里面用作为深度值,所以视口变换一般都不会影响到z坐标)

背面剔除

每一个三角形都有两个面,在Direct3D中会采用以下约定对这两个面进行区分,假设组成三角形顺序的顶点为v1,v2,v3,那么我们会通过以下计算来得到这个三角形的法线:

e1 = v2 - v1;

e2 = v3 - v1;

n = (e1 x e2) / (||e1 x e2||)

法向量由正面射出,则另一面为三角形的背面。在这种约定之下,根据观察者的视角看过去,顶点绕序为顺时针方向的为正面朝向,顶点绕序为逆时针方向的位背面朝向。由于背面朝向的三角形都会被正面朝向的三角形遮挡,所以绘制背面朝向的三角形是没有意义的,背面剔除就是用于将背面朝向的三角形从渲染流水线中剔除的流程,这种操作可以将待处理的三角形数量减少一半。

在默认的情况下,Direct3D将以观察者的视角把顺时针绕序的三角形看作是正面朝向的,把逆时针绕序的三角形视为是背面朝向的,但是,通过对Direct3D渲染状态的设置,我们也可以把这个约定颠倒过来。

5.10.3 顶点属性插值

回顾前文可知,我们要通过顶点来定义三角形,除了位置信息之外,我们还可以给顶点附加颜色、法向量、纹理坐标、深度值等其他属性,经过视口变换之后,我们需要为求取三角形内每一个像素所附的属性进行线性插值运算,为了得到屏幕空间中各个顶点的属性插值,我们一般都会使用一种名为透视校正插值。从本质上来说,插值法即利用三角形的三个顶点属性值计算出其内部像素的属性值

我们无需考虑透视校正插值法处理像素属性的数学细节,因为硬件会自动的完成相应的处理,如果有人对其中的数学细节感兴趣。可以在[Eberly01]中找到相应的数学推导过程

5.11 像素着色器阶段

我们编写的像素着色器(pixel shader)是一种由GPU执行的程序,他会针对每一个像素片段进行处理(即每处理一个像素都要执行一次像素种着色器),并根据顶点的插值属性作为输入来计算出对应的像素颜色。像素着色器既可以返回一种单一的恒定颜色,也可以实现如逐像素光照、反射以及阴影等更为复杂的效果。

5.12 输出合并阶段

通过像素着色器生成的像素片段会被移送至渲染流水线的输出合并阶段,在此阶段,一些像素片段可能会被丢弃(比如未通过深度测试或模板测试的像素片段)。而没有被丢弃的像素片段则会被写入后台缓冲区。

混合操作也是在输出合并阶段完成的,这项技术可以使当前处理的像素片段与后台缓冲区中原有的像素片段进行融合,而不是简单的对后台缓冲区进行覆盖。

小结

  1. 根据人们的生活经验,我们可以总结一些规律用于Direct3D中:平行线会相聚于消失点、物体的尺寸受其深度的影响(近大远小)、离观察者近的物体会遮挡其后距观察者远的物体、光照和阴影的明暗对比可以刻画出3D物体的实体形状和体积感、阴影还暗示了光源的位置以及场景中不同物体之间的相对位置
  2. 我们用三角形网格来近似的表示3D物体对象,并通过指定三角形的3个顶点来定义三角形,在许多网格中都存在着顶点被不同三角形共用的情况,而索引列表则可以用于避免因为重复使用顶点而复制顶点数据所带来的亢余信息
  3. 我们一般通过红绿蓝三色光的强度来描述颜色,利用三色光不同强度的相加混色,可以表示出数以千万计种颜色。一般来说我们还会加入一种名为Alpha分量的颜色分量,这个分量用于表示颜色的不透明度,这在混合技术中是很有用的。综上,我们可以使用4D颜色向量来表示四个颜色分量。
  4. 假设在一个场景中有一台虚拟摄像机,那么渲染流水线就是根据该虚拟摄像机的视角,生成能呈现在显示器中对应的2D图像的一系列完整的步骤
  5. 渲染流水线可以分为8个重要阶段,分别是:输入装配阶段、顶点着色器阶段、曲面细分阶段、几何着色器阶段、裁剪阶段、光栅化阶段、像素着色器阶段、输出合并阶段。