IDirect3DDevice9::SetTextureStageState
Sets the state value for the currently assigned texture.
HRESULT SetTextureStageState( DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value);Parameters
Stage
[in] Stage identifier of the texture for which the state value is set. Stage identifiers are zero-based. Devices can have up to eight set textures, so the maximum value allowed for Stage is 7.
Type
[in] Texture state to set. This parameter can be any member of the enumerated type.
Value
[in] State value to set. The meaning of this value is determined by the Type parameter.
Return Values
If the method succeeds, the return value is D3D_OK. If the method fails, the return value can be D3DERR_INVALIDCALL.
Requirements
Header: Declared in D3D9.h.
Library: Use D3D9.lib.
中英结合
三个参数:
1.指定当前设置的纹理层序号(Or ID)。貌似只能是0-7
2.设置纹理的渲染状态,是枚举值。可以是这些东西:
typedef enum D3DTEXTURESTAGESTATETYPE{ D3DTSS_COLOROP = 1, D3DTSS_COLORARG1 = 2, D3DTSS_COLORARG2 = 3, D3DTSS_ALPHAOP = 4, D3DTSS_ALPHAARG1 = 5, D3DTSS_ALPHAARG2 = 6, D3DTSS_BUMPENVMAT00 = 7, D3DTSS_BUMPENVMAT01 = 8, D3DTSS_BUMPENVMAT10 = 9, D3DTSS_BUMPENVMAT11 = 10, D3DTSS_TEXCOORDINDEX = 11, D3DTSS_BUMPENVLSCALE = 22, D3DTSS_BUMPENVLOFFSET = 23, D3DTSS_TEXTURETRANSFORMFLAGS = 24, D3DTSS_COLORARG0 = 26, D3DTSS_ALPHAARG0 = 27, D3DTSS_RESULTARG = 28, D3DTSS_CONSTANT = 32, D3DTSS_FORCE_DWORD = 0x7fffffff,} D3DTEXTURESTAGESTATETYPE, *LPD3DTEXTURESTAGESTATETYPE;DX文档上面有详细的解释。3.设置的状态值,根据第二个参数的不同,第三个参数具有不同的取值方法和含义。下面来看DX的Demo-Textures: g_pd3dDevice->SetTexture( 0, g_pTexture );
g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE );
g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
g_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE );都什么意思呢?这5句分别是:1.设置g_pTexture这个纹理对象的ID撒。2.D3DTSS_COLOROP是设置纹理颜色的混合方法,Value值属于D3DTEXTUREOP这个枚举的成员:typedef enum D3DTEXTUREOP{ D3DTOP_DISABLE = 1, D3DTOP_SELECTARG1 = 2, D3DTOP_SELECTARG2 = 3, D3DTOP_MODULATE = 4, D3DTOP_MODULATE2X = 5, D3DTOP_MODULATE4X = 6, D3DTOP_ADD = 7, D3DTOP_ADDSIGNED = 8, D3DTOP_ADDSIGNED2X = 9, D3DTOP_SUBTRACT = 10, D3DTOP_ADDSMOOTH = 11, D3DTOP_BLENDDIFFUSEALPHA = 12, D3DTOP_BLENDTEXTUREALPHA = 13, D3DTOP_BLENDFACTORALPHA = 14, D3DTOP_BLENDTEXTUREALPHAPM = 15, D3DTOP_BLENDCURRENTALPHA = 16, D3DTOP_PREMODULATE = 17, D3DTOP_MODULATEALPHA_ADDCOLOR = 18, D3DTOP_MODULATECOLOR_ADDALPHA = 19, D3DTOP_MODULATEINVALPHA_ADDCOLOR = 20, D3DTOP_MODULATEINVCOLOR_ADDALPHA = 21, D3DTOP_BUMPENVMAP = 22, D3DTOP_BUMPENVMAPLUMINANCE = 23, D3DTOP_DOTPRODUCT3 = 24, D3DTOP_MULTIPLYADD = 25, D3DTOP_LERP = 26, D3DTOP_FORCE_DWORD = 0x7fffffff,} D3DTEXTUREOP, *LPD3DTEXTUREOP;D3DTOP_MODULATE把第一个和第二个颜色相乘输出。这个第一个(Arg1)和(Arg2)到底是什么东西呢?继续往下看撒~~3.D3DTSS_COLORARG1 第一个颜色撒~~ 这里设置为纹理颜色4.D3DTSS_COLORARG2 第二个颜色是定点的漫反色颜色。5.禁用Alpha操作然后咱们看看DX的BasicHLSL的Shader:PS_OUTPUT RenderScenePS( VS_OUTPUT In,
uniform bool bTexture )
{
PS_OUTPUT Output; // Lookup mesh texture and modulate it with diffuse
if( bTexture )
Output.RGBColor = tex2D(MeshTextureSampler, In.TextureUV) * In.Diffuse;
else
Output.RGBColor = In.Diffuse; return Output;
}
第08章 使用多重纹理
***p149-151
哪些应用到多重纹理?
黑暗映射、发光映射、细节映射。
多次渲染(Mulitpass Rendering)
多重纹理(mulitexturing):在一次渲染中访问到2张或更多纹理。
颜色操作时通过D3DTSS_COLOROP来设置的。大致代码如下:
m_pd3dDevice->SetRenderState(0, D3DTSS_COLORARG1, arg);
m_pd3dDevice->SetRenderState(0, D3DTSS_COLORARG2, arg);
m_pd3dDevice->SetRenderState(0, D3DTSS_COLOROP, op);
其中可用的op参数,位于D3DTEXTUREOP枚举中:
typedef enum D3DTEXTUREOP
{
D3DTOP_DISABLE = 1,
D3DTOP_SELECTARG1 = 2,
D3DTOP_SELECTARG2 = 3,
D3DTOP_MODULATE = 4,
D3DTOP_MODULATE2X = 5,
D3DTOP_MODULATE4X = 6,
D3DTOP_ADD = 7,
D3DTOP_ADDSIGNED = 8,
D3DTOP_ADDSIGNED2X = 9,
D3DTOP_SUBTRACT = 10,
D3DTOP_ADDSMOOTH = 11,
D3DTOP_BLENDDIFFUSEALPHA = 12,
D3DTOP_BLENDTEXTUREALPHA = 13,
D3DTOP_BLENDFACTORALPHA = 14,
D3DTOP_BLENDTEXTUREALPHAPM = 15,
D3DTOP_BLENDCURRENTALPHA = 16,
D3DTOP_PREMODULATE = 17,
D3DTOP_MODULATEALPHA_ADDCOLOR = 18,
D3DTOP_MODULATECOLOR_ADDALPHA = 19,
D3DTOP_MODULATEINVALPHA_ADDCOLOR = 20,
D3DTOP_MODULATEINVCOLOR_ADDALPHA = 21,
D3DTOP_BUMPENVMAP = 22,
D3DTOP_BUMPENVMAPLUMINANCE = 23,
D3DTOP_DOTPRODUCT3 = 24,
D3DTOP_MULTIPLYADD = 25,
D3DTOP_LERP = 26,
D3DTOP_FORCE_DWORD = 0x7fffffff,
} D3DTEXTUREOP, *LPD3DTEXTUREOP;
D3DTOP_DISABLE:禁用纹理层颜色输出.
D3DTOP_SELECTARG1:将该纹理层的第一个混合参数直接输出.对于D3DTSS_COLOROP,则输出由D3DTSS_COLORARG1指定的第一个表示RGB的参数;对于D3DTSS_ALPHAOP,则输出由D3DTSS_ALPHAARG1指定的第一个表示ALPHA的参数.
D3DTOP_SELECTARG2:同上.
D3DTOP_MODULATE:将两个混合参数相乘后输出.
D3DTOP_MODULATE2X:将两个混合参数相乘后再乘以2输出.
D3DTOP_MODULATE4X:将两上混合参数相乘后再乘以4输出.
D3DTOP_ADD:将两上混合参数相加后输出.
D3DTOP_ADDSIGNED:将两个混合参数相加后减去0.5输出.(可以这样理解:本来范围在[0,1]里,减0.5使其范围限制在[-0.5, 0.5]中.这样相加后,比较暗的部分,即负数部分,将对原来的纹理产生衰减,而较亮部分则对原来的纹理增强.可用作细节纹理.如果有的显卡不支持D3DTOP_ADDSIGNED方式,可考虑采用D3DTOP_MODULATE2X来模拟这种效果).
D3DTOP_ADDSIGNED2X:同上,结果再乘以2输出.
D3DTOP_SUBTRACT:将两个混合参数相减后输出.
D3DTOP_ADDSMOOTH:将两个混合参数的和减去两个混合参数的积.
D3DTSS_COLORARG1 与 D3DTSS_COLORARG2 可以指定的纹理混合参数:
D3DTA_CURRENT:前一个纹理层的输出颜色.如果在第0层,则为漫反射颜色.
D3DTA_DIFFUSE:像素的漫反射颜色值.这种颜色是在使用GOURAUD着色模式时对顶点的漫反射颜色插值得到的.
D3DTA_TEXTURE:当前纹理的颜色值.
D3DTA_TFACTOR:参数值为SetRenderState()通过D3DRS_TEXTURE_EFACTOR设置的系数值.
D3DTA_SPECULAR:参数值为像素的镜面反射颜色值.这种颜色是在使用高洛德着色模色时对顶点的镜面反射颜色插值得到的.
D3DTA_SELECTMASK:该状态在设置纹理参数时不起作用.
D3DTA_TEMP:参数值存储在一个临时寄存器中.
D3DTA_CONSTANT:参数值为一个常数值.
D3DTA_COMPLEMENT:该参数必须与以上任意一个参数同时设置(或操作?),表示用一减去原参数.
D3DTA_ALPHAREPLICATE:该参数必须与以上除了D3DTA_COMPLEMENT之外的任一参数同时设置,表示将原参数的ALPHA值复制到R,G,B中.
D3DTOP_MODULATE
将参数的每个成分相乘
D3DTOP_MODULATE2X
将参数的每个成分相乘,并将乘积左移1位(等同于乘以2的效果)以使其变得更亮
D3DTOP_MODULATE4X
将参数的每个成分相乘,并将乘积左移2位(等同于乘以4的效果)以使其变得更亮
D3DTOP_ADD
Add the components of the arguments.
D3DTOP_ADDSIGNED
Add the components of the arguments with a - 0.5 bias, making the effective range of values from - 0.5 through 0.5.
D3DTOP_ADDSIGNED2X
Add the components of the arguments with a - 0.5 bias, and shift the products to the left 1 bit.
D3DTOP_SUBTRACT
Subtract the components of the second argument from those of the first argument.
D3DTOP_ADDSMOOTH
Add the first and second arguments; then subtract their product from the sum.
D3DTOP_BLENDDIFFUSEALPHA
使用由每个顶点计算而得的alpha插值来线性混合本纹理阶段。
D3DTOP_BLENDTEXTUREALPHA
使用本阶段纹理中的alpha来线性混合本纹理阶段。
D3DTOP_BLENDFACTORALPHA
使用通过D3DRS_TEXTUREFACTOR 渲染状态设置的alpha标量来线性混合本纹理阶段。
D3DTOP_BLENDTEXTUREALPHAPM
使用一个预乘alpha来线性混合本纹理阶段。
D3DTOP_BLENDCURRENTALPHA
使用来自上一纹理阶段的alpha来线性混合本纹理阶段。
D3DTOP_PREMODULATE
D3DTOP_PREMODULATE is set in stage n. The output of stage n is arg1. Additionally, if there is a texture in stage n + 1, any D3DTA_CURRENT in stage n + 1 is premultiplied by texture in stage n + 1.
D3DTOP_MODULATEALPHA_ADDCOLOR
D3DTSS_COLOROP 参数,使用第1个参数alpha来调制第2个参数的颜色,然后将结果加上第1个参数。Result = Arg1RGB+ Arg1Alpha* Arg2RGB
D3DTOP_MODULATECOLOR_ADDALPHA
Modulate the arguments; then add the alpha of the first argument. This operation is supported only for color operations (D3DTSS_COLOROP).
D3DTOP_MODULATEINVALPHA_ADDCOLOR
Similar to D3DTOP_MODULATEALPHA_ADDCOLOR, but use the inverse of the alpha of the first argument. This operation is supported only for color operations (D3DTSS_COLOROP).
D3DTOP_MODULATEINVCOLOR_ADDALPHA
Similar to D3DTOP_MODULATECOLOR_ADDALPHA, but use the inverse of the color of the first argument. This operation is supported only for color operations (D3DTSS_COLOROP).
D3DTOP_BUMPENVMAP
Perform per-pixel bump mapping, using the environment map in the next texture stage, without luminance. This operation is supported only for color operations (D3DTSS_COLOROP).
D3DTOP_BUMPENVMAPLUMINANCE
Perform per-pixel bump mapping, using the environment map in the next texture stage, with luminance. This operation is supported only for color operations (D3DTSS_COLOROP).
D3DTOP_DOTPRODUCT3
Modulate the components of each argument as signed components, add their products; then replicate the sum to all color channels, including alpha. This operation is supported for color and alpha operations.
In DirectX 6 and DirectX 7, multitexture operations the above inputs are all shifted down by half (y = x - 0.5) before use to simulate signed data, and the scalar result is automatically clamped to positive values and replicated to all three output channels. Also, note that as a color operation this does not updated the alpha it just updates the RGB components.
However, in DirectX 8.1 shaders you can specify that the output be routed to the .rgb or the .a components or both (the default). You can also specify a separate scalar operation on the alpha channel.
D3DTOP_MULTIPLYADD
完成一个累积乘法(multiply-accumulate)操作。它取2个参数相乘,然后将乘积加上另一个输入/源参数,最后将和作为结果。 Result = Arg0+ Arg1* Arg2
SRGBA = Arg1 + Arg2 * Arg3
D3DTOP_LERP
根据第1个源参数指定的比例计算第2、3个源参书的线性插值。
Result = (Arg0)*Arg1 +(1-Arg0)*Arg2
SRGBA = (Arg1) * Arg2 + (1- Arg1) * Arg3.
D3DTOP_FORCE_DWORD
Forces this enumeration to compile to 32 bits in size. Without this value, some compilers would allow this enumeration to compile to a size other than 32 bits. This value is not used.
每种颜色操作可接受最多3个颜色参数:D3DTSS_COLORARG0、D3DTSS_COLORARG1、D3DTSS_COLORARG2,可以将这些颜色参数设为以下值:
D3DTA_DIFFUSE,使用漫反射颜色,这种颜色是在使用Gouraud着色时,对顶点成分中的漫反射颜色作插值计算而得的;
D3DTA_SPECULAR,使用镜面反射颜色,这种颜色是在使用Gouraud着色时,对顶点成分中的镜面反射颜色作插值计算而得的;
D3DTA_TFACTOR,使用纹理系数,可事先通过渲染状态D3DRS_TEXTUREFACTOR调用SetRenderState()来设置的纹理系数;
D3DTA_TEXTURE,使用本文里阶段的纹理颜色;
D3DTA_CURRENT,使用上一个混合阶段的结果;
D3DTA_TEMP,使用一个用于读/写的临时寄存器颜色;
D3DTA_ALPHAREPLICATE,复制alpha信息;
D3DTA_COMPLEMENT,对颜色参数取反。
8.2.1 黑暗映射(Darkl Mapping)p134
逐顶点(per-vertex)计算方式的缺点:参与计算和实际计算的是三角形的每个顶点,而不是对每个像素进行的渲染。这导致如果顶点未受到光照,或受到很少光照(比如光源在三角形中央或靠近中央时,整个三角形只有非常少的光亮,并且它的中央会有一个亮点),则无法计算出正确的三角形面的颜色。
将2张纹理的颜色相乘称为:“光照映射(light mapping)”,由于这种技术经常被用于使一张纹理变暗,所以也称“黑暗映射”。
实例代码:
m_pd3dDevice->SetTexture(0, m_pWallTexture);
m_pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
//将D3DTA_TEXTURE作为D3DTSS_COLORARG1的值
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
m_pd3dDevice->SetTexture(1, m_pEnvTexture);
m_pd3dDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
m_pd3dDevice->SetTextureStageState (1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_pd3dDevice->SetTextureStageState (1, D3DTSS_ COLORARG2, D3DTA_CURRENT);
m_pd3dDevice->SetTextureStageState (1, D3DTSS_COLOROP, D3DTOP_MODULATE);
第1个阶段(阶段0)的默认操作是D3DTOP_MODULATE;其他纹理阶段的默认操作是D3DTOP_DISABLE。
下面的代码和上面的效果相同,通过多次渲染技术:
//Set texture for the cube
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
m_pd3dDevice->SetTextureStageState (D3DRS_ALPHABLENDENABLE, FALSE);
m_pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
m_pd3dDevice->SetTexture(0, m_pWallTexture);
//draw polygon
//Set darkmap
m_pd3dDevice->SetTextureStageState (D3DRS_ALPHABLENDENABLE, TRUE);
m_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ ZERO);
m_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_SRCCOLOR);
m_pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
m_pd3dDevice->SetTexture(1, m_pEnvTexture);
//draw polygon
其中alpha混合公式:
FinalColor = SourcePixelColor * 0.0 + DestPixelColor * SourcePixelColor
8.2.3 混合纹理和材质漫反射颜色p138
基础贴图+漫反射插值 实例代码:
m_pd3dDevice->SetTexture(0, m_pWallTexture);
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLOROP, D3DTOP_ADD);
在固定功能的流水线中,有3种获取漫反射颜色的途径,即从以下途径获取:
材质:D3DMCS_MATERIAL
漫反射顶点颜色:D3DMCS_COLOR1
镜面反射顶点颜色:D3DMCS_COLOR2
从何处获取漫反射颜色取决于SetRenderState()函数中的D3DRS_DIFFUSEMATERIALSOURCE参数:
SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL)
默认颜色源为漫反射顶点颜色:D3DMCS_COLOR1。如果还未选择颜色,则默认颜色为不透明的白色。
8.2.4 混合黑暗贴图和材质漫反射颜色p140
(基础贴图*光的颜色)*黑暗贴图 实例代码:
m_pd3dDevice->SetTexture(0, m_pWallTexture);
m_pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLOROP, D3DTOP_MODULATE); //相乘
如果没有光照,则纹理颜色会乘以0,因此便看不到墙壁纹理了,只能看到第二张纹理。
m_pd3dDevice->SetTexture(1, m_pEnvTexture);
m_pd3dDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
m_pd3dDevice->SetTextureStageState (1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_pd3dDevice->SetTextureStageState (1, D3DTSS_ COLORARG2, D3DTA_CURRENT);
m_pd3dDevice->SetTextureStageState (1, D3DTSS_COLOROP, D3DTOP_MODULATE);
如果第1张纹理是不可见的,则第1阶段的值0与第2阶段的颜色相乘,还是0(黑色)。当你处于一个真正漆黑的地方时,由于光线微弱,你是无法看到周围物体的颜色的,因此以上代码对于模拟这种情况很有用。
***********************************************************************************************
Direct3D 在一次最多支持8张TEXTURE的融合,可以用于各种的光影的实现。
通过设定各层纹理的融合信息,
HRESULT SetTextureStageState(
DWORD Stage,
D3DTEXTURESTAGESTATETYPE Type,
DWORD Value
);
每层纹理通过2个融合参数来进行融合
D3DTEXTURESTAGESTATETYPE
指定Texture的状态。主要是D3DTSS_COLOROP(像素融合) D3DTSS_ALPHAOP(ALPHA融合)
然后通过D3DTSS_COLORARG1,D3DTSS_COLORARG2设定2个融合参数。
主要流程。
ms.GetDevice()->SetTexture(0,tex1);
ms.GetDevice()->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_SELECTARG1);//取arg1操作
ms.GetDevice()->SetTextureStageState(0,D3DTSS_COLORARG1,D3DTA_TEXTURE);//0层的Texture像素
ms.GetDevice()->SetTexture(1,tex);
ms.GetDevice()->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE );//乘法操作
ms.GetDevice()->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE );//1层的Texture像素
ms.GetDevice()->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT );//上一层结果
默认 1 层纹理以上都为 D3DTOP_DISABLE
本文列举了Direct3D中各种纹理应用实现:黑暗贴图,发光贴图,漫反射映射贴图,细节纹理,纹理混合,有较详尽的注解。其中黑暗贴图,发光贴图,细节纹理都是采用多重纹理的方法实现(也可以采用多次渲染混合实现)。
示例代码使用Beginning direct3D game programming中的框架,省去不少事情,可以专注纹理话题。代码:
点此下载下面来看代码与效果:
正常的纹理贴图效果:
正常的纹理贴图代码:
1
//基本纹理
2
void drawNormalTexture()
3
{
4
// 设置box纹理贴图
5
Device->SetTexture(0, texBox);
6
Device->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0); //使用纹理坐标
7
Device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); // 颜色来源-材质
8
Device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); // 使用当前颜色作为第一个texture stage的输出
9
10
// 描绘box
11
Box->draw(0, 0, 0);
12
}
黑暗纹理贴图效果:
黑暗纹理贴图代码:
1
//黑暗映射纹理
2
void drawDarkMapTexture()
3
{
4
// Multi texture:多重纹理,此处为两重纹理
5
// finalColor = destPixelColor * sourcePixelColor
6
// 设置box纹理贴图
7
Device->SetTexture(0, texBox);
8
Device->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
9
Device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); // 颜色来源-材质
10
Device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); // 使用当前颜色作为第一个texture stage的输出
11
12
// 设置黑暗纹理贴图
13
Device->SetTexture(1, texAlpha);
14
Device->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
15
Device->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE); // 颜色来源-材质
16
Device->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT); // 颜色来源-前一个texture stage
17
Device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE); // 颜色混合:相乘
18
19
// 描绘box
20
Box->draw(0, 0, 0);
21
}
漫反射映射贴图效果:夜光镜效果
漫反射映射贴图代码:
1
//漫射光映射纹理
2
void drawDiffuseTexture()
3
{
4
// 设置box纹理贴图
5
Device->SetTexture(0, texBox);
6
Device->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
7
Device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); // 颜色来源-材质
8
Device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); // 颜色来源-漫反射
9
Device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); // 颜色混合
10
11
// 设置材质:绿色材质实现类似夜光镜的效果
12
Device->SetMaterial(&d3d::GREEN_MTRL);
13
14
// 描绘box
15
Box->draw(0, 0, 0);
16
}
发光映射纹理贴图效果:
发光映射纹理贴图代码:
1
//发光映射纹理
2
void drawGlowMapTexture()
3
{
4
// Multi texture:多重纹理,此处为两重纹理
5
// finalColor = sourcePixelColor * 1.0 + destPixelColor * 1.0
6
// 设置box纹理贴图
7
Device->SetTexture(0, texBox);
8
Device->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
9
Device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); // 颜色来源-材质
10
Device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); // 使用当前颜色作为第一个texture stage的输出
11
12
// 设置黑暗纹理贴图
13
Device->SetTexture(1, texAlpha);
14
Device->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
15
Device->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE); // 颜色来源-材质
16
Device->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT); // 颜色来源-前一个texture stage
17
Device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADD); // 颜色混合:相加
18
19
// 描绘box
20
Box->draw(0, 0, 0);
21
}
细节映射纹理贴图:实现粗糙的凹凸效果
细节映射纹理贴图代码:
1
//细节映射纹理:实现凹凸效果
2
void drawDetailMapTexture()
3
{
4
// Multi texture:多重纹理,此处为两重纹理
5
// finalColor = sourcePixelColor * destPixelColor + destPixelColor * sourcePixelColor
6
// 设置box纹理贴图
7
Device->SetTexture(0, texBox);
8
Device->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
9
Device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); // 颜色来源-材质
10
Device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); // 使用当前颜色作为第一个texture stage的输出
11
12
// 设置细节纹理贴图
13
Device->SetTexture(1, texDetail);
14
Device->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
15
Device->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE); // 颜色来源-材质
16
Device->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT); // 颜色来源-前一个渲染通道
17
Device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADDSIGNED); // 颜色混合
18
19
// 描绘box
20
Box->draw(0, 0, 0);
21
}
alpha纹理混合效果:多次渲染实现
alph纹理混合代码:
1
//alpha混合纹理
2
void drawAlphaBlendTexture()
3
{
4
// 多次渲染实现纹理混合
5
// finalColor = sourcePixelColor * sourceBlendFactor + destPixelColor * destBlendFactor
6
// 设置纹理混合参数
7
Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); // alpha值来自纹理
8
Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
9
10
// 设置混合因子实现透明效果
11
Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
12
Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
13
14
//使用box纹理贴图实现第一次渲染,无alpha混合
15
Device->SetTexture(0, texBox);
16
Device->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
17
Device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); // 颜色来源-材质
18
Device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); // 使用当前颜色作为第一个texture stage的输出
19
20
// 第一次描绘box
21
Box->draw(&boxWorldMatrix, 0, 0);
22
23
//使用带alpha值得flower纹理贴图实现第二次渲染,有alpha混合
24
Device->SetTexture(0, texAlphaFlower);
25
Device->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
26
Device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); // 颜色来源-材质
27
Device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); // 使用当前颜色作为第一个texture stage的输出
28
29
// 打开纹理混合
30
Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
31
32
// 第一次描绘box
33
Box->draw(&boxWorldMatrix, 0, 0);
34
35
// 关闭纹理混合
36
Device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
37
}
多重纹理MultiTexture:使用多张纹理来渲染一个场景,只调用一次(DrawPrimitive)
纹理混合TextureBlending:按要求混合在同一个位置的像素点上色彩或透明度
在多重纹理(multitexturing)出现之前,DirectX支持的是多次渲染(multipass rendering)。但是因为OnFrameRender中要进行多次渲染(即Draw*的调用),效率很低。自从显卡开始支持多重纹理之后,游戏的性能得到了很大的提升:因为多重纹理只渲染一次,而在渲染之前通过调用SetTexture和SetTextureStageState函数来对不同的纹理进行混合,这个过程就叫做纹理混合(texture blending)。
多重纹理在pixel shader出现之前有着广泛的应用,主要可以用在以下方面:
① 黑暗映射:将两张纹理颜色相乘,模拟弱光照射
② 纹理和材质漫反射的混合:模拟强光照射
③ ①②效果累加:(光的颜色*墙壁纹理)*黑暗贴图
④ 发光映射:将两张纹理颜色相加
⑤ 细节映射
⑥ Alpha操作,分为材质alpha和贴图alpha操作
在执行多纹理操作之前必须对显卡能力进行检查,Caps中与纹理和alpha操作有关的是:
l MaxSimultaneousTextures 多纹理最大支持数
l DestBlendCaps alpha混合
l SrcBlendCaps 对于源色彩混合的支持
l TextureOpCaps 对于各种纹理映射的支持
l TextureAddressCaps 对于寻址模式的支持
Example 1 Multipass Rendering + Material Alpha Blending
D3DX系列函数中有些是内建生成mesh的,例如D3DXCreateTeapot,就可以生成一个茶壶的mesh。Mesh俗称网格,在D3D中就是很多顶点定义的集合。D3DXCreateTeapot就可以生成一个茶壶顶点的集合。在以后的章节中会详细讲到mesh。在这里这样使用mesh就行了:
IDirect3DDevice9* m_device;
ID3DXMesh* m_teapot
V_RETURN( D3DXCreateTeapot( m_device, &m_teapot, NULL) );
m_teapot->DrawSubset(NULL);
这个例子中调用了两次Draw*,分别画背景和茶壶。背景类是一张纹理,渲染过程和以前类似。茶壶类如下:
classTeapot
{
public:
Teapot(IDirect3DDevice9* device);
HRESULTOnResetDevice();
VOIDOnLostDevice();
VOIDCreate();
VOIDOnFrameRender( D3DMATERIAL9* mtrl, IDirect3DTexture9* tex );
~Teapot();
private:
D3DMATERIAL9m_mtrl;
IDirect3DDevice9* m_device;
ID3DXMesh* m_teapot;
};
Teapot::Teapot(IDirect3DDevice9* device)
{
m_device = device;
}
Teapot::~Teapot()
{
}
inlineVOIDTeapot::Create()
{
m_mtrl.Ambient = D3DXCOLOR(1,0,0,0);
m_mtrl.Diffuse = D3DXCOLOR(1,0,0,0.3f);
m_mtrl.Specular = D3DXCOLOR(1,0,0,0);
m_mtrl.Emissive = D3DXCOLOR(1,0,0,0);
m_mtrl.Power = 1.0f;
}
inlineHRESULTTeapot::OnResetDevice()
{
HRESULThr;
Create();
V_RETURN( D3DXCreateTeapot( m_device, &m_teapot, NULL) );
// Set alpha blending only for diffuse material
m_device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
// Set alpha operation
m_device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
// Set alpha blend equation. This is transparent equation
m_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
m_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
returnS_OK;
}
inlineVOIDTeapot::OnLostDevice()
{
SAFE_RELEASE( m_teapot );
}
inlineVOIDTeapot::OnFrameRender( D3DMATERIAL9* mtrl, IDirect3DTexture9* tex)
{
if( mtrl )
m_device->SetMaterial(mtrl);
elseif (&m_mtrl)
m_device->SetMaterial(&m_mtrl);
if( tex )
m_device->SetTexture(0, tex);
else
m_device->SetTexture(0, 0);
m_device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
m_teapot->DrawSubset(NULL);
m_device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
}
Create函数中漫反射材质的alpha值改成了0.3,反射颜色为只反射红色。这个是材质alpha混合的核心思想。在OnResetDevice中有两个SetTextureState和SetRenderState函数调用,是用来设定如何进行alpha计算的,具体用法参见代码注释。
茶壶的DrawSubset调用似乎默认是中心画在( 0,0,0 )的,因此要特别注意背景必须画在茶壶的后面。如果观察矩阵设置的是从z负半轴向原点看去的,那么必须将背景画在z正半轴,否则背景会将茶壶覆盖,效果就像没有画茶壶一样。
①背景的原点定义是这样的:
// x, y, z, nx, ny, nz, tu, tv
{ -3.0f, -3.0f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, },
{ -3.0f, 3.0f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, },
{ 3.0f, 3.0f, 1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, },
{ 3.0f, -3.0f, 1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, },
只有4个定点,可以看出来使用了index buffer。
②背景的材质是这样的
m_mtrl.Ambient = D3DXCOLOR(255,255,255,255);
m_mtrl.Diffuse = D3DXCOLOR(255,255,255,255);
m_mtrl.Specular = D3DXCOLOR(255,255,255,255);
m_mtrl.Emissive = D3DXCOLOR(0,0,0,0);
m_mtrl.Power = 2.0f;
在漫反射上不透明。最后背景调用DrawPrimitive完成渲染。
③光源设置是这样的:
D3DLIGHT9dir;
::ZeroMemory(&dir, sizeof(dir));
dir.Type = D3DLIGHT_DIRECTIONAL;
dir.Diffuse = D3DXCOLOR(255,255,255,255);
dir.Specular = D3DXCOLOR(255* 0.2,255* 0.2,255* 0.2,255* 0.6) ;
dir.Ambient = D3DXCOLOR(255* 0.6,255* 0.6,255* 0.6,255* 0.6);
dir.Direction = D3DXVECTOR3(0.0f, 0.0f, -2.0 f);
pd3dDevice->SetLight(0, &dir);
pd3dDevice->LightEnable(0, true);
采用的是平行光,其中漫反射是白光。
④OnFrameMove主回调函数是这样的:
D3DXVECTOR3 vEyePt( 0.0f, 0.0f, -3.0f );
D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f );
D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );
D3DXMATRIXA16 matView;
D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec );
pd3dDevice->SetTransform( D3DTS_VIEW, &matView );
D3DXMATRIXA16 matProj;
D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/2, 1.0f, 1.0f, 1000.0f );
pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
这里设置了观察矩阵和投影矩阵。由vEyePt和vLookatPt可以看到观察矩阵式由z负半轴向原点看的。
⑤光照设置放在OnResetDevice中,以避免窗口大小改变之后的光源丢失,是这样的:
D3DLIGHT9dir;
::ZeroMemory(&dir, sizeof(dir));
dir.Type = D3DLIGHT_DIRECTIONAL;
dir.Diffuse = D3DXCOLOR(1,1,1,1);
dir.Specular = D3DXCOLOR(1,1,1,1)*0.2f ;
dir.Ambient = D3DXCOLOR(1,1,1,1)*0.6f;
dir.Direction = D3DXVECTOR3(0.0, 0.0f, -2.0f);
pd3dDevice->SetLight(0, &dir);
pd3dDevice->LightEnable(0, true);
注意:如果禁用背面剔出(culling)的话将会发现茶壶颜色呈深浅红色状。
效果如下:
Example 2 Rotate the teapot based on Example 1
要使茶壶旋转,但是背景不旋转很简单,就是不要在OnFrameMove主回调函数中设置世界矩阵,而是要在Draw*调用之前设置世界矩阵。每一次调用Draw*只响应最近一次世界矩阵变换的操作。
具体来说,我们可以在Teapot类和BackGround类中添加OnFrameMove函数,以便在各自的OnFrameRender中调用(也可以在主回调函数中调用)。代码如下:
// Things to do when frame move for this background
inlineboolBackGround::OnFrameMove()
{
D3DXMATRIXW;
D3DXMatrixIdentity(&W);
m_device->SetTransform(D3DTS_WORLD, &W);
returntrue;
}
// Things to do when frame move for this teapot
inlineVOIDTeapot::OnFrameMove()
{
D3DXMATRIXA16matWorld;
UINT iTime = timeGetTime() % 5000;
FLOATfAngle = iTime * (2.0f * D3DX_PI) / 5000.0f;
constD3DXVECTOR3 *v = newD3DXVECTOR3(0.0f,1.0f,0.0f);
D3DXMatrixRotationAxis( &matWorld, v, fAngle );
m_device->SetTransform( D3DTS_WORLD, &matWorld );
}
效果就是背景不转而茶壶转,如图:
Example 3 Texture Alpha Blending
和例一和例二类似,也要实现透明效果,但是不使用材质alpha混合,而是使用缓冲帧中的纹理和现有纹理进行混合。在第一和第二例中是将材质的漫反射alpha属性设为小于0xFF的值,打开alpha渲染,调用如下代码,然后禁用alpha渲染,最后还需要设置光照等的信息。
// Set alpha blending only for diffuse material
m_device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
// Set alpha operation
m_device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
// Set alpha blend equation. This is transparent equation
m_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
m_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
现在讲材质改为纹理,可以省去材质和光照的设置。另外将上面的代码改为:
// use alpha channel in texture for alpha
m_device->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);
m_device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2);
// set blending factors so that alpha component determines transparency
m_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCCOLOR);
m_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR);
表示使用纹理alpha混合,混合公式就是景点的透明算法公式
FinalColor = SourceColor * SourceFactor + DestColor * (1- SourceFactor)
不要忘记在适当的时候启用和关闭alpha混合,以及关闭光照。效果如下:
Example 4 Blending Texture Color And Material Diffuse Color
混合材质的漫反射颜色和纹理颜色可以使物体看起来有被强光照射的效果。它的原理是将上述两种颜色值相加。这种混合很简单,因为它只需要一次渲染,一重纹理,但是必须给物体设置好材质,并且需要有恰当的灯光。
①给想要使用这种效果的物体设置漫反射材质:
m_mtrl->Diffuse = D3DXCOLOR(1,1,1,1)*0.5f;
m_mtrl->Ambient = D3DXCOLOR(0,0,0,0);
m_mtrl->Specular = D3DXCOLOR(0,0,0,0);
m_mtrl->Emissive = D3DXCOLOR(0,0,0,0);
m_mtrl->Power = 0.3f;
D3DXCOLOR重载了乘法操作,可以用*0.5f来设置漫反射的强度。
②设置全局的光源。定向光一定要注意光源的方向,点光源要注意光源的位置,否则设置不当还是白忙一场,看到的只是黑色的图像。
VOIDSetLights(IDirect3DDevice9* pd3dDevice)
{
// Turn off ambient light
pd3dDevice->SetRenderState( D3DRS_AMBIENT, 0);
// Enable the light effect
pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE );
D3DLIGHT9dir;
::ZeroMemory(&dir, sizeof(dir));
dir.Type = D3DLIGHT_DIRECTIONAL;
dir.Diffuse = D3DXCOLOR(1,1,1,1);
dir.Specular = D3DXCOLOR(1,1,1,1);
dir.Ambient = D3DXCOLOR(1,1,1,1);
dir.Direction = D3DXVECTOR3(0.0, 0.0f, 2.0f);
pd3dDevice->SetLight(0, &dir);
pd3dDevice->LightEnable(0, true);
pd3dDevice->SetRenderState(D3DRS_NORMALIZENORMALS, true);
pd3dDevice->SetRenderState(D3DRS_SPECULARENABLE, true);
}
这里设置的是定向光,dir.Direction设置的是方向朝z正半轴的,即朝屏幕里面的。
③给想要应用强光照射效果的物体设置色彩混合。可以在Draw*函数前调用。
m_device->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_device->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
m_device->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_ADD);
④给不想要强光照射效果的物体设置TextureStateState
m_device->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_device->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
上面代码告诉D3D只使用纹理颜色。
最终效果如下:
Example 5 Multi Texture and Glow Mapping
终于见到多纹理了。这次要实现的效果是发光映射,就是使物体的某一部分自己发光。这种效果不是材质的Emissive,因为Emissive只能是整个物体看起来都在发光。实现这个效果需要有以下步骤:
①准备一张这样的贴图,高亮部分将映射到物体的发光部分。
②这个效果不需要光源和材质,但是需要在不应用该效果的物体渲染之前禁用两个纹理阶段的色彩操作,代码如下:
m_device->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_DISABLE );
m_device->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_DISABLE );
然后对于不应用该效果的物体,还应指定它的默认纹理:
m_device->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_device->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
③对于应用该效果的物体,添加以下代码:
m_device->SetTexture( 0, m_texture );
m_device->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, 0 );
m_device->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
m_device->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 );
m_device->SetTexture( 1, m_envTex );
m_device->SetTextureStageState( 1, D3DTSS_TEXCOORDINDEX, 0 );
m_device->SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_TEXTURE );
m_device->SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_CURRENT );
m_device->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_ADD );
这里使用了两个纹理阶段,第一个纹理阶段使用纹理本身色彩,第二个纹理阶段接收到之后混合上面的那张中间高亮的纹理。最后在进行Draw*函数的调用。注意在设定完纹理之后必须调用SetTextureStageState( 1, D3DTSS_TEXCOORDINDEX, 0 ),否则第二张纹理将不知道如何进行纹理坐标寻址。效果图是这样的: