第05章:几何与网格建模
Ara3D.Geometry 是 SDK 的几何内核:网格类型、拓扑、程序化建模、空间查询、包围体与导出。它的一个核心哲学是不可变(immutable)——所有变换都返回新的几何对象,不做原地修改。本章系统讲解网格类型、变换、程序化生成与 AABB 空间查询。
1. 网格类型体系
几何类型都是 [StructLayout(Pack=1)] 的值类型 struct(由 Plato 生成,落在 Ara3D.Geometry 命名空间),用 IReadOnlyList<Point3D> 存点,用整数索引存面。
1.1 TriangleMesh3D——三角网格
public partial struct TriangleMesh3D : IIndexedGeometry3D<TriangleMesh3D, Integer3>
{
public readonly IReadOnlyList<Point3D> Points;
public readonly IReadOnlyList<Integer3> FaceIndices; // 每个 Integer3 = (A,B,C) 顶点索引
public TriangleMesh3D(IReadOnlyList<Point3D> points, IReadOnlyList<Integer3> faceIndices);
public static TriangleMesh3D Create(IReadOnlyList<Point3D> points, IReadOnlyList<Integer3> faceIndices);
public Bounds3D Bounds { get; }
// 变换(不可变,返回新网格)
public TriangleMesh3D Deform(Func<Point3D, Point3D> f);
public TriangleMesh3D Transform(Transform3D t);
public TriangleMesh3D Scale(Vector3 v);
public TriangleMesh3D Scale(Number s);
public TriangleMesh3D Rotate(Quaternion q);
public TriangleMesh3D RotateX(Angle a); // 以及 RotateY / RotateZ
public TriangleMesh3D Translate(Vector3 v);
public TriangleMesh3D TranslateX(Number s); // 以及 TranslateY / TranslateZ
// 派生数据
public IReadOnlyList<Triangle3D> Triangles { get; }
public Triangle3D Triangle(Integer3 index);
}
关键点:所有变换都是不可变的——它们返回一个新的 struct,绝不原地修改。这让几何管线天然可组合、可并行、易推理。
1.2 其他网格类型
// 四边形网格
public partial struct QuadMesh3D : IIndexedGeometry3D<QuadMesh3D, Integer4>
{
public readonly IReadOnlyList<Point3D> Points;
public readonly IReadOnlyList<Integer4> FaceIndices; // (A,B,C,D)
}
// 线网格(每个面是一对索引)
public partial struct LineMesh3D : IIndexedGeometry3D<LineMesh3D, Integer2>
{
public readonly IReadOnlyList<Point3D> Points;
public readonly IReadOnlyList<Integer2> FaceIndices; // 索引对
}
// 参数化二维网格(行×列点阵)
public partial struct QuadGrid3D
{
public IReadOnlyList2D<Point3D> Points { get; } // 二维点阵
public bool ConnectU { get; } // U 方向闭合(圆柱)
public bool ConnectV { get; } // V 方向闭合(环面)
public IReadOnlyList<Integer4> FaceIndices { get; }
}
// 带逐顶点颜色的三角网格
public partial struct ColoredTriangleMesh3D
{
public readonly TriangleMesh3D Mesh;
public readonly IReadOnlyList<Vector3> VertexColors;
}
QuadGrid3D 是程序化建模的“中间产物”:先构造一张参数化网格,再 Triangulate() 成 TriangleMesh3D。ConnectU/ConnectV 控制是否首尾闭合,从而区分平面、圆柱、环面等拓扑。
1.3 几何接口
public interface IPointGeometry3D<TSelf> { IReadOnlyList<Point3D> Points { get; } }
public interface IIndexedGeometry3D<TSelf, TFace> : IPointGeometry3D<TSelf>
{ IReadOnlyList<TFace> FaceIndices { get; } }
public interface ITransformable3D<TSelf> { TSelf Transform(Transform3D t); }
public interface IDeformable3D<TSelf> { TSelf Deform(Func<Point3D, Point3D> f); }
Transform 是刚性/仿射变换,Deform 是逐点任意变形——后者是实现扭曲、球化、噪声等效果的通用入口。
2. 变换与变形
2.1 变换类型
库把变换细分为多种语义类型:
public struct Transform3D // 基础变换(包一个 Matrix4x4)
public struct Translation3D // : 纯平移
public struct Rotation3D // : 基于四元数的旋转
public struct Scaling3D // : 非均匀缩放
所有网格类型都实现 Transform(Transform3D t) 与 Deform(Func<Point3D,Point3D> f)。
2.2 链式不可变管线
var mesh = PlatonicSolids.TriangulatedCube
.Scale(new Vector3(2, 1, 1))
.RotateY(Angle.Degrees(45))
.Translate(new Vector3(0, 0, 5));
2.3 用 Deform 做自定义变形
Deform 接收“旧点 → 新点”的函数,配合 InverseLerp(把点映射到包围盒内的 [0,1] 参数)可以做出各种效果。例如“扭曲变形器”(来自官方示例):
var bounds = mesh.Bounds;
var twisted = mesh.Deform(p =>
{
var v = p.InverseLerp(bounds); // 归一化到 [0,1]^3
var amount = v.Z; // 沿 Z 的参数
var axisAngle = new AxisAngle(Vector3.UnitZ, (amount * revolutions).Turns);
return p.Transform(axisAngle); // 越往上扭得越多
});
3. 程序化几何构造
SurfaceConstructors(Ara3D.Geometry/SurfaceConstructors.cs)提供了一组返回不可变几何的程序化操作:
// 拉伸(沿向量把折线扫出面)
public static QuadGrid3D Extrude(this IReadOnlyList<Point3D> points, Vector3 vector, int count = 1);
public static QuadGrid3D Extrude(this IReadOnlyList<Point2D> points, Number height, int count = 1);
// 旋转成型(绕轴旋转,如车床/lathe)
public static QuadGrid3D Revolve(this IReadOnlyList<Point3D> points, Vector3 axis, int count);
// 扫掠(把剖面沿一串变换扫出)
public static QuadGrid3D Sweep(this IReadOnlyList<Point3D> points,
IReadOnlyList<Transform3D> transforms, bool connectU, bool connectV);
// 转换/三角化
public static QuadMesh3D ToQuadMesh3D(this QuadGrid3D grid);
public static TriangleMesh3D Triangulate(this QuadGrid3D grid);
// 把线网格拉伸成四边网格面
public static QuadMesh3D Extrude(this LineMesh3D lineMesh, Vector3 direction, int count);
// 合并多张网格
public static QuadMesh3D ToQuadMesh(this IEnumerable<QuadGrid3D> grids);
示例——用旋转成型构造一个圆柱:
// 在 XZ 平面上采样一个圆作为剖面
var profile = 32.Select(i =>
{
var a = Angle.FractionOfTurn(i, 32); // 第 i/32 圈
return new Point3D(a.Cos, 0, a.Sin);
});
// 绕 Y 轴旋转成型
var cylinderGrid = profile.Revolve(Vector3.UnitY, 32);
// 三角化得到可渲染网格
TriangleMesh3D cylinder = cylinderGrid.Triangulate();
3.1 基本体与曲线(示例项目所用)
官方示例项目 examples/Ara3D.Studio.Examples 大量使用一批高层辅助(随几何库提供,实际名称以你的版本为准):
PlatonicSolids.TriangulatedCube、PlatonicSolids.Icosahedron等——现成的柏拉图立体;Primitives.Cylinder(sides, radius, height, segments)——直接生成参数化圆柱;Curves.Circle、Curves.Helix(height, revolutions)——参数化曲线,.Sample(n)采样点、.GetTransforms(n)生成沿路径的坐标系(frames);Sweep——把剖面沿曲线坐标系扫掠成管、螺旋等。
示例——沿螺旋线扫掠圆形剖面(来自 SweepDemo):
var profile = Curves.Circle.RotateX(0.25f.Turns());
var path = Curves.Helix(height: 3, revolutions: 3);
var profilePoints = profile.Sample(16);
var pathFrames = path.GetTransforms(16);
QuadGrid3D tube = profilePoints.Sweep(pathFrames, connectU: true, connectV: false);
3.2 函数式二维网格
FunctionalReadOnlyList2D<T> 用 lambda 直接定义点阵而不预分配数组:
var points = new FunctionalReadOnlyList2D<Point3D>(
cols + 1, rows + 1,
(i, j) => new Point3D(i / (float)cols, j / (float)rows, 0));
var grid = new QuadGrid3D(points, ConnectU: false, ConnectV: false);
这在生成大规模参数曲面(如波浪面、地形)时非常省内存。
4. 几何工具方法
GeometryUtil 提供了一批实用扩展:
public static class GeometryUtil
{
// 集合包围盒
public static Bounds3D Bounds(this IEnumerable<TriangleMesh3D> meshes);
public static Bounds3D Bounds(this IEnumerable<QuadMesh3D> meshes);
// 向量批处理
public static IReadOnlyList<Vector3> Normalize(this IReadOnlyList<Vector3> vectors);
public static IReadOnlyList<Vector3> Rotate(this IReadOnlyList<Vector3> self, Vector3 axis, float angle);
public static IReadOnlyList<Vector3> Transform(this IReadOnlyList<Vector3> self, Matrix4x4 matrix);
// 2D 线-线相交
public static bool Intersection(this Line2D l1, Line2D l2, out Vector2 point, float epsilon = 1e-6f);
// 插值辅助
public static Number Unlerp(this Number value, Number min, Number max);
public static Vector3 InverseLerp(this Point3D point, Bounds3D bounds);
public static Color Lerp(this Color c0, Color c1, float amount);
}
5. 包围体 Bounds3D
Bounds3D 是轴对齐包围盒(AABB):
public partial struct Bounds3D
{
public readonly Point3D Min;
public readonly Point3D Max;
public Vector3 Size { get; }
public Point3D Center { get; }
public static Bounds3D Empty { get; }
public Bounds3D Include(Point3D p);
public Bounds3D Include(Bounds3D other);
public Bounds3D Transform(Matrix4x4 m);
}
Bounds3D.Empty 作为起点,逐步 Include 点或子包围盒即可增量求整体包围盒。
6. AABB 树——空间查询
对于“射线求交、范围查询、最近邻”等空间查询,几何库提供了 AabbTree(Ara3D.Geometry/AABBTree.cs):
public interface IAabbTreeQuery
{
bool ShouldVisit(in Bounds3D boundsA, out float priority);
bool Visit(int index, in Bounds3D bounds);
bool ShouldStop { get; }
}
public sealed class AabbTree
{
public AabbTree(IReadOnlyList<Bounds3D> bounds, int maxItemsPerLeaf = 8);
public int Count { get; }
public Bounds3D TotalBounds { get; } // 根包围盒
// 泛型遍历:TQuery 必须是实现 IAabbTreeQuery 的 struct
public bool Traverse<TQuery>(ref TQuery query) where TQuery : struct, IAabbTreeQuery;
public IReadOnlyList<Node> GetAllNodes();
public Bounds3D GetBounds(int index);
}
- 构建:沿中心点包围盒最长轴做递归中位数分割;
- 遍历:按优先级的广度优先,
ShouldStop可提前终止; - 性能技巧:查询以
struct实现IAabbTreeQuery并按ref传入Traverse,避免装箱与虚调用开销。
// 为每个网格算包围盒,建树
var meshBounds = model.Meshes.Select(m => m.Bounds).ToList();
var tree = new AabbTree(meshBounds, maxItemsPerLeaf: 8);
// 自定义查询:实现 IAabbTreeQuery 的 struct,再 tree.Traverse(ref query)
7. SDF/体素说明
README 的元包描述里提到 Ara3D.Geometry 含“SDFs、voxels”。在撰写本教程所依据的源码快照里,src/Ara3D.Geometry/ 尚未见到公开的 ISdf / Voxel 等类型(可能位于 WIP 分支或后续版本,或以更高层封装存在)。示例项目中确有 SDF/ 子目录演示相关思路。如果你的版本提供了这些能力,请以实际 API 为准。
8. 本章小结
- 几何类型是不可变值类型:
TriangleMesh3D、QuadMesh3D、LineMesh3D、QuadGrid3D、ColoredTriangleMesh3D; - 所有变换(
Scale/Rotate/Translate/Transform)返回新对象,Deform支持逐点自定义变形; - 程序化建模用
Extrude / Revolve / Sweep生成QuadGrid3D,再Triangulate(); - 高层辅助(
PlatonicSolids、Primitives、Curves、FunctionalReadOnlyList2D)让参数化建模非常简洁; Bounds3D提供 AABB,AabbTree提供高性能空间查询(struct 查询 + ref 遍历)。
下一章我们把网格组织成场景——Ara3D.Models 的 Model3D、实例化与 GPU 就绪的渲染缓冲区。