WinUI(WASDK)使用HelixToolkit載入3D模型併進行項目實踐

来源:https://www.cnblogs.com/GreenShade/archive/2023/06/07/17462143.html
-Advertisement-
Play Games

## 前言 本人之前開發了一個叫[電子腦殼](https://github.com/maker-community/ElectronBot.DotNet)的上位機應用,給稚暉君[ElectronBot](https://github.com/peng-zhihui/ElectronBot)開源機器人 ...


前言

本人之前開發了一個叫電子腦殼的上位機應用,給稚暉君ElectronBot開源機器人提供一些功能,但是由於是結合硬體才能使用的軟體,如果擁有硬體的人員太少,就會導致我的軟體沒什麼人用,於是我就想著能不能將機器人硬體的模型載入到軟體里,這樣用戶就可以不使用硬體也可以使用我的軟體了。於是就有了在WinUI(WASDK)里使用3D模型的需求。

先來個B站復刻機器人的開箱視頻吧。(如果感覺無聊可以直接拖到代碼講解部分)

B站視頻演示鏈接

庫選擇的糾結過程

在選擇庫的過程中其實並不是一帆風順,因為WinUI(WASDK)是個比較新的框架,框架本身也沒有提供3D模型載入的功能,於是我就在想到底選擇什麼樣的辦法載入,之前有看到一些UWP載入模型的demo,但是基本上就是封裝的c++的庫,對於我這c++垃圾的人來說還是很痛苦的,我的要求就是不能有c++代碼。

要滿足模型的載入,模型的旋轉變換平移,模型材質的更換,在經過篩選以後,備選方案有three.js,HelixToolkit。

  • three.js

    GitHub Copilot: three.js 是一個基於 JavaScript 的 3D 圖形庫,它使用 WebGL 技術在瀏覽器中渲染 3D 圖形。它提供了一組易於使用的 API,使用戶可以創建高度可定製的 3D 場景,包括模型、紋理、光源和相機等部分。此外,three.js 還支持動畫、物理模擬和粒子系統等功能,使用戶可以創建逼真的 3D 動畫效果。three.js 可以在多種瀏覽器和設備上運行,並且有一個龐大的社區支持和貢獻。

    由於要和前端框架進行互操作,所以這個我也不是優先考慮了。

  • HelixToolkit(基於SharpDX再次封裝的)

所用框架和庫介紹

1. WASDK

這個框架是微軟最新的UI框架,我主要是用來開發程式的主體,做一些交互和功能的承載,本質上和wpf,uwp這類程式沒什麼太大的區別,區別就是一些工具鏈的不同。

2. HelixToolkit

GitHub Copilot: HelixToolkit是一個面向.NET開發人員的3D圖形庫。它提供了一組控制項和輔助類,用於在WPF和WinForms應用程式中創建和渲染3D場景。HelixToolkit支持多種3D文件格式,包括OBJ、STL和FBX,並包括照明、材質、紋理和動畫等功能。它還支持高級渲染技術,如陰影、反射和透明度。HelixToolkit是開源的,可以從GitHub下載。

代碼講解

1. Demo項目介紹

demo項目地址

項目結構如下圖:
project

處理過程

=>載入器載入模型=>將模型載入到模型組=>初始化相機=>進行數據到控制項的綁定

2. 核心代碼講解

預備知識

  • SharpDX的坐標系是左手坐標系

    GitHub Copilot: DirectX使用左手坐標系來描述3D空間中的對象位置和方向。在左手坐標系中,x軸向右,y軸向上,z軸向外(屏幕外)。這與右手坐標系的z軸方向相反。左手坐標系在電腦圖形學中廣泛使用,因為它與人眼的觀察方式相符合。

model1

  • 3D場景下的模型變換(平移 旋轉 縮放)

模型載入完成以後,就需要讓模型動起來,這樣才能算是完整的過程了。

核心旋轉代碼如下:

core-code

場景是將模型繞手臂點進行旋轉,需要將手臂模型組移動到原點,旋轉完成之後再移動到原來的位置。

3. 實際項目使用

下載電子腦殼源碼

braincase

動作序列以及表情幀數據處理實際代碼如下:

 private void Instance_ModelActionFrame(object? sender, Verdure.ElectronBot.Core.Models.ModelActionFrame e)
    {
        BodyModel.HxTransform3D = _bodyMt * Matrix.RotationY(MathUtil.DegreesToRadians((e.J6)));

        Material = new DiffuseMaterial()
        {
            EnableUnLit = false,
            DiffuseMap = LoadTextureByStream(e.FrameStream)
        };


        var nodeList = HeadModel.GroupNode;

        foreach (var itemMode in nodeList.Items)
        {
            if (itemMode.Name == "Head3.obj")
            {
                foreach (var node in itemMode.Traverse())
                {
                    if (node is MeshNode meshNode)
                    {
                        meshNode.Material = Material;
                    }
                }
            }
        }

        var rightList = RightShoulderBoundingBox.GetCorners();

        var rightAverage = new SharpDX.Vector3(
            (rightList[1].X + rightList[5].X) / 2f,
            ((rightList[1].Y + rightList[5].Y) / 2f) - 8f,
            (rightList[1].Z + rightList[5].Z) / 2f);

        var leftList = LeftShoulderBoundingBox.GetCorners();

        var leftAverage = new SharpDX.Vector3(
            (leftList[0].X + leftList[4].X) / 2f,
            ((leftList[0].Y + leftList[4].Y) / 2f) - 8f,
            (leftList[0].Z + leftList[4].Z) / 2f);

        var translationMatrix = Matrix.Translation(-rightAverage.X, -rightAverage.Y, -rightAverage.Z);

        var tr2 = _rightArmMt * translationMatrix;

        var tr3 = tr2 * Matrix.RotationZ(MathUtil.DegreesToRadians(-(e.J2)));
        var tr4 = tr3 * Matrix.RotationX(MathUtil.DegreesToRadians(-(e.J3)));

        var tr5 = tr4 * Matrix.Translation(rightAverage.X, rightAverage.Y, rightAverage.Z);


        var tr6 = tr5 * Matrix.RotationY(MathUtil.DegreesToRadians((e.J6)));

        RightArmModel.HxTransform3D = tr6;


        var leftMatrix = Matrix.Translation(-leftAverage.X, -leftAverage.Y, -leftAverage.Z);

        var leftTr2 = _leftArmMt * leftMatrix;

        var leftTr3 = leftTr2 * Matrix.RotationZ(MathUtil.DegreesToRadians((e.J4)));
        var leftTr4 = leftTr3 * Matrix.RotationX(MathUtil.DegreesToRadians(-(e.J5)));

        var leftTr5 = leftTr4 * Matrix.Translation(leftAverage.X, leftAverage.Y, leftAverage.Z);


        var leftTr6 = leftTr5 * Matrix.RotationY(MathUtil.DegreesToRadians((e.J6)));

        LeftArmModel.HxTransform3D = leftTr6;

        var headMatrix = Matrix.Translation(-HeadModelCentroidPoint.X, -HeadModelCentroidPoint.Y, -HeadModelCentroidPoint.Z);

        var headTr2 = _headMt * headMatrix;

        var headTr3 = headTr2 * Matrix.RotationX(MathUtil.DegreesToRadians(-(e.J1)));

        var headTr4 = headTr3 * Matrix.Translation(HeadModelCentroidPoint.X, HeadModelCentroidPoint.Y, HeadModelCentroidPoint.Z);


        var headTr5 = headTr4 * Matrix.RotationY(MathUtil.DegreesToRadians((e.J6)));

        HeadModel.HxTransform3D = headTr5;
    }

最終效果如下:
ElectronBot-model

個人感悟

通過模型載入的這個過程的學習,最大的感悟就是編程開發其實只是運用工具實現自己的想法,這個過程中我們對於工具的使用可能比較熟悉了,但是如果我們的領域知識不夠豐富,那基本上也是做不了什麼的,所以學習一些東西的時候我們的相關知識也要進行學習才好。

我也是在這個過程中才學習了什麼是左手坐標系,以及4x4矩陣的變換和相機視角。這些東西都是和框架無關的知識,假如我們都學精通了,用什麼框架實現我們的想法其實都不是問題了。

總結,還是要努力學習呀。

參考推薦文檔項目如下

demo地址

電子腦殼

WASDK文檔地址

ElectronBot

HelixToolkit


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 基於java的養老院管理系統設計與實現;可適用於敬老院管理系統,老人管理系統,老人信息管理系統,java養老院信息系統,java敬老院信息管理系統; ...
  • 一、開通OpenAI賬號 1.註冊OpenAI賬號 官網地址:https://openai.com/ 註意:提前準備好國外手機號,沒有的話用簡訊平臺購買手機號接收簡訊 2.購買國外手機號 地址:https://tiger-sms.com/ 我用支付寶充值了30多元(起充要30,加上手續費30多有點坑 ...
  • 當《阿裡巴巴Java開發手冊》發佈後,我也是仔細進行了閱讀,想從中找出一些“標準”,讓自己的代碼質量提高。手冊中對 Object 的 equals 方法的使用進行了強制,而且推薦使用 JDK7 中工具類 Objects 的 equals 方法,至此之後我就很少使用 Object.equals() 方... ...
  • # if語句-語法格式 簡單理解if語句之後,我們的if語句語法格式有多種,選擇使用哪種取決於要測試的條件數 # 1.if結構 最簡單的if語句只有一個條件測試和一個代碼塊 其語法格式: ![image](https://img2023.cnblogs.com/blog/3179433/202306 ...
  • #### 使用thymeleaf做html模板,由xhtmlrenderer/flying-saucer-pdf-openpdf將html轉為PDF > LGPL 和 MPL 許可 #### pom.xml引入依賴 ````java org.springframework.boot spring-b ...
  • > 文中所涉及到的代碼運行結果均是在64位機器上執行得到的. ## 基礎知識回顧 在Go中,我們可以使用`unsafe.Sizeof(x)`來查看變數所占的記憶體大小。以下是Go內置的數據類型占用的記憶體大小: | 類型 | 記憶體大小(位元組數) | | : | : | | bool | 1 | | in ...
  • 博客推行版本更新,成果積累制度,已經寫過的博客還會再次更新,不斷地琢磨,高質量高數量都是要追求的,工匠精神是學習必不可少的精神。因此,大家有何建議歡迎在評論區踴躍發言,你們的支持是我最大的動力,你們敢投,我就敢肝 ...
  • ## 教程簡介 Ajax即Asynchronous Javascript And XML(非同步JavaScript和XML)在 2005年被Jesse James Garrett提出的新術語,用來描述一種使用現有技術集合的‘新’方法,包括: HTML 或 XHTML, CSS, JavaScript ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...