|
|
发表于 2005-6-13 18:40:00
|
显示全部楼层
Re:关于Bump Mapping, 高手再给指点指点
首先实现bump mapping一个重点是tangent space,
要把灯光方向向量和视觉向量转化到tangent space去,
可以| Tx Ty Tz |
| Bx By Bz | 把灯光方向向量和视觉向量转化到tangent space去
| Nx Ny Ny |
其中T是顶点切线, B是顶点副法线, N是顶点法线
通过读取法线图来获得法线,
这样就可以这几个变量在pixel shader中计算了..
下面是代码:
// 顶点着色器输入结构
struct VS_INPUT
{
float4 Position : POSITION0;
float3 Normal : NORMAL0; // 顶点法线
float2 Texcoord : TEXCOORD0;
float3 Binormal : TEXCOORD1; // 顶点副法线
float3 Tangent : TEXCOORD2; // 顶点切线
};
// 顶点着色器输出结构, 实现漫反射光照, 有凸凹效果
struct VS_OUTPUT_DIFFUSE_BUMP
{
float4 Position : POSITION0;
float2 BaseTexcd : TEXCOORD0; // 表面纹理坐标
float2 BumpTexcd : TEXCOORD1; // 法线纹理坐标
float3 LightDir : TEXCOORD2; // 变换到tangent space的灯光向量
};
// 漫反射凸凹光照顶点着色器函数
VS_OUTPUT_DIFFUSE_BUMP DiffuseBump_VS_Main(VS_INPUT Input)
{
VS_OUTPUT_DIFFUSE_BUMP Output;
Output.Position = mul(Input.Position, g_mWorldViewProj);
Output.BaseTexcd = Input.Texcoord;
Output.BumpTexcd = Input.Texcoord;
float3 vP = mul(Input.Position, g_mWorld); // 顶点位置
float3 vL = g_vLightPos - vP; // 灯光方向向量
float3 vN = mul(float4(Input.Normal, 1.0f), g_mWorld).xyz;
float3 vB = mul(float4(Input.Binormal, 1.0f), g_mWorld).xyz;
float3 vT = mul(float4(Input.Tangent, 1.0f), g_mWorld).xyz;
// 把 vL 转化到tangent space去
float x = dot(vL, vT);
float y = dot(vL, vB);
float z = dot(vL, vN);
Output.LightDir = normalize(float3(x, y, z));
return Output;
}
// 像素着色器输入结构, 实现漫反射光照, 有凸凹效果
struct PS_INPUT_DIFFUSE_BUMP
{
float2 BaseTexcd : TEXCOORD0;
float2 BumpTexcd : TEXCOORD1;
float3 LightDir : TEXCOORD2;
};
// 漫反射凸凹光照像素着色器函数
float4 DiffuseBump_PS_Main(PS_INPUT_DIFFUSE_BUMP Input) : COLOR0
{
float3 vL = Input.LightDir;
float3 vN = normalize(tex2D(BumpSampler, Input.BumpTexcd).xyz * 2.0f -1.0f));
float4 vDiffuse = g_vLightCol * (g_vMatrlAmbient + max(0, dot(vN, vL)) * g_vMatrlDiffuse);
return saturate(tex2D(BaseSampler, Input.BaseTexcd) * vDiffuse);
}
在这些问题中, 要计算顶点的法线, 副法线, 切线.
可以通过下面的方法来计算:
E,F,G分别是每一个面的顶点, <sE, tE>, <sF, tF>, <sG, sG>要按照顺序的
P = F - E; Q = G - E;
<s1, t1> = <sF - sE, tF - tE>
<s2, t2> = <sG - sE, tG - tE>
| Tx Ty Tz | | t2 -t1 | | Px Py Pz |
| Bx By Bz | = (1 / s1*t2 - s2*t1) | -s2 s1 | | Qx Qy Qz |
T = T - ( N . T)N;
B = N * T;
| Tx Ty Tz |
| Bx By Bz | 就是 tangent space 矩阵了
| Nx Ny Ny |
下面是我用D3D写的代码:
struct sVertexBump
{
D3DXVECTOR3 Position;
D3DXVECTOR3 Normal;
float u, v;
D3DXVECTOR3 Binormal;
D3DXVECTOR3 Tangent;
static const DWORD FVF;
};
const DWORD CBumpMesh::sVertexBump::FVF =
D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX3 |
D3DFVF_TEXCOORDSIZE2(0) | D3DFVF_TEXCOORDSIZE3(1) |
D3DFVF_TEXCOORDSIZE3(2);
//------------------------------
// 计算 tangent, binormal
//------------------------------
HRESULT CBumpMesh::ComputeTangentAndBinormal(IDirect3DDevice9 *pd3dDevice)
{
HRESULT hr;
// 创建一个有 tangent, binormal 的mesh
ID3DXMesh* pTempMesh;
V(m_pMesh->CloneMeshFVF(m_pMesh->GetOptions(),
sVertexBump::FVF,
pd3dDevice, &pTempMesh));
V(D3DXComputeNormals(pTempMesh, NULL));
SAFE_RELEASE(m_pMesh);
m_pMesh = pTempMesh;
// 计算 tangent, binormal
WORD* tri = NULL;
sVertexBump* pVertex = NULL;
V(m_pMesh->LockIndexBuffer( D3DLOCK_READONLY, (void**)&tri));
V(m_pMesh->LockVertexBuffer( D3DLOCK_DISCARD, (void**)&pVertex));
DWORD dwTotalVertex = m_pMesh->GetNumVertices();
DWORD dwTotalTri = m_pMesh->GetNumFaces();
//WORD* wNumShared = new WORD[dwTotalVertex];
D3DXVECTOR3* tan1 = new D3DXVECTOR3[dwTotalVertex];
memset(tan1, 0, dwTotalVertex * sizeof(D3DXVECTOR3));
// 计算 tangent, binormal
for ( DWORD i=0; i<dwTotalTri; i++ )
{
int nOffset = 3 * i;
int idxVertex0 = tri[nOffset];
int idxVertex1 = tri[nOffset + 1];
int idxVertex2 = tri[nOffset + 2];
D3DXVECTOR3 v0 = D3DXVECTOR3(pVertex[idxVertex0].Position);
D3DXVECTOR3 v1 = D3DXVECTOR3(pVertex[idxVertex1].Position);
D3DXVECTOR3 v2 = D3DXVECTOR3(pVertex[idxVertex2].Position);
D3DXVECTOR2 w0 = D3DXVECTOR2(pVertex[idxVertex0].u, pVertex[idxVertex0].v);
D3DXVECTOR2 w1 = D3DXVECTOR2(pVertex[idxVertex1].u, pVertex[idxVertex1].v);
D3DXVECTOR2 w2 = D3DXVECTOR2(pVertex[idxVertex2].u, pVertex[idxVertex2].v);
float x1 = v1.x - v0.x;
float x2 = v2.x - v0.x;
float y1 = v1.y - v0.y;
float y2 = v2.y - v0.y;
float z1 = v1.z - v0.z;
float z2 = v2.z - v0.z;
float s1 = w1.x - w0.x;
float s2 = w2.x - w0.x;
float t1 = w1.y - w0.y;
float t2 = w2.y - w0.y;
float r = 1.0f / (s1 * t2 - s2 * t1);
D3DXVECTOR3 sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r);
D3DXVECTOR3 tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r);
//++(wNumShared[idxVertex0]);
//++(wNumShared[idxVertex1]);
//++(wNumShared[idxVertex2]);
tan1[idxVertex0] += sdir;
tan1[idxVertex1] += sdir;
tan1[idxVertex2] += sdir;
}
for( i = 0; i < dwTotalVertex; i ++ )
{
D3DXVECTOR3 &N = pVertex.Normal;
D3DXVECTOR3 &T = tan1/* / wNumShared*/;
D3DXVec3Normalize(&T, &T);
float a = D3DXVec3Dot(&N, &T);
D3DXVec3Normalize(&pVertex.Tangent, &(T - N * a));
D3DXVECTOR3 temp;
D3DXVec3Cross(&temp, &N, &pVertex.Tangent);
D3DXVec3Normalize(&pVertex.Binormal, &temp);
}
//delete[] wNumShared;
delete[] tan1;
m_pMesh->UnlockIndexBuffer();
m_pMesh->UnlockVertexBuffer();
return S_OK;
}
|
|