基于 HelixToolkit.SharpDX 渲染ply点云 [复制链接]
发表于 4 天前 | 显示全部楼层 |阅读模式
HelixToolkit.SharpDX 是 HelixToolkit 生态中基于 DirectX(DX) 底层本领封装的 .NET 开源 3D 可视化库;DirectX 是微软为 Windows 平台开辟的底层多媒体 API,可高效调用显卡、声卡等硬件实现高性能图形渲染,而该库基于此本领,兼容 .NET Framework/.NET Core/.NET 5+ 全平台,专为 Windows 桌面应用提供低门槛、高性能的 3D 渲染,完善适配呆板臂可视化、点云处理惩罚、装备仿真等工业开辟场景;
一、NuGet 包管理器中下载干系包

NuGet 依赖:安装 HelixToolkit.Wpf 和HelixToolkit.SharpDX.Core.Wpf

二、引入HelixToolkit.SharpDX

xmlns:hx="http://helix-toolkit.org/wpf/SharpDX"
三、示例工程文件

MainWindow.xaml
  1. <Window
  2.     x:
  3.     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  4.     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  5.     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  6.     xmlns:hx="http://helix-toolkit.org/wpf/SharpDX"
  7.     xmlns:local="clr-namespace:HelixToolkit.DX.PointCloud"
  8.     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  9.     xmlns:prism="http://prismlibrary.com/"
  10.     xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
  11.     xmlns:vm="clr-namespace:HelixToolkit.DX.PointCloud.ViewModels"
  12.     prism:ViewModelLocator.AutoWireViewModel="True"
  13.     ui:WindowHelper.SystemBackdropType="Mica"
  14.     ui:WindowHelper.UseModernWindow
  15.     mc:Ignorable="d">
  16.     <Grid>
  17.         <hx:Viewport3DX
  18.             BackgroundColor="Black"
  19.             EffectsManager="{Binding EffectsManager}"
  20.             IsRotationEnabled="True"
  21.             IsShadowMappingEnabled="True"
  22.             RotateAroundMouseDownPoint="True"
  23.             ShowCoordinateSystem="True"
  24.             ShowFrameRate="True"
  25.             ShowViewCube="True"
  26.             ZoomAroundMouseDownPoint="True"
  27.             ZoomExtentsWhenLoaded="True">
  28.             
  29.             <hx:Viewport3DX.InputBindings>
  30.                
  31.                 <KeyBinding Command="hx:ViewportCommands.ZoomExtents" Gesture="Control+E" />
  32.                
  33.                 <MouseBinding Command="hx:ViewportCommands.Rotate" Gesture="RightClick" />
  34.                
  35.                 <MouseBinding Command="hx:ViewportCommands.Zoom" Gesture="MiddleClick" />
  36.                
  37.                 <MouseBinding Command="hx:ViewportCommands.Pan" Gesture="LeftClick" />
  38.             </hx:Viewport3DX.InputBindings>
  39.             
  40.             <hx:Viewport3DX.Camera>
  41.                 <hx:PerspectiveCamera
  42.                     LookDirection="0,0,-10"
  43.                     Position="0,0,10"
  44.                     UpDirection="0,1,0" />
  45.             </hx:Viewport3DX.Camera>
  46.             
  47.             <hx:ShadowMap3D OrthoWidth="200" />
  48.             
  49.             <hx:AmbientLight3D Color="White" />
  50.             
  51.             <hx:DirectionalLight3D Direction="100, -100, -150" />
  52.             <hx:PointGeometryModel3D
  53.                 Figure="Ellipse"
  54.                 Geometry="{Binding BatchedGeometry}"
  55.                 Size="1,1"
  56.                 Color="White" />
  57.         </hx:Viewport3DX>
  58.         
  59.         <Button
  60.             Margin="20"
  61.             Padding="10,5"
  62.             HorizontalAlignment="Left"
  63.             VerticalAlignment="Top"
  64.             Command="{Binding LoadPlyCommand}"
  65.             Content="Load PLY File" />
  66.     </Grid>
  67. </Window>
