
在第一章中我们创建了一个简单的3D场景并添加了基础模型。但在实际工业应用中仅靠立方体和球体是远远不够的。涡轮叶片、管道连接件、机械臂关节等复杂零件需要精确控制每个顶点的位置和每个面的颜色。这就引出了本章的核心工具——MeshBuilder。如果说BoxVisual3D是厨房里的速冻水饺那MeshBuilder就是面粉、擀面杖和馅料——它能让你从零开始和面揉团打造任何形状的模型。2.1 MeshBuilder——从零构建任意几何体2.1.1 为什么需要MeshBuilder回顾第一章的两种建模方式便捷类方式如BoxVisual3D代码简洁适合快速原型但无法自定义形状MeshBuilder方式手动定义顶点和三角形可以创建任意复杂的几何体MeshBuilder是HelixToolkit中最核心的几何构建工具它封装了复杂的三角剖分算法让你通过简单的API就能完成以下操作添加基本几何体立方体、球体、圆柱体等通过顶点和索引构建自定义形状自动生成法线用于光照计算自动进行三角剖分将多边形分解为三角形2.1.2 核心概念顶点与三角形索引任何3D模型本质上都是由三角形组成的网格。为什么是三角形因为三个点确定一个平面这是渲染器能够处理的最基本单位。text顶点 (Vertices) 三角形索引 (TriangleIndices) v0 (0,1,0) Face 1: [0,1,2] / \ Face 2: [0,2,3] / \ v1(0,0,0)---v2(1,0,0) 每个面由3个顶点索引组成 | v3(1,0,-1)关键数据结构属性类型说明PositionsListPoint3D所有顶点的三维坐标TriangleIndicesListint每个三角形的三个顶点索引NormalsListVector3D每个顶点的法线方向影响光照效果TextureCoordinatesListPoint纹理映射的UV坐标2.1.3 MeshBuilder的基本用法示例手动构建一个金字塔csharpusing HelixToolkit.Wpf; using System.Windows.Media.Media3D; private MeshGeometryModel3D CreatePyramid() { var builder new MeshBuilder(false, false); // 定义金字塔的5个顶点 Point3D top new Point3D(0, 1, 0); // 顶点 Point3D front new Point3D(0, 0, 0.5); // 正面底部中点 Point3D back new Point3D(0, 0, -0.5); // 背面底部中点 Point3D left new Point3D(-0.5, 0, 0); // 左侧底部中点 Point3D right new Point3D(0.5, 0, 0); // 右侧底部中点 // 添加顶点到MeshBuilder builder.Positions.Add(top); builder.Positions.Add(front); builder.Positions.Add(back); builder.Positions.Add(left); builder.Positions.Add(right); // 构建4个三角形侧面每个侧面由顶点两个底部点组成 // 正面top - left - front builder.TriangleIndices.Add(0); builder.TriangleIndices.Add(3); builder.TriangleIndices.Add(1); // 正面top - front - right builder.TriangleIndices.Add(0); builder.TriangleIndices.Add(1); builder.TriangleIndices.Add(4); // 背面top - right - back builder.TriangleIndices.Add(0); builder.TriangleIndices.Add(4); builder.TriangleIndices.Add(2); // 背面top - back - left builder.TriangleIndices.Add(0); builder.TriangleIndices.Add(2); builder.TriangleIndices.Add(3); // 底部由两个三角形组成矩形剖分 builder.TriangleIndices.Add(1); builder.TriangleIndices.Add(3); builder.TriangleIndices.Add(2); builder.TriangleIndices.Add(2); builder.TriangleIndices.Add(3); builder.TriangleIndices.Add(4); // 生成模型 var mesh new MeshGeometryModel3D { Geometry builder.ToMesh(), Material MaterialHelper.CreateMaterial(Brushes.Gold) }; return mesh; }代码解析MeshBuilder(false, false): 第一个参数createNormals表示是否自动生成法线设为false时需手动处理第二个参数useGeoCentric表示是否使用几何中心手动添加Positions和TriangleIndices是最底层的方式适合完全自定义的形状注意三角形顶点的环绕顺序顺时针表示面向相机逆时针表示背向相机2.2 内置几何体生成器MeshBuilder提供了丰富的方法来生成常见几何体无需手动定义每个顶点。2.2.1 基础几何体API方法参数说明AddBoxcenter, width, height, depth生成长方体AddSpherecenter, radius, thetaDiv, phiDiv生成球体可控制细分程度AddCylinderp1, p2, radius, div生成圆柱体AddConep1, p2, radius1, radius2, div生成圆锥/圆台AddToruscenter, majorRadius, minorRadius, div生成圆环AddPipepoints, radius, div生成管道沿路径示例创建一个多组件模型csharpprivate Model3DGroup CreateRobotArm() { var builder new MeshBuilder(false, false); var group new Model3DGroup(); // 底座圆柱体 builder.AddCylinder(new Point3D(0, -0.5, 0), new Point3D(0, 0, 0), 0.8, 32); // 身体立方体 builder.AddBox(new Point3D(0, 0.5, 0), 0.6, 1, 0.6); // 头部球体 builder.AddSphere(new Point3D(0, 1.2, 0), 0.4, 32, 32); // 手臂圆柱体左 builder.AddCylinder(new Point3D(-0.5, 0.8, 0), new Point3D(-1.2, 0.5, 0), 0.15, 16); // 手臂圆柱体右 builder.AddCylinder(new Point3D(0.5, 0.8, 0), new Point3D(1.2, 0.5, 0), 0.15, 16); // 创建材质 var material MaterialHelper.CreateMaterial(Brushes.SteelBlue); var model new MeshGeometryModel3D { Geometry builder.ToMesh(), Material material }; group.Children.Add(model); return group; }2.2.2 细分控制参数说明在使用AddSphere等方法时thetaDiv和phiDiv参数决定了模型的精细程度细分值顶点数渲染效果性能影响8约100有明显棱角最低32约1000平滑中等64约4000非常平滑较高128约16000极致平滑高建议在开发调试阶段使用较低的细分值最终发布时再根据需求调整。2.3 材质与着色技术有了几何体还需要材质来定义其外观。HelixToolkit提供了多种材质类型满足从简单颜色到复杂纹理的需求。2.3.1 材质类型总览材质类适用场景特点DiffuseMaterial漫反射表面如塑料、木材光线均匀散射SpecularMaterial高光表面如金属、陶瓷有镜面反射高光EmissiveMaterial自发光物体如光源、屏幕不受光照影响PhongMaterial高级材质SharpDX版本支持法线贴图、高光控制2.3.2 使用MaterialHelper创建材质MaterialHelper是HelixToolkit提供的便捷类简化了材质的创建过程csharp// 纯色材质 Material redMaterial MaterialHelper.CreateMaterial(Brushes.Red); // 带高光的金属材质 Material metalMaterial MaterialHelper.CreateMaterial( Brushes.Gold, materialMethod: MaterialMethod.Metal ); // 半透明材质 Material glassMaterial MaterialHelper.CreateMaterial( Brushes.Blue, opacity: 0.5 ); // 纹理材质 var imageBrush new ImageBrush(new BitmapImage(new Uri(texture.jpg, UriKind.Relative))); Material texturedMaterial MaterialHelper.CreateMaterial(imageBrush);2.3.3 纹理映射基础纹理映射是将2D图像包裹到3D模型表面的技术。每个顶点需要指定一个UV坐标0到1范围告诉渲染器图像的哪个点对应这个顶点。csharpprivate MeshGeometryModel3D CreateTexturedCube() { var builder new MeshBuilder(false, false); // 创建一个带纹理坐标的立方体 // 注意AddBox方法默认不生成纹理坐标需要手动构建 var positions new ListPoint3D(); var texCoords new ListPoint(); var indices new Listint(); // 定义6个面每个面4个顶点代码简化实际需要完整定义 // 正面 positions.Add(new Point3D(-1, 1, 1)); texCoords.Add(new Point(0, 0)); positions.Add(new Point3D(1, 1, 1)); texCoords.Add(new Point(1, 0)); positions.Add(new Point3D(1, -1, 1)); texCoords.Add(new Point(1, 1)); positions.Add(new Point3D(-1, -1, 1)); texCoords.Add(new Point(0, 1)); // 设置三角形索引每个矩形需要2个三角形 // ... 省略其余5个面的代码 ... var geometry new MeshGeometry3D { Positions new Point3DCollection(positions), TextureCoordinates new PointCollection(texCoords), TriangleIndices new Int32Collection(indices) }; // 加载纹理图片 var imageBrush new ImageBrush(); imageBrush.ImageSource new BitmapImage(new Uri(crate_texture.jpg, UriKind.Relative)); var material new DiffuseMaterial(imageBrush); return new MeshGeometryModel3D { Geometry geometry, Material material }; }纹理坐标规则(0,0) 对应图像左上角(1,0) 对应图像右上角(1,1) 对应图像右下角(0,1) 对应图像左下角2.4 法线与光照效果2.4.1 为什么需要法线法线是垂直于表面的向量它告诉渲染器这个面朝向哪个方向。光照计算依赖于法线来确定光线入射角度从而决定像素的亮度。有法线光照正确物体呈现出立体感和光影效果无法线物体呈现黑色或完全平坦的外观2.4.2 自动生成法线MeshBuilder提供了自动计算法线的功能csharp// 创建时启用自动法线生成 var builder new MeshBuilder(true, false); // 第一个参数设为true // 添加几何体 builder.AddSphere(new Point3D(0, 0, 0), 1); // 法线会自动计算无需额外代码 var mesh builder.ToMesh();2.4.3 手动指定法线对于需要精确控制光照效果的场景如平面光源、非均匀反射表面可以手动指定法线csharpvar builder new MeshBuilder(false, false); // 添加一个平面 builder.AddBox(new Point3D(0, 0, 0), 2, 0.1, 2); // 获取顶点列表和法线列表 var positions builder.Positions; var normals builder.Normals; // 为每个顶点设置朝向 // 注意顶点和法线的数量必须一一对应 for (int i 0; i positions.Count; i) { // 让所有法线都朝上Y轴正方向 normals.Add(new Vector3D(0, 1, 0)); }2.4.4 顶点着色除了使用材质还可以为每个顶点单独指定颜色实现渐变或热力图效果csharpprivate MeshGeometryModel3D CreateHeatMapMesh() { var builder new MeshBuilder(false, false); // 创建一个矩形网格 double step 0.2; for (double x -1; x 1; x step) { for (double z -1; z 1; z step) { double y Math.Sin(x * Math.PI) * Math.Cos(z * Math.PI); // 高度 builder.Positions.Add(new Point3D(x, y, z)); } } // 构建三角形索引... // 根据高度值设置顶点颜色 var colors new ListColor(); foreach (var pos in builder.Positions) { // 高度从 -1 到 1映射到 蓝色(-1) - 绿色(0) - 红色(1) double t (pos.Y 1) / 2; byte r (byte)(t * 255); byte g (byte)((1 - Math.Abs(t - 0.5) * 2) * 255); byte b (byte)((1 - t) * 255); colors.Add(Color.FromRgb(r, g, b)); } // 使用顶点颜色材质 var material new ColorBindingMaterial { ColorArray colors.ToArray() }; return new MeshGeometryModel3D { Geometry builder.ToMesh(), Material material }; }2.5 透明材质的正确使用2.5.1 透明渲染的原理问题在3D渲染中透明物体的处理是一个常见的技术难点。主要原因是渲染顺序问题不透明物体渲染器可以按任意顺序绘制因为后面的物体会被前面的完全遮挡透明物体需要从后往前绘制否则透过前面物体看到的将是背景而不是后面的物体2.5.2 解决方案IsTransparent属性HelixToolkit.SharpDX通过IsTransparent属性解决了这个问题csharp// 正确配置透明材质适用于HelixToolkit.SharpDX var transparentModel new MeshGeometryModel3D { Geometry CreateSphereMesh(), Material new PhongMaterial { DiffuseColor new Color4(1, 1, 1, 0.5f), // Alpha0.5表示半透明 }, IsTransparent true // 关键告诉渲染器按透明规则处理 };最佳实践透明物体数量不宜过多会影响性能Alpha值建议在0.1-0.9之间先添加不透明物体最后添加透明物体多个透明物体叠加时效果可能不完美需谨慎设计2.6 综合示例旋转的彩色齿轮下面的综合示例结合了本章学习的多个知识点MeshBuilder构建复杂几何体、材质设置、顶点着色、动画控制。csharp// MainWindow.xaml.cs using HelixToolkit.Wpf; using System.Windows.Media.Media3D; using System.Windows.Threading; public partial class MainWindow : Window { private MeshGeometryModel3D gearModel; private RotationTransform3D rotationTransform; public MainWindow() { InitializeComponent(); CreateGear(); StartAnimation(); } private void CreateGear() { var builder new MeshBuilder(true, true); // 启用法线和纹理坐标 // 齿轮参数 double outerRadius 1.0; double innerRadius 0.7; double toothHeight 0.3; int toothCount 24; double thickness 0.2; // 生成齿轮主体圆柱 builder.AddCylinder(new Point3D(0, -thickness/2, 0), new Point3D(0, thickness/2, 0), innerRadius, 64); // 生成齿形 for (int i 0; i toothCount; i) { double angle i * 2 * Math.PI / toothCount; double nextAngle (i 1) * 2 * Math.PI / toothCount; // 齿的顶点 Point3D p1 new Point3D(outerRadius * Math.Cos(angle), -thickness/2, outerRadius * Math.Sin(angle)); Point3D p2 new Point3D(outerRadius * Math.Cos(nextAngle), -thickness/2, outerRadius * Math.Sin(nextAngle)); Point3D p3 new Point3D((outerRadius toothHeight) * Math.Cos(angle), -thickness/2, (outerRadius toothHeight) * Math.Sin(angle)); Point3D p4 new Point3D((outerRadius toothHeight) * Math.Cos(nextAngle), -thickness/2, (outerRadius toothHeight) * Math.Sin(nextAngle)); // 添加齿的5个面简化实际需要完整代码 // builder.AddPolygon(...) } // 创建金属材质 var material MaterialHelper.CreateMaterial(Brushes.Bronze, materialMethod: MaterialMethod.Metal); gearModel new MeshGeometryModel3D { Geometry builder.ToMesh(), Material material }; // 添加Transform用于动画 rotationTransform new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), 0)); gearModel.Transform rotationTransform; viewport3D.Children.Add(gearModel); viewport3D.ZoomExtents(); } private void StartAnimation() { var timer new DispatcherTimer(); timer.Interval TimeSpan.FromMilliseconds(16); // 约60fps double angle 0; timer.Tick (s, e) { angle 2; if (angle 360) angle - 360; ((AxisAngleRotation3D)rotationTransform.Rotation).Angle angle; }; timer.Start(); } }2.7 本章小结通过本章的学习你已掌握✅MeshBuilder核心概念顶点、三角形索引、法线的基本原理✅内置几何体生成使用AddBox、AddSphere等方法快速创建模型✅材质系统MaterialHelper创建各种材质纹理映射基础✅法线与光照自动/手动管理法线顶点着色实现渐变效果✅透明材质正确处理半透明物体的渲染顺序