有时候,我们只有一个粗糙的模型,但是我们想渲染纹理细节,比如一个砖墙,我们如何在只有一个平面的时候,渲染出砖墙凹凸的效果。
比如只有这样的墙:
但是我们想要这样的效果:
怎么办呢?这时候,我们可以考虑对第一张图进行处理,生成它的法向图,存储在一张纹理中,生成法向图的主要算法是:
对于一张图片,假设像素排列如上图所示,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
代码下载: