WPF-3D图形

WPF的3D功能可以在不编写任何c#代码的情况下进行绘制,只需要使用xaml即可完成3D图形的渲染。本文主要讲述了WPF-3D中的关键概念, 以及常用到的命中测试、2d控件如何在3D对象中进行渲染,除此之外,还演示了如何导入外部3D模型。

关键概念

视口

视口指的是图像要展示在哪里,可以理解为展示图形的舞台。在WPF中视口使用Viewport3D标签表示。

相机

如果把视口比作舞台,那相机就可以理解为观众的眼睛,不同的眼睛位置会看到不同的角度。

<Viewport3D>
<!--相机-->
<Viewport3D.Camera>
<!--透视相机-->
<PerspectiveCamera Position="8,5,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="45" Axis="0,1,0"/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
</PerspectiveCamera.Transform>
</PerspectiveCamera>
<!--正交相机,用法类似-->
<!--<OrthographicCamera/>-->
</Viewport3D.Camera>

光源

没有光源也就看不到3D对象

<!--光线-->
<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>

材质

3D几何对象只是将轮廓定义出来,表面是没有定义的,所以需要使用材质来展现出不同的物体表面。也可以理解为3D几何对象只是勾勒出物体的轮廓,而材质则是上颜色。

<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>

3D对象

3D对象则是具体的对象,在WPF中视口使用<ModelUIElement3D>标签表示。在WPF中,图形是以三角面片作为最基本的展示单元,因为三角形是最稳定的即三个点可以确定出唯一的一个平面,任何复杂的图形都是由多个三角面片组成的。在给TriangleIndices属性赋值时,一定注意三个点的顺序。

命中测试(鼠标交互)

想要使用鼠标点击得到某个图形,可以在具体的某个3D对象中,增加MouseLeftButtonDown事件

<ModelUIElement3D MouseLeftButtonDown="ModelUIElement3D_MouseLeftButtonDown">事件中可以进行改变颜色等操作

private void ModelUIElement3D_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
ModelUIElement3D mui3d = sender as ModelUIElement3D;
var model = mui3d.Model as GeometryModel3D;
(model.Material as DiffuseMaterial).Brush = Brushes.Orange;
}

如果有很多3D对象,在每个具体的对象上面增加事件会很麻烦,也可以直接在Viewport3D中增加事件

<Viewport3D MouseLeftButtonDown="Viewport3D_MouseLeftButtonDown">在时间中急性转换处理

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)
{
...//具体操作
}
}

3D对象中2D控件渲染

如果要在3D对象中增加控件,可以使用Viewport2DVisual3D标签,实现如下图所示的效果。

<Viewport3D>
<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="White"/>
</Viewport2DVisual3D.Material>
<Viewport2DVisual3D.Visual>
<Border BorderThickness="1" BorderBrush="Yellow">
<StackPanel>
<TextBlock Text="Hello World" Foreground="Green" />
<Button Content="Button" Click="Button_Click"/>
</StackPanel>
</Border>
</Viewport2DVisual3D.Visual>
</Viewport2DVisual3D>
<Viewport3D>

外部导入3D模型

在wpf中绘制3D模型还是非常麻烦的,在实际工作中用的比较多的是从外部导入已有的3d模型。推荐一个比较好的第三方库HelixToolKit

<Window x:Class="WpfApp2.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:WpfApp2"
xmlns:helix="http://helix-toolkit.org/wpf"
mc:Ignorable="d"
Title="MainWindow" 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>
namespace WpfApp2
{
public partial class MainWindow : Window
{
List<string> modelPaths = new List<string>();
string basePath = AppDomain.CurrentDomain.BaseDirectory + "\\ModelFiles\\";
public MainWindow()
{
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;
}
}
}

HelixToolKit使用文档

