1. 简介
1. 基础理论
- 模型是由点和面构成的,每三个点构成一个三角面。
- 根据三个顶点排列的顺序,根据左手定则决定三角面的正反面
- 三角形序列是一组int数组,数组元素数量为3的倍数,表示三角形的顶点 对应的顶点ID
- Unity想要显示一个模型,必须在对象上挂载组件Mesh Filter(网格过滤器)与Mesh Renderer(网格渲染器)
2. Mesh构成
- Vertex
- Normal
- UV
- Trangle(三角形序列)
2. 基本函数
1. 使用代码创建Mesh模型
1. Mesh类
- Unity允许使用Mesh类创建模型,并将创建的Mesh赋值给MeshFilter。
- 模型顶点列表 记录了所有顶点的位置信息;
三角形序列 决定了哪三个顶点会构成一个三角面。以左手定则决定面的正方向;
UV坐标列表 决定了每个顶点对应UV纹理坐标的具体位置。三角面之间的UV值将通过三个顶点的UV值,以插值的方式确定面每个位置的的UV值;
Mesh参数 | 描述 |
---|---|
name | 模型名称 |
vertices | 模型顶点列表 |
triangles | 三角形序列(序列数量必须为3的倍数) |
uv | UV坐标列表 |
normals | 法线列表 |
tangents | 切线列表 |
Mesh函数 | 描述 |
---|---|
RecalculateNormals() | 自动计算法线方向 |
RecalculateTangents() | 自动计算切线方向 |
RecalculateBounds() | 重新计算模型边缘界限 |
2. 示例
一个正方形
MeshFilter filter;
void Start()
{
filter = GetComponent<MeshFilter>();
Mesh mesh = new Mesh();
mesh.name = "Mesh_01";
mesh.vertices = GetVertexs();
mesh.triangles = GetTriangles();
mesh.uv = GetUVs();
mesh.normals = GetNormals();
mesh.tangents = GetTangent();
filter.mesh = mesh;
}
// 设定顶点位置
Vector3[] GetVertexs()
{
return new Vector3[]
{
new Vector3(0, 0, 0),
new Vector3(1, 0, 0),
new Vector3(0, 1, 0),
new Vector3(1, 1, 0),
};
}
// 设定三角形序列
int[] GetTriangles()
{
return new int[]
{
0, 1, 2, 1, 3, 2
};
}
// 设定UV
Vector2[] GetUVs()
{
return new Vector2[]
{
new Vector2(1, 0),
new Vector2(0, 0),
new Vector2(1, 1),
new Vector2(0, 1),
};
}
// 设定顶点位置
Vector3[] GetNormals()
{
return new Vector3[]
{
new Vector3(0, 0, 1),
new Vector3(0, 0, 1),
new Vector3(0, 0, 1),
new Vector3(0, 0, 1),
};
}
// 设定顶点位置
Vector4[] GetTangent()
{
return new Vector4[]
{
new Vector4(-1, 0, 0, -1),
new Vector4(-1, 0, 0, -1),
new Vector4(-1, 0, 0, -1),
new Vector4(-1, 0, 0, -1),
};
}
动态创建指定尺寸的矩形
public int width, height;
MeshFilter filter;
private void OnDrawGizmos()
{
Start();
}
void Start()
{
filter = GetComponent<MeshFilter>();
Mesh mesh = new Mesh();
DrawMesh(mesh);
filter.mesh = mesh;
}
void DrawMesh(Mesh mesh)
{
mesh.name = "Mesh_02";
width = width > 0 ? width : 0;
height = height > 0 ? height : 0;
// 顶点列表
Vector3[] vertices = new Vector3[(width + 1) * (height + 1)];
Vector2[] uvs = new Vector2[(width + 1) * (height + 1)];
int[] triangles = new int[width * height * 2 * 3];
for (int y = 0; y < height + 1; y++)
{
for (int x = 0; x < width + 1; x++)
{
vertices[x + y * (width + 1)] = new Vector3(x, y, 0);
uvs[x + y * (width + 1)] = new Vector2((float)x / width, (float)y / height);
}
}
// 开始坐标依次递增,内层循环在最后额外让开始坐标+1.从而避开了结尾点
for (int y = 0, trianglesIndex = 0, startIndex = 0; y < height; y++, startIndex++)
{
for (int x = 0; x < width; x++, startIndex++, trianglesIndex += 6)
{
triangles[trianglesIndex] = startIndex + width + 1;
triangles[trianglesIndex + 1] = startIndex + 1;
triangles[trianglesIndex + 2] = startIndex;
triangles[trianglesIndex + 3] = startIndex + width + 1;
triangles[trianglesIndex + 4] = startIndex + width + 2;
triangles[trianglesIndex + 5] = startIndex + 1;
}
}
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.uv = uvs;
}
创建正方体
void DrawMesh(Mesh mesh)
{
mesh.name = "Mesh_03";
Vector3[] vertices = new Vector3[8]
{
new Vector3(-0.5f, -0.5f, -0.5f),
new Vector3(0.5f, -0.5f, -0.5f),
new Vector3(-0.5f, 0.5f, -0.5f),
new Vector3(0.5f, 0.5f, -0.5f),
new Vector3(-0.5f, -0.5f, 0.5f),
new Vector3(0.5f, -0.5f, 0.5f),
new Vector3(-0.5f, 0.5f, 0.5f),
new Vector3(0.5f, 0.5f, 0.5f),
};
int[] triangles = new int[]
{
0, 3, 1,
0, 2, 3,
2, 6, 7,
2, 7, 3,
3, 7, 5,
3, 5, 1,
0, 4, 6,
0, 6, 2,
1, 5, 4,
1, 4, 0,
6, 4, 5,
6, 5, 7
};
foreach (var vertex in vertices) Gizmos.DrawSphere(vertex + transform.position, 0.15f);
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.RecalculateNormals();
mesh.RecalculateTangents();
mesh.RecalculateBounds();
}
创建圆角正方体,胶囊体,球体
public int XGridCount, YGridCount, ZGridCount;
public float XSize, YSize, ZSize, Radius;
public bool debug;
MeshFilter filter;
/// <summary>
/// 顶点数量
/// </summary>
int VertexCount
{
get
{
// 顶角顶点数量
int connerVertexCount = 8;
// 边顶点数量
int edgeVertexCount = (XGridCount + YGridCount + ZGridCount - 3) * 4;
// 面顶点数量
int faceVertexCount = (XGridCount - 1) * (YGridCount - 1) * 2 + (XGridCount - 1) * (ZGridCount - 1) * 2 + (ZGridCount - 1) * (YGridCount - 1) * 2;
return connerVertexCount + edgeVertexCount + faceVertexCount;
}
}
/// <summary>
/// 三角形序列数量
/// </summary>
int TriangeCount
{
get
{
return XGridCount * YGridCount * 4 + XGridCount * ZGridCount * 4 + ZGridCount * YGridCount * 4;
}
}
int FloorCount
{
get
{
return (XGridCount + ZGridCount) * 2;
}
}
private void OnDrawGizmos()
{
Start();
}
void Start()
{
filter = GetComponent<MeshFilter>();
Mesh mesh = new Mesh();
DrawMesh(mesh);
filter.mesh = mesh;
}
void DrawMesh(Mesh mesh)
{
mesh.name = "Mesh_04";
XGridCount = XGridCount >= 1 ? XGridCount : 1;
YGridCount = YGridCount >= 1 ? YGridCount : 1;
ZGridCount = ZGridCount >= 1 ? ZGridCount : 1;
XSize = XSize >= 1 ? XSize : 1;
YSize = YSize >= 1 ? YSize : 1;
ZSize = ZSize >= 1 ? ZSize : 1;
Radius = Radius < XSize / 2 ? Radius : XSize / 2;
Radius = Radius < YSize / 2 ? Radius : YSize / 2;
Radius = Radius < ZSize / 2 ? Radius : ZSize / 2;
SetVertex(mesh);
SetNormal(mesh);
SetTriange(mesh);
}
void SetVertex(Mesh mesh)
{
Vector3[] vertices = new Vector3[VertexCount];
int index = 0;
for (int y = 0; y < YGridCount + 1; y++)
{
for (int i = 0; i < XGridCount; i++, index++)
{
vertices[index] = new Vector3((float)i / XGridCount * XSize, (float)y / YGridCount * YSize, 0);
}
for (int i = 0; i < ZGridCount; i++, index++)
{
vertices[index] = new Vector3(XSize, (float)y / YGridCount * YSize, (float)i / ZGridCount * ZSize);
}
for (int i = XGridCount; i > 0; i--, index++)
{
vertices[index] = new Vector3((float)i / XGridCount * XSize, (float)y / YGridCount * YSize, ZSize);
}
for (int i = ZGridCount; i > 0; i--, index++)
{
vertices[index] = new Vector3(0, (float)y / YGridCount * YSize, (float)i / ZGridCount * ZSize);
}
}
for (int z = 1; z < ZGridCount; z++)
{
for (int x = 1; x < XGridCount; x++, index++)
{
vertices[index] = new Vector3((float)x / XGridCount * XSize, ZSize, (float)z / ZGridCount * ZSize);
}
}
for (int z = 1; z < ZGridCount; z++)
{
for (int x = 1; x < XGridCount; x++, index++)
{
vertices[index] = new Vector3((float)x / XGridCount * XSize, 0, (float)z / ZGridCount * ZSize);
}
}
mesh.vertices = vertices;
}
void SetTriange(Mesh mesh)
{
int[] triangles = new int[TriangeCount * 3];
int index = 0;
// 生成边缘一圈
for (int h = 0; h < YGridCount; h++)
{
for (int i = 0; i < FloorCount - 1; i++, index++)
{
SetQuad(triangles, index * 6, index, index + 1, index + FloorCount, index + FloorCount + 1);
}
SetQuad(triangles, index * 6, index, h * FloorCount, index++ + FloorCount, (h + 1) * FloorCount);
}
// 生成顶面左下边
for (int x = 0; x < XGridCount - 1; x++, index++)
{
SetQuad(triangles, index * 6, index, index + 1, index + FloorCount - 1, index + FloorCount);
}
// 生成顶面右下角
SetQuad(triangles, index * 6, index, index + 1, index + FloorCount - 1, index++ + 2);
// 第二行第一个面的v00
int topMin = FloorCount * (YGridCount + 1) - 1;
// 生成左右侧边
for (int i = 0; i < ZGridCount - 2; i++, index++)
{
SetQuad(triangles, index * 6, topMin - i, topMin + 1 + i * (XGridCount - 1), topMin - 1 - i, topMin + 1 + (i + 1) * (XGridCount - 1));
}
for (int i = 0; i < ZGridCount - 2; i++, index++)
{
SetQuad(triangles, index * 6, topMin + (XGridCount - 1) * (i + 1), topMin - FloorCount + XGridCount + 2 + i, topMin + (XGridCount - 1) * (i + 2), topMin - FloorCount + XGridCount + 3 + i);
}
for (int z = 0; z < ZGridCount - 2; z++)
{
for (int x = 0; x < XGridCount - 2; x++, index++)
{
SetQuad(triangles, index * 6,
topMin + x + z * (XGridCount - 1) + 1,
topMin + x + z * (XGridCount - 1) + 2,
topMin + x + z * (XGridCount - 1) + XGridCount,
topMin + x + z * (XGridCount - 1) + XGridCount + 1);
}
}
SetQuad(triangles, index++ * 6, topMin - ZGridCount + 2, topMin + (XGridCount - 1) * (ZGridCount - 2) + 1, topMin - ZGridCount + 1, topMin - ZGridCount);
for (int i = 0; i < XGridCount - 2; i++, index++)
{
SetQuad(triangles, index * 6, topMin + (XGridCount - 1) * (ZGridCount - 2) + 1 + i, topMin + (XGridCount - 1) * (ZGridCount - 2) + 2 + i, topMin - ZGridCount - i, topMin - ZGridCount - 1 - i);
}
SetQuad(triangles, index++ * 6, topMin + (XGridCount - 1) * (ZGridCount - 1), topMin - (ZGridCount - 1) - (XGridCount - 1) - 2, topMin - (ZGridCount - 1) - (XGridCount - 1), topMin - (ZGridCount - 1) - (XGridCount - 1) - 1);
// 地面中间第一个点
int btmMin = topMin + (XGridCount - 1) * (ZGridCount - 1) + 1;
SetQuad(triangles, index++ * 6, FloorCount - 1, btmMin, 0, 1);
for (int i = 0; i < XGridCount - 2; i++, index++)
{
SetQuad(triangles, index * 6, btmMin + i, btmMin + 1 + i, 1 + i, 2 + i);
}
SetQuad(triangles, index++ * 6, btmMin + XGridCount - 2, XGridCount + 1, XGridCount - 1, XGridCount);
for (int i = 0; i < ZGridCount - 2; i++, index++)
{
SetQuad(triangles, index * 6, FloorCount - 2 - i, btmMin + (XGridCount - 1) * (i + 1), FloorCount - 1 - i, btmMin + (XGridCount - 1) * i);
}
for (int i = 0; i < ZGridCount - 2; i++, index++)
{
SetQuad(triangles, index * 6, btmMin + (XGridCount - 1) * (2 + i) - 1, XGridCount + 2 + i, btmMin + (XGridCount - 1) * (1 + i) - 1, XGridCount + 1 + i);
}
for (int z = 0; z < ZGridCount - 2; z++)
{
for (int x = 0; x < XGridCount - 2; x++, index++)
{
SetQuad(triangles, index * 6,
btmMin + x + (z + 1) * (XGridCount - 1),
btmMin + x + (z + 1) * (XGridCount - 1) + 1,
btmMin + x + z * (XGridCount - 1),
btmMin + x + z * (XGridCount - 1) + 1);
}
}
SetQuad(triangles, index++ * 6, FloorCount - ZGridCount, FloorCount - ZGridCount - 1, FloorCount - ZGridCount + 1, btmMin + (XGridCount - 1) * (ZGridCount - 2));
for (int i = 0; i < XGridCount - 2; i++, index++)
{
SetQuad(triangles, index * 6, FloorCount - ZGridCount - i - 1, FloorCount - ZGridCount - i - 2, btmMin + (XGridCount - 1) * (ZGridCount - 2) + i, btmMin + (XGridCount - 1) * (ZGridCount - 2) + i + 1);
}
SetQuad(triangles, index++ * 6, ZGridCount + XGridCount + 1, ZGridCount + XGridCount, btmMin + (XGridCount - 1) * (ZGridCount - 1) - 1, ZGridCount + XGridCount - 1);
mesh.triangles = triangles;
}
void SetNormal(Mesh mesh)
{
Vector3[] normals = new Vector3[VertexCount];
Vector3[] vertices = mesh.vertices;
for (int i = 0; i < vertices.Length; i++)
{
SetNormalPoint(vertices, normals, i);
if (debug) Gizmos.DrawSphere(transform.position + vertices[i], .07f);
}
mesh.normals = normals;
mesh.vertices = vertices;
}
/// <summary>
/// 构建圆角四边形
/// </summary>
void SetNormalPoint(Vector3[] vertices, Vector3[] normals, int i)
{
var vertex = vertices[i];
// 圆心
var inner = vertex;
if (vertex.x < Radius)
{
inner.x = Radius;
}
else if (vertex.x > XSize - Radius)
{
inner.x = XSize - Radius;
}
if (vertex.y < Radius)
{
inner.y = Radius;
}
else if (vertex.y > YSize - Radius)
{
inner.y = YSize - Radius;
}
if (vertex.z < Radius)
{
inner.z = Radius;
}
else if (vertex.z > ZSize - Radius)
{
inner.z = ZSize - Radius;
}
normals[i] = (vertex - inner).normalized;
vertices[i] = normals[i] * Radius + inner;
}
/// <summary>
/// 设置面片
/// </summary>
void SetQuad(int[] triangles, int i, int v00, int v10, int v01, int v11)
{
triangles[i] = v00;
triangles[i + 1] = v01;
triangles[i + 2] = v11;
triangles[i + 3] = v00;
triangles[i + 4] = v11;
triangles[i + 5] = v10;
}