3.1 多纹理化概览
阅读此文表明您已同意文末的声明
多纹理化(Multitexturing)可能是能用像素着色器实现的最简单的技巧了。此外,因为像素着色器替换多纹理化阶段,那么接下来我们应该对多纹理化“是什么”和“做什么”有一个最基本的理解。本节介绍多纹理化的简明概览。
当我们一开始讨论纹理化(texturing)的时候(译者注:对纹理化的讨论不在译文范围内,具体可参照原书第六章,本文是原书“第四部分:着色器和效果”的第三章),我们忽略了固定功能管线中对多纹理化的讨论,这有两个原因:第一,多纹理化是有一点棘手的过程,我们考虑到这在当时是一个高级话题;此外,固定功能多纹理化阶段被新的和更强有力的像素着色器替换掉了。因此花时间在已经过时的固定功能纹理化阶段上是无意义的。
多纹理化后面的思想有一点和混合(blending)相关。在第七章中(译者注:原书第七章不在译文范围内)我们了解到:可以将正要被光栅化的像素与之前写入后台缓冲的像素进行混合来达成一种特效。我们推广这种相同的思想到多个纹理(multiple texture)。也就是说,我们一次允许几个纹理,然后定义这些纹理如何被混合在一起以到达一种特殊效果。多纹理化的一个通常的用法是执行光照。作为在顶点处理阶段使用Direct3D的光照模型的替代,我们使用一种叫做“光照贴图”(light map)的特殊纹理贴图(texture map),它编码(encode)表面是如何被光照的(译者注:这句话的意思是“它定义表面是如何被光照的”)。例如,假设我们希望一盏聚光灯(spotlight)照在一个大木箱上,我们要么可以定义一个D3DLIGHT9结构的聚光灯,要么可以将代表木箱的纹理贴图与代表聚光灯的光照映射混合在一起,如图3.1所示。
图18.1:使用多纹理化渲染一个通过聚光灯照亮的木箱。这里我们通过将相应的纹理像素(texels)相乘来将这两个纹理组合起来。
注意:用第七章里的混合,结果图像依赖于纹理被混合的方式。在固定功能管线的多纹理化阶段,混合方程式被纹理渲染状态(texture render state)控制。用像素着色器,我们可以以可编程的方式在代码中写出混合函数的简单表达式。这使我们可以以任何我们想要的方式混合纹理。我们将在讨论为本章准备的样例应用程序时详细讨论纹理混合。
混合多个纹理(本例中是两个)来照亮木箱比起Direct3D的光照来有两个好处:
n 光照是是预先在聚光灯的光照贴图里计算好的。因此,光照不需要在运行时被计算,这节省了处理时间。当然,只有静态对象和静态灯光的光照可以被预先计算。
n 因为光照贴图是预先计算好的,我们能够使用比Direct3D的(光照)模型多的多的更加精确的和复杂的光照模型。(在更加真实的场景中的更好的光照结果。)
备注:多纹理化阶段典型的用于实现静态对象的完全光照引擎(full lighting engine)。例如,我们可以用一个纹理贴图保存对象的颜色,比如木箱的纹理贴图。然后我们可以用一个散射光照贴图(diffuse light map)保存散射表面着色(diffuse surface shade),一个单独的镜面光照贴图保存镜面表面着色,一个雾状物贴图(fog map)保存覆盖在表面的雾状物的总量,还有可以用一个详细贴图(detail map)保存小的、高访问率的表面的细节。当所有这些纹理被组合起来,只需到这些预先计算的纹理中检索,就可以有效的照亮、着色并且增加细节到场景中去。
注意:聚光灯光照贴图是最基本的光照贴图的一个微不足道的例子。典型的,特定的程序用于在给定的场景和光源下生成光照贴图。生成光照贴图超越了本书的范围。有兴趣的读者可以参考Alan Watt和Fabio Policarpo在3D Games: Real-time Rendering and Software Technology中描述的光照贴图。
3.1.1 允许多个纹理
回忆一下,纹理是用IDirect3DDevice9::SetTexture方法设置,而采样器状态(sampler state)是用IDirect3DDevice9::SetSamplerState方法设置,原型如下:
HRESULT IDirect3DDevice9::SetTexture(
DWORD Stage, // specifies the texture stage index
IDirect3DBaseTexture9 *pTexture
);
HRESULT IDirect3DDevice9::SetSamplerState(
DWORD Sampler, // specifies the sampler stage index
D3DSAMPLERSTATETYPE Type,
DWORD Value
);
注意:一个特定的采样器阶段索引i与第i个纹理阶段(texture stage)相关联。也就是说,第i个采样器阶段指定了第i集(set)纹理的采样器状态。
纹理/采样器阶段索引标识了我们希望设置的纹理/采样器的纹理/采样器阶段。因此,我们可以允许多个纹理并通过使用不同的阶段索引设置其相应的采样器状态。在本书前面的部分中,我们总是指定0,来指示第一个阶段,因为我们一次仅使用一个纹理。所以例如,假设我们要允许三个纹理,我们像这样使用阶段0,1和2:
// Set first texture and corresponding sampler states.
Device->SetTexture( 0, Tex1);
Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
// Set second texture and corresponding sampler states.
Device->SetTexture( 1, Tex2);
Device->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
// Set third texture and corresponding sampler states.
Device->SetTexture( 2, Tex3);
Device->SetSamplerState(2, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(2, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(2, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
这段代码允许Tex1, Tex2和Tex3,并设置每个纹理的过滤模式。
3.1.2 多纹理坐标
回忆一下第六章,对于每个3D三角形,我们要在纹理上定义一个相应的三角形以映射到该3D三角形。我们通过对每个顶点增加纹理坐标完成这一点。因此,构成一个三角形的每三个顶点定义了一个在纹理上的对应的三角形。
因为我们现在需要使用多个纹理,对于每三个顶点定义的一个三角形,我们需要在每个被允许的纹理上定义一个相应的三角形。我们做这件事通过增加额外的纹理坐标的集合到每个顶点——每个顶点一集,而且相应地,每个允许的纹理。举个例子,如果我们混合三个纹理到一起,那么每个顶点必须有三集纹理坐标以索引到三个允许纹理。因此,一个三纹理的多纹理化顶点结构看起来可能像这样:
struct MultiTexVertex
{
MultiTexVertex(float x, float y, float z,
float u0, float v0,
float u1, float v1,
float u2, float v2)
{
_x = x; _y = y; _z = z;
_u0 = u0; _v0 = v0;
_u1 = u1; _v1 = v1;
_u2 = u2; _v2 = v2;
}
float _x, _y, _z;
float _u0, _v0; // Texture coordinates for texture at stage 0.
float _u1, _v1; // Texture coordinates for texture at stage 1.
float _u2, _v2; // Texture coordinates for texture at stage 2.
static const DWORD FVF;
};
const DWORD MultiTexVertex::FVF = D3DFVF_XYZ | D3DFVF_TEX3;
注意,指定自由顶点格式标记D3DFVF_TEX3表明顶点结构包含三集纹理坐标。固定功能管线支持最多八集纹理坐标。要使用多于八集,你必须使用顶点声明和可编程顶点管线。
注意:在较新的像素着色器版本中,我们可以使用一集纹理坐标以索引到多个纹理,并因此消除了对多个纹理坐标的需要。当然这得假设每个纹理阶段使用相同的纹理坐标。如果每个阶段的纹理坐标不同,则我们仍然需要多纹理坐标。
[声明]:本文译自Frank Luna的《Introduction to 3D Game Programming with DirectX 9.0》,限于译者水平,文中难免错漏之处,欢迎各位网友批评指正;本文仅用于学习交流与参考用途,不得用于任何形式的商业用途;如需转载需事先征得译者同意,保持文章的完整性,并注明译者和出处,译者保留对译文的所有权利。对于违反以上条款造成的后果,译者对此不负任何责任。我的MSN是Raymond_King123@hotmail.com,欢迎热爱3D图形和游戏,并有一定图形编程经验的朋友与我进行交流。