WPF-3D图形的更多相关文章

  1. 去除WPF中3D图形的锯齿

    原文:去除WPF中3D图形的锯齿 理论上讲PC在计算3D图形的时候是无法避免不出现锯齿的,因为3D图形都是又若干个三角形组成,如果3D图形想平滑就必须建立多个三角形,你可以想象一下正5边形和正100边 ...

  2. WPF,Silverlight与XAML读书笔记第三十九 - 可视化效果之3D图形

    原文:WPF,Silverlight与XAML读书笔记第三十九 - 可视化效果之3D图形 说明:本系列基本上是<WPF揭秘>的读书笔记.在结构安排与文章内容上参照<WPF揭秘> ...

  3. WPF 3D 知识点大全以及实例

    引言 现在物联网概念这么火,如果监控的信息能够实时在手机的客服端中以3D形式展示给我们,那种体验大家可以发挥自己的想象. 那生活中我们还有很多地方用到这些,如上图所示的Kinect 在医疗上的应用,当 ...

  4. WPF 3D 小小小小引擎 - ·WPF 3D变换应用

    原文:WPF 3D 小小小小引擎 - ·WPF 3D变换应用 WPF可以提供的3D模型使我们可以轻松地创建3D实体,虽然目前来看还很有一些性能上的问题,不过对于一些简单的3D应用应该是可取的,毕竟其开 ...

  5. WPF 3D:使用GeometryModel3D的BackMaterial

    原文 WPF 3D:使用GeometryModel3D的BackMaterial 使用BackMaterial,我们可以定义3D物体的内部材质(或者说是背面),比如,我们定义一个四方体容器,外面现实的 ...

  6. WPF 3D:MeshGeometry3D的定义和光照

    原文 WPF 3D:MeshGeometry3D的定义和光照 由于WPF计算光照会根据整个平面的方向向量,所以如果在不同面上使用同一个点可能会达到不同的光照效果.让我们用不同的定义Mesh的方法来演示 ...

  7. WPF 3D: MeshGeometry3D纹理坐标的正确定义

    原文 WPF 3D: MeshGeometry3D纹理坐标的正确定义 为了使基于2D的纹理显示在3D对象中,我们必须定义3D Mesh对象的纹理贴图坐标.在WPF中,此项功能则通过MeshGeomet ...

  8. WPF 3D:简单的Point3D和Vector3D动画创造一个旋转的正方体

    原文:WPF 3D:简单的Point3D和Vector3D动画创造一个旋转的正方体 运行结果: 事实上很简单,定义好一个正方体,处理好纹理.关于MeshGeometry3D的正确定义和纹理这里就不多讲 ...

  9. WPF 3D model - Sphere, Cone, and Cylinder

    原文:WPF 3D model - Sphere, Cone, and Cylinder   Extending Visual3D - Sphere, Cone, and Cylinder http: ...

  10. WPF 3D 获取鼠标在场景的3d坐标

    原文:WPF 3D 获取鼠标在场景的3d坐标 上一篇中我们谈到了WPF 3d做图的一些简单原理,这里我们简单介绍一下怎样获得鼠标在场景中的3d坐标,知道了3d坐标就可以进行很多操作了: 首先介绍一下3 ...

随机推荐

  1. 测试架构师CAP原理(最简单)

    测试架构师CAP原理(最简单) 很多人都不是很了解CAP理论,其实CAP很简单,不要想复杂了! C:一致性,就是数据一致性,就是数据不出错! A:可用性,就是说速度快,不延迟,无论请求成功失败都很快返 ...

  2. Codeforces Round #820 (Div. 3) A-G

    比赛链接 A 题解 知识点:模拟 时间复杂度 \(O(1)\) 空间复杂度 \(O(1)\) 代码 #include <bits/stdc++.h> #define ll long lon ...

  3. 从0到1搭建redis6.0.7续更~

    "心有所向,日复一日,必有精进" 前言: 想必大家看完我之前写的搭建redis服务器,大家都已经把redis搭建起来了吧如果没有搭建起来的小可爱请移步这里哦从0到1搭建redis6 ...

  4. luoguP1505旅游(处理边权的树剖)

    /* luogu1505 非常简单的处理边权的树剖题. 在树上将一条边定向,把这条边的权值赋给这条边的出点 树剖的时候不计算lca权值即可 */ #include<bits/stdc++.h&g ...

  5. MongoDB 数据库的学习

    一.MongoDB的简介 1.MongoDB是什么? MongoDB 是由 C++ 语言编写的,基于分布式文件存储的数据库,是一个介于关系数据库和非关系数据库之间的产品,是最接近于关系型数据库的 No ...

  6. 基于 MQ 的分布式 Serverless 多租任务处理系统架构演进

    本文作者:史明伟 , 阿里云智能高级技术专家. 1 Serverless 异步任务处理系统诞生和挑战 无论是对于云的开发者,还是尝试业务升级的企业客户,Serverless的三个概念 "极致 ...

  7. 有关二级指针char **p 的问题

    int main() { char *str[] = {"welcome", "to", "Fortemdia", "Nanjin ...

  8. 获取联通光猫PT952G的管理员密码

    前言 普通用户的帐号和密码在光猫的背面 输入光猫网关即可跳转到登录界面 但是没有什么权限操作东西,所以我找到了管理员界面 输入 网关+cu.html 即可跳转到管理员界面 例如我这里是http://1 ...

  9. 解决PyQt5报错defaultServiceProvider::requestService(): no service found for..

    简述 之前因为这个报错解决了很长时间,因为我之前一直是用 pip3 工具安装的 PyQt5 ,但是用 pip3 工具安装 PyQt5 后, 自己写的音乐播放器一直没有声音,而且还有不能调用 fcitx ...

  10. uniCloud云开发入门以及对传统开发方式的思考

    事情缘由 作为选修了移动互联网应用的一员,老师讲的什么JS基础,还有ES6和uniapp,当然是没怎么听,因为是之前大二的时候都大概看过. 但是快到期末,老师讲了云开发,并且布置了与此相关的大作业,自 ...