WPF-3D图形

news2025/1/10 23:29:15

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标签,实现如下图所示的效果。

image-20230126121543225

<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

1

<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使用文档

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/179603.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

InstanceNorm LayerNorm

InstanceNorm && LayerNorm author: SUFEHeisenberg date: 2023/01/26 先说结论: 将Transformer类比于RNN&#xff1a;一个token就是一层layer&#xff0c;对一整句不如token有意义原生Bert代码或huggingface中用的都是InstanceNorm instead of LayerNorm&#xff…

【AAAI2023】Head-Free Lightweight Semantic Segmentation with Linear Transformer

论文&#xff1a;【AAAI2023】Head-Free Lightweight Semantic Segmentation with Linear Transformer 代码&#xff1a;https://github.com/dongbo811/AFFormer 这是来自阿里巴巴的工作&#xff0c;作者构建了一个轻量级的Transformer网络用于语义分割&#xff0c;主要有两点…

发现下属的学历造假,但是他的工作能力又很强,该开除他吗?

在职场上混&#xff0c;学历是敲门砖还是定音锤呢&#xff1f;一位网友问&#xff1a;发现下属的学历造假&#xff0c;但是他的工作能力又很强&#xff0c;该开除他吗?有人觉得一定要开除&#xff0c;这就是钻空子&#xff0c;受影响最大的人不是他&#xff0c;而是那些真才实…

上采样与下采样

数据分析中的上采样和下采样 背景&#xff1a; 在分类问题中&#xff0c;由于各种原因&#xff0c;我们所获取到的数据集很容易出现正负样本的不平衡&#xff0c;或者某些数据特别多&#xff0c;有些数据则特别少&#xff0c;在这样的数据集中&#xff0c;进行训练&#xff0c…

OpenCV直方图Java 演示程序

直方图Java 演示程序以下文件编码为utf-8 为佳。代码文件名&#xff1a;OpenCvMain.javapackage org.opencv;import java.net.URL;import java.util.LinkedList;import java.util.List;import org.opencv.core.Core;import org.opencv.core.CvType;import org.opencv.core.Mat;…

Linux常用命令——setpci命令

