Introduction to 3D Game Programming with DirectX 11学习笔记 5 渲染管线(一)

渲染管线是指:在给定一个3D场景的几何描述及一架已确定位置和方向的虚拟摄像机(virtual camera)时,根据虚拟摄像机的视角生成2D图像的一系列步骤(如图5.1所示)。本章的内容大部分是理论性的——下一章才会把理论用于实践。在我们开始学习渲染管线之前,读者应该先了解两个基本概念:一是构成3D视觉的基本要素(也就是,研究如何通过2D屏幕来呈现3D场景);二是讲解如何在Direct3D中以数学方式表示和使用颜色。
这里写图片描述

图5.1:左图是放置在3D场景中的一些物体以及一架已确定位置和方向的摄像机;中图表示的是同一个场景,但是是从上方向下看的视角。“棱锥体”表示观察者所能看到的视域空间;视域空间之外的物体(或者物体的一部分)不会被看到。右图是根据摄像机的“视角”生成的2D图像。


3D物体可以通过三角形网格近似地模拟表示,三角形是构成物体模型的基本单位。图5.7说明,我们可以通过三角形网格来模拟真实世界中的任何3D物体。一般来说,网格的三角形密度越大,模拟出来的效果就越好。当然,我们使用的三角形越多,所要求的硬件性能也就越高,所以必须根据应用程序目标用户的硬件性能来决定模型的精度。除三角形外,有时还需要绘制点和直线。例如,通过绘制一系列1像素宽的短线段可以模拟出一条平滑曲线。
这里写图片描述
图 5.7:(左)由低密度三角形网格模拟的轿车。(右)由高密度三角形网格模拟的头骨。

图5.7中的大规则三角形网格说明了一件事情:要以手工方式编写一个3D模型的三角形列表是一件极其困难的事情。除了最简单的模型外,所有的模型都是用专门的3D建模软件生成的。这些建模软件提供了可视化的交互环境以及非常丰富的建模工具,用户可以使用些软件来创作复杂而逼真的网格模型,整个建模过程非常简单,很容易就能学会。


基本计算机颜色

计算机显示器通过每个像素发射红、绿、蓝光的混合光线。当混合光线进入人的眼睛时会触碰到视网膜的某些区域,对锥感细胞产生刺激,神经触突会通过视觉神经传送到大脑。大脑会解释些信号并生成颜色。随着混合光线的变化,细胞会受到不同的刺激,从而在我们的思想意识中产生不同的颜色。图5.8说明了红、绿、蓝三色的混合方式以及不同强度的红色。通过为每个颜色分量指定不同的强度并对其进行混合,可以描述我们所要显示的真实图像中的所有颜色。
这里写图片描述
图5.8 (上)对纯红、纯绿、纯蓝三种颜色进行混合,得到新的颜色。(下)通过控制红光的强度得到不同明暗程度的红色。

显示器所能发射的红、绿、蓝光的强度有最大限制。我们使用从0到1的规范化区间来描述光线强度。0表示没有强度,1表示最高强度。中间值表示中等强度。例如,值(0.25,0.67,1.0)表示混合光由强度为25%的红光、强度为67%的绿和强度为100%的蓝光组成。如本例所示,我们可以通过3D向量(r,g,b)来表示颜色,其中0≤r,g,b≤1,三个颜色分量分别描述红、绿、蓝光的强度。

颜色运算

某些向量运算也适用于颜色向量。例如,我们可以把颜色向量加在一起得到一个新的颜色:

(0.0, 0.5, 0.0) + (0.0, 0.0, 0.25) = (0.0, 0.5, 0.25)

通过混合一个中等强度的绿色和一个低强度的蓝色,得到一个深绿色。也可以通过颜色相减来得到一个新的颜色:

(1, 1,1) − (1, 1,0) = (0, 0,1)

也就是,我们从白色中减去它的红色和绿色部分,得到最终的蓝色。

标量乘法也有意义。例如:

0.5(1, 1,1) = (0.5, 0.5, 0.5)

也就是,将白色乘以0.5,得到一个中等强度的灰色。而运算2(0.25, 0.0, 0.0) = (0.5, 0.0, 0.0),可使红色分量的强度增大一倍。

颜色向量的点积和叉积没有意义。不过,颜色向量有一种特殊的乘法运算,叫做分量乘法(componentwise multiplication)。其定义如下:

(cr,cg,cb)⨂(kr,kg,kb) = (crkr,cgkg,cbkb)

这一运算主要用于光照方程。例如,一个颜色为(r,g,b)的入射光,照射在一个平面上。该平面反射50%的红光、75%的绿和25%的蓝光,其余线均被平面吸收。则折回的反射光颜色为:

(r,g,b)⨂(0.5,0.75,0.25) = (0.5r,0.75g,0.25b)

我们可以看到,由于平面吸收了一些线,所以当光线照射在平面上时会丢失一些颜色。

当进行颜色运算时,某些颜色分量可能会超出[0,1]区间;例如,方程(1,0.1,0.6) + (0.0,0.3,0.5) = (1,0.4,1.1)。由于1.0表示颜色分量的最大强度,任何分量都不能大于该值。所以,我们要把1.1截取为1.0。同样,显示器不能发射负光,所以任何负的颜色分量(负值是由减法运算取得的结果)都必须截取为0.0。

128位颜色

通常,在颜色中会包含一个附加的颜色分量,叫做alpha分量。alpha分量用于表示颜色的不透明度,我们会在第9章“混合”中使用alpha分量。(由于我们目前还用不到混合,所以现在暂且将alpha分量设置为1。)

