别再只会用建模软件了!手把手教你用C#脚本在Unity里“捏”出一个3D模型(附完整项目源码)
用C#脚本在Unity中创造程序化3D艺术:从数学公式到动态网格生成
在数字艺术与游戏开发领域,程序化建模正逐渐成为创作者们的新宠。与传统的Blender、Maya等建模工具不同,通过代码直接生成3D模型不仅能实现动态变化的效果,更能将数学之美转化为视觉奇观。本文将带您深入Unity的Mesh系统,探索如何用C#脚本从零构建复杂几何形态,让算法成为您的3D雕刻刀。
1. 理解Unity网格系统的核心架构
1.1 Mesh类:数字雕塑的骨架
Unity中的Mesh类就像3D模型的DNA,它通过几组关键数据定义了模型的形态:
public class Mesh {
public Vector3[] vertices; // 顶点坐标集合
public int[] triangles; // 三角形索引序列
public Vector3[] normals; // 每个顶点的法线方向
public Vector2[] uv; // 纹理映射坐标
public Color[] colors; // 顶点颜色数据(可选)
}
这些数据之间的关系可以用以下结构表示:
| 数据元素 | 作用 | 示例值 |
|---|---|---|
| vertices | 定义3D空间中的点 | Vector3(0,1,0) |
| triangles | 连接顶点形成面 | [0,1,2]表示三个顶点组成的三角形 |
| normals | 决定光照反射方向 | Vector3(0,0,1)表示Z轴正向 |
| uv | 控制纹理贴图映射 | Vector2(0.5,0.5)表示纹理中心 |
1.2 渲染管线中的关键组件
要让创建的网格真正显示在场景中,需要理解Unity的渲染组件协同工作方式:
- MeshFilter :存储网格数据容器
- MeshRenderer :负责将网格数据转化为屏幕像素
- Material :定义表面着色规则和外观特性
重要提示:修改vertices数组后必须调用RecalculateNormals()和RecalculateBounds(),否则可能导致光照异常或视锥体裁剪错误。
2. 从基础几何到复杂形态的构建方法
2.1 构建参数化基本几何体
让我们从创建一个可配置的圆环面开始,演示如何用数学参数控制形状:
Mesh CreateTorus(float radius, float thickness, int segments, int sides) {
Mesh mesh = new Mesh();
List<Vector3> vertices = new List<Vector3>();
List<int> triangles = new List<int>();
// 生成顶点
for (int i = 0; i < segments; i++) {
float segmentAngle = i * Mathf.PI * 2 / segments;
Vector3 segmentCenter = new Vector3(
Mathf.Cos(segmentAngle) * radius,
0,
Mathf.Sin(segmentAngle) * radius);
for (int j = 0; j < sides; j++) {
float sideAngle = j * Mathf.PI * 2 / sides;
Vector3 offset = new Vector3(
Mathf.Cos(sideAngle) * thickness,
Mathf.Sin(sideAngle) * thickness,
0);
vertices.Add(segmentCenter + offset);
}
}
// 连接三角形
for (int i = 0; i < segments; i++) {
for (int j = 0; j < sides; j++) {
int current = i * sides + j;
int next = current + sides;
if (next >= vertices.Count) next -= vertices.Count;
triangles.Add(current);
triangles.Add((j == sides-1) ? current-j : current+1);
triangles.Add(next);
triangles.Add(next);
triangles.Add((j == sides-1) ? current-j : current+1);
triangles.Add((j == sides-1) ? next-j : next+1);
}
}
mesh.vertices = vertices.ToArray();
mesh.triangles = triangles.ToArray();
mesh.RecalculateNormals();
return mesh;
}
通过调整radius(主半径)、thickness(截面半径)、segments(环段数)和sides(截面边数)参数,可以创建从光滑圆环到棱角分明的多边环等各种形态。
2.2 应用噪声算法创造有机形态
Perlin噪声是生成自然形态的利器,以下示例展示如何用噪声函数变形网格:
void ApplyNoiseDeformation(Mesh mesh, float noiseScale, float strength) {
Vector3[] vertices = mesh.vertices;
for (int i = 0; i < vertices.Length; i++) {
Vector3 vertex = vertices[i];
float noise = Mathf.PerlinNoise(
vertex.x * noiseScale,
vertex.z * noiseScale);
vertices[i] = vertex + Vector3.up * noise * strength;
}
mesh.vertices = vertices;
mesh.RecalculateNormals();
}
将此技术应用于基础球体网格,可以轻松创建出类似地形、云朵或生物表皮的有机形态。
3. 高级程序化建模技术实战
3.1 分形几何的递归生成
分形结构以其无限细节著称,以下代码展示如何递归生成分形四面体:
void GenerateFractalTetrahedron(Mesh mesh, int depth, Vector3 a, Vector3 b, Vector3 c, Vector3 d) {
if (depth <= 0) {
AddTetrahedron(mesh, a, b, c, d);
return;
}
Vector3 ab = (a + b) / 2;
Vector3 ac = (a + c) / 2;
Vector3 ad = (a + d) / 2;
Vector3 bc = (b + c) / 2;
Vector3 bd = (b + d) / 2;
Vector3 cd = (c + d) / 2;
GenerateFractalTetrahedron(mesh, depth-1, a, ab, ac, ad);
GenerateFractalTetrahedron(mesh, depth-1, ab, b, bc, bd);
GenerateFractalTetrahedron(mesh, depth-1, ac, bc, c, cd);
GenerateFractalTetrahedron(mesh, depth-1, ad, bd, cd, d);
}
每增加一级递归深度,几何复杂度呈指数增长,可以创造出令人惊叹的细节结构。
3.2 动态合批与性能优化
当场景中存在大量程序化生成的网格时,动态合批技术至关重要:
| 优化技术 | 实施方法 | 适用场景 |
|---|---|---|
| 静态合批 | 标记为Static | 不变的背景元素 |
| GPU Instancing | 使用相同材质 | 重复但位置不同的对象 |
| 自定义合批 | 合并顶点数据 | 需要动态变形的对象 |
实现自定义合批的示例代码:
Mesh CombineMeshes(List<Mesh> meshes) {
CombineInstance[] combine = new CombineInstance[meshes.Count];
for (int i = 0; i < meshes.Count; i++) {
combine[i].mesh = meshes[i];
combine[i].transform = Matrix4x4.identity;
}
Mesh finalMesh = new Mesh();
finalMesh.CombineMeshes(combine);
return finalMesh;
}
4. 从数学方程到视觉奇迹:创意编码实践
4.1 参数化曲面生成
许多迷人的3D形态都可以用数学方程描述。以下代码展示如何将参数方程转化为网格:
Mesh CreateParametricSurface(int uSteps, int vSteps, Func<float, float, Vector3> equation) {
Mesh mesh = new Mesh();
Vector3[] vertices = new Vector3[uSteps * vSteps];
Vector2[] uv = new Vector2[vertices.Length];
int[] triangles = new int[(uSteps-1) * (vSteps-1) * 6];
// 生成顶点
for (int u = 0; u < uSteps; u++) {
for (int v = 0; v < vSteps; v++) {
float uNorm = (float)u / (uSteps-1);
float vNorm = (float)v / (vSteps-1);
vertices[u * vSteps + v] = equation(uNorm, vNorm);
uv[u * vSteps + v] = new Vector2(uNorm, vNorm);
}
}
// 连接三角形
int triIndex = 0;
for (int u = 0; u < uSteps-1; u++) {
for (int v = 0; v < vSteps-1; v++) {
int current = u * vSteps + v;
int next = current + vSteps;
triangles[triIndex++] = current;
triangles[triIndex++] = current + 1;
triangles[triIndex++] = next;
triangles[triIndex++] = next;
triangles[triIndex++] = current + 1;
triangles[triIndex++] = next + 1;
}
}
mesh.vertices = vertices;
mesh.uv = uv;
mesh.triangles = triangles;
mesh.RecalculateNormals();
return mesh;
}
使用这个通用生成器,只需传入不同的方程就能创造各种曲面:
// 克莱因瓶
Func<float, float, Vector3> kleinBottle = (u, v) => {
u *= Mathf.PI * 2;
v *= Mathf.PI * 2;
float x = 3 * Mathf.Cos(u) * (1 + Mathf.Sin(u)) +
(2 * (1 - Mathf.Cos(u)/2)) * Mathf.Cos(u) * Mathf.Cos(v);
float y = -8 * Mathf.Sin(u) -
2 * (1 - Mathf.Cos(u)/2) * Mathf.Sin(u) * Mathf.Cos(v);
float z = 2 * (1 - Mathf.Cos(u)/2) * Mathf.Sin(v);
return new Vector3(x, y, z) * 0.1f;
};
4.2 实时变形与交互响应
程序化建模的强大之处在于可以实现实时动态变化。以下代码展示如何让网格对鼠标交互做出反应:
public class InteractiveMesh : MonoBehaviour {
private Mesh originalMesh;
private Mesh deformedMesh;
private Vector3[] originalVertices;
void Start() {
originalMesh = GetComponent<MeshFilter>().mesh;
deformedMesh = Instantiate(originalMesh);
GetComponent<MeshFilter>().mesh = deformedMesh;
originalVertices = originalMesh.vertices;
}
void Update() {
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit)) {
Vector3[] vertices = deformedMesh.vertices;
for (int i = 0; i < vertices.Length; i++) {
Vector3 vertex = originalVertices[i];
float distance = Vector3.Distance(
transform.TransformPoint(vertex),
hit.point);
if (distance < 2.0f) {
float falloff = 1 - (distance / 2.0f);
vertices[i] = vertex + hit.normal * falloff * 0.5f;
} else {
vertices[i] = originalVertices[i];
}
}
deformedMesh.vertices = vertices;
deformedMesh.RecalculateNormals();
}
}
}
这种技术可以用于创建可塑材料、交互式地形等动态效果。
更多推荐

所有评论(0)