在线Linux命令查询工具(http://www.lzltool.com/LinuxCommand) setpci 查询和配置PCI设备的使用工具 补充说明 setpci命令是一个查询和配置PCI设备的使用工具。 语法 setpci(选项)(参数)选项 -v&#xff1a;显示指令执行的细节信息&#xff1b; -f&#xff1a;当没有任何…

Opencv形态学操作——腐蚀、膨胀、梯度、开运算、闭运算、礼帽、黑帽(附案例详细讲解及可执行代码)

Opencv形态学操作 腐蚀膨胀梯度开运算闭运算礼帽黑帽总结腐蚀 在地理或者化学中,我们学习过腐蚀,是指在某种 作用下产生损耗与破坏的过程。你也可以理解为减肥。在Opencv中,腐蚀操作可以使白色轮廓变小,也就是说可以去除一些白色的噪声。 如果你接触过卷积核的话,腐蚀就更…

【JavaSE专栏8】运算符、表达式和语句

作者主页&#xff1a;Designer 小郑 作者简介&#xff1a;Java全栈软件工程师一枚&#xff0c;来自浙江宁波&#xff0c;负责开发管理公司OA项目&#xff0c;专注软件前后端开发&#xff08;Vue、SpringBoot和微信小程序&#xff09;、系统定制、远程技术指导。CSDN学院、蓝桥云…

盖子的c++小课堂——第十三讲:二维数组

前言 过了几天了&#xff0c;终于有时间更新了&#xff0c;有个通知&#xff0c;以后我不用颜色区分了&#xff0c;不然换了背景看不见&#xff0c;理解一下&#xff0c;蟹蟹~~ 举例 作者&#xff1a;一下是某次奥运会的奖牌榜&#xff0c;你知道如何储存奖牌榜吗~~ 粉丝&am…

机器学习中软投票和硬投票的不同含义和理解

设置一个场景&#xff0c;比如对于今天音乐会韩红会出现的概率三个人三个观点 A&#xff1a;韩红出现的概率为47% B&#xff1a;韩红出现的概率为57% C&#xff1a;韩红出现的概率为97% 软投票&#xff1a;软投票会认为韩红出现的概率为1/3*(47%57%97%)67% 硬投票&#xff1a;…

“子序列问题”系列总结,一文读懂(Java实现)

目录 前言 一、最长递增子序列 1.1、dp定义 1.2、递推公式 1.3、初始化 1.4、注意 1.5、解题代码 二、最长连续递增序列 2.1、分析 2.2、解题代码 三、最长重复子数组 3.1、dp定义 3.2、递推公式 3.3、初始化 3.4、解题代码 四、最长公共子序列 4.1、分析 4.2…

Opencv项目实战:20 单手识别数字0到5

目录 0、项目介绍 1、效果展示 2、项目搭建 3、项目代码展示 HandTrackingModule.py Figures_counter.py 4、项目资源 5、项目总结 0、项目介绍 今天要做的是单手识别数字0到5&#xff0c;通过在窗口展示&#xff0c;实时的展示相应的图片以及文字。 在网上找了很久的…

硬核来袭!!!一篇文章教你入门Python爬虫网页解析神器——BeautifulSoup详细讲解

文章目录一、BeautifulSoup介绍二、安装三、bs4数据解析的原理四、bs4 常用的方法和属性1、BeautifulSoup构建1.1 通过字符串构建1.2 从文件加载2、BeautifulSoup四种对象2.1 Tag对象2.2 NavigableString对象2.3 BeautifulSoup对象2.4 Comment对象五、contents、children与desc…

springboot自定义拦截器的简单使用和一个小例子

springboot自定义拦截器的使用1. 自定义拦截器2. 拦截器登录验证的小demo2.1 配置pom.xml2.2 创建User的bean组件2.3 创建需要的表单页面以及登录成功的页面2.4 编写controller映射关系2.5 自定义拦截器类&#xff0c;实现intercepetor接口2.6注册添加拦截器&#xff0c;自定义…

【SpringCloud】Nacos集群搭建

集群结构图官方给出的Nacos集群图如下&#xff1a;其中包含3个nacos节点&#xff0c;然后一个负载均衡器代理3个Nacos。这里负载均衡器可以使用nginx。我们接下来要尝试 Nacos集群搭建&#xff0c;效果图如下所示&#xff1a;三个nacos节点的地址&#xff1a;节点ipportnacos1l…

二、Java框架之Spring注解开发

文章目录1. IOC/DI注解开发1.1 Component注解ComponentController Service Repository1.2 纯注解开发模式1.3 注解开发bean管理ScopePostConstruct PreDestroy1.4 注解开发依赖注入Autowired QualifierValuePropertySource1.5 第三方bean管理Beanimport&#xff08;多个Config类…

Redisson 完成分布式锁

1、简介 Redisson 是架设在 Redis 基础上的一个 Java 驻内存数据网格&#xff08;In-Memory Data Grid&#xff09;。充分 的利用了 Redis 键值数据库提供的一系列优势&#xff0c;基于 Java 实用工具包中常用接口&#xff0c;为使用者 提供了一系列具有分布式特性的常用工具类…

JavaWeb | 揭开SQL注入问题的神秘面纱

本专栏主要是记录学习完JavaSE后学习JavaWeb部分的一些知识点总结以及遇到的一些问题等&#xff0c;如果刚开始学习Java的小伙伴可以点击下方连接查看专栏 本专栏地址&#xff1a;&#x1f525;JDBC Java入门篇&#xff1a; &#x1f525;Java基础学习篇 Java进阶学习篇&#x…

MyEclipse提示过期,MyEclipse Subscription Expired解决方案

一、错误描述 某一天打开MyEclipse&#xff0c;突然发现出现如下提示框&#xff1a; 1.错误日志 Thank you for choosing MyEclipse Your license expired 1091 days ago. To continue use of MyEclipse please choose "Buy" to purchase a MyEclipse license. I…

离散系统的数字PID控制仿真-3

离散PID控制的封装界面如图1所示&#xff0c;在该界面中可设定PID的三个系数、采样时间及控制输入的上下界。仿真结果如图2所示。图1 离散PID控制的封装界面图2 阶跃响应结果仿真图&#xff1a;离散PID控制的比例、积分和微分三项分别由Simulink模块实现。离散PID控制器仿真图&…