C# 零基础到精通教程 - WPF 深度专题:3D 图形与视觉增强
前面三个 WPF 专题我们学习了布局、控件、数据绑定、MVVM、自定义控件等核心知识。WPF 还有一个强大的功能——3D 图形支持。这一深度专题将带你进入 WPF 的 3D 世界学习如何创建旋转的立方体、3D 模型、光照和相机控制。前置知识要求已完成 WPF 专题一、二、三了解基本的 3D 坐标概念X、Y、Z 轴熟悉 C# 和 XAML 基础4.1 WPF 3D 概述4.1.1 WPF 3D 的核心组件WPF 的 3D 功能位于System.Windows.Media.Media3D命名空间中。text┌─────────────────────────────────────────────────────────────────┐ │ WPF 3D 架构 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ Viewport3D │ │ │ │ (3D 场景容器类似 2D 中的 Canvas) │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ ┌───────────────┼───────────────┐ │ │ ▼ ▼ ▼ │ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │ │ Camera │ │ Model3D │ │ Lights │ │ │ │ (相机) │ │ (模型) │ │ (光源) │ │ │ └────────────┘ └────────────┘ └────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────┐ │ │ │ Geometry3D │ │ │ │ Material │ │ │ │ (几何 材质) │ │ │ └─────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘4.1.2 核心概念速览组件用途类比Viewport3D3D 场景容器2D 中的 CanvasCamera观察视角眼睛的位置和方向Model3D3D 物体实体对象Geometry3D物体形状物体的骨架Material材质外观物体的皮肤、颜色、纹理Light光源照明系统Transform3D变换位置、旋转、缩放4.2 第一个 3D 程序旋转立方体4.2.1 创建 3D 场景xmlWindow x:ClassWpf3DDemo.MainWindow xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:xhttp://schemas.microsoft.com/winfx/2006/xaml TitleWPF 3D 立方体 Height500 Width700 WindowStartupLocationCenterScreen Grid !-- Viewport3D 是 3D 场景的根容器 -- Viewport3D x:NameMainViewport !-- 1. 相机定义观察场景的视角 -- Viewport3D.Camera PerspectiveCamera x:NameMainCamera Position5,3,5 !-- 相机位置 (X,Y,Z) -- LookDirection-5,-3,-5 !-- 相机看向的方向 -- UpDirection0,1,0 !-- 相机的上方向 -- FieldOfView45 !-- 视野角度 -- NearPlaneDistance0.1 !-- 近裁剪面 -- FarPlaneDistance100/ !-- 远裁剪面 -- /Viewport3D.Camera !-- 2. 光源照亮场景 -- ModelVisual3D ModelVisual3D.Content AmbientLight Color#404040/ !-- 环境光 -- /ModelVisual3D.Content /ModelVisual3D ModelVisual3D ModelVisual3D.Content DirectionalLight Color#FFFFFF Direction-1,-1,-1/ !-- 方向光 -- /ModelVisual3D.Content /ModelVisual3D !-- 3. 模型立方体 -- ModelVisual3D ModelVisual3D.Content GeometryModel3D !-- 几何定义立方体的8个顶点和12个三角形 -- GeometryModel3D.Geometry MeshGeometry3D Positions -1, 1, 1 0 !-- 前面顶点0 -- 1, 1, 1 1 !-- 顶点1 -- 1,-1, 1 2 !-- 顶点2 -- -1,-1, 1 3 !-- 顶点3 -- -1, 1,-1 4 !-- 后面顶点4 -- 1, 1,-1 5 !-- 顶点5 -- 1,-1,-1 6 !-- 顶点6 -- -1,-1,-1 7 !-- 顶点7 -- TriangleIndices !-- 前面 -- 0,1,2 0,2,3 !-- 右面 -- 1,5,6 1,6,2 !-- 后面 -- 5,4,7 5,7,6 !-- 左面 -- 4,0,3 4,3,7 !-- 上面 -- 4,5,1 4,1,0 !-- 下面 -- 3,2,6 3,6,7 TextureCoordinates 0,0 1,0 1,1 0,1 0,0 1,0 1,1 0,1 0,0 1,0 1,1 0,1 0,0 1,0 1,1 0,1 0,0 1,0 1,1 0,1 0,0 1,0 1,1 0,1/ /GeometryModel3D.Geometry !-- 材质外观 -- GeometryModel3D.Material DiffuseMaterial BrushRed/ /GeometryModel3D.Material !-- 背面材质背面可见时显示 -- GeometryModel3D.BackMaterial DiffuseMaterial BrushDarkRed/ /GeometryModel3D.BackMaterial !-- 变换位置、旋转、缩放 -- GeometryModel3D.Transform Transform3DGroup RotateTransform3D x:NameCubeRotation RotateTransform3D.Rotation AxisAngleRotation3D Axis0.5,1,0 Angle0/ /RotateTransform3D.Rotation /RotateTransform3D TranslateTransform3D OffsetX0 OffsetY0 OffsetZ0/ /Transform3DGroup /GeometryModel3D.Transform /GeometryModel3D /ModelVisual3D.Content /ModelVisual3D !-- 辅助坐标轴可选-- ModelVisual3D ModelVisual3D.Content !-- X 轴红色-- GeometryModel3D Geometry{StaticResource ArrowGeometry} GeometryModel3D.Material DiffuseMaterial BrushRed/ /GeometryModel3D.Material /GeometryModel3D /ModelVisual3D.Content /ModelVisual3D /Viewport3D /Grid /Window4.2.2 添加动画让立方体旋转csharp// MainWindow.xaml.cs using System.Windows; using System.Windows.Media.Animation; using System.Windows.Media.Media3D; namespace Wpf3DDemo { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); // 启动动画 StartRotationAnimation(); } private void StartRotationAnimation() { // 获取旋转对象 var rotation (AxisAngleRotation3D)CubeRotation.Rotation; // 创建角度动画 var animation new DoubleAnimation { From 0, To 360, Duration TimeSpan.FromSeconds(8), RepeatBehavior RepeatBehavior.Forever }; // 应用到旋转角度 rotation.BeginAnimation(AxisAngleRotation3D.AngleProperty, animation); } } }4.3 3D 坐标系深入理解4.3.1 坐标系统textY 向上 │ │ │ │ └─────────► X 向右 / / / Z 向屏幕外右手坐标系 WPF 使用右手坐标系 - 拇指指向 X 正方向 - 食指指向 Y 正方向 - 中指指向 Z 正方向4.3.2 相机详解相机类型说明适用场景PerspectiveCamera透视相机近大远小大多数 3D 场景OrthographicCamera正交相机无透视2.5D 游戏、技术图纸xml!-- 透视相机有景深感 -- PerspectiveCamera Position5,5,5 LookDirection-5,-5,-5 UpDirection0,1,0 FieldOfView60/ !-- 正交相机平行投影物体大小与距离无关 -- OrthographicCamera Position5,5,5 LookDirection-5,-5,-5 UpDirection0,1,0 Width10/4.3.3 理解 LookDirectioncsharp// LookDirection 从相机位置指向目标点的方向向量 // 公式LookDirection Target - Position // 相机在 (5, 3, 5)看向原点 (0, 0, 0) // LookDirection (0 - 5, 0 - 3, 0 - 5) (-5, -3, -5) // 相机在 (0, 0, 10)看向原点 // LookDirection (0, 0, -10) // 简化相机在 (0, 0, 10)看向 Z 轴负方向 PerspectiveCamera Position0,0,10 LookDirection0,0,-1 UpDirection0,1,0/4.4 几何网格MeshGeometry3D4.4.1 网格的基本概念3D 模型由三角形组成。三角形是最小的平面单元任何复杂形状都可以用三角形拼成。text一个矩形由 2 个三角形组成 v0 ───── v1 │╲ │ │ ╲ │ │ ╲ │ │ ╲ │ v3 ───── v2 三角形1: v0, v1, v3 三角形2: v1, v2, v34.4.2 创建立方体的完整代码csharppublic static MeshGeometry3D CreateCubeMesh(double size) { var mesh new MeshGeometry3D(); double half size / 2; // 8 个顶点 Point3D[] vertices new Point3D[] { new Point3D(-half, half, half), // 0: 前上左 new Point3D( half, half, half), // 1: 前上右 new Point3D( half, -half, half), // 2: 前下右 new Point3D(-half, -half, half), // 3: 前下左 new Point3D(-half, half, -half), // 4: 后上左 new Point3D( half, half, -half), // 5: 后上右 new Point3D( half, -half, -half), // 6: 后下右 new Point3D(-half, -half, -half), // 7: 后下左 }; foreach (var v in vertices) mesh.Positions.Add(v); // 12 个三角形每个面 2 个三角形共 6 个面 int[,] triangles new int[,] { // 前面 (Z half) {0, 1, 2}, {0, 2, 3}, // 右面 (X half) {1, 5, 6}, {1, 6, 2}, // 后面 (Z -half) {5, 4, 7}, {5, 7, 6}, // 左面 (X -half) {4, 0, 3}, {4, 3, 7}, // 上面 (Y half) {4, 5, 1}, {4, 1, 0}, // 下面 (Y -half) {3, 2, 6}, {3, 6, 7} }; for (int i 0; i triangles.GetLength(0); i) { mesh.TriangleIndices.Add(triangles[i, 0]); mesh.TriangleIndices.Add(triangles[i, 1]); mesh.TriangleIndices.Add(triangles[i, 2]); } return mesh; }4.4.3 纹理坐标UV 映射csharp// UV 坐标0 到 1 之间告诉纹理图片如何贴到三角形上 // (0,0) 是纹理左上角(1,1) 是纹理右下角 // 为立方体的每个面设置 UV 坐标 public static void SetCubeTextureCoordinates(MeshGeometry3D mesh) { // 每个面 4 个顶点对应 UV 坐标 Point[] uvs new Point[] { // 前面 new Point(0,0), new Point(1,0), new Point(1,1), new Point(0,1), // 右面 new Point(0,0), new Point(1,0), new Point(1,1), new Point(0,1), // 后面 new Point(0,0), new Point(1,0), new Point(1,1), new Point(0,1), // 左面 new Point(0,0), new Point(1,0), new Point(1,1), new Point(0,1), // 上面 new Point(0,0), new Point(1,0), new Point(1,1), new Point(0,1), // 下面 new Point(0,0), new Point(1,0), new Point(1,1), new Point(0,1) }; foreach (var uv in uvs) mesh.TextureCoordinates.Add(uv); }4.4.4 生成球体网格csharppublic static MeshGeometry3D CreateSphereMesh(double radius, int segments) { var mesh new MeshGeometry3D(); for (int lat 0; lat segments; lat) { double theta lat * Math.PI / segments; double sinTheta Math.Sin(theta); double cosTheta Math.Cos(theta); for (int lon 0; lon segments; lon) { double phi lon * 2 * Math.PI / segments; double sinPhi Math.Sin(phi); double cosPhi Math.Cos(phi); double x radius * cosPhi * sinTheta; double y radius * cosTheta; double z radius * sinPhi * sinTheta; mesh.Positions.Add(new Point3D(x, y, z)); // UV 坐标 double u (double)lon / segments; double v (double)lat / segments; mesh.TextureCoordinates.Add(new Point(u, v)); } } // 创建索引 for (int lat 0; lat segments; lat) { for (int lon 0; lon segments; lon) { int current lat * (segments 1) lon; int next current segments 1; mesh.TriangleIndices.Add(current); mesh.TriangleIndices.Add(current 1); mesh.TriangleIndices.Add(next); mesh.TriangleIndices.Add(next); mesh.TriangleIndices.Add(current 1); mesh.TriangleIndices.Add(next 1); } } return mesh; }4.5 材质系统4.5.1 材质类型对比材质类型说明效果DiffuseMaterial漫反射材质哑光效果均匀反射光SpecularMaterial高光材质有光泽有高光点EmissiveMaterial自发光材质自己发光不受光照影响MaterialGroup材质组合多种材质混合xml!-- 漫反射材质红砖效果 -- DiffuseMaterial BrushRed/ !-- 高光材质金属效果 -- SpecularMaterial BrushWhite SpecularPower50/ !-- 自发光材质霓虹灯效果 -- EmissiveMaterial BrushRed/ !-- 材质组 -- MaterialGroup DiffuseMaterial BrushRed/ SpecularMaterial BrushWhite SpecularPower30/ /MaterialGroup4.5.2 纹理材质贴图xml!-- 从图片加载纹理 -- DiffuseMaterial DiffuseMaterial.Brush ImageBrush ImageSource/Images/BrickTexture.jpg TileModeTile Viewport0,0,0.2,0.2/ /DiffuseMaterial.Brush /DiffuseMaterialcsharp// 代码中设置纹理 public static DiffuseMaterial CreateTextureMaterial(string imagePath) { var bitmap new BitmapImage(); bitmap.BeginInit(); bitmap.UriSource new Uri(imagePath, UriKind.Relative); bitmap.EndInit(); var brush new ImageBrush(bitmap); brush.TileMode TileMode.Tile; brush.Viewport new Rect(0, 0, 0.2, 0.2); brush.ViewportUnits BrushMappingMode.RelativeToBoundingBox; return new DiffuseMaterial(brush); }4.5.3 渐变材质xml!-- 线性渐变 -- DiffuseMaterial DiffuseMaterial.Brush LinearGradientBrush StartPoint0,0 EndPoint1,1 GradientStop ColorRed Offset0/ GradientStop ColorBlue Offset1/ /LinearGradientBrush /DiffuseMaterial.Brush /DiffuseMaterial !-- 径向渐变 -- DiffuseMaterial DiffuseMaterial.Brush RadialGradientBrush Center0.5,0.5 RadiusX0.5 RadiusY0.5 GradientStop ColorYellow Offset0/ GradientStop ColorOrange Offset0.5/ GradientStop ColorRed Offset1/ /RadialGradientBrush /DiffuseMaterial.Brush /DiffuseMaterial4.6 光照系统4.6.1 光源类型光源类型说明效果AmbientLight环境光均匀照亮所有面DirectionalLight方向光如太阳平行光线PointLight点光源如灯泡向四周发散SpotLight聚光灯圆锥形光线4.6.2 完整光源配置示例xmlModelVisual3D ModelVisual3D.Content Model3DGroup !-- 环境光基础照明 -- AmbientLight Color#404040/ !-- 方向光主光源 -- DirectionalLight Color#FFFFFF Direction-1,-2,-1/ !-- 点光源暖色补光 -- PointLight Color#FFAA66 Position2,1,3 ConstantAttenuation1 LinearAttenuation0.1 QuadraticAttenuation0.01/ !-- 聚光灯强调效果 -- SpotLight Color#66AAFF Position-2,3,-1 Direction1,-1,0 InnerConeAngle20 OuterConeAngle45 ConstantAttenuation1/ /Model3DGroup /ModelVisual3D.Content /ModelVisual3D4.6.3 动态光源跟随相机csharppublic partial class MainWindow : Window { private PointLight _followLight; public MainWindow() { InitializeComponent(); SetupFollowLight(); } private void SetupFollowLight() { // 创建跟随相机移动的点光源 _followLight new PointLight { Color Colors.White, ConstantAttenuation 1, LinearAttenuation 0.1 }; // 添加到场景 var lightVisual new ModelVisual3D(); lightVisual.Content _followLight; MainViewport.Children.Add(lightVisual); // 绑定光源位置到相机位置 var binding new Binding { Source MainCamera, Path new PropertyPath(Position) }; _followLight.SetBinding(PointLight.PositionProperty, binding); } }4.7 变换系统4.7.1 变换类型变换类型说明应用场景TranslateTransform3D平移移动物体RotateTransform3D旋转绕轴旋转ScaleTransform3D缩放放大/缩小MatrixTransform3D矩阵变换复杂组合变换4.7.2 组合变换示例xmlGeometryModel3D.Transform Transform3DGroup !-- 1. 旋转 -- RotateTransform3D RotateTransform3D.Rotation AxisAngleRotation3D Axis0,1,0 Angle45/ /RotateTransform3D.Rotation /RotateTransform3D !-- 2. 缩放 -- ScaleTransform3D ScaleX1.5 ScaleY1.5 ScaleZ1.5/ !-- 3. 平移 -- TranslateTransform3D OffsetX2 OffsetY1 OffsetZ0/ /Transform3DGroup /GeometryModel3D.Transform4.7.3 键盘控制物体移动csharp// MainWindow.xaml.cs public partial class MainWindow : Window { private TranslateTransform3D _cubeTransform; private double _moveSpeed 0.2; public MainWindow() { InitializeComponent(); // 获取立方体的平移变换 _cubeTransform GetTranslateTransform(CubeModel); // 注册键盘事件 this.KeyDown MainWindow_KeyDown; } private void MainWindow_KeyDown(object sender, KeyEventArgs e) { switch (e.Key) { case Key.Up: _cubeTransform.OffsetZ - _moveSpeed; break; case Key.Down: _cubeTransform.OffsetZ _moveSpeed; break; case Key.Left: _cubeTransform.OffsetX - _moveSpeed; break; case Key.Right: _cubeTransform.OffsetX _moveSpeed; break; case Key.PageUp: _cubeTransform.OffsetY _moveSpeed; break; case Key.PageDown: _cubeTransform.OffsetY - _moveSpeed; break; } // 更新相机看向的物体可选 UpdateCameraLookAt(); } private TranslateTransform3D GetTranslateTransform(GeometryModel3D model) { var transformGroup model.Transform as Transform3DGroup; if (transformGroup ! null) { foreach (var transform in transformGroup.Children) { if (transform is TranslateTransform3D translate) return translate; } } return new TranslateTransform3D(); } }4.8 3D 模型从文件加载4.8.1 使用 HelixToolkit推荐第三方库bash# 安装 HelixToolkit支持加载 OBJ、STL 等格式 dotnet add package HelixToolkit.WpfxmlWindow xmlns:helixhttp://helix-toolkit.org/wpf Grid !-- 使用 HelixViewport3D 替代 Viewport3D -- helix:HelixViewport3D x:NameViewport !-- 加载 OBJ 文件 -- helix:FileModelVisual3D x:NameModel FileNameModels/teapot.obj Material{StaticResource MetalMaterial}/ !-- 添加地面网格 -- helix:GridLinesVisual3D Width20 Length20 MajorStep1 MinorStep0.2/ !-- 添加坐标轴 -- helix:AxisVisual3D/ /helix:HelixViewport3D /Grid /Windowcsharp// 代码中加载模型 private void LoadModel(string filePath) { var importer new HelixToolkit.Wpf.ModelImporter(); var model importer.Load(filePath); var visual new ModelVisual3D(); visual.Content model; Viewport.Children.Add(visual); }4.9 交互式 3D4.9.1 鼠标控制相机旋转csharppublic class MouseCameraController { private PerspectiveCamera _camera; private Point _lastMousePosition; private double _rotationX 30; private double _rotationY 45; private double _distance 10; public void Attach(FrameworkElement element, PerspectiveCamera camera) { _camera camera; element.MouseLeftButtonDown OnMouseDown; element.MouseMove OnMouseMove; element.MouseWheel OnMouseWheel; } private void OnMouseDown(object sender, MouseButtonEventArgs e) { _lastMousePosition e.GetPosition((IInputElement)sender); ((IInputElement)sender).CaptureMouse(); } private void OnMouseMove(object sender, MouseEventArgs e) { if (e.LeftButton ! MouseButtonState.Pressed) return; var currentPos e.GetPosition((IInputElement)sender); var delta currentPos - _lastMousePosition; _rotationX delta.Y * 0.5; _rotationY delta.X * 0.5; UpdateCameraPosition(); _lastMousePosition currentPos; } private void OnMouseWheel(object sender, MouseWheelEventArgs e) { _distance - e.Delta * 0.01; _distance Math.Max(3, Math.Min(20, _distance)); UpdateCameraPosition(); } private void UpdateCameraPosition() { double radX _rotationX * Math.PI / 180; double radY _rotationY * Math.PI / 180; double x _distance * Math.Sin(radY) * Math.Cos(radX); double y _distance * Math.Sin(radX); double z _distance * Math.Cos(radY) * Math.Cos(radX); _camera.Position new Point3D(x, y, z); _camera.LookDirection new Vector3D(-x, -y, -z); } }4.9.2 鼠标拾取 3D 物体csharpprivate void Viewport_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { // 获取鼠标点击的 3D 位置 var hitParams new PointHitTestParameters(e.GetPosition(MainViewport)); VisualTreeHelper.HitTest(MainViewport, null, result { var rayHit result as RayHitTestResult; if (rayHit ! null) { // 获取击中的模型 var model rayHit.ModelHit as GeometryModel3D; if (model ! null) { // 高亮效果 HighlightModel(model); } } return HitTestResultBehavior.Stop; }, hitParams); } private void HighlightModel(GeometryModel3D model) { // 保存原始材质 _originalMaterial model.Material; // 设置为高亮材质 model.Material new EmissiveMaterial(Brushes.Yellow); // 创建恢复动画 var timer new DispatcherTimer { Interval TimeSpan.FromSeconds(0.5) }; timer.Tick (s, e) { model.Material _originalMaterial; timer.Stop(); }; timer.Start(); }4.10 综合示例旋转的地球仪xml!-- EarthWindow.xaml -- Window x:ClassWpf3DDemo.EarthWindow xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:xhttp://schemas.microsoft.com/winfx/2006/xaml Title3D 地球仪 Height600 Width800 WindowStartupLocationCenterScreen Window.Resources !-- 地球纹理 -- ImageBrush x:KeyEarthTexture ImageSource/Images/earth_map.jpg/ !-- 大气层材质 -- RadialGradientBrush x:KeyAtmosphereBrush Center0.5,0.5 RadiusX0.5 RadiusY0.5 GradientStop Color#00FFFFFF Offset0.8/ GradientStop Color#44FFFFFF Offset0.95/ GradientStop Color#00FFFFFF Offset1/ /RadialGradientBrush !-- 地球材质 -- DiffuseMaterial x:KeyEarthMaterial Brush{StaticResource EarthTexture}/ !-- 高光材质 -- SpecularMaterial x:KeyEarthSpecular BrushWhite SpecularPower30/ !-- 组合材质 -- MaterialGroup x:KeyEarthMaterialGroup StaticResource ResourceKeyEarthMaterial/ StaticResource ResourceKeyEarthSpecular/ /MaterialGroup /Window.Resources Grid Viewport3D x:NameMainViewport ClipToBoundsTrue !-- 相机 -- Viewport3D.Camera PerspectiveCamera x:NameCamera Position0,0,5 LookDirection0,0,-5 UpDirection0,1,0 FieldOfView45/ /Viewport3D.Camera !-- 光源 -- ModelVisual3D ModelVisual3D.Content Model3DGroup AmbientLight Color#333333/ DirectionalLight Color#FFFFFF Direction-1,-2,-1/ PointLight Color#FFAA66 Position3,2,4/ /Model3DGroup /ModelVisual3D.Content /ModelVisual3D !-- 地球模型 -- ModelVisual3D ModelVisual3D.Content GeometryModel3D x:NameEarthModel GeometryModel3D.Geometry MeshGeometry3D x:NameEarthMesh/ /GeometryModel3D.Geometry GeometryModel3D.Material StaticResource ResourceKeyEarthMaterialGroup/ /GeometryModel3D.Material GeometryModel3D.Transform RotateTransform3D x:NameEarthRotation RotateTransform3D.Rotation AxisAngleRotation3D Axis0,1,0 Angle0/ /RotateTransform3D.Rotation /RotateTransform3D /GeometryModel3D.Transform /GeometryModel3D /ModelVisual3D.Content /ModelVisual3D !-- 大气层效果 -- ModelVisual3D ModelVisual3D.Content GeometryModel3D GeometryModel3D.Geometry MeshGeometry3D x:NameAtmosphereMesh/ /GeometryModel3D.Geometry GeometryModel3D.Material DiffuseMaterial Brush{StaticResource AtmosphereBrush}/ /GeometryModel3D.Material /GeometryModel3D /ModelVisual3D.Content /ModelVisual3D !-- 星空背景 -- ModelVisual3D ModelVisual3D.Content GeometryModel3D GeometryModel3D.Geometry MeshGeometry3D Positions-100,-100,100 100,-100,100 100,100,100 -100,100,100 TriangleIndices0,1,2 0,2,3/ /GeometryModel3D.Geometry GeometryModel3D.Material DiffuseMaterial DiffuseMaterial.Brush ImageBrush ImageSource/Images/stars.jpg TileModeTile/ /DiffuseMaterial.Brush /DiffuseMaterial /GeometryModel3D.Material /GeometryModel3D /ModelVisual3D.Content /ModelVisual3D /Viewport3D !-- UI 控制面板 -- StackPanel HorizontalAlignmentRight VerticalAlignmentBottom Margin20 Background#80000000 Padding15 CornerRadius8 Slider x:NameRotationSpeedSlider Minimum0 Maximum10 Value5 Width200 Margin0,0,0,10/ Label Content{Binding Value, ElementNameRotationSpeedSlider, StringFormat转速: {0:F1}/秒} ForegroundWhite HorizontalContentAlignmentCenter/ Slider x:NameZoomSlider Minimum2 Maximum15 Value5 Width200/ /StackPanel /Grid /Windowcsharp// EarthWindow.xaml.cs using System.Windows; using System.Windows.Media.Animation; using System.Windows.Media.Media3D; using System.Windows.Threading; namespace Wpf3DDemo { public partial class EarthWindow : Window { private DispatcherTimer _rotationTimer; private double _currentAngle 0; public EarthWindow() { InitializeComponent(); // 创建地球网格 EarthMesh CreateSphereMesh(1.0, 64); AtmosphereMesh CreateSphereMesh(1.02, 64); // 启动自动旋转 StartAutoRotation(); // 绑定 UI 事件 RotationSpeedSlider.ValueChanged RotationSpeed_ValueChanged; ZoomSlider.ValueChanged ZoomSlider_ValueChanged; } private MeshGeometry3D CreateSphereMesh(double radius, int segments) { var mesh new MeshGeometry3D(); var vertices new ListPoint3D(); var uvs new ListPoint(); for (int lat 0; lat segments; lat) { double theta lat * Math.PI / segments; double sinTheta Math.Sin(theta); double cosTheta Math.Cos(theta); for (int lon 0; lon segments; lon) { double phi lon * 2 * Math.PI / segments; double sinPhi Math.Sin(phi); double cosPhi Math.Cos(phi); double x radius * cosPhi * sinTheta; double y radius * cosTheta; double z radius * sinPhi * sinTheta; vertices.Add(new Point3D(x, y, z)); uvs.Add(new Point((double)lon / segments, (double)lat / segments)); } } foreach (var v in vertices) mesh.Positions.Add(v); foreach (var uv in uvs) mesh.TextureCoordinates.Add(uv); // 创建索引 for (int lat 0; lat segments; lat) { for (int lon 0; lon segments; lon) { int current lat * (segments 1) lon; int next current segments 1; mesh.TriangleIndices.Add(current); mesh.TriangleIndices.Add(current 1); mesh.TriangleIndices.Add(next); mesh.TriangleIndices.Add(next); mesh.TriangleIndices.Add(current 1); mesh.TriangleIndices.Add(next 1); } } return mesh; } private void StartAutoRotation() { _rotationTimer new DispatcherTimer(); _rotationTimer.Interval TimeSpan.FromMilliseconds(16); // ~60fps _rotationTimer.Tick (s, e) { double speed RotationSpeedSlider.Value; if (speed 0) { _currentAngle speed * 0.5; // 每帧增加角度 if (_currentAngle 360) _currentAngle - 360; var rotation (AxisAngleRotation3D)EarthRotation.Rotation; rotation.Angle _currentAngle; } }; _rotationTimer.Start(); } private void RotationSpeed_ValueChanged(object sender, RoutedPropertyChangedEventArgsdouble e) { // 速度变化时不需要额外处理定时器会读取最新值 } private void ZoomSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgsdouble e) { double zoom ZoomSlider.Value; Camera.Position new Point3D(0, 0, zoom); Camera.LookDirection new Vector3D(0, 0, -zoom); } } }4.11 性能优化4.11.1 减少三角形数量csharp// 对于不需要细节的物体使用较少的细分 // 近处物体高精度远处物体低精度 public enum DetailLevel { Low, // 8x8 细分512 三角形 Medium, // 32x32 细分2048 三角形 High // 128x128 细分32768 三角形 } public static int GetSegments(DetailLevel level) { return level switch { DetailLevel.Low 16, DetailLevel.Medium 32, DetailLevel.High 64, _ 32 }; }4.11.2 冻结对象提高性能csharp// 冻结不会改变的对象提高渲染性能 var mesh CreateSphereMesh(1.0, 32); mesh.Freeze(); // 冻结后不能再修改 var material new DiffuseMaterial(Brushes.Red); material.Freeze();4.11.3 使用 GeometryModel3D 缓存csharppublic class ModelCache { private static Dictionarystring, GeometryModel3D _cache new(); public static GeometryModel3D GetModel(string key, FuncGeometryModel3D factory) { if (!_cache.ContainsKey(key)) { var model factory(); model.Freeze(); // 缓存后冻结 _cache[key] model; } // 返回克隆因为不能共享同一个实例 return CloneModel(_cache[key]); } private static GeometryModel3D CloneModel(GeometryModel3D original) { return new GeometryModel3D { Geometry original.Geometry, Material original.Material, BackMaterial original.BackMaterial, Transform original.Transform }; } }