草庐IT

WPF-3D图形

步、步、为营 2023-04-05 原文

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. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  2. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  3. Unity 3D 制作开关门动画,旋转门制作,推拉门制作,门把手动画制作 - 2

    Unity自动旋转动画1.开门需要门把手先动,门再动2.关门需要门先动,门把手再动3.中途播放过程中不可以再次进行操作觉得太复杂?查看我的文章开关门简易进阶版效果:如果这个门可以直接打开的话,就不需要放置"门把手"如果门把手还有钥匙需要旋转,那就可以把钥匙放在门把手的"门把手",理论上是可以无限套娃的可调整参数有:角度,反向,轴向,速度运行时点击Test进行测试自己写的代码比较垃圾,命名与结构比较拉,高手轻点喷,新手有类似的需求可以拿去做参考上代码usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;u

  4. [Vuforia]二.3D物体识别 - 2

    之前说过10之后的版本没有3dScan了,所以还是9.8的版本或者之前更早的版本。 3d物体扫描需要先下载扫描的APK进行扫面。首先要在手机上装一个扫描程序,扫描现实中的三维物体,然后上传高通官网,在下载成UnityPackage类型让Unity能够使用这个扫描程序可以从高通官网上进行下载,是一个安卓程序。点到Tools往下滑,找到VuforiaObjectScanner下载后解压数据线连接手机,将apk文件拷入手机安装然后刚才解压文件中的Media文件夹打开,两个PDF图打印第一张A4-ObjectScanningTarget.pdf,主要是用来辅助扫描的。好了,接下来就是扫描三维物体。将瓶

  5. python - Ruby 或 Python 的 3d 游戏引擎? - 2

    关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于StackOverflow来说是偏离主题的,因为它们往往会吸引自以为是的答案和垃圾邮件。相反,describetheproblem以及迄今为止为解决该问题所做的工作。关闭9年前。Improvethisquestion是否有适用于这些的3d游戏引擎?

  6. ruby - 图形与 Prawn - 2

    寻找一个gem为prawn添加gtraphing功能,我找到了thisone但它似乎有点过时了。有没有更活跃的gem? 最佳答案 直接在Prawn内部绘图没有什么非常活跃的,但是Gruff是一个高度可配置的活跃gem,可以让你制作各种图表。事实上,prawn-graph基本上是gruff的包装器!我的建议是使用gruff生成所需的图表和图形,然后将它们作为图像嵌入到Prawn文档中。所以代码看起来像这样:g=Gruff::Line.new(400)g.title="TransparentBackground"g.theme={:co

  7. 【自动驾驶环境感知项目】——基于Paddle3D的点云障碍物检测 - 2

    文章目录1.自动驾驶实战:基于Paddle3D的点云障碍物检测1.1环境信息1.2准备点云数据1.3安装Paddle3D1.4模型训练1.5模型评估1.6模型导出1.7模型部署效果附录show_lidar_pred_on_image.py1.自动驾驶实战:基于Paddle3D的点云障碍物检测项目地址——自动驾驶实战:基于Paddle3D的点云障碍物检测课程地址——自动驾驶感知系统揭秘1.1环境信息硬件信息CPU:2核AI加速卡:v100总显存:16GB总内存:16GB总硬盘:100GB环境配置Python:3.7.4框架信息框架版本:PaddlePaddle2.4.0(项目默认框架版本为2.3

  8. Unity3D : 本地坐标系,世界坐标系,和TransformPoint,TransformVector,TransformDirection的区别 - 2

    目录一、世界坐标系与本地坐标系二、srcGameObject.transform.TransformPoint(Vector3 vec)三、srcGameObject.transform.TransformVector(Vector3 vec)四、srcGameObject.transform.TransformDirection(Vector3 vec)五:示例一、世界坐标系与本地坐标系    世界坐标很好理解,就是模型的transform.position,通常在无父物体的情况下,创建出来的模型默认位置就是世界坐标系的原点。    每个物体都有自身的坐标系,此坐标系就是本地坐标系。本地坐标

  9. ruby - ruby 的 3D 引擎 - 2

    我正在寻找用于开发ruby​​游戏的3D引擎。我找到了一些类似G3Druby或ogreb的东西。哪个更好用,功能更好?还有比这些更好的引擎吗? 最佳答案 两者似乎都是G3D和Ogre的包装器,因此您实际上应该比较G3D或Ogre是否更适合您的需求。通过包装器的大部分ruby​​外访问将在设置场景时进行,因此繁重的工作(每一帧)仍然在C/C++库和图形硬件上完成。因此,您应该比较这两个库。我不知道G3D,但它似乎提供了Ogre所缺乏的离线渲染功能。如果您需要专业游戏渲染引擎的广泛功能,Ogre通常是首选,并且您会发现几乎所有您会遇到的

  10. U3D游戏开发工程师正确入行姿势指南 - 2

    2021年,游戏圈上演了一场精彩绝伦的抢人大战。在上海游戏圈,年薪百万的人越来越多了。据多名HR估算,在上海,过去一年TA、引擎、美术等稀缺岗位拟的薪资涨幅大概在20%-30%左右。某位圈内知名资深游戏猎头对此发出感叹:“50K的数值策划、角色原画;70K的技术美术;80K的技术总监...他们的年薪总包都接近百万,就连应届生入行的薪资也水涨船高,这要是放在以往都是不敢想象的”。以往含年薪、期权等的年总包收入上百万元,起码得是总监级别。如今工作五六年的人从广深跳到上海游戏公司,年薪能从50-70万跃上100万元,拿百万年薪的游戏从业者越来越多了上海游戏圈近年发展迅速,既有颇具发展潜力的中生代F4

随机推荐