复制代码
MainWindowViewModel
  1. using HelixToolkit.SharpDX.Core;
  2. using HelixToolkit.SharpDX.Core.Core;
  3. using HelixToolkit.Wpf.SharpDX;
  4. using Microsoft.Win32;
  5. using SharpDX;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Globalization;
  9. using System.IO;
  10. using System.Linq;
  11. using System.Text;
  12. using System.Threading.Tasks;
  13. using System.Windows;
  14. using System.Windows.Input;
  15. using System.Windows.Media;
  16. using System.Windows.Media.Media3D;
  17. using PerspectiveCamera = HelixToolkit.Wpf.SharpDX.PerspectiveCamera;
  18. namespace HelixToolkit.DX.PointCloud.ViewModels
  19. {
  20.     public class MainWindowViewModel : BindableBase, IDisposable
  21.     {
  22.         /// <summary>
  23.         /// 消息对话框实例
  24.         /// </summary>
  25.         private readonly IDialogHelper _dialogHelper;
  26.         public MainWindowViewModel(IContainerExtension container)
  27.         {
  28.             _dialogHelper = container.Resolve<IDialogHelper>();
  29.         }
  30.         private DefaultEffectsManager _effectsManager = new();
  31.         /// <summary>
  32.         /// 特效管理器
  33.         /// 管理渲染管线、着色器等
  34.         /// </summary>
  35.         public DefaultEffectsManager EffectsManager
  36.         {
  37.             get => _effectsManager;
  38.             set => SetProperty(ref _effectsManager, value);
  39.         }
  40.         private PointGeometry3D? _batchedGeometry;
  41.         /// <summary>
  42.         /// 点云几何体
  43.         /// 定义3D场景中的点云数据
  44.         /// </summary>
  45.         public PointGeometry3D? BatchedGeometry
  46.         {
  47.             get => _batchedGeometry;
  48.             set => SetProperty(ref _batchedGeometry, value);
  49.         }
  50.         /// <summary>
  51.         /// 选择PLY文件并加载点云事件
  52.         /// </summary>
  53.         public ICommand LoadPlyCommand => new DelegateCommand(SelectPlyFile);
  54.         /// <summary>
  55.         /// 选择PLY文件并加载点云
  56.         /// </summary>
  57.         private void SelectPlyFile()
  58.         {
  59.             var openFileDialog = new OpenFileDialog
  60.             {
  61.                 Filter = "PLY files (*.ply)|*.ply|All files (*.*)|*.*",
  62.                 Title = "Select a PLY file"
  63.             };
  64.             if (openFileDialog.ShowDialog() == true)
  65.             {
  66.                 try
  67.                 {
  68.                     LoadPlyFile(openFileDialog.FileName);
  69.                 }
  70.                 catch (Exception ex)
  71.                 {
  72.                     _dialogHelper.ShowMessageAsync("报错", $"Error loading file:\n{ex.Message}");
  73.                 }
  74.             }
  75.         }
  76.         /// <summary>
  77.         /// 加载PLY文件并创建点云几何体
  78.         /// </summary>
  79.         /// <param name="filePath"></param>
  80.         private void LoadPlyFile(string filePath)
  81.         {
  82.             // 解析PLY,获取位置和颜色(所有点都包含颜色)
  83.             var (positions, colors) = ParsePly(filePath);
  84.             if (positions.Count == 0)
  85.             {
  86.                 _dialogHelper.ShowMessageAsync("报错", "No points found in the file.");
  87.                 return;
  88.             }
  89.             // 创建几何体,设置位置和颜色
  90.             var geometry = new PointGeometry3D
  91.             {
  92.                 Positions = new Vector3Collection(positions),
  93.                 Colors = new Color4Collection(colors)   // colors 一定存在且数量匹配
  94.             };
  95.             // 创建点云模型
  96.             var pointModel = new PointGeometryModel3D
  97.             {
  98.                 Geometry = geometry,
  99.                 Size = new Size(1, 1),          // 点大小设大一点便于观察
  100.                 Figure = PointFigure.Ellipse,
  101.                 Color = Colors.White,
  102.             };
  103.             // 添加到视口
  104.             BatchedGeometry = geometry;
  105.             _dialogHelper.ShowMessageAsync("成功", $"成功加载 {positions.Count} 个点。");
  106.         }
  107.         /// <summary>
  108.         /// 解析ASCII PLY文件,提取顶点位置和颜色。
  109.         /// 假设文件包含:x y z red green blue (red/green/blue为uchar 0-255)
  110.         /// </summary>
  111.         private (List<Vector3> positions, List<Color4> colors) ParsePly(string filePath)
  112.         {
  113.             var positions = new List<Vector3>();
  114.             var colors = new List<Color4>();
  115.             bool readingData = false;
  116.             foreach (string line in File.ReadLines(filePath))
  117.             {
  118.                 string trimmed = line.Trim();
  119.                 if (string.IsNullOrEmpty(trimmed))
  120.                     continue;
  121.                 if (!readingData)
  122.                 {
  123.                     // 查找头部结束标志
  124.                     if (trimmed == "end_header")
  125.                     {
  126.                         readingData = true;
  127.                     }
  128.                     continue;
  129.                 }
  130.                 // 解析数据行,每行至少6个值:x y z r g b
  131.                 string[] parts = trimmed.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
  132.                 if (parts.Length < 6)
  133.                     continue;   // 不符合要求,跳过(但你的数据应该都符合)
  134.                 try
  135.                 {
  136.                     float x = float.Parse(parts[0], CultureInfo.InvariantCulture);
  137.                     float y = float.Parse(parts[1], CultureInfo.InvariantCulture);
  138.                     float z = float.Parse(parts[2], CultureInfo.InvariantCulture);
  139.                     positions.Add(new Vector3(x, y, z));
  140.                     // 解析RGB值(0-255),转换为0-1范围的float
  141.                     byte r = byte.Parse(parts[3], CultureInfo.InvariantCulture);
  142.                     byte g = byte.Parse(parts[4], CultureInfo.InvariantCulture);
  143.                     byte b = byte.Parse(parts[5], CultureInfo.InvariantCulture);
  144.                     colors.Add(new Color4(r / 255.0f, g / 255.0f, b / 255.0f, 1.0f));
  145.                 }
  146.                 catch (Exception ex)
  147.                 {
  148.                     // 如果某行解析失败,可以选择跳过或报错。这里简单跳过并输出警告
  149.                     _dialogHelper.ShowMessageAsync("警告", $"Skipping line due to error:\n{ex.Message}");
  150.                     continue;
  151.                 }
  152.             }
  153.             // 确保颜色数量与顶点数量一致(正常情况下应该一致)
  154.             if (positions.Count != colors.Count)
  155.                 _dialogHelper.ShowMessageAsync("报错", $"Parsed {positions.Count} positions but {colors.Count} colors.");
  156.             return (positions, colors);
  157.         }
  158.         /// <summary>
  159.         /// 释放资源
  160.         /// </summary>
  161.         public void Dispose()
  162.         {
  163.             GC.SuppressFinalize(this);
  164.         }
  165.     }
  166. }
复制代码
四、结果图


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表