三维渲染与模型生成
第九章:三维渲染与模型生成
9.1 三维渲染系统概述
9.1.1 ThreeJs4Net简介
FY_Layout使用ThreeJs4Net作为三维渲染引擎,这是Three.js的.NET实现,基于OpenGL提供高性能的3D渲染能力。
┌─────────────────────────────────────────┐
│ IElement3dAction │
│ (三维操作接口) │
├─────────────────────────────────────────┤
│ ThreeJs4Net │
│ Scene | Camera | Renderer | Mesh │
├─────────────────────────────────────────┤
│ OpenGL / OpenTK │
│ (底层图形API) │
├─────────────────────────────────────────┤
│ GPU 硬件 │
└─────────────────────────────────────────┘
9.1.2 核心类
| 类名 | 功能 |
|---|---|
| Object3D | 所有三维对象的基类 |
| Mesh | 网格对象,由几何体和材质组成 |
| Geometry | 几何数据(顶点、面等) |
| Material | 材质(颜色、纹理、反射等) |
| Scene | 场景,包含所有三维对象 |
| Camera | 相机,定义观察视角 |
9.2 IElement3dAction接口
9.2.1 接口定义
public interface IElement3dAction
{
/// <summary>
/// 获取元素的三维对象
/// </summary>
/// <param name="element">元素实例</param>
/// <param name="docRt">文档运行时</param>
/// <returns>三维对象</returns>
Object3D Get3dObject(LcElement element, DocumentRuntime docRt);
}
9.2.2 基本实现
public class MyElement3dAction : IElement3dAction
{
public Object3D Get3dObject(LcElement element, DocumentRuntime docRt)
{
var myEle = element as MyElement;
// 创建几何体
var geometry = CreateGeometry(myEle);
// 创建材质
var material = CreateMaterial(myEle);
// 创建网格
var mesh = new Mesh(geometry, material);
// 设置位置
mesh.Position.X = myEle.Position.X;
mesh.Position.Z = myEle.Position.Y; // 2D的Y对应3D的Z
return mesh;
}
}
9.3 几何体创建
9.3.1 内置几何体
// 立方体
var boxGeometry = new BoxGeometry(width, height, depth);
// 球体
var sphereGeometry = new SphereGeometry(radius, widthSegments, heightSegments);
// 圆柱体
var cylinderGeometry = new CylinderGeometry(radiusTop, radiusBottom, height, segments);
// 平面
var planeGeometry = new PlaneGeometry(width, height);
// 圆环
var torusGeometry = new TorusGeometry(radius, tube, radialSegments, tubularSegments);
9.3.2 自定义几何体
public static Geometry CreateCustomGeometry()
{
var geometry = new Geometry();
// 添加顶点
geometry.Vertices.Add(new Vector3(0, 0, 0));
geometry.Vertices.Add(new Vector3(100, 0, 0));
geometry.Vertices.Add(new Vector3(100, 100, 0));
geometry.Vertices.Add(new Vector3(0, 100, 0));
// 添加面(三角形)
geometry.Faces.Add(new Face3(0, 1, 2));
geometry.Faces.Add(new Face3(0, 2, 3));
// 计算法线
geometry.ComputeFaceNormals();
geometry.ComputeVertexNormals();
return geometry;
}
9.3.3 BufferGeometry
对于大量数据,使用BufferGeometry获得更好的性能:
public static BufferGeometry CreateBufferGeometry(double[] vertices, int[] indices)
{
var geometry = new BufferGeometry();
// 设置顶点位置
geometry.SetAttribute("position", new BufferAttribute(vertices, 3));
// 设置索引
geometry.SetIndex(new BufferAttribute(indices, 1));
// 计算法线
geometry.ComputeVertexNormals();
return geometry;
}
9.4 从2D轮廓生成3D模型
9.4.1 拉伸几何体
将2D轮廓拉伸成3D模型:
public Object3D ExtrudeFromOutline(Polyline2d outline, double height)
{
// 创建Shape(2D形状)
var shape = new Shape();
var points = outline.Curve2ds.Select(c => c.GetStartPoint()).ToList();
if (points.Count < 3) return null;
// 移动到第一个点
shape.MoveTo(points[0].X, points[0].Y);
// 连接其余点
for (int i = 1; i < points.Count; i++)
{
shape.LineTo(points[i].X, points[i].Y);
}
// 闭合
shape.LineTo(points[0].X, points[0].Y);
// 创建拉伸几何体
var extrudeSettings = new ExtrudeGeometryOptions
{
Depth = height,
BevelEnabled = false
};
var geometry = new ExtrudeGeometry(shape, extrudeSettings);
var material = new MeshLambertMaterial { Color = new ThreeJs4Net.Color(0xCCCCCC) };
return new Mesh(geometry, material);
}
9.4.2 基坑三维模型
基坑需要生成坡面,比简单拉伸更复杂:
public class FoundationPit3dAction : IElement3dAction
{
public Object3D Get3dObject(LcElement element, DocumentRuntime docRt)
{
var pit = element as QdFoundationPit;
var root = new Object3D();
// 获取参数
var outline = pit.Outline;
var bottom = pit.Bottom;
var elevation = pit.Elevation;
var factor = pit.Factor;
var pattern = pit.Pattern;
// 计算放坡宽度
var slopeWidth = (elevation - bottom) * factor;
// 获取顶部和底部轮廓
var topCurves = GetOffsetCurves(outline, pattern == 0 ? slopeWidth : 0);
var bottomCurves = GetOffsetCurves(outline, pattern == 0 ? 0 : -slopeWidth);
// 创建坑底面
var pitBottom = CreatePitBottom(bottomCurves, bottom);
root.Add(pitBottom);
// 创建坡面
var slopes = CreateSlopes(topCurves, bottomCurves, elevation, bottom);
foreach (var slope in slopes)
{
root.Add(slope);
}
return root;
}
private Mesh CreatePitBottom(List<Curve3d> curves, double elevation)
{
// 创建底面几何体
var posArr = new List<double>();
var idxArr = new List<int>();
// 三角化底面
var points = curves.SelectMany(c => c.GetPoints()).ToList();
var triangles = Triangulate(points);
int idxOffset = 0;
foreach (var tri in triangles)
{
posArr.Add(tri.A.X); posArr.Add(elevation); posArr.Add(tri.A.Y);
posArr.Add(tri.B.X); posArr.Add(elevation); posArr.Add(tri.B.Y);
posArr.Add(tri.C.X); posArr.Add(elevation); posArr.Add(tri.C.Y);
idxArr.Add(idxOffset);
idxArr.Add(idxOffset + 1);
idxArr.Add(idxOffset + 2);
idxOffset += 3;
}
var geometry = new BufferGeometry();
geometry.SetAttribute("position", new BufferAttribute(posArr.ToArray(), 3));
geometry.SetIndex(new BufferAttribute(idxArr.ToArray(), 1));
geometry.ComputeVertexNormals();
var material = new MeshLambertMaterial { Color = new ThreeJs4Net.Color(0x808080) };
return new Mesh(geometry, material);
}
private List<Mesh> CreateSlopes(List<Curve3d> topCurves, List<Curve3d> bottomCurves,
double topElevation, double bottomElevation)
{
var meshes = new List<Mesh>();
for (int i = 0; i < topCurves.Count; i++)
{
var topLine = topCurves[i] as Line3d;
var bottomLine = bottomCurves[i] as Line3d;
// 创建坡面四边形
var vertices = new double[]
{
topLine.Start.X, topElevation, topLine.Start.Z,
topLine.End.X, topElevation, topLine.End.Z,
bottomLine.End.X, bottomElevation, bottomLine.End.Z,
bottomLine.Start.X, bottomElevation, bottomLine.Start.Z
};
var indices = new int[] { 0, 1, 2, 0, 2, 3 };
var geometry = new BufferGeometry();
geometry.SetAttribute("position", new BufferAttribute(vertices, 3));
geometry.SetIndex(new BufferAttribute(indices, 1));
geometry.ComputeVertexNormals();
var material = new MeshLambertMaterial { Color = new ThreeJs4Net.Color(0xA0522D) };
meshes.Add(new Mesh(geometry, material));
}
return meshes;
}
}
9.5 材质系统
9.5.1 基本材质
// 基础材质(无光照影响)
var basicMaterial = new MeshBasicMaterial
{
Color = new ThreeJs4Net.Color(0xFF0000),
Transparent = true,
Opacity = 0.5
};
// Lambert材质(漫反射)
var lambertMaterial = new MeshLambertMaterial
{
Color = new ThreeJs4Net.Color(0x00FF00),
Side = Side.DoubleSide
};
// Phong材质(高光)
var phongMaterial = new MeshPhongMaterial
{
Color = new ThreeJs4Net.Color(0x0000FF),
Specular = new ThreeJs4Net.Color(0xFFFFFF),
Shininess = 30
};
// 标准PBR材质
var standardMaterial = new MeshStandardMaterial
{
Color = new ThreeJs4Net.Color(0xFFFFFF),
Roughness = 0.5,
Metalness = 0.5
};
9.5.2 MaterialManager
FY_Layout使用MaterialManager管理预定义材质:
public static class MaterialManager
{
// 预定义材质UUID
public static readonly string LawnUuid = "lawn-material-uuid";
public static readonly string ConcreteUuid = "concrete-material-uuid";
public static readonly string Metal1Uuid = "metal1-material-uuid";
public static readonly string AsphaltUuid = "asphalt-material-uuid";
private static Dictionary<string, MaterialInfo> Materials = new Dictionary<string, MaterialInfo>();
public static void Initialize()
{
// 注册草坪材质
Materials[LawnUuid] = new MaterialInfo
{
Uuid = LawnUuid,
Name = "草坪",
Color = new ThreeJs4Net.Color(0x228B22),
Roughness = 0.9,
Metalness = 0.0
};
// 注册混凝土材质
Materials[ConcreteUuid] = new MaterialInfo
{
Uuid = ConcreteUuid,
Name = "混凝土",
Color = new ThreeJs4Net.Color(0x808080),
Roughness = 0.8,
Metalness = 0.1
};
// 注册金属材质
Materials[Metal1Uuid] = new MaterialInfo
{
Uuid = Metal1Uuid,
Name = "金属",
Color = new ThreeJs4Net.Color(0xC0C0C0),
Roughness = 0.3,
Metalness = 0.8
};
}
public static MaterialInfo GetMaterial(string uuid)
{
return Materials.TryGetValue(uuid, out var mat) ? mat : null;
}
}
public class MaterialInfo
{
public string Uuid { get; set; }
public string Name { get; set; }
public ThreeJs4Net.Color Color { get; set; }
public double Roughness { get; set; }
public double Metalness { get; set; }
public string TexturePath { get; set; }
}
9.6 草坪三维实现
9.6.1 Lawn3dAction
public class Lawn3dAction : IElement3dAction
{
public Object3D Get3dObject(LcElement element, DocumentRuntime docRt)
{
var lawn = element as QdLawn;
// 获取轮廓点
var outline = lawn.Outline;
var points = outline.Curve2ds.SelectMany(c => c.GetPoints()).ToList();
if (points.Count < 3) return new Object3D();
// 创建形状
var shape = new Shape();
shape.MoveTo(points[0].X, points[0].Y);
for (int i = 1; i < points.Count; i++)
{
shape.LineTo(points[i].X, points[i].Y);
}
shape.LineTo(points[0].X, points[0].Y);
// 创建几何体(薄层)
var geometry = new ExtrudeGeometry(shape, new ExtrudeGeometryOptions
{
Depth = 50, // 草坪厚度50mm
BevelEnabled = false
});
// 获取草坪材质
var materialInfo = lawn.Material ?? MaterialManager.GetMaterial(MaterialManager.LawnUuid);
var material = new MeshLambertMaterial
{
Color = materialInfo.Color
};
var mesh = new Mesh(geometry, material);
mesh.Position.Y = lawn.Bottom; // 设置底部标高
return mesh;
}
}
9.7 围栏三维实现
9.7.1 Fence3dAction
围栏需要生成立柱和横杆:
public class Fence3dAction : IElement3dAction
{
public Object3D Get3dObject(LcElement element, DocumentRuntime docRt)
{
var fence = element as QdFence;
var root = new Object3D();
var path = fence.Path;
var height = fence.Height;
var postSpacing = fence.PostSpacing;
// 沿路径生成立柱
double accumulatedLength = 0;
foreach (var curve in path.Curve2ds)
{
var curveLength = curve.Length;
var startLength = accumulatedLength;
// 在曲线上生成立柱
while (accumulatedLength - startLength < curveLength)
{
var t = (accumulatedLength - startLength) / curveLength;
var point = curve.GetPointAt(t);
var post = CreatePost(height);
post.Position.X = point.X;
post.Position.Z = point.Y;
root.Add(post);
accumulatedLength += postSpacing;
}
accumulatedLength = startLength + curveLength;
}
// 生成横杆
var rails = CreateRails(path, height);
foreach (var rail in rails)
{
root.Add(rail);
}
return root;
}
private Mesh CreatePost(double height)
{
var geometry = new BoxGeometry(100, height, 100); // 100mm见方的立柱
var material = new MeshLambertMaterial
{
Color = new ThreeJs4Net.Color(0x404040)
};
var mesh = new Mesh(geometry, material);
mesh.Position.Y = height / 2; // 中心点在中间
return mesh;
}
private List<Object3D> CreateRails(Polyline2d path, double fenceHeight)
{
var rails = new List<Object3D>();
// 生成多层横杆
var railHeights = new double[] { fenceHeight * 0.3, fenceHeight * 0.6, fenceHeight * 0.9 };
foreach (var railHeight in railHeights)
{
foreach (var curve in path.Curve2ds)
{
if (curve is Line2d line)
{
var rail = CreateRailSegment(line, railHeight);
rails.Add(rail);
}
}
}
return rails;
}
private Mesh CreateRailSegment(Line2d line, double height)
{
var length = line.Length;
var angle = Math.Atan2(line.End.Y - line.Start.Y, line.End.X - line.Start.X);
var center = new Vector2((line.Start.X + line.End.X) / 2, (line.Start.Y + line.End.Y) / 2);
var geometry = new BoxGeometry(length, 50, 50); // 50mm方形横杆
var material = new MeshLambertMaterial
{
Color = new ThreeJs4Net.Color(0x606060)
};
var mesh = new Mesh(geometry, material);
mesh.Position.X = center.X;
mesh.Position.Y = height;
mesh.Position.Z = center.Y;
mesh.Rotation.Y = -angle;
return mesh;
}
}
9.8 Provider系统集成
9.8.1 Provider概述
QdLayoutProvider项目提供了另一种三维模型生成方式,通过Provider机制实现参数化建模:
// Provider注册
public class QdLayoutDllProviderImporter : IDllProviderImporter
{
public (ShapeProviderCollection, SolidProviderCollection) GetImportProviders()
{
QdFenceProvider.RegistProviders();
QdLawnProvider.RegistProviders();
QdFoundationPitProvider.RegistProviders();
QdGroundProvider.RegistProviders();
// ... 更多Provider注册
return (ShapeProviders, SolidProviders);
}
}
9.8.2 Provider实现示例
internal static class QdFoundationPitProvider
{
internal static void RegistProviders()
{
// 注册Shape Provider(二维图案)
ConvertToProviders(new List<(string uuid, string name, CreateShape creator)>
{
("69FBC6C4-F356-23B2-2704-56C0809CBF3B", "基坑", 基坑)
});
// 注册Solid Provider(三维模型)
ConvertToProvider(
"3E2422F7-F11D-27E4-B026-F4EE87ED08A6",
nameof(GetSolid_基坑),
GetSolid_基坑,
GetSolidMats
);
}
/// <summary>
/// 生成基坑二维图案
/// </summary>
internal static Curve2dGroupCollection 基坑(LcParameterSet pset, ShapeCreator creator)
{
var outline = (creator.ComIns as DirectComponent).BaseCurve as Polyline2d;
var bottom = pset.GetValue<double>("Bottom");
var pattern = pset.GetValue<int>("Pattern");
var factor = pset.GetValue<double>("Factor");
var elevation = pset.GetValue<double>("Elevation");
var width = (elevation - bottom) * factor;
// 生成放坡图案...
var curves = new List<Curve2d>();
// ... 计算逻辑
return new Curve2dGroupCollection { new Curve2dGroup { Curve2ds = curves } };
}
/// <summary>
/// 生成基坑三维模型
/// </summary>
private static Solid3dCollection GetSolid_基坑(
LcComponentDefinition definition,
LcParameterSet pset,
SolidCreator creator)
{
var outline = pset.GetValue<Polyline2d>("Outline");
var bottom = pset.GetValue<double>("Bottom");
var elevation = pset.GetValue<double>("Elevation");
var factor = pset.GetValue<double>("Factor");
var pattern = pset.GetValue<int>("Pattern");
// 生成三维几何数据...
var solids = new Solid3dCollection();
// 坑底
var pitSolid = new Solid3d
{
Name = "Pit",
Geometry = CreatePitGeometry(outline, bottom)
};
solids.Add(pitSolid);
// 坡面
var slopeSolid = new Solid3d
{
Name = "Slope",
Geometry = CreateSlopeGeometry(outline, bottom, elevation, factor, pattern)
};
solids.Add(slopeSolid);
return solids;
}
/// <summary>
/// 获取材质
/// </summary>
private static MaterialInfo[] GetSolidMats(
LcComponentDefinition definition,
LcParameterSet pset,
SolidCreator creator,
Solid3d solid)
{
return new MaterialInfo[]
{
MaterialManager.GetMaterial(MaterialManager.Metal1Uuid),
MaterialManager.GetMaterial(MaterialManager.ConcreteUuid)
};
}
}
9.9 性能优化
9.9.1 几何体合并
public static BufferGeometry MergeGeometries(List<BufferGeometry> geometries)
{
var allVertices = new List<double>();
var allIndices = new List<int>();
int indexOffset = 0;
foreach (var geometry in geometries)
{
var positions = geometry.GetAttribute("position").Array;
var indices = geometry.Index.Array;
allVertices.AddRange(positions);
allIndices.AddRange(indices.Select(i => i + indexOffset));
indexOffset += positions.Length / 3;
}
var merged = new BufferGeometry();
merged.SetAttribute("position", new BufferAttribute(allVertices.ToArray(), 3));
merged.SetIndex(new BufferAttribute(allIndices.ToArray(), 1));
merged.ComputeVertexNormals();
return merged;
}
9.9.2 LOD(细节层次)
public Object3D CreateLODObject(LcElement element)
{
var lod = new LOD();
// 高细节模型(近距离)
var highDetail = CreateHighDetailMesh(element);
lod.AddLevel(highDetail, 0);
// 中细节模型(中距离)
var mediumDetail = CreateMediumDetailMesh(element);
lod.AddLevel(mediumDetail, 5000);
// 低细节模型(远距离)
var lowDetail = CreateLowDetailMesh(element);
lod.AddLevel(lowDetail, 20000);
return lod;
}
9.9.3 实例化渲染
对于大量相同物体,使用实例化渲染:
public Object3D CreateInstancedMesh(List<Vector3> positions, Geometry geometry, Material material)
{
var instancedMesh = new InstancedMesh(geometry, material, positions.Count);
for (int i = 0; i < positions.Count; i++)
{
var matrix = new Matrix4().MakeTranslation(positions[i].X, positions[i].Y, positions[i].Z);
instancedMesh.SetMatrixAt(i, matrix);
}
instancedMesh.InstanceMatrix.NeedsUpdate = true;
return instancedMesh;
}
9.10 本章小结
本章详细介绍了FY_Layout的三维渲染与模型生成:
- 三维渲染架构:ThreeJs4Net和OpenGL的使用
- IElement3dAction接口:三维操作类的实现方式
- 几何体创建:内置几何体和自定义几何体
- 2D到3D转换:从轮廓拉伸生成三维模型
- 材质系统:MaterialManager和各种材质类型
- 具体实现:草坪、围栏、基坑的三维生成
- Provider系统:参数化三维模型生成
- 性能优化:几何体合并、LOD、实例化渲染
掌握这些三维渲染技术,可以为CAD元素创建专业的三维可视化效果。下一章我们将学习Provider系统与参数化设计。

浙公网安备 33010602011771号