学习目标:不作为3D专业建模软件,掌握加载3D图形即可,可用SolidWork、Blender、Unity3D建模保存.xaml/.stl格式文件然后导入进来。
正交相机:在这种投影模式下,无论物体距离相机距离远或者近,在最终渲染的图片中物体的大小都保持不变,这对于渲染2D场景或者UI元素是非常有用的。
透视相机:这一模式被用来模拟人眼看到的现象,它是3D场景中使用最普遍的渲染模式。
<!--相机-->
<Viewport3D.Camera>
<!--正交相机-->
<!--<OrthographicCamera/>-->
<!--透视相机-->
<PerspectiveCamera Position="8,5,10"
LookDirection="-7,-2,-10"
FarPlaneDistance="40"
NearPlaneDistance="10"
FieldOfView="100">
<PerspectiveCamera.Transform>
<RotateTransform3D CenterX="1.5" CenterY="1" CenterZ="0.5">
<RotateTransform3D.Rotation>
<AxisAngleRotation3D Angle="{Binding ElementName=slider,Path=Value}" Axis="0,1,0"/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
</PerspectiveCamera.Transform>
</PerspectiveCamera>
</Viewport3D.Camera>
属性含义
LookDirection:X,Y,Z方向移动距离
FarPlaneDistance/NearPlaneDistance:定义多远/近的距离看不到
FieldOfView:近景远景,越大越远
AxisAngleRotation3D :旋转角度 Axis:旋转的方向
1.3D图形绘制
相关代码
<Window x:Class="WPF3D图形.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPF3D图形"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Slider Name="slider" Minimum="0" Maximum="180" Value="0"/>
<Viewport3D Grid.Row="1" MouseLeftButtonDown="Viewport3D_MouseLeftButtonDown">
<!--相机-->
<Viewport3D.Camera>
<!--正交相机-->
<!--<OrthographicCamera/>-->
<!--透视相机-->
<PerspectiveCamera Position="10,3,10"
LookDirection="-7,-2,-10"
FarPlaneDistance="40"
NearPlaneDistance="10"
FieldOfView="60">
<PerspectiveCamera.Transform>
<RotateTransform3D CenterX="1.5" CenterY="1" CenterZ="0.5">
<RotateTransform3D.Rotation>
<AxisAngleRotation3D Angle="{Binding ElementName=slider,Path=Value}" Axis="0,1,0"/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
</PerspectiveCamera.Transform>
</PerspectiveCamera>
</Viewport3D.Camera>
<!--光线-->
<ModelVisual3D>
<ModelVisual3D.Content>
<Model3DGroup>
<!--散射光线-->
<AmbientLight Color="#FFF"/>
<!--平行光-->
<!--<DirectionalLight Color="#FFF" Direction="0,-1,0"/>-->
<!--点光源-->
<!--<PointLight Position="0,0,0"/>-->
<!--锥形辐射光:手电筒-->
<!--<SpotLight Position="0,0,0" Direction="0,0,-3"/>-->
</Model3DGroup>
</ModelVisual3D.Content>
</ModelVisual3D>
<Viewport2DVisual3D>
<Viewport2DVisual3D.Geometry>
<MeshGeometry3D Positions="0,0,1 0,2,1 3,2,1 3,0,1
0,0,0 0,2,0 3,2,0 3,0,0"
TriangleIndices="0,2,1 0,3,2 6,4,5 6,7,4"
TextureCoordinates="0,1 0,0 1,0 1,1"/>
<!--TextureCoordinates:表示的二维平面坐标,原点:左上角-->
</Viewport2DVisual3D.Geometry>
<Viewport2DVisual3D.Material>
<DiffuseMaterial Viewport2DVisual3D.IsVisualHostMaterial="True" Brush="Red"/>
</Viewport2DVisual3D.Material>
<Border BorderBrush="Orange" BorderThickness="2">
<StackPanel>
<TextBlock Text="WPF3D图形" Foreground="Black"/>
<Button Content="点击" Click="Button_Click"/>
</StackPanel>
</Border>
</Viewport2DVisual3D>
<!--红色面-->
<!--<ModelUIElement3D MouseLeftButtonDown="ModelUIElement3D_MouseLeftButtonDown">
<ModelUIElement3D.Model>
<GeometryModel3D>
--><!--正面材质--><!--
<GeometryModel3D.Material>
<DiffuseMaterial Brush="#FF2B2B"/>
</GeometryModel3D.Material>
--><!--背面材质--><!--
<GeometryModel3D.BackMaterial>
<DiffuseMaterial Brush="Yellow"/>
</GeometryModel3D.BackMaterial>
<GeometryModel3D.Geometry>
--><!--Positions:图形坐标点的位置,TriangleIndices:渲染的顺序--><!--
<MeshGeometry3D Positions="0,0,1 0,2,1 3,2,1 3,0,1
0,0,0 3,0,0 3,2,0 0,2,0 "
TriangleIndices="0,2,1 0,3,2 4,6,5 4,7,6 "/>
</GeometryModel3D.Geometry>
</GeometryModel3D>
</ModelUIElement3D.Model>
</ModelUIElement3D>-->
<!--橙色面-->
<ModelUIElement3D>
<GeometryModel3D>
<!--<GeometryModel3D.Material>
<DiffuseMaterial Brush="Red"/>
</GeometryModel3D.Material>-->
<GeometryModel3D.BackMaterial>
<DiffuseMaterial Brush="Orange"/>
</GeometryModel3D.BackMaterial>
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions="0,0,-2 3,0,-2 3,2,-2 0,2,-2"
TriangleIndices="0,2,1 0,3,2"/>
</GeometryModel3D.Geometry>
</GeometryModel3D>
</ModelUIElement3D>
<!--蓝色面-->
<ModelUIElement3D >
<ModelUIElement3D.Model>
<GeometryModel3D>
<!--材质-->
<GeometryModel3D.Material>
<!--散射材质-->
<DiffuseMaterial Brush="Blue"/>
<!--镜面材质-->
<!--<SpecularMaterial SpecularPower="1" Brush="Blue"/>-->
<!--自发光材质-->
<!--<EmissiveMaterial Color="Green" />-->
</GeometryModel3D.Material>
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions="0,0,1 0,2,1 3,2,1 3,0,1
0,0,0 0,2,0 3,2,0 3,0,0"
TriangleIndices="2,3,7 7,6,2 1,5,4 0,1,4"/>
</GeometryModel3D.Geometry>
</GeometryModel3D>
</ModelUIElement3D.Model>
</ModelUIElement3D>
<!--绿色面-->
<ModelUIElement3D >
<ModelUIElement3D.Model>
<GeometryModel3D>
<GeometryModel3D.Material>
<DiffuseMaterial Brush="Green"/>
</GeometryModel3D.Material>
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions="0,0,1 0,2,1 3,2,1 3,0,1
0,0,0 0,2,0 3,2,0 3,0,0"
TriangleIndices="5,1,2 6,5,2"/>
</GeometryModel3D.Geometry>
</GeometryModel3D>
</ModelUIElement3D.Model>
</ModelUIElement3D>
</Viewport3D>
</Grid>
</Window>
相关事件
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WPF3D图形
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
//红色面命中事件
private void ModelUIElement3D_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
ModelUIElement3D uIElement3D= sender as ModelUIElement3D;
var model= uIElement3D.Model as GeometryModel3D;
(model.Material as DiffuseMaterial).Brush = Brushes.Orange;
}
//整个3D图形命中事件
private void Viewport3D_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Viewport3D viewport3D = sender as Viewport3D;
Point location=e.GetPosition(viewport3D);
HitTestResult hitTestResult=VisualTreeHelper.HitTest(viewport3D, location);
if (hitTestResult != null)
{
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
}
}
}
2.使用第三方HelixToolkit加载3D文件
第一步:添加引用
第二步:添加xaml代码
<Window x:Class="WPF3D图形.HelixToolKit"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPF3D图形"
xmlns:helix="http://helix-toolkit.org/wpf"
mc:Ignorable="d"
Title="HelixToolKit" Height="450" Width="800">
<Grid>
<helix:HelixViewport3D Name="viewPort3d"
ShowViewCube="True"
ViewCubeBackText="后" ViewCubeFrontText="前" ViewCubeHeight="100" ViewCubeWidth="100"
ViewCubeVerticalPosition="Bottom"
ViewCubeHorizontalPosition="Right"
ShowCoordinateSystem="True"
CoordinateSystemLabelForeground="Red"
CoordinateSystemHorizontalPosition="Left"
CoordinateSystemVerticalPosition="Bottom"
ShowFrameRate="True"
IsViewCubeEdgeClicksEnabled="False">
<helix:HelixViewport3D.Camera>
<PerspectiveCamera FieldOfView="45"
LookDirection="0,0,-414.387754871885"
FarPlaneDistance="30000"
NearPlaneDistance="0.1"
Position="9.9475983006414E-14,91.037123633789,414.387754871885"
UpDirection="0,1,0"/>
</helix:HelixViewport3D.Camera>
<helix:HelixViewport3D.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#444" Offset="0"/>
<GradientStop Color="#EEE" Offset="1"/>
</LinearGradientBrush>
</helix:HelixViewport3D.Background>
<helix:GridLinesVisual3D Width="16000" Length="16000" Thickness="2" MinorDistance="500" MajorDistance="500" Fill="Gray" />
<!--很重要,没有灯光场景是黑的-->
<helix:DefaultLights/>
<ModelVisual3D x:Name="model"></ModelVisual3D>
</helix:HelixViewport3D>
</Grid>
</Window>
第三步:加载3D文件
using HelixToolkit.Wpf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Shapes;
namespace WPF3D图形
{
/// <summary>
/// HelixToolKit.xaml 的交互逻辑
/// </summary>
public partial class HelixToolKit : Window
{
List<string> modelPaths = new List<string>();
string basePath = AppDomain.CurrentDomain.BaseDirectory + "ModelFiles\\";
public HelixToolKit()
{
InitializeComponent();
modelPaths.Add("IRB4600_20kg-250_LINK1_CAD_rev04.stl");
modelPaths.Add("IRB4600_20kg-250_LINK2_CAD_rev04.stl");
modelPaths.Add("IRB4600_20kg-250_LINK3_CAD_rev005.stl");
modelPaths.Add("IRB4600_20kg-250_LINK4_CAD_rev04.stl");
modelPaths.Add("IRB4600_20kg-250_LINK5_CAD_rev04.stl");
modelPaths.Add("IRB4600_20kg-250_LINK6_CAD_rev04.stl");
modelPaths.Add("IRB4600_20kg-250_LINK3_CAD_rev04.stl");
modelPaths.Add("IRB4600_20kg-250_CABLES_LINK1_rev03.stl");
modelPaths.Add("IRB4600_20kg-250_CABLES_LINK2_rev03.stl");
modelPaths.Add("IRB4600_20kg-250_CABLES_LINK3_rev03.stl");
modelPaths.Add("IRB4600_20kg-250_BASE_CAD_rev04.stl");
this.Loaded += MainWindow_Loaded;
viewPort3d.RotateGesture = new MouseGesture(MouseAction.RightClick);
viewPort3d.PanGesture = new MouseGesture(MouseAction.LeftClick);
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
viewPort3d.Camera.LookDirection = new Vector3D(2038, -5200, -2930);
viewPort3d.Camera.UpDirection = new Vector3D(-0.145, 0.372, 0.917);
viewPort3d.Camera.Position = new Point3D(-1571, 4801, 3774);
this.model.Content = InitializeModels(this.modelPaths);
}
private Model3DGroup InitializeModels(List<string> modelsNames)
{
Model3DGroup group = new Model3DGroup();
try
{
ModelImporter import = new ModelImporter();
foreach (string modelName in modelsNames)
{
var materialGroup = new MaterialGroup();
Color mainColor = Colors.White;
//EmissiveMaterial emissMat = new EmissiveMaterial(new SolidColorBrush(mainColor));
DiffuseMaterial diffMat = new DiffuseMaterial(new SolidColorBrush(mainColor));
//SpecularMaterial specMat = new SpecularMaterial(new SolidColorBrush(mainColor), 2000);
//materialGroup.Children.Add(emissMat);
materialGroup.Children.Add(diffMat);
//materialGroup.Children.Add(specMat);
var link = import.Load(basePath + modelName);
GeometryModel3D model = link.Children[0] as GeometryModel3D;
model.Material = materialGroup;
model.BackMaterial = materialGroup;
group.Children.Add(link);
}
}
catch (Exception e)
{
MessageBox.Show("未知异常:" + e.StackTrace);
}
return group;
}
}
}
3.WPF事件
生命周期事件
Startup: 在调用 Application 对象的 Run 方法时发生
SessionEnding:在用户通过注销或关闭操作系统而结束 Windows 会话时发生
Activated:当应用程序成为前台应用程序时发生,App任意窗口激活
Deactivated:当应用程序停止作为前台应用程序时发生,App中所有窗口非激活
Exit:在应用程序关闭之前发生
Browser类型的应用
Navigating:在应用程序中的导航器请求新导航时发生
LoadCompleted:在已经加载、分析并开始呈现应用程序中的导航器导航到的内容时发生
Navigated:在已经找到应用程序中的导航器要导航到的内容时发生,尽管此时该内容可能尚未完成加载
NavigationFailed:在应用程序中的导航器在导航到所请求内容时出现错误的情况下发生
NavigationProgress:在由应用程序中的导航器管理的下载过程中定期发生,以提供导航进度信息
NavigationStopped:在调用应用程序中的导航器的 StopLoading 方法时发生,或者当导航器在当前导航正在进行期间请求了一个新导航时发生
异常捕获事件
DispatcherUnhandledException:在异常由应用程序引发但未进行处理时发生,UI线程,无法捕获多线程异常
UnhandledException:专门捕获所有线程中的异常
UnobservedTaskException:专门捕获Task异常,在垃圾回收的时候触发(具体时间不确定),如要想立马捕获,可使用GC.Collect(0);GC.WaitForPendingFinalizers();
窗体事件
SourceInitialized:操作系统给窗口分配 句柄的时候触发,获取窗口句柄
ContentRendered:内容渲染,窗口首次呈现
Loaded:内容渲染
Activated:当应用程序成为前台应用程序时发生,只针对当前窗口
Deactivated:当应用程序停止作为前台应用程序时发生,只针对当前窗口
Closing:关闭中
Closed:关闭
相关代码
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace WPF事件
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public App()
{
//在调用 Application 对象的 Run 方法时发生。
this.Startup += App_Startup;
// Browser类型的应用
// 在应用程序中的导航器请求新导航时发生。
this.Navigating += App_Navigating;
//在已经加载、分析并开始呈现应用程序中的导航器导航到的内容时发生
this.LoadCompleted += App_LoadCompleted;
//在已经找到应用程序中的导航器要导航到的内容时发生,尽管此时该内容可能尚未完成加载。
this.Navigated += App_Navigated;
//在应用程序中的导航器在导航到所请求内容时出现错误的情况下发生。
this.NavigationFailed += App_NavigationFailed;
//在由应用程序中的导航器管理的下载过程中定期发生,以提供导航进度信息。
this.NavigationProgress += App_NavigationProgress;
//在调用应用程序中的导航器的 StopLoading 方法时发生,或者当导航器在当前导航正在进行期间请求了一个新导航时发生。
this.NavigationStopped += App_NavigationStopped;
//在用户通过注销或关闭操作系统而结束 Windows 会话时发生。
this.SessionEnding += App_SessionEnding;
//当应用程序成为前台应用程序时发生。
//App任意窗口激活
this.Activated += App_Activated;
//当应用程序停止作为前台应用程序时发生。
//App中所有窗口非激活
this.Deactivated += App_Deactivated;
//在应用程序关闭之前发生,无法取消。
this.Exit += App_Exit;
//在异常由应用程序引发但未进行处理时发生。UI线程
//无法捕获多线程异常
this.DispatcherUnhandledException += App_DispatcherUnhandledException;
//专门捕获所有线程中的异常
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
//专门捕获Task异常,在垃圾回收的时候触发(具体时间不确定),如要想立马捕获,可使用GC.Collect(0);GC.WaitForPendingFinalizers();
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
}
#region 异常捕获
private void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
Debug.WriteLine("-----App_DispatcherUnhandledException--" + e.Exception.Message);
}
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Debug.WriteLine("-----CurrentDomain_UnhandledException--" + (e.ExceptionObject as Exception).Message);
}
private void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e)
{
Debug.WriteLine("-----TaskScheduler_UnobservedTaskException--" + e.Exception.Message);
e.SetObserved();
}
#endregion
private void App_Startup(object sender, StartupEventArgs e)
{
Debug.WriteLine("-----App_Startup");
}
private void App_SessionEnding(object sender, SessionEndingCancelEventArgs e)
{
Debug.WriteLine("-----App_SessionEnding");
e.Cancel = true;// 列表 有哪些程序未关闭, 仍然关闭、取消
}
private void App_Activated(object? sender, EventArgs e)
{
Debug.WriteLine("-----App_Activated");
}
private void App_Deactivated(object? sender, EventArgs e)
{
Debug.WriteLine("-----App_Deactivated");
}
private void App_Exit(object sender, ExitEventArgs e)
{
Debug.WriteLine("-----App_Exit");
}
#region Browser类型的应用(Page)
private void App_Navigating(object sender, System.Windows.Navigation.NavigatingCancelEventArgs e)
{
MessageBox.Show("-----App_Navigating");
}
private void App_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
Debug.WriteLine("-----App_LoadCompleted");
}
private void App_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
Debug.WriteLine("-----App_Navigated");
}
private void App_NavigationFailed(object sender, System.Windows.Navigation.NavigationFailedEventArgs e)
{
Debug.WriteLine("-----App_NavigationFailed");
}
private void App_NavigationProgress(object sender, System.Windows.Navigation.NavigationProgressEventArgs e)
{
Debug.WriteLine("-----App_NavigationProgress");
}
private void App_NavigationStopped(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
Debug.WriteLine("-----App_NavigationStopped");
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WPF事件
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
int i = 0;
public MainWindow()
{
InitializeComponent();
_ = 1 / i;// 除零
Task.Run(() =>
{
_ = 1 / i;
// C#的垃圾回收的时候
// 不知道什么时候
});
// 操作系统给窗口分配 句柄 的时候触发
// 获取窗口句柄
this.SourceInitialized += MainWindow_SourceInitialized;
// 内容渲染 窗口首次呈现
this.ContentRendered += MainWindow_ContentRendered;
//
this.Loaded += MainWindow_Loaded;
// 只针对当前窗口
this.Activated += MainWindow_Activated;
this.Deactivated += MainWindow_Deactivated;
//
this.Closing += MainWindow_Closing;
this.Closed += MainWindow_Closed;
}
private void MainWindow_Closed(object? sender, EventArgs e)
{
Debug.WriteLine("-----MainWindow_Closed");
}
private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
Debug.WriteLine("-----MainWindow_Closing");
e.Cancel = true;
}
private void MainWindow_Deactivated(object? sender, EventArgs e)
{
Debug.WriteLine("-----MainWindow_Deactivated");
}
private void MainWindow_Activated(object? sender, EventArgs e)
{
Debug.WriteLine("-----MainWindow_Activated");
}
private void MainWindow_ContentRendered(object? sender, EventArgs e)
{
Debug.WriteLine("-----MainWindow_ContentRendered");
}
private void MainWindow_SourceInitialized(object? sender, EventArgs e)
{
Debug.WriteLine("-----MainWindow_SourceInitialized");
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
Debug.WriteLine("-----MainWindow_Loaded");
GC.Collect(0);
GC.WaitForPendingFinalizers();
}
}
}