【DX11地形篇】12-基于地形高度的摄像机位移

参考教程

Tutorial 11: Height Based Movement

学习记录

  这篇文章中我们介绍基于地形高度的摄像机移动,本篇代码基于上一篇,修改很少。

  在 TerrainClass 中,我们添加获得地形当前高度的接口 bool GetHeightAtPosition(float, float, float&); ,传入 x , z 的值获得高度,其实现如下:

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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
bool TerrainClass::GetHeightAtPosition(float inputX, float inputZ, float& height)
{
int i, cellId, index;
float vertex1[3], vertex2[3], vertex3[3];
bool foundHeight;
float maxWidth, maxHeight, maxDepth, minWidth, minHeight, minDepth;


// Loop through all of the terrain cells to find out which one the inputX and inputZ would be inside.
cellId = -1;
for(i=0; i<m_cellCount; i++)
{
// Get the current cell dimensions.
m_TerrainCells[i].GetCellDimensions(maxWidth, maxHeight, maxDepth, minWidth, minHeight, minDepth);

// Check to see if the positions are in this cell.
if((inputX < maxWidth) && (inputX > minWidth) && (inputZ < maxDepth) && (inputZ > minDepth))
{
cellId = i;
i = m_cellCount;
}
}

// If we didn't find a cell then the input position is off the terrain grid.
if(cellId == -1)
{
return false;
}

// If this is the right cell then check all the triangles in this cell to see what the height of the triangle at this position is.
for(i=0; i<(m_TerrainCells[cellId].GetVertexCount() / 3); i++)
{
index = i * 3;

vertex1[0] = m_TerrainCells[cellId].m_vertexList[index].x;
vertex1[1] = m_TerrainCells[cellId].m_vertexList[index].y;
vertex1[2] = m_TerrainCells[cellId].m_vertexList[index].z;
index++;

vertex2[0] = m_TerrainCells[cellId].m_vertexList[index].x;
vertex2[1] = m_TerrainCells[cellId].m_vertexList[index].y;
vertex2[2] = m_TerrainCells[cellId].m_vertexList[index].z;
index++;

vertex3[0] = m_TerrainCells[cellId].m_vertexList[index].x;
vertex3[1] = m_TerrainCells[cellId].m_vertexList[index].y;
vertex3[2] = m_TerrainCells[cellId].m_vertexList[index].z;

// Check to see if this is the polygon we are looking for.
foundHeight = CheckHeightOfTriangle(inputX, inputZ, height, vertex1, vertex2, vertex3);
if(foundHeight)
{
return true;
}
}

return false;
}

