多重纹理的混合会产生很多漂亮的效果, 这里我们来实现其中最基础的几种效果, dark & light mapping, detail mapping, glow mapping.
无奈于硬件(TNT2 32M 显卡)和模型的不足, 这集的例子只能为game7中地球化化装.
14.1 dark & detail mapping
想象一下这样的场景, 在一个漆黑的夜晚, 借着微弱的手电筒赶路, 隐隐的看到路的前方有一堵墙堵住前进的方向, 而越来越清楚的后面的怪物的低吼声又让你没有勇气回退, 一阵无力感从心中升起 …
14.1.1 dark mapping 的实现
如果我们用光照来实现上面的场景,光源的位置及衰减参数的实时调整很难. 相对使用dark mapping, 实现就简单了, dark mapping使用一特殊的纹理图, 这张图片包含的是光亮度, 使用多重纹理的过程大致如下,
(1). texture stage 0 设置墙壁纹理.
(2). Texture stage 1 设置dark 纹理, 两输入参数为D3DTA_TEXTURE,
D3DTA_CURRENT, 混合系数是D3DTOP_MODULATE
这样得出的最终纹理是混合光亮度的纹理了.
为产生手电筒的移动光照效果, 还需要设置纹理坐标变换,
(3). 使用纹理矩阵需先设置,
m_pDev->SetTextureStageState(1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);
表示 第二层的纹理矩阵变换的纹理是2维的.
(4). 变化纹理矩阵, 产生动态光照效果,
m_pDev->SetTransform(D3DTS_TEXTURE1, &mTex);
变换的是第二层的纹理坐标
图14.1
14.1.2 detail mapping 的实现
生存的本能指引着你奋力想爬上高墙, 但一次又一次的努力还是失败了, 跪在墙角边, 等待死亡的降临, 突然, 沿着手电筒照射在墙上的光线, 你发现在凹凸不平的墙面上有 -- 一个按钮, ...
如果只使用单一的墙面纹理, 当在上面场景中, 墙面被放大时会有明显的走样, 这时我们需要另加一张细节纹理, 细节纹理是凹凸纹理的一种, 它可以让原来的纹理产生粗糙的效果, 如图14.2, 图中上半部分是没有使用细节纹理时放大的效果, 下半部分使用细节纹理时放大的效果.
图14.2
过程大致如下,
(1). texture stage 0 设置墙壁纹理.
(2). Texture stage 1 设置detail map, 两输入参数为D3DTA_TEXTURE,
D3DTA_CURRENT, 混合系数是D3DTOP_ADDSIGNED
14.2 Dark mapping & Detail mapping 的例子
14.2.1 代码更新
我们来看看 game16 的主要更新的代码 ( 下载 game16 project),
---------------------------------------------------------------
// d9graphics.cpp 中
VOID CD9Graphics::Render()
{
// ...
m_pDev->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
D3DCOLOR_XRGB(0, 0, 128), 1.0f, 0);
if (FAILED(m_pDev->BeginScene()))
{
return;
}
m_pDev->SetStreamSource(0, m_pVB, 0, sizeof(MYVERTEXTEX));
m_pDev->SetFVF(D3DFVF_MYVERTEXTEX);
m_pDev->SetTexture(0, m_pTexture);
m_pDev->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_pDev->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
if (g_nType == 0) // dark mapping
{
//----------------------------
m_pDev->SetTexture(1, m_pTexDark);
m_pDev->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE);
//----------------------------
}
else // detail mapping
{
//----------------------------
m_pDev->SetTexture(1, m_pTexDetail);
m_pDev->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADDSIGNED);
//----------------------------
}
m_pDev->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);
// 为不影响后面的渲染, 关闭不需要的texture stage
m_pDev->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
m_pDev->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
//----------------------------
// ...
}
---------------------------------------------------------------
14.2.2 例子说明
例子很简单, 当选取不同效果时显示不同的纹理混合结果.
14.3 Glow mapping
迅速按下墙上的按钮, 眼前的高墙上浮现一个奇怪的渐渐发亮的图案, 在短暂的眩晕后, 你看到了 -- 地狱 ...
现在来看看我们怎么实现这渐渐发亮的图案, 如果使用一张普通墙面纹理和一张含亮图案的墙面纹理的切换来实现, 很明显缺少亮度从暗到明的过度, 这时可以使用glow map, glow map 和 dark map其实是等同的, Texture stage 1使用混合D3DTOP_BLENDFACTORALPHA, 然后动态改变D3DRS_TEXTUREFACTOR设置颜色的Alpha值来形成亮度过度效果.
过程参考dark mapping.
14.4 多重纹理的多效果例子
14.4.1 代码更新
我们来看看 game17 的主要更新的代码 ( 下载 game17 project),
---------------------------------------------------------------
// d9stars.cpp 中使用128个静态的粒子来模拟星空
VOID CStars::Render()
{
m_pDev->SetRenderState(D3DRS_POINTSPRITEENABLE, TRUE);
m_pDev->SetRenderState(D3DRS_POINTSCALEENABLE, TRUE);
m_pDev->SetRenderState(D3DRS_POINTSIZE, FTOD(0.26f));
m_pDev->SetRenderState(D3DRS_POINTSIZE_MIN, FTOD(0.1f));
m_pDev->SetRenderState(D3DRS_POINTSCALE_A, FTOD(0.0f));
m_pDev->SetRenderState(D3DRS_POINTSCALE_B, FTOD(0.0f));
m_pDev->SetRenderState(D3DRS_POINTSCALE_C, FTOD(1.0f));
m_pDev->SetRenderState(D3DRS_LIGHTING, FALSE);
m_pDev->SetStreamSource(0, m_pVB, 0, sizeof(MYVERTEX));
m_pDev->SetFVF(D3DFVF_MYVERTEX);
m_pDev->SetTexture(0, NULL);
m_pDev->DrawPrimitive(D3DPT_POINTLIST, 0, 128);
m_pDev->SetRenderState(D3DRS_LIGHTING, TRUE);
m_pDev->SetRenderState(D3DRS_POINTSPRITEENABLE, FALSE);
m_pDev->SetRenderState(D3DRS_POINTSCALEENABLE, FALSE);
}
// d9earth.cpp, 根据不同的选择渲染不同的地球效果
VOID CEarth::Render()
{
// 下面两行代码的作用是防止纹理的撕裂现象
// 参考DirectX9c SDK 中Texture Addressing Modes主题
m_pDev->SetRenderState(D3DRS_WRAP0, D3DWRAP_U | D3DWRAP_V);
m_pDev->SetRenderState(D3DRS_WRAP1, D3DWRAP_U | D3DWRAP_V);
m_pDev->SetStreamSource(0, m_pVB, 0, sizeof(MYVERTEXTEX));
m_pDev->SetFVF(D3DFVF_MYVERTEXTEX);
m_pDev->SetTexture(0, m_pTex);
if (g_bFlag[0])
{
m_pDev->SetTexture(1, m_pTexDump);
m_pDev->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
m_pDev->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_pDev->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
m_pDev->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADDSIGNED);
}
m_pDev->SetIndices(m_pIB);
m_pDev->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, 0,
m_nVertices, 0, m_nTriangle);
if (g_bFlag[0])
{
m_pDev->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
}
if (g_bFlag[1])
{
m_pDev->SetTexture(0, m_pTexGlow);
m_pDev->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_pDev->SetTextureStageState(0, D3DTSS_COLORARG2,
D3DTA_DIFFUSE | D3DTA_COMPLEMENT);
m_pDev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
m_pDev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
m_pDev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
m_pDev->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, 0,
m_nVertices, 0, m_nTriangle);
m_pDev->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT);
m_pDev->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
}
if (g_bFlag[2])
{
m_nRot++;
if (m_nRot > MAXROT)
{
m_nRot = 0;
}
FLOAT fC = FLOAT(m_nRot * ROT) / 8.0f;
D3DXMATRIX mCould, mX, mY;
D3DXMatrixRotationX(&mX, fC);
D3DXMatrixRotationY(&mY, fC);
D3DXMatrixMultiply(&mCould, &mX, &mY);
m_pDev->SetTransform(D3DTS_WORLD, &mCould);
m_pDev->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_pDev->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT);
m_pDev->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG2);
m_pDev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
m_pDev->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
m_pDev->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
m_pDev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
m_pDev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
m_pDev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
m_pDev->SetStreamSource(0, m_pVBC, 0, sizeof(MYVERTEXTEX));
m_pDev->SetFVF(D3DFVF_MYVERTEXTEX);
m_pDev->SetTexture(0, m_pTex);
m_pDev->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, 0,
m_nVertices, 0, m_nTriangle);
m_pDev->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
m_pDev->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
m_pDev->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_CURRENT);
}
m_pDev->SetRenderState(D3DRS_WRAP0, 0);
m_pDev->SetRenderState(D3DRS_WRAP1, 0);
}
14.4.2 例子说明
例子中使用的是前几集的知识, 还有为增强效果, 加入了"special effects game programming" 中的22 - Lens Flare效果.
第十四集 小结
瀑布型纹理流水线能产生更多奇妙的效果, 去发现它们, 理解它们.