- A+
概述
本文介绍采用WPF进行3D开发的一些基础知识,还有HelixToolkit控件的介绍以及在MVVM模式下使用3D框架。
3D开发入门
官方文档对3D开发的一些基础知识已经描述的比较详细了,地址:三维图形概述 - WPF .NET Framework | Microsoft Docs
在学习WPF 3D应首先了解文档中介绍的一些基本概念。
通过以下,代码我们创建了一个基本的立方体
<Grid> <Border Grid.Column="0" BorderThickness="1" BorderBrush="Gray"> <Viewport3D > <Viewport3D.Camera> <PerspectiveCamera Position="6 5 4" LookDirection="-6 -5 -4"/> </Viewport3D.Camera> <ModelVisual3D> <ModelVisual3D.Content> <DirectionalLight Direction="-1,-1,-1"/> </ModelVisual3D.Content> </ModelVisual3D> <ModelVisual3D> <ModelVisual3D.Content> <GeometryModel3D> <GeometryModel3D.Geometry> <MeshGeometry3D Positions="0 0 0 1 0 0 0 1 0 1 1 0 0 0 1 1 0 1 0 1 1 1 1 1" TriangleIndices="2 3 1 2 1 0 7 1 3 7 5 1 6 5 7 6 4 5 6 2 0 2 0 4 2 7 3 2 6 7 0 1 5 0 5 4"> </MeshGeometry3D> </GeometryModel3D.Geometry> <GeometryModel3D.Material> <DiffuseMaterial> <DiffuseMaterial.Brush> <SolidColorBrush Color="Red"/> </DiffuseMaterial.Brush> </DiffuseMaterial> </GeometryModel3D.Material> </GeometryModel3D> </ModelVisual3D.Content> </ModelVisual3D> </Viewport3D> </Border> </Grid>
View Code
可以先拷贝并修改以上代码查看效果。
HelixToolkit框架
采用原生的框架进行开发是比较困难或麻烦的,所以考虑采用第三方框架进行开发。
HelixToolkit提供一些3D模型组件,可以方便用户使用
开源项目地址:GitHub - helix-toolkit/helix-toolkit: Helix Toolkit is a collection of 3D components for .NET
先看一段设计代码:
<HelixToolkit:HelixViewport3D ShowFrameRate="True"
ZoomExtentsWhenLoaded="True"
ZoomAroundMouseDownPoint="True"
RotateAroundMouseDownPoint="True"
IsTopBottomViewOrientedToFrontBack="True"
IsViewCubeEdgeClicksEnabled="True"> <HelixToolkit:SunLight /> <ModelVisual3D x:Name="model"></ModelVisual3D> <!-- You can also add elements here in the xaml --> <HelixToolkit:GridLinesVisual3D Width="180" Length="180" MajorDistance="10" MinorDistance="10" Thickness="0.1" /> </HelixToolkit:HelixViewport3D>
控件要求所有内容应包含在HelixToolkit:HelixViewport3D标签内,包括:光照、地平线和其他模型。
模型一般采用ModelVisual3D 标签,定义模型的方式有两种:标签或代码:
用标签定义:
<ModelVisual3D> <ModelVisual3D.Content> <GeometryModel3D> <GeometryModel3D.Geometry> <MeshGeometry3D x:Name="meshMain" Positions="50 50 50 71 60 60 60 71 60 71 71 60 60 60 71 71 60 71 60 71 71 71 71 71" TriangleIndices="2 3 1 2 1 0 7 1 3 7 5 1 6 5 7 6 4 5 6 2 0 2 0 4 2 7 3 2 6 7 0 1 5 0 5 4"> </MeshGeometry3D> </GeometryModel3D.Geometry> <GeometryModel3D.Material> <DiffuseMaterial x:Name="matDiffuseMain"> <DiffuseMaterial.Brush> <SolidColorBrush Color="LightPink"/> </DiffuseMaterial.Brush> </DiffuseMaterial> </GeometryModel3D.Material> </GeometryModel3D> </ModelVisual3D.Content> </ModelVisual3D>
用代码定义:
设计端: <ModelVisual3D x:Name="model"/> 代码端: public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.Loaded += MainWindow_Loaded; } private void MainWindow_Loaded(object sender, RoutedEventArgs e) { // Create a model group Model3DGroup modelGroup = new Model3DGroup(); MeshBuilder meshBoxBuilder = new MeshBuilder(false, false); meshBoxBuilder.AddBox(new Point3D(0, 0, 0), 40, 40, 40); MeshGeometry3D meshBox = meshBoxBuilder.ToMesh(true); var whiteMaterial = MaterialHelper.CreateMaterial(Colors.Green); var insideMaterial = MaterialHelper.CreateMaterial(Colors.Gray); modelGroup.Children.Add(new GeometryModel3D { Geometry = meshBox, Material = whiteMaterial, BackMaterial = insideMaterial }); this.model.Content = modelGroup; } }
框架也支持MVVM模式
设计端: <Window x:Class="Learn3D.Helix.MainWindow"> <Window.DataContext> <local:MainViewModel /> </Window.DataContext> <Grid> <HelixToolkit:HelixViewport3D > <!-- The content of this visual is defined in MainViewModel.cs --> <ModelVisual3D Content="{Binding Model}" /> </HelixToolkit:HelixViewport3D> </Grid> </Window> 代码端: public class MainViewModel { public Model3D Model { get; set; } public MainViewModel() { // Create a model group Model3DGroup modelGroup = new Model3DGroup(); // Create a mesh builder and add a box to it MeshBuilder meshBuilder = new MeshBuilder(false, false); meshBuilder.AddBox(new Point3D(0, 0, 0), 20, 10, 5); // Create a mesh from the builder (and freeze it) MeshGeometry3D mesh = meshBuilder.ToMesh(true); // Create some materials var blueMaterial = MaterialHelper.CreateMaterial(Colors.Red); var insideMaterial = MaterialHelper.CreateMaterial(Colors.Yellow); // Add model to the group (using the same mesh, that's why we had to freeze it) modelGroup.Children.Add(new GeometryModel3D { Geometry = mesh, Transform = new TranslateTransform3D(60, 0, 60), Material = blueMaterial, BackMaterial = insideMaterial }); // Set the property, which will be bound to the Content property of the ModelVisual3D (see MainWindow.xaml) this.Model = modelGroup; } }
基本上我们一般不会采用这种方式,和Stylet框架结合更加方便。
HelixToolkit和Stylet框架结合
和Stylet框架相关的知识这里就不重复介绍了,不了解的可以去看我博客内的相关文章。
这里主要演示使用Stylet框架实现模型的变化。
基本原理如下:
首先建立一个模型,并绑定到ViewModel内的一个对象
前端: <ModelVisual3D Content="{Binding ModelDynamic}" /> 后端: public Model3D ModelDynamic { get; set; }
当用户通过界面上的控件修改模型的属性时,将触发事件,
public byte Color_R { get; set; } = 0; public byte Color_G { get; set; } = 0; public byte Color_B { get; set; } = 0; private void InitColorBind() { this.Bind(s => Color_R, (o, e) => ColorChanged()); this.Bind(s => Color_G, (o, e) => ColorChanged()); this.Bind(s => Color_B, (o, e) => ColorChanged()); } public void ColorChanged() { LoadDynamicModel(); }
在事件内重新构造模型:
public Model3D ModelDynamic { get; set; } public void LoadDynamicModel() { // Create some materials var redMaterial = MaterialHelper.CreateMaterial(Color.FromRgb(Color_R, Color_G, Color_B)); var insideMaterial = MaterialHelper.CreateMaterial(Colors.Gray); // Create a model group Model3DGroup modelDynamic = new Model3DGroup(); //模型 MeshBuilder meshBoxBuilder = new MeshBuilder(false, false); meshBoxBuilder.AddEllipsoid(new Point3D(20, 20, 10), 5, 5, 5); MeshGeometry3D meshBox = meshBoxBuilder.ToMesh(true); modelDynamic.Children.Add(new GeometryModel3D { Geometry = meshBox, Material = redMaterial, BackMaterial = insideMaterial }); this.ModelDynamic = modelDynamic; }
加载模型
全部通过代码来实现模型是非常困难的,特别是一些较复杂的模型,可以通过3D软件进行设计,并把设计好的模型导入进来,这样就比较愉快了。
public Model3DGroup ModelBase { get; set; } private void LoadBaseModel() { model_1 = LoadModel(@"D:3DModel1.stl"); ModelBase = new Model3DGroup(); ModelBase.Children.Add(model_1); }
LoadModel方法如下:
private Model3DGroup LoadModel(string path) { if (path == null) { return null; } string ext = System.IO.Path.GetExtension(path).ToLower(); Model3DGroup model; switch (ext) { case ".3ds": { var r = new HelixToolkit.Wpf.StudioReader(); model = r.Read(path); break; } case ".lwo": { var r = new HelixToolkit.Wpf.LwoReader(); model = r.Read(path); break; } case ".obj": { var r = new HelixToolkit.Wpf.ObjReader(); model = r.Read(path); break; } case ".objz": { var r = new HelixToolkit.Wpf.ObjReader(); model = r.ReadZ(path); break; } case ".stl": { var r = new HelixToolkit.Wpf.StLReader(); model = r.Read(path); break; } case ".off": { var r = new HelixToolkit.Wpf.OffReader(); model = r.Read(path); break; } default: throw new InvalidOperationException("File format not supported."); } return model; }
View Code
方法支持的格式比较多,推荐stl格式。
在实际件使用时,组件内是可以加入多个模型的,可以把不会变化的模型采用导入的方式来实现,有些变化的模型(尺寸、位置、颜色等)通过代码来实现。
最后需要说明的是,采用WPF进行3D开发,其功能是很有限的,很难实现较复杂的效果和交互功能,想要更好的效果可能采用Unity更加合适了。
资源
系列目录:WPF开发快速入门【0】前言与目录