雖然讀取了3ds文件,但是3ds文件里並不包含頂點法線。為了計算光照,我們需要手工計算法線。過程如下:
三個不共線點構成一個三角形:

計算方法:向量ab與向量ac的叉乘得到過點a垂直於三角形abc所在平面的一條直線,這條垂線就是頂點a的法線。
下面就是代碼:【修正了法線計算bug,2012.04.04】
1 void CModel3ds::CalcNormals()
2 {
3 for(int i = 0; i<m_PolygonNum; ++i)
4 {
5 Vector3 v1 = Vector3_Sub(Vector3(m_pVBbuff[m_pIBbuff[i].p1].X, m_pVBbuff[m_pIBbuff[i].p1].Y, m_pVBbuff[m_pIBbuff[i].p1].Z),
6 Vector3(m_pVBbuff[m_pIBbuff[i].p2].X, m_pVBbuff[m_pIBbuff[i].p2].Y, m_pVBbuff[m_pIBbuff[i].p2].Z));
7 Vector3 v2 = Vector3_Sub(Vector3(m_pVBbuff[m_pIBbuff[i].p1].X, m_pVBbuff[m_pIBbuff[i].p1].Y, m_pVBbuff[m_pIBbuff[i].p1].Z),
8 Vector3(m_pVBbuff[m_pIBbuff[i].p3].X, m_pVBbuff[m_pIBbuff[i].p3].Y, m_pVBbuff[m_pIBbuff[i].p3].Z));
9 Vector3 tmp1 = Vector3_cross(v1,v2);
10 m_pVBbuff[m_pIBbuff[i].p1].nx += tmp1.x;
11 m_pVBbuff[m_pIBbuff[i].p1].ny += tmp1.y;
12 m_pVBbuff[m_pIBbuff[i].p1].nz += tmp1.z;
19 m_pVBbuff[m_pIBbuff[i].p2].nx += tmp1.x;
20 m_pVBbuff[m_pIBbuff[i].p2].ny += tmp1.y;
21 m_pVBbuff[m_pIBbuff[i].p2].nz += tmp1.z;
28 m_pVBbuff[m_pIBbuff[i].p3].nx += tmp1.x;
29 m_pVBbuff[m_pIBbuff[i].p3].ny += tmp1.y;
30 m_pVBbuff[m_pIBbuff[i].p3].nz += tmp1.z;
31 }
32
33 for(int i = 0; i < m_VertexNum; ++i)
34 {
35 Vector3_norm(m_pVBbuff[i].nx,m_pVBbuff[i].ny,m_pVBbuff[i].nz);
36 }
37 }
上面這段代碼簡單來說就是求共頂點的三角形在其公共頂點的法線的平均值,他的結果當然不是精確的,但是大多數時候都是正常工作的:)
代碼中定義的向量運算用到了我寫的一個數學庫中的函數,這個數學庫不是為了效率,只是為了讓上面的代碼跟dx解耦,避免發代碼還要包含一堆頭文件。不然的話拷貝這段代碼就很肯能因為頭文件的問題導致不能編譯,這又會導致一大堆麻煩事,所以下面這個庫代碼,僅僅就是保證正常工作(或許也有bug,但我會努力去fix和避免這種情況):
View Code
1 #ifndef myVector_h__
2 #define myVector_h__
3
4 namespace MY_MATH
5 {
6 class Vector3
7 {
8 public:
9 Vector3():x(0),y(0),z(0){}
10 Vector3(float _x, float _y, float _z):x(_x),y(_y),z(_z){}
11 float x;
12 float y;
13 float z;
14 };
15
16 Vector3 Vector3_Sub(Vector3 src,Vector3 dest)
17 {
18 return Vector3(src.x - dest.x,src.y - dest.y,src.z - dest.z);
19 }
20
21 void Vector3_norm(float& x, float& y, float& z)
22 {
23 float tmp = sqrtf(x*x+y*y+z*z);
24 x = x/tmp;y = y/tmp; z=z/tmp;
25 }
26
27 void Vector3_norm(Vector3& vec)
28 {
29 float tmp = sqrtf(vec.x*vec.x + vec.y*vec.y + vec.z*vec.z);
30 vec.x = vec.x/tmp;
31 vec.y = vec.y/tmp;
32 vec.z = vec.z/tmp;
33 }
34
35 Vector3 Vector3_cross(Vector3& v1, Vector3& v2)
36 {
37 return Vector3(v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x);
38 }
39 }
40
41
42
43 #endif // myVector_h__

這個就是根據我們計算出來的法線進行基本光照渲染出來的截圖,HLSL版本shader如下:
cbuffer mybuff
{
uniform row_major float4x4 world_matrix;
uniform row_major float4x4 view_matrix;
uniform row_major float4x4 proj_matrix;
uniform float4 light_pos;
};
struct VS_INPUT
{
float4 Position : POSITION;
float3 Norm : NORMAL0;
float2 Tex : TEXCOORD0;
};
struct VS_OUTPUT
{
float4 Position : SV_POSITION;
float3 lt : TEXCOORD0;
float3 norm : TEXCOORD1;
};
VS_OUTPUT VS_MAIN(VS_INPUT In)
{
VS_OUTPUT Output;
float4x4 tmp = mul(world_matrix,view_matrix);
tmp = mul(tmp,proj_matrix);
Output.Position = mul(In.Position, tmp);
Output.lt = normalize(light_pos - In.Position);
Output.norm = normalize(mul(In.Norm, world_matrix));
return Output;
}
float4 PS_MAIN(VS_OUTPUT In) : SV_Target
{
float4 diff = {1.0,1.0,1.0,1.0};
float4 ambient = {0.1,0.1,0.1,1.0};
return ambient + diff*saturate(dot(In.lt,In.norm));
}
