3-14 Slope-Cliff类型三角形连接区域构建 底部区域

  在上一章中,我们创建了Slope-Cliff三角形连接区域的方法。在这里要注意,Slope-Cliff类型与SSF类型不同,因为我们不知道的left和right地图单元之间,高度差是等于1还是大于1的,但是我们能确定的一点是left与bottom地图单元之间的高度差为1。根据下图我们发现,不论right的高度如何变化,三角形连接区域的一条边始终是需要进行阶梯化的。所以我们就将Slope-Cliff的三角形连接区域分为两部分进行构建。这里先从一定会阶梯化的底部开始。

  对这部分进行构建,也有两种情况。第一种如下图所示,三角形的其中一条边与阶梯部分相连,被分成若干段。这条边上分段的顶点与三角形的另一个顶点相连,这样就完成了三角剖分。但是这样的构建方法会让三角形连接区域中,视觉效果比较差,例如三角形连接区域中,分段相同,但是一条边上断电稀疏,但是越靠近三角形的一个角的时候越密集。而且在之后进行上半段构建的时候,这个情况会被放大。

  那么解决这个问题的方法,就是在三角形的一条边上取一个点,所有阶梯化的端点,够与这个点相连接,这样既将三角形连接区域分为了两部分,又不会出现顶点向一处汇聚,显得太过拥挤。

  既然找出了一个比较合理的解决方案,那么我们就需要知道三角形上的这个点的具体位置怎么得出。这里是使用了bottom和right两个地图单元的高度差进行插值得到的。也就是最低的地图单元和最高的地图单元之间的高度差,在进行插值,计算出三角形上这个点及其对应的颜色。代码如下:

HexMesh.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
private void TriangulateCornerTerracesCliff(
Vector3 begin, HexCell beginCell,
Vector3 left, HexCell leftCell,
Vector3 right, HexCell rightCell)
{
//这里将Slope-Cliff类型的三角形连接区域拆分成两部分进行构建
//示意图 http://magi-melchiorl.gitee.io/pages/Pics/Hexmap/3-14-2.png
//即三角形一个边进行阶梯化,阶梯化后的端点,都与另一条边上的一点相连
//边上一点,是通过bottom与right高度差,在进行插值得到的
float b = 1f / (rightCell.Elevation - beginCell.Elevation);
Vector3 boundary = Vector3.Lerp(begin, right, b);
Color boundaryColor = Color.Lerp(beginCell.color, rightCell.color, b);
}

  计算出了三角形连接区域边上一点,那么我们来构建一个三角形,观察一下这个点是否正确。代码如下:

HexMesh.cs
1
2
3
4
5
6
7
8
9
10
11
private void TriangulateCornerTerracesCliff(
Vector3 begin, HexCell beginCell,
Vector3 left, HexCell leftCell,
Vector3 right, HexCell rightCell)
{


//测试通过插值找到的三角形上一点是否正确
AddTriangle(begin, left, boundary);
AddTriangleColor(beginCell.color, leftCell.color, boundaryColor);
}

  我们确定了三角形连接区域边上一点没有问题,那么接下来就要构建这个区域了。与之前构建SSF三角形连接区域类似,首先构建起始的一个三角面片。代码如下:

HexMesh.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private void TriangulateCornerTerracesCliff(
Vector3 begin, HexCell beginCell,
Vector3 left, HexCell leftCell,
Vector3 right, HexCell rightCell)
{


//与构建其他阶梯状区域类似,首先构建第一个三角面片
Vector3 v2 = HexMetrics.TerraceLerp(begin, left, 1);
Color c2 = HexMetrics.TerraceLerp(beginCell.color, leftCell.color, 1);

AddTriangle(begin, v2, boundary);
AddTriangleColor(beginCell.color, c2, boundaryColor);
}

  然后直接将剩余的区域直接构建为一个三角面片。代码如下:

HexMesh.cs
1
2
3
4
5
6
7
8
9
10
11
private void TriangulateCornerTerracesCliff(
Vector3 begin, HexCell beginCell,
Vector3 left, HexCell leftCell,
Vector3 right, HexCell rightCell)
{


//构建剩余区域
AddTriangle(v2, left, boundary);
AddTriangleColor(c2, leftCell.color, boundaryColor);
}

  接着我们循环构建中间部分的三角面片,代码如下:

HexMesh.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void TriangulateCornerTerracesCliff(
Vector3 begin, HexCell beginCell,
Vector3 left, HexCell leftCell,
Vector3 right, HexCell rightCell)
{


for (int i = 2; i < HexMetrics.terraceSteps; i++)
{
Vector3 v1 = v2;
Color c1 = c2;
v2 = HexMetrics.TerraceLerp(begin, left, i);
c2 = HexMetrics.TerraceLerp(beginCell.color, leftCell.color, i);
AddTriangle(v1, v2, boundary);
AddTriangleColor(c1, c2, boundaryColor);
}

//构建剩余区域
AddTriangle(v2, left, boundary);
AddTriangleColor(c2, leftCell.color, boundaryColor);
}

  这样,我们在三角形连接区域的一条边上,通过插值计算出了一个点,利用这个点将三角形连接区域拆分成了两个部分。并完成了三角形连接区域底部的构建工作。在下一章中,我们将完成顶部的构建。

为什么底部不能像SSF那样使用阶梯的方式构建,而使用三角形进行构建?
如果我们对这部分区域进行类似SSF那种阶梯状方式构建,而不是三角面片构建。这就需要分析计算得出的插值点。因为插值点是跟bottom与right的高度差有关,而两者的高度差并不固定,所以这可能导致每个阶梯的四边形,并不在一个平坦的平面上,也就是说一个四边形左右两侧的斜率不同,这样不仅看起来四边形会被拉伸,而且市局效果会很乱。

Github代码