【DX11地形篇】5-定向光照

参考教程

Tutorial 4: Terrain Lighting

学习记录

  这篇文章中主要介绍对已经渲染的地形进行光照渲染(其实没什么新东西)。本篇代码基于上一篇,框架如下:

1

  为地形添加光照,我们主要是需要在 TerrainClass 中计算法线,然后使用 LightShaderClass 代替 TextureShaderClass 渲染就行了。在 TerrainClass 中,我们修改顶点及模型等结构体为其添加法线的变量,然后新增一个计算法线的方法。修改后的 TerrainClass 声明如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
////////////////////////////////////////////////////////////////////////////////
// Class name: TerrainClass
////////////////////////////////////////////////////////////////////////////////
class TerrainClass
{
private:
struct VertexType
{
XMFLOAT3 position;
XMFLOAT2 texcoord;
XMFLOAT3 normal;
};

struct HeightMapType {
float x, y, z;
float nx, ny, nz;
};

struct ModelType {
float x, y, z;
float tu, tv;
float nx, ny, nz;
};

struct VectorType {
float x, y, z;
};

public:
TerrainClass();
TerrainClass(const TerrainClass&);
~TerrainClass();

bool Initialize(ID3D11Device* , char*);
void Shutdown();
bool Render(ID3D11DeviceContext*);

int GetIndexCount();

private:
bool InitializeBuffers(ID3D11Device*);
void ShutdownBuffers();
void RenderBuffers(ID3D11DeviceContext*);

bool CalculateNormals();
bool LoadSetupFile(CHAR*);
bool LoadBitmapHeightMap();
void ShutdownHeightMap();
void SetTerrainCoordinates();
bool BuildTerrainModel();
void ShutdownTerrainModel();

private:
ID3D11Buffer *m_vertexBuffer, *m_indexBuffer;
int m_vertexCount, m_indexCount;

int m_terrainHeight , m_terrainWidth;
float m_heightScale;
LPSTR m_terrainFilename;
HeightMapType* m_heightMap;
ModelType* m_terrainModel;
};

  法线计算方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
bool TerrainClass::CalculateNormals()
{
int i, j, index1, index2, index3, index;
float vertex1[3], vertex2[3], vertex3[3], vector1[3], vector2[3], sum[3], length;
VectorType* normals;


// Create a temporary array to hold the face normal vectors.
normals = new VectorType[(m_terrainHeight-1) * (m_terrainWidth-1)];
if(!normals)
{
return false;
}

// Go through all the faces in the mesh and calculate their normals.
for(j=0; j<(m_terrainHeight-1); j++)
{
for(i=0; i<(m_terrainWidth-1); i++)
{
index1 = ((j+1) * m_terrainWidth) + i; // Bottom left vertex.
index2 = ((j+1) * m_terrainWidth) + (i+1); // Bottom right vertex.
index3 = (j * m_terrainWidth) + i; // Upper left vertex.

// Get three vertices from the face.
vertex1[0] = m_heightMap[index1].x;
vertex1[1] = m_heightMap[index1].y;
vertex1[2] = m_heightMap[index1].z;

vertex2[0] = m_heightMap[index2].x;
vertex2[1] = m_heightMap[index2].y;
vertex2[2] = m_heightMap[index2].z;

vertex3[0] = m_heightMap[index3].x;
vertex3[1] = m_heightMap[index3].y;
vertex3[2] = m_heightMap[index3].z;

// Calculate the two vectors for this face.
vector1[0] = vertex1[0] - vertex3[0];
vector1[1] = vertex1[1] - vertex3[1];
vector1[2] = vertex1[2] - vertex3[2];
vector2[0] = vertex3[0] - vertex2[0];
vector2[1] = vertex3[1] - vertex2[1];
vector2[2] = vertex3[2] - vertex2[2];

index = (j * (m_terrainWidth - 1)) + i;

// Calculate the cross product of those two vectors to get the un-normalized value for this face normal.
normals[index].x = (vector1[1] * vector2[2]) - (vector1[2] * vector2[1]);
normals[index].y = (vector1[2] * vector2[0]) - (vector1[0] * vector2[2]);
normals[index].z = (vector1[0] * vector2[1]) - (vector1[1] * vector2[0]);

// Calculate the length.
length = (float)sqrt((normals[index].x * normals[index].x) + (normals[index].y * normals[index].y) +
(normals[index].z * normals[index].z));

// Normalize the final value for this face using the length.
normals[index].x = (normals[index].x / length);
normals[index].y = (normals[index].y / length);
normals[index].z = (normals[index].z / length);
}
}

// Now go through all the vertices and take a sum of the face normals that touch this vertex.
for(j=0; j<m_terrainHeight; j++)
{
for(i=0; i<m_terrainWidth; i++)
{
// Initialize the sum.
sum[0] = 0.0f;
sum[1] = 0.0f;
sum[2] = 0.0f;

// Bottom left face.
if(((i-1) >= 0) && ((j-1) >= 0))
{
index = ((j-1) * (m_terrainWidth-1)) + (i-1);

sum[0] += normals[index].x;
sum[1] += normals[index].y;
sum[2] += normals[index].z;
}

// Bottom right face.
if((i<(m_terrainWidth-1)) && ((j-1) >= 0))
{
index = ((j - 1) * (m_terrainWidth - 1)) + i;

sum[0] += normals[index].x;
sum[1] += normals[index].y;
sum[2] += normals[index].z;
}

// Upper left face.
if(((i-1) >= 0) && (j<(m_terrainHeight-1)))
{
index = (j * (m_terrainWidth-1)) + (i-1);

sum[0] += normals[index].x;
sum[1] += normals[index].y;
sum[2] += normals[index].z;
}

// Upper right face.
if((i < (m_terrainWidth-1)) && (j < (m_terrainHeight-1)))
{
index = (j * (m_terrainWidth-1)) + i;

sum[0] += normals[index].x;
sum[1] += normals[index].y;
sum[2] += normals[index].z;
}

// Calculate the length of this normal.
length = (float)sqrt((sum[0] * sum[0]) + (sum[1] * sum[1]) + (sum[2] * sum[2]));

// Get an index to the vertex location in the height map array.
index = (j * m_terrainWidth) + i;

// Normalize the final shared normal for this vertex and store it in the height map array.
m_heightMap[index].nx = (sum[0] / length);
m_heightMap[index].ny = (sum[1] / length);
m_heightMap[index].nz = (sum[2] / length);
}
}

// Release the temporary normals.
delete [] normals;
normals = 0;

return true;
}

  除此之外,我们在 ShaderManagerClass 里新增 LightShaderClass 的对象,以及在 ZoneClass 里新增 LightClass 的对象,然后修改渲染的方式就可以了。

  最终效果如下:

2

  有光照的情况下我们的地形显得更加真实,源代码地址:DX11TerrainTutorial-TerrainLight

0%