有時候,我們隻有一個粗糙的模型,但是我們想渲染紋理細節,比如一個磚牆,我們如何在隻有一個平面的時候,渲染出磚牆凹凸的效果。
比如隻有這樣的牆:

但是我們想要這樣的效果:
怎麼辦呢?這時候,我們可以考慮對第一張圖進行處理,生成它的法向圖,存儲在一張紋理中,生成法向圖的主要算法是:
對于一張圖檔,假設像素排列如上圖所示,Hg,Hr,Ha分别表示這些點的RGB(或灰階)值,我們得到第一個向量(1,0,Hr-Hg),第二個向量(0,1,Ha-Hg),則法向可以通過它們的差積得到:
對所有的像素進行處理,我們可以得到一個紋理的法向圖,比如第一張牆紋理貼圖,經過計算,它的法向圖為:
在物體表面,每個點都都有一個(T,N,B)的局部坐标系,分别對應切向、法向,副法向,有了紋理圖後,我們可以通過公式
bumpNormal = normal + bumpMap.x * tangent + bumpMap.y * binormal; 得到最終的法向。其中bumpMap為前面計算得到的法向貼圖,或者說normal map。
1、首先我們改寫CubeModelClass類,設定頂點格式為:
struct VertexType
{
D3DXVECTOR3 position;
D3DXVECTOR3 normal; //法向
D3DXVECTOR3 tangent; //切向
D3DXVECTOR3 binormal; //副法向
D3DXVECTOR2 texture; //紋理坐标
D3DXVECTOR4 Kd; //材質漫反射系數
D3DXVECTOR4 Ks; //材質的高光系數
};
增加了切向和副法向的計算,主要通過頂點的uv坐标以及頂點值求得
void CubeModelClass::CalculateTangentBinormal(TempVertexType vertex1, TempVertexType vertex2, TempVertexType vertex3,
VectorType& tangent, VectorType& binormal)
{
float vector1[3], vector2[3];
float tuVector[2], tvVector[2];
float den;
float length;
// 計算2個向量.
vector1[0] = vertex2.x - vertex1.x;
vector1[1] = vertex2.y - vertex1.y;
vector1[2] = vertex2.z - vertex1.z;
vector2[0] = vertex3.x - vertex1.x;
vector2[1] = vertex3.y - vertex1.y;
vector2[2] = vertex3.z - vertex1.z;
// 計算tu和tv向量.
tuVector[0] = vertex2.tu - vertex1.tu;
tvVector[0] = vertex2.tv - vertex1.tv;
tuVector[1] = vertex3.tu - vertex1.tu;
tvVector[1] = vertex3.tv - vertex1.tv;
den = 1.0f / (tuVector[0] * tvVector[1] - tuVector[1] * tvVector[0]);
tangent.x = (tvVector[1] * vector1[0] - tvVector[0] * vector2[0]) * den;
tangent.y = (tvVector[1] * vector1[1] - tvVector[0] * vector2[1]) * den;
tangent.z = (tvVector[1] * vector1[2] - tvVector[0] * vector2[2]) * den;
binormal.x = (tuVector[0] * vector2[0] - tuVector[1] * vector1[0]) * den;
binormal.y = (tuVector[0] * vector2[1] - tuVector[1] * vector1[1]) * den;
binormal.z = (tuVector[0] * vector2[2] - tuVector[1] * vector1[2]) * den;
length = sqrt((tangent.x * tangent.x) + (tangent.y * tangent.y) + (tangent.z * tangent.z));
// 歸一化
tangent.x = tangent.x / length;
tangent.y = tangent.y / length;
tangent.z = tangent.z / length;
// 計算向量的長度.
length = sqrt((binormal.x * binormal.x) + (binormal.y * binormal.y) + (binormal.z * binormal.z));
// 歸一化向量
binormal.x = binormal.x / length;
binormal.y = binormal.y / length;
binormal.z = binormal.z / length;
return;
}
得到面的法向、切向以及副法向後,把他們指派給頂點。
然後我們在LightTex2ShaderClass中,改變layout,然後修改lighttex2.vs和lighttex2.ps
特别是ps中,我們改變normal的計算方式:
float3 N;
float4 textureColor;
float4 textureColor1 = shaderTexture[0].Sample(SampleType, input.tex);
float4 textureColor2 = shaderTexture[1].Sample(SampleType, input.tex);
float4 bumpMap;
float3 bumpNormal;
//從範圍[0,1]轉換到[-1,1],因為預設法向圖我們用[0,1]的這種方式打開
bumpMap = (textureColor2 * 2.0f) - 1.0f;
N = input.worldnormal + bumpMap.x*input.worldtangent + bumpMap.y*input.worldbinormal;
N = normalize(N);
程式執行後,我們按R鍵,可以得到下面的結果:
因為使用的是面法向,我們再看側面,有着不同效果,:
完整的代碼請參考:
工程檔案myTutorialD3D11_37
代碼下載下傳:
<a href="http://files.cnblogs.com/mikewolf2002/d3d1127-28.zip">http://files.cnblogs.com/mikewolf2002/d3d1127-28.zip</a>
<a href="http://files.cnblogs.com/mikewolf2002/pictures.zip">http://files.cnblogs.com/mikewolf2002/pictures.zip</a>