用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的渲染组件协同工作方式:

  1. MeshFilter :存储网格数据容器
  2. MeshRenderer :负责将网格数据转化为屏幕像素
  3. 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();
        }
    }
}

这种技术可以用于创建可塑材料、交互式地形等动态效果。

更多推荐