使用JobSystem优化阶梯地形生成算法
缘由
几年前玩过的一款游戏
,其中的实时可交互的地形算法让我印象十分深刻,
恰巧前一段时间看到了介绍该算法的博客.
然后根据文章中的算法实现了一波,效果如下:
原理
原始算法大概流程:
- 基于Height Map 生成Mesh
- 遍历Mesh所有三角形
- 将遍历到的三角形,按照Meandering Triangless算法进行基于高度分层切割
- 将切割出来的新三角形添加到Mesh
上述算法涉及到大量计算,所有很自然的想到用多线程来优化性能,在Unity中自然就是JobSystem。
基于JobSystem的阶梯地形算法流程:
- 构造数据对象
public struct QuadVertices { public int a; public int b; public int c; public int d; } public struct QuadFace { // vertex public Vector3 a; public Vector3 b; public Vector3 c; public Vector3 d; // uv public Vector2 u1; public Vector2 u2; public Vector2 u3; public Vector2 u4; // vertex color public Color32 c1; public Color32 c2; public Color32 c3; public Color32 c4; }
- 从Height Map计算获得
QuadVertices[], 其中存储了所有相邻四个顶点组成的2个三角形Mesh数据的顶点信息 - 遍历所有
QuadVertices[] - 对每个
QuadVertices做如下计算,其中ProcessTriangle也是针对单个三角形使用Meandering Triangles 算法
var quad = quads[index]; var a = vertices[quad.a]; var b = vertices[quad.b]; var c = vertices[quad.c]; var d = vertices[quad.d]; if (h_max - h_min < 1) { #if !DISABLE_TERRACE_MESH_WITH_COLOR var color_c = colors[Mathf.Abs(h_min + 10) % colors.Length]; #endif quadFaces.AddNoResize(new QuadFace() { a = new Vector3(a.x, h_min, a.z), b = new Vector3(b.x, h_min, b.z), c = new Vector3(c.x, h_min, c.z), d = new Vector3(d.x, h_min, d.z), c1 = color_c, c2 = color_c, c3 = color_c, c4 = color_c, u1 = u1_c, u2 = u2_c, u3 = u3_c, u4 = u4_c }); return; } // 处理 1/2/3 序号顶点组成的三角形 ProcessTriangle(a, b, c); // 处理 1/2/4 序号顶点组成的三角形 ProcessTriangle(a, c, d);
- 根据
NativeList<QuadFace>中的数据构造成Mesh
var q = rawMeshData[i]; var i4 = 4 * i; var i6 = 6 * i; vertices[i4 + 0] = q.a; vertices[i4 + 1] = q.b; vertices[i4 + 2] = q.c; vertices[i4 + 3] = q.d; uvs[i4 + 0] = q.u1; uvs[i4 + 1] = q.u2; uvs[i4 + 2] = q.u3; uvs[i4 + 3] = q.u4; colors[i4 + 0] = q.c1; colors[i4 + 1] = q.c2; colors[i4 + 2] = q.c3; colors[i4 + 3] = q.c4; triangles[i6 + 0] = i4; triangles[i6 + 1] = i4 + 1; triangles[i6 + 2] = i4 + 2; triangles[i6 + 3] = i4; triangles[i6 + 4] = i4 + 2; triangles[i6 + 5] = i4 + 3;
其他
- 还可以通过分区的方式来降低运算量,具体可参考:https://github.com/mshandle/Terraced-Terrain
- 基于四边形的算法在同一个QuadFace内,只有4个顶点,而三角形则有6个顶点,这也是个极大优势
- 因为
Meandering Triangles算法的特点,在计算的过程中会生成新的三角形数据,所以这里数据的长度未知就不能用NativeArray, 而需要使用NativeList
// 声明
[WriteOnly] public NativeList<QuadFace>.ParallelWriter quadFaces;
// 初始化,20000为List的最大长度
quadFaces = new NativeList<QuadFace>(20000, Allocator.Persistent);
// 传递数据到IJob
quadFaces = data.quadFaces.AsParallelWriter(),