bool TerrainClass::CheckHeightOfTriangle(float x, float z, float& height, float v0[3], float v1[3], float v2[3])
{
float startVector[3], directionVector[3], edge1[3], edge2[3], normal[3];
float Q[3], e1[3], e2[3], e3[3], edgeNormal[3], temp[3];
float magnitude, D, denominator, numerator, t, determinant;


// Starting position of the ray that is being cast.
startVector[0] = x;
startVector[1] = 0.0f;
startVector[2] = z;

// The direction the ray is being cast.
directionVector[0] = 0.0f;
directionVector[1] = -1.0f;
directionVector[2] = 0.0f;

// Calculate the two edges from the three points given.
edge1[0] = v1[0] - v0[0];
edge1[1] = v1[1] - v0[1];
edge1[2] = v1[2] - v0[2];

edge2[0] = v2[0] - v0[0];
edge2[1] = v2[1] - v0[1];
edge2[2] = v2[2] - v0[2];

// Calculate the normal of the triangle from the two edges.
normal[0] = (edge1[1] * edge2[2]) - (edge1[2] * edge2[1]);
normal[1] = (edge1[2] * edge2[0]) - (edge1[0] * edge2[2]);
normal[2] = (edge1[0] * edge2[1]) - (edge1[1] * edge2[0]);

magnitude = (float)sqrt((normal[0] * normal[0]) + (normal[1] * normal[1]) + (normal[2] * normal[2]));
normal[0] = normal[0] / magnitude;
normal[1] = normal[1] / magnitude;
normal[2] = normal[2] / magnitude;

// Find the distance from the origin to the plane.
D = ((-normal[0] * v0[0]) + (-normal[1] * v0[1]) + (-normal[2] * v0[2]));

// Get the denominator of the equation.
denominator = ((normal[0] * directionVector[0]) + (normal[1] * directionVector[1]) + (normal[2] * directionVector[2]));

// Make sure the result doesn't get too close to zero to prevent divide by zero.
if(fabs(denominator) < 0.0001f)
{
return false;
}

// Get the numerator of the equation.
numerator = -1.0f * (((normal[0] * startVector[0]) + (normal[1] * startVector[1]) + (normal[2] * startVector[2])) + D);

// Calculate where we intersect the triangle.
t = numerator / denominator;

// Find the intersection vector.
Q[0] = startVector[0] + (directionVector[0] * t);
Q[1] = startVector[1] + (directionVector[1] * t);
Q[2] = startVector[2] + (directionVector[2] * t);

// Find the three edges of the triangle.
e1[0] = v1[0] - v0[0];
e1[1] = v1[1] - v0[1];
e1[2] = v1[2] - v0[2];

e2[0] = v2[0] - v1[0];
e2[1] = v2[1] - v1[1];
e2[2] = v2[2] - v1[2];

e3[0] = v0[0] - v2[0];
e3[1] = v0[1] - v2[1];
e3[2] = v0[2] - v2[2];

// Calculate the normal for the first edge.
edgeNormal[0] = (e1[1] * normal[2]) - (e1[2] * normal[1]);
edgeNormal[1] = (e1[2] * normal[0]) - (e1[0] * normal[2]);
edgeNormal[2] = (e1[0] * normal[1]) - (e1[1] * normal[0]);

// Calculate the determinant to see if it is on the inside, outside, or directly on the edge.
temp[0] = Q[0] - v0[0];
temp[1] = Q[1] - v0[1];
temp[2] = Q[2] - v0[2];

determinant = ((edgeNormal[0] * temp[0]) + (edgeNormal[1] * temp[1]) + (edgeNormal[2] * temp[2]));

// Check if it is outside.
if(determinant > 0.001f)
{
return false;
}

// Calculate the normal for the second edge.
edgeNormal[0] = (e2[1] * normal[2]) - (e2[2] * normal[1]);
edgeNormal[1] = (e2[2] * normal[0]) - (e2[0] * normal[2]);
edgeNormal[2] = (e2[0] * normal[1]) - (e2[1] * normal[0]);

// Calculate the determinant to see if it is on the inside, outside, or directly on the edge.
temp[0] = Q[0] - v1[0];
temp[1] = Q[1] - v1[1];
temp[2] = Q[2] - v1[2];

determinant = ((edgeNormal[0] * temp[0]) + (edgeNormal[1] * temp[1]) + (edgeNormal[2] * temp[2]));

// Check if it is outside.
if (determinant > 0.001f)
{
return false;
}

// Calculate the normal for the third edge.
edgeNormal[0] = (e3[1] * normal[2]) - (e3[2] * normal[1]);
edgeNormal[1] = (e3[2] * normal[0]) - (e3[0] * normal[2]);
edgeNormal[2] = (e3[0] * normal[1]) - (e3[1] * normal[0]);

// Calculate the determinant to see if it is on the inside, outside, or directly on the edge.
temp[0] = Q[0] - v2[0];
temp[1] = Q[1] - v2[1];
temp[2] = Q[2] - v2[2];

determinant = ((edgeNormal[0] * temp[0]) + (edgeNormal[1] * temp[1]) + (edgeNormal[2] * temp[2]));

// Check if it is outside.
if(determinant > 0.001f)
{
return false;
}

// Now we have our height.
height = Q[1];

return true;
}

  最后我们在 ZoneClass 里,每一帧设置摄像机的位置:

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
bool ZoneClass::Frame(D3DClass* Direct3D, InputClass* Input, ShaderManagerClass* ShaderManager , TextureManagerClass* TextureManager, float frameTime, int fps)
{
bool result;
float posX, posY, posZ, rotX, rotY, rotZ , height;
bool foundHeight;
static float prevHeight = 0;

// Do the frame input processing.
HandleMovementInput(Input, frameTime);

// Get the view point position/rotation.
m_Position->GetPosition(posX, posY, posZ);
m_Position->GetRotation(rotX, rotY, rotZ);

// Do the frame processing for the user interface.
result = m_UserInterface->Frame(Direct3D->GetDeviceContext(), fps, posX, posY, posZ, rotX, rotY, rotZ);
if(!result)
{
return false;
}

// Do the terrain frame processing.
m_Terrain->Frame();

// If the height is locked to the terrain then position the camera on top of it.
if(m_heightLocked)
{
posY -= prevHeight;
// Get the height of the triangle that is directly underneath the given camera position.
foundHeight = m_Terrain->GetHeightAtPosition(posX, posZ, height);
if(foundHeight)
{
// If there was a triangle under the camera then position the camera just above it by three meters.
m_Position->SetPosition(posX, height + posY, posZ);
m_Camera->SetPosition(posX, height + posY, posZ);
prevHeight = height;
}
}

// Render the graphics.
result = Render(Direct3D, ShaderManager , TextureManager);
if(!result)
{
return false;
}

return true;
}

  最终效果:

1

  源代码:DX11TerrainTutorial-HeightBasedMovement

0%