包含alpha分量意味着我们要使用4D向量(r,g,b,a)来表示颜色,其中0≤r,g,b,a≤1。要表示一个128位颜色,可以为每个分量指定一个浮点值。因为从数学上来说,颜色就是一个4D向量,所以我们可以在代码中使用XMVECTOR类型表示一个颜色,而且还可以利用XNA数学矢量函数所用的SIMD操作带来的优势进行颜色运算(例如颜色相加、相减、标量乘法)。对于分量乘法,XNA数学库提供了以下方法:

XMVECTOR XMColorModulate(// Returns (cr, cg, cb, ca) ⊗ (kr,kg,kb,ka)
    FXMVECTOR C1, // (cr, cg, cb, ca)
    FXMVECTOR C2 // (kr, kg, kb, ka) 
);

32位颜色

当使用32位表示一个颜色时,每个字节会对应于一个颜色分量。由于每个颜色分量占用一个8位字节,所以每个颜色分量可以表示256种不同的明暗强度——0表示没有强度,255表示最高强度,中间值表示中等强度。从表面上看,为每个颜色分量分配一个字节似乎很小,但是通过计算组合值(256×256×256 = 16,777,216)可以发现,**这种方式可以表示上千万种不同的颜色。**XNA数学库提供了以下结构用于存储32位颜色:

// ARGB Color; 8-8-8-8 bit unsigned normalized integer components packed into
// a 32 bit integer.  The normalized color is packed into 32 bits using 8 bit
// unsigned, normalized integers for the alpha, red, green, and blue components.
// The alpha component is stored in the most significant bits and the blue
// component in the least significant bits (A8R8G8B8):
// [32] aaaaaaaa rrrrrrrr gggggggg bbbbbbbb [0]
typedef struct _XMCOLOR
{
    union
    {
        struct
        {
            UINT b    : 8;  // Blue:    0/255 to 255/255
            UINT g    : 8;  // Green:   0/255 to 255/255
            UINT r    : 8;  // Red:     0/255 to 255/255
            UINT a    : 8;  // Alpha:   0/255 to 255/255
        };
        UINT c;
    };

#ifdef __cplusplus

    _XMCOLOR() {};
    _XMCOLOR(UINT Color) : c(Color) {};
    _XMCOLOR(FLOAT _r, FLOAT _g, FLOAT _b, FLOAT _a);
    _XMCOLOR(CONST FLOAT *pArray);

    operator UINT () { return c; }

    _XMCOLOR& operator= (CONST _XMCOLOR& Color);
    _XMCOLOR& operator= (CONST UINT Color);

#endif // __cplusplus

} XMCOLOR;

通过将整数区间[0,255]映射到实数区间[0,1],可以将一个32位颜色转换为一个128位颜色。这一映射工作是通过将每个分量除以255来实现。也就是,当n为0到255之间的一个整数时,对应于规范化区间[0,1]的分量值为0≤n255n255≤1。例如,32位颜色(80,140,200,255)变为:

(80,140,200,255) → (80255,140255,200255,25525580255,140255,200255,255255 ) ≈ (0.31,0.55,0.78,1.0)

另一方面,通过将每个颜色分量乘以255并进行四舍五入,可以将一个128位颜色转换为一个32位颜色。例如:

(0.3,0.6,0.9,1.0) → (0.3*255,0.6*255,0.9*255,1.0*255) = (77,153,230,255)

当把一个32位颜色转换为一个128位颜色或者进行反向转换时,通常要执行额外的位运算,因为8位颜色分量通常会被封装在一个32位整数中(例如,无符号整数),即在XMCOLOR中。XNA数学库使用以下函数处理一个XMCOLOR并以XMVECTOR的形式返回:

XMVECTOR XMLoadColor(CONST XMCOLOR* pSource);

这里写图片描述
图5.10:一个32位颜色,它为每个颜色分量分配一个字节。

图5.10说明了如何将4个8位颜色分量封装为一个无符号整数。注意,这只是用于封装颜色分量的方式之一。除使用ARGB外,还可以使用ABGR或RGBA。不过,XMCOLOR类使用ARGB格式。XNA数学库提供了一个函数可以将一XMVECTOR颜色转化为一个XMCOLOR:

VOID XMStoreColor(XMCOLOR* pDestination,FXMVECTOR V);

通常,许多颜色运算(例如,在像素着色器中)使用的都是128位颜色值;通过这一方式,我们可以有足够多的二进制位来保证计算的精确度,减少算术错误的累积。不过,最终的像素颜色通常是存储在后台缓冲区的32位颜色值中;目前的物理显示设备还不能充分利用更高的分辨率颜色。


渲染管线概述

渲染管线(rendering pipeline)是指:在给定一个3D场景的几何描述及一架已确定位置和方向的虚拟摄像机时根据虚拟摄像机的视角生成2D图像的一系列步骤(译者注:渲染管线由许多步骤组成,每个步骤称为一个阶段)。图5.11所示为构成渲染管线的各个阶段,以及与各个阶段相关的内存资源。从内存指向阶段的箭头表示该阶段可以从内存读取数据;例如,像素着色器阶段(pixel shader stage)可以从内存中的纹理资源中读取数据。从阶段指向内存的箭头表示该阶段可以向内存写入数据;例如,输出合并器阶段(output merger stage)可以将数据写入后台缓冲区和深度/模板缓冲区。我们还可以看到输出合并器阶段的箭头是双向的(可以读取和写入GPU资源)。大多数阶段并不会写入GPU资源,它们只是将输出传递到下一阶段;例如,顶点着色器阶段(Vertex Shader Stage)读取输入装配阶段的数据,然后进行处理,接着将结果输出到几何着色器阶段(Geometry Shader Stage)。在随后的几节中,我们将分别讲解渲染管线的各个阶段。
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值