如何将HelixToolkit.SharpDX渲染的ply点云成?

摘要:HelixToolkit.SharpDX 是 HelixToolkit 生态中基于 DirectX(DX) 底层能力封装的 .NET 开源 3D 可视化库;DirectX 是微软为 Windows 平台开发的底层多媒体 API,可高效调用显
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 <Window x:Class="HelixToolkit.DX.PointCloud.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:hx="http://helix-toolkit.org/wpf/SharpDX" xmlns:local="clr-namespace:HelixToolkit.DX.PointCloud" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:prism="http://prismlibrary.com/" xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" xmlns:vm="clr-namespace:HelixToolkit.DX.PointCloud.ViewModels" prism:ViewModelLocator.AutoWireViewModel="True" ui:WindowHelper.SystemBackdropType="Mica" ui:WindowHelper.UseModernWindowStyle="True" mc:Ignorable="d"> <Grid> <hx:Viewport3DX BackgroundColor="Black" EffectsManager="{Binding EffectsManager}" IsRotationEnabled="True" IsShadowMappingEnabled="True" RotateAroundMouseDownPoint="True" ShowCoordinateSystem="True" ShowFrameRate="True" ShowViewCube="True" ZoomAroundMouseDownPoint="True" ZoomExtentsWhenLoaded="True"> <!-- 视口输入绑定:定义鼠标和键盘操作 --> <hx:Viewport3DX.InputBindings> <!-- Ctrl+E快捷键:缩放至整个模型 --> <KeyBinding Command="hx:ViewportCommands.ZoomExtents" Gesture="Control+E" /> <!-- 鼠标右键:旋转视图 --> <MouseBinding Command="hx:ViewportCommands.Rotate" Gesture="RightClick" /> <!-- 鼠标中键:缩放视图 --> <MouseBinding Command="hx:ViewportCommands.Zoom" Gesture="MiddleClick" /> <!-- 鼠标左键:平移视图 --> <MouseBinding Command="hx:ViewportCommands.Pan" Gesture="LeftClick" /> </hx:Viewport3DX.InputBindings> <!-- 相机:默认位置 --> <hx:Viewport3DX.Camera> <hx:PerspectiveCamera LookDirection="0,0,-10" Position="0,0,10" UpDirection="0,1,0" /> </hx:Viewport3DX.Camera> <!-- 阴影贴图:定义阴影的渲染参数 --> <hx:ShadowMap3D OrthoWidth="200" /> <!-- 环境光:基础照明 --> <hx:AmbientLight3D Color="White" /> <!-- 平行光:方向性光源,光线方向向量 --> <hx:DirectionalLight3D Direction="100, -100, -150" /> <hx:PointGeometryModel3D Figure="Ellipse" Geometry="{Binding BatchedGeometry}" Size="1,1" Color="White" /> </hx:Viewport3DX> <!-- 简单的加载按钮 --> <Button Margin="20" Padding="10,5" HorizontalAlignment="Left" VerticalAlignment="Top" Command="{Binding LoadPlyCommand}" Content="Load PLY File" /> </Grid> </Window> MainWindowViewModel using HelixToolkit.SharpDX.Core; using HelixToolkit.SharpDX.Core.Core; using HelixToolkit.Wpf.SharpDX; using Microsoft.Win32; using SharpDX; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Media3D; using PerspectiveCamera = HelixToolkit.Wpf.SharpDX.PerspectiveCamera; namespace HelixToolkit.DX.PointCloud.ViewModels { public class MainWindowViewModel : BindableBase, IDisposable { /// <summary> /// 消息对话框实例 /// </summary> private readonly IDialogHelper _dialogHelper; public MainWindowViewModel(IContainerExtension container) { _dialogHelper = container.Resolve<IDialogHelper>(); } private DefaultEffectsManager _effectsManager = new(); /// <summary> /// 特效管理器 /// 管理渲染管线、着色器等 /// </summary> public DefaultEffectsManager EffectsManager { get => _effectsManager; set => SetProperty(ref _effectsManager, value); } private PointGeometry3D? _batchedGeometry; /// <summary> /// 点云几何体 /// 定义3D场景中的点云数据 /// </summary> public PointGeometry3D? BatchedGeometry { get => _batchedGeometry; set => SetProperty(ref _batchedGeometry, value); } /// <summary> /// 选择PLY文件并加载点云事件 /// </summary> public ICommand LoadPlyCommand => new DelegateCommand(SelectPlyFile); /// <summary> /// 选择PLY文件并加载点云 /// </summary> private void SelectPlyFile() { var openFileDialog = new OpenFileDialog { Filter = "PLY files (*.ply)|*.ply|All files (*.*)|*.*", Title = "Select a PLY file" }; if (openFileDialog.ShowDialog() == true) { try { LoadPlyFile(openFileDialog.FileName); } catch (Exception ex) { _dialogHelper.ShowMessageAsync("报错", $"Error loading file:\n{ex.Message}"); } } } /// <summary> /// 加载PLY文件并创建点云几何体 /// </summary> /// <param name="filePath"></param> private void LoadPlyFile(string filePath) { // 解析PLY,获取位置和颜色(所有点都包含颜色) var (positions, colors) = ParsePly(filePath); if (positions.Count == 0) { _dialogHelper.ShowMessageAsync("报错", "No points found in the file."); return; } // 创建几何体,设置位置和颜色 var geometry = new PointGeometry3D { Positions = new Vector3Collection(positions), Colors = new Color4Collection(colors) // colors 一定存在且数量匹配 }; // 创建点云模型 var pointModel = new PointGeometryModel3D { Geometry = geometry, Size = new Size(1, 1), // 点大小设大一点便于观察 Figure = PointFigure.Ellipse, Color = Colors.White, }; // 添加到视口 BatchedGeometry = geometry; _dialogHelper.ShowMessageAsync("成功", $"成功加载 {positions.Count} 个点。"); } /// <summary> /// 解析ASCII PLY文件,提取顶点位置和颜色。 /// 假设文件包含:x y z red green blue (red/green/blue为uchar 0-255) /// </summary> private (List<Vector3> positions, List<Color4> colors) ParsePly(string filePath) { var positions = new List<Vector3>(); var colors = new List<Color4>(); bool readingData = false; foreach (string line in File.ReadLines(filePath)) { string trimmed = line.Trim(); if (string.IsNullOrEmpty(trimmed)) continue; if (!readingData) { // 查找头部结束标志 if (trimmed == "end_header") { readingData = true; } continue; } // 解析数据行,每行至少6个值:x y z r g b string[] parts = trimmed.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); if (parts.Length < 6) continue; // 不符合要求,跳过(但你的数据应该都符合) try { float x = float.Parse(parts[0], CultureInfo.InvariantCulture); float y = float.Parse(parts[1], CultureInfo.InvariantCulture); float z = float.Parse(parts[2], CultureInfo.InvariantCulture); positions.Add(new Vector3(x, y, z)); // 解析RGB值(0-255),转换为0-1范围的float byte r = byte.Parse(parts[3], CultureInfo.InvariantCulture); byte g = byte.Parse(parts[4], CultureInfo.InvariantCulture); byte b = byte.Parse(parts[5], CultureInfo.InvariantCulture); colors.Add(new Color4(r / 255.0f, g / 255.0f, b / 255.0f, 1.0f)); } catch (Exception ex) { // 如果某行解析失败,可以选择跳过或报错。这里简单跳过并输出警告 _dialogHelper.ShowMessageAsync("警告", $"Skipping line due to error:\n{ex.Message}"); continue; } } // 确保颜色数量与顶点数量一致(正常情况下应该一致) if (positions.Count != colors.Count) _dialogHelper.ShowMessageAsync("报错", $"Parsed {positions.Count} positions but {colors.Count} colors."); return (positions, colors); } /// <summary> /// 释放资源 /// </summary> public void Dispose() { GC.SuppressFinalize(this); } } } 四、效果图