用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(); ListVector3 vertices new ListVector3(); Listint triangles new Listint(); // 生成顶点 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 : current1); triangles.Add(next); triangles.Add(next); triangles.Add((j sides-1) ? current-j : current1); triangles.Add((j sides-1) ? next-j : next1); } } 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(ListMesh 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, Funcfloat, 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; }使用这个通用生成器只需传入不同的方程就能创造各种曲面// 克莱因瓶 Funcfloat, 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 GetComponentMeshFilter().mesh; deformedMesh Instantiate(originalMesh); GetComponentMeshFilter().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(); } } }这种技术可以用于创建可塑材料、交互式地形等动态效果。
别再只会用建模软件了!手把手教你用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(); ListVector3 vertices new ListVector3(); Listint triangles new Listint(); // 生成顶点 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 : current1); triangles.Add(next); triangles.Add(next); triangles.Add((j sides-1) ? current-j : current1); triangles.Add((j sides-1) ? next-j : next1); } } 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(ListMesh 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, Funcfloat, 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; }使用这个通用生成器只需传入不同的方程就能创造各种曲面// 克莱因瓶 Funcfloat, 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 GetComponentMeshFilter().mesh; deformedMesh Instantiate(originalMesh); GetComponentMeshFilter().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(); } } }这种技术可以用于创建可塑材料、交互式地形等动态效果。