WPF-3D图形
WPF的3D功能可以在不编写任何c#代码的情况下进行绘制,只需要使用xaml即可完成3D图形的渲染。本文主要讲述了WPF-3D中的关键概念, 以及常用到的命中测试、2d控件如何在3D对象中进行渲染,除此之外,还演示了如何导入外部3D模型。
关键概念
视口
视口指的是图像要展示在哪里,可以理解为展示图形的舞台。在WPF中视口使用Viewport3D
标签表示。
相机
如果把视口比作舞台,那相机就可以理解为观众的眼睛,不同的眼睛位置会看到不同的角度。
<Viewport3D>
<!--相机-->
<Viewport3D.Camera>
<!--透视相机-->
<PerspectiveCameraPosition="8,5,10"
LookDirection="-7,-2,-10"
FarPlaneDistance="40"
NearPlaneDistance="10"
FieldOfView="60">
<PerspectiveCamera.Transform>
<RotateTransform3DCenterX="1.5"CenterY="1"CenterZ="0.5">
<RotateTransform3D.Rotation>
<AxisAngleRotation3DAngle="45"Axis="0,1,0"/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
</PerspectiveCamera.Transform>
</PerspectiveCamera>
<!--正交相机,用法类似-->
<!--<OrthographicCamera/>-->
</Viewport3D.Camera>
光源
没有光源也就看不到3D对象
<!--光线-->
<ModelVisual3D>
<ModelVisual3D.Content>
<Model3DGroup>
<!--散射光线-->
<AmbientLightColor="#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>
<!--散射材质-->
<DiffuseMaterialBrush="Blue"/>
<!--镜面材质-->
<!--<SpecularMaterial SpecularPower="1" Brush="Blue"/>-->
<!--自发光材质-->
<!--<EmissiveMaterial Color="Green" />-->
</GeometryModel3D.Material>
<GeometryModel3D.Geometry>
<MeshGeometry3DPositions="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">
事件中可以进行改变颜色等操作
privatevoidModelUIElement3D_MouseLeftButtonDown(objectsender, MouseButtonEventArgse)
{
ModelUIElement3Dmui3d=senderasModelUIElement3D;
varmodel=mui3d.ModelasGeometryModel3D;
(model.MaterialasDiffuseMaterial).Brush=Brushes.Orange;
}
如果有很多3D对象,在每个具体的对象上面增加事件会很麻烦,也可以直接在Viewport3D
中增加事件
<Viewport3D MouseLeftButtonDown="Viewport3D_MouseLeftButtonDown">
在时间中急性转换处理
privatevoidViewport3D_MouseLeftButtonDown(objectsender, MouseButtonEventArgse)
{
Viewport3Dviewport3D=senderasViewport3D;
Pointlocation=e.GetPosition(viewport3D);
HitTestResulthitTestResult=VisualTreeHelper.HitTest(viewport3D, location);
if (hitTestResult!=null)
{
...//具体操作
}
}
3D对象中2D控件渲染
如果要在3D对象中增加控件,可以使用Viewport2DVisual3D
标签,实现如下图所示的效果。
<Viewport3D>
<Viewport2DVisual3D>
<Viewport2DVisual3D.Geometry>
<MeshGeometry3DPositions="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>
<DiffuseMaterialViewport2DVisual3D.IsVisualHostMaterial="True"Brush="White"/>
</Viewport2DVisual3D.Material>
<Viewport2DVisual3D.Visual>
<BorderBorderThickness="1"BorderBrush="Yellow">
<StackPanel>
<TextBlockText="Hello World"Foreground="Green"/>
<ButtonContent="Button"Click="Button_Click"/>
</StackPanel>
</Border>
</Viewport2DVisual3D.Visual>
</Viewport2DVisual3D>
<Viewport3D>
外部导入3D模型
在wpf中绘制3D模型还是非常麻烦的,在实际工作中用的比较多的是从外部导入已有的3d模型。推荐一个比较好的第三方库HelixToolKit
<Windowx: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>
<PerspectiveCameraFieldOfView="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>
<LinearGradientBrushEndPoint="0.5,1"StartPoint="0.5,0">
<GradientStopColor="#444"Offset="0"/>
<GradientStopColor="#EEE"Offset="1"/>
</LinearGradientBrush>
</helix:HelixViewport3D.Background>
<helix:GridLinesVisual3DWidth="16000"Length="16000"Thickness="2"MinorDistance="500"MajorDistance="500"Fill="Gray"/>
<!--很重要,没有灯光场景是黑的-->
<helix:DefaultLights/>
<ModelVisual3Dx:Name="model"></ModelVisual3D>
</helix:HelixViewport3D>
</Grid>
</Window>
namespaceWpfApp2
{
publicpartialclassMainWindow : Window
{
List<string>modelPaths=newList<string>();
stringbasePath=AppDomain.CurrentDomain.BaseDirectory+"\\ModelFiles\\";
publicMainWindow()
{
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=newMouseGesture(MouseAction.RightClick);
viewPort3d.PanGesture=newMouseGesture(MouseAction.LeftClick);
}
privatevoidMainWindow_Loaded(objectsender, RoutedEventArgse)
{
viewPort3d.Camera.LookDirection=newVector3D(2038, -5200, -2930);
viewPort3d.Camera.UpDirection=newVector3D(-0.145, 0.372, 0.917);
viewPort3d.Camera.Position=newPoint3D(-1571, 4801, 3774);
this.model.Content=InitializeModels(this.modelPaths);
}
privateModel3DGroupInitializeModels(List<string>modelsNames)
{
Model3DGroupgroup=newModel3DGroup();
try
{
ModelImporterimport=newModelImporter();
foreach (stringmodelNameinmodelsNames)
{
varmaterialGroup=newMaterialGroup();
ColormainColor=Colors.White;
//EmissiveMaterial emissMat = new EmissiveMaterial(new SolidColorBrush(mainColor));
DiffuseMaterialdiffMat=newDiffuseMaterial(newSolidColorBrush(mainColor));
//SpecularMaterial specMat = new SpecularMaterial(new SolidColorBrush(mainColor), 2000);
//materialGroup.Children.Add(emissMat);
materialGroup.Children.Add(diffMat);
//materialGroup.Children.Add(specMat);
varlink=import.Load(basePath+modelName);
GeometryModel3Dmodel=link.Children[0] asGeometryModel3D;
model.Material=materialGroup;
model.BackMaterial=materialGroup;
group.Children.Add(link);
}
}
catch (Exceptione)
{
MessageBox.Show("未知异常:"+e.StackTrace);
}
returngroup;
}
}
}