輕鬆集成HMS CGKit體積雲實現雲海仙境

前言

小時候最喜歡看西遊記,總是幻想着自己能像孫悟空那樣,腳踏筋斗雲穿梭雲海間,生活在仙境中。長大後做了圖形程序,一直想做一個真正的雲海出來,但由於移動端的計算瓶頸,一直沒能做出一個兼顧性能和效果的體積雲(體積雲是基於物理的雲渲染系統,在遊戲中模擬出具有半透明、無規則的表現效果的雲)。

本人是一個遊戲開發愛好者,經常會fellow一些前沿的技術,並且將一些感興趣的技術點開發成一個可以方便使用的插件。最近看到華爲HMS Core 中的CGKit提供了一個體積雲插件,所以就花了兩天時間按照官方文檔集成到Unity中,下圖是一個簡單場景的效果(上面的云爲天空盒,下面的云爲集成後的體積雲),可以看到體積雲整體偏真實,“金邊”效果也比較明顯,支持動態光照,可以在雲中任意穿梭。最重要的是我在一個低端機(榮耀8青春版)上測試了一下性能,分辨率爲720P的情況下,竟然可以跑到50幀!該插件還有一個有意思的功能是,支持開發者定製雲的形狀,這樣我就可以擁有一朵任意形狀的雲了。

在這裏插入圖片描述

在這裏插入圖片描述

接下來就和大家分享一下我是如何將HMS CGKit體積雲插件集成到Unity中的,希望對大家的開發有所幫助。

2、開發準備

1、Visual Studio,推薦使用2017及以上版本; 2、Android Studio,推薦使用4.0及以上版本; 3、Unity,推薦使用2018.4.12及以上版本; 4、EMUI 8.0及以上華爲手機或Android 8.0及以上非華爲手機; 5、下載SDK

到華爲開發者聯盟下載SDK,下載鏈接及說明文檔鏈接如下:

SDK下載鏈接:https://developer.huawei.com/consumer/cn/doc/development/HMSCore-Library-V5/sdk-download-0000001050441521-V5

開發指南:https://developer.huawei.com/consumer/cn/doc/development/HMSCore-Guides/development-preparations-0000001076931602

API參考:https://developer.huawei.com/consumer/cn/doc/development/HMSCore-References-V5/overview-0000001077661506-V5

將SDK下載下來,目錄結構如下:

在這裏插入圖片描述

通過官方的說明文檔可知: RenderingVolumeCloud.dll是基於OpenGL的PC端調試插件,libRenderingVolumeCloud.so是基於OpenGLES30的Android端插件,assets目錄下的兩個bin文件是渲染體積雲時需要用到的資源文件,include目錄下的頭文件是該插件的接口定義。

3 開發詳細記錄

由於該體積雲插件對外暴露的是C++接口,所以要想集成到Unity中,需要將其封裝爲Unity的原生插件(Native plugin),然後集成到Unity獲得體積雲的渲染結果。下面分別給大家介紹一下我的集成過程。

3.1 原生插件的封裝

Unity原生插件是指用C、C++、Objective-C 等編寫的原生代碼庫,插件允許遊戲代碼(用Javascript或C#編寫)調用這些庫中的函數。Unity原生插件的封裝可以參照如下官方文檔以及官方示例教程:

文檔:https://docs.unity3d.com/Manual/NativePluginInterface.html

代碼:https://github.com/Unity-Technologies/NativeRenderingPlugin

根據Unity官方示例,需要分別編譯so和dll作爲動態鏈接庫供Unity調用。可以分別用Visual Studio和Android Stuido直接對官方發佈的開源代碼進行修改,集成體積雲渲染功能,並編譯出對應的庫文件,下面簡要介紹修改方式。

RenderingPlugin.def文件中的函數就是原生插件暴露給Unity的接口,其具體實現在RenderingPlugin.cpp中。我們保留RenderingPlugin.cpp中的UnityPluginLoad、UnityPluginUnload、OnGraphicsDeviceEvent、OnRenderEvent和GetRenderEventFunc這幾個Unity原生插件必須的函數以及相關的靜態全局變量,並添加了3個接口(ReleaseSource,BakeMultiMesh,SetRenderParasFromUnity),如下圖所示。

在這裏插入圖片描述

接下來通過對官方源代碼修改,利用這幾個接口實現對CGKit體積雲插件的調用。修改如下:

(1) 修改OnGraphicsDeviceEvent函數。當eventType 爲kUnityGfxDeviceEventInitialize時,調用CGKit體積雲插件CreateRenderAPI函數創建RenderAPI類型的變量,並調用RenderAPI.CreateResources()函數;當eventType 爲kUnityGfxDeviceEventInitialize時delete RenderAPI類變量。

(2) 修改OnRenderEvent函數。將SetTextureFromUnity函數中設置的全局靜態變量作爲參數,在該函數中直接調用RenderAPI. RenderCloudFrameTexture()函數。

(3) 定義SetTextureFromUnity函數。將RenderAPI. RenderCloudFrameTexture()函數所需的4個輸入值傳給定義好的靜態全局變量,方便以後直接調用該函數進行體積雲渲染。

(4) 定義SetRenderParasFromUnity函數。在該函數中調用RenderAPI. SetRenderCloudParas()函數。

(5) 定義ReleaseSource函數。在該函數中調用RenderAPI. ReleaseData()函數。

爲了實現雲形態的定製,PC端插件需要集成體積雲的烘焙功能,所以dll要比so多一個烘焙接口。在編譯dll時需要額外定義BakeMultiMesh函數,首先調用CreateBakeShapeAPI創建BakeShapeAPI類變量,然後調用BakeShapeAPI.BakeMultiMesh()函數進行烘焙。

3.2 集成到Unity

原生插件封裝成功後,就可以得到適配Unity和該體積雲插件的libUnityPluginAdaptive.so和UnityPluginAdaptive.dll。接下來就是新建一個Unity 3D工程,來實現體積雲功能的調用,這裏我實現了ARM64版本,以此爲例給大家介紹一下。

將ARM64版本的libUnityPluginAdaptive.so、libRenderingVolumeCloud.so、UnityPluginAdaptive.dll和RenderingVolumeCloud.dll放到Assets/Plugin/x86_64文件夾下,沒有該文件夾的話自己新建一個就好。需要對兩個so和dll分別進行如下配置:

在這裏插入圖片描述

在這裏插入圖片描述

由於該體積雲插件的PC端調試插件是基於OpenGL的,所以需要做如下設置:

在這裏插入圖片描述

由於該體積雲插件的Android端插件是基於OpenGLES30的,所以需要做如下設置:

在這裏插入圖片描述

該插件還提供了兩個bin文件,將這兩個文件放到工程任意文件夾下,只要在傳入參數中定義好對應的路徑即可,其中noise.bin是體積雲的細節噪聲,shape.bin是體積雲的三維形狀紋理。也可以調用插件的BakeMultiMesh接口自定義體積雲的形態,這一部分會在後面詳細展開,先暫時使用插件提供的三維形狀紋理。

3.3 實時渲染體積雲

調用體積雲插件前,需要添加打點功能依賴jar包,可以通過修改代碼工程的應用級build.gradle文件來實現,任意jar包版本號均可。

在這裏插入圖片描述

將下載後的jar包,拷貝到Unity工程中,Assets/Plugin/Android/bin文件夾下,沒有該文件夾的話自己新建一個就好。

接下來就可以編寫C#腳本調用相關接口,這裏我顯式調用的適配層接口如下:

在這裏插入圖片描述

(1) SetTextureFromUnity函數的作用是設置cloudTexture的指針、depthTexture的指針,以及cloudTexture的尺寸。其中cloudTexture是用於體積雲繪製的紋理數據, depthTexture是有當前幀depth數據的紋理。該函數只需執行一次即可。

(2) SetRenderParasFromUnity函數的作用是調用CGKit體積雲插件參數設置的接口,設置體積雲的參數。由於這些參數每一幀都要更新,所以該函數每幀都要執行一次。

(3) GetRenderEventFunc函數的作用是調用CGKit體積雲插件繪製接口,將體積雲繪製到cloudTexture上。該函數的調用方式爲GL.IssuePluginEvent(GetRenderEventFunc(), 1),也可以以CommandBuffer的形式調用 commandBuffer.IssuePluginEvent (GetRenderEventFunc(), 1)。每幀都要執行一次。

(4) ReleaseSource函數的作用是調用CGKit體積雲插件釋放資源接口。只需要最後調用一次即可。

接口定義好以後,調用流程如下:

在這裏插入圖片描述

上圖中灰色的部分是需要根據我們自己來實現的,這裏給大家介紹一下我的一個簡單實現。在調用渲染接口之前,需要創建兩個RenderTexture,其中一個用來保存體積雲插件渲染結果,另外一個用來保存depth。然後調用SetTextureFromUnity接口,將兩個RenderTexture的NativeTexturePtr以及尺寸作爲輸入參數,這樣後面就可以得到體積雲渲染的結果。

在Update階段,需要更新體積雲插件的結構體參數,該參數需要參考CGKit體積雲插件包include目錄下VolumeRenderParas.h文件,在C#腳本中需要定義同樣的結構體。參數具體含義可以參考CGKit體積雲插件說明文檔,需要注意的是結構體要一字節對齊,結構體中表示矩陣的4個數組採用行優先排列。下圖是一個簡單的示例:

在這裏插入圖片描述

更新結構體變量後,調用SetRenderParasFromUnity接口以引用的方式傳遞體積雲渲染的參數,之後的渲染就會基於這組參數進行。

調用渲染接口時,我們以OnRenderImage後處理的形式將體積雲繪製到屏幕上,當然也可以使用CommandBuffer的形式在其他階段進行配置。在OnRenderImage階段,首先要將depth繪製到之前創建的depthTexture RT上,然後調用GL.IssuePluginEvent(GetRenderEventFunc(),將體積雲繪製到之前創建的cloudTexture RT上,最後將cloudTexture和OnRenderImage的輸入src利用透明度融合到dst上,就可以在屏幕上顯示體積雲。

3.4 打包APK到手機端驗證

在PC端調試好效果之後,就可以直接打包到APK進行Android端驗證。Android端和PC端唯一的區別就是結構體參數中的兩個字符串數組,分別是shape.bin和noise.bin文件的路徑,這兩個路徑需要在不同平臺區分開來,可以將兩個bin文件放到Application.persistentDataPath文件夾下。

3.5 烘焙三維形狀紋理

該體積雲插件同時也提供了烘焙接口,可以自定義三維形狀紋理。同樣將其集成到適配層,前邊已經有過介紹,接口函數爲:

在這裏插入圖片描述

函數的輸入爲BakeData結構體變量以及保存bin文件的路徑,同時會生成類似於shape.bin的文件,其中savePath需包含完整路徑以及後綴(.bin)。由於數組長度不定,所以該結構體的成員變量爲數組指針類型和int型,參數具體含義可以參考CGKit體積雲插件說明文檔。根據說明文檔我們可以知道,烘焙接口的作用是將一個Boundingbox內的多個mesh烘焙爲三維形狀紋理。Boundingbox的大小由結構體內的兩個參數決定,分別爲minBox和maxBox。

如下圖所示,調用接口前,先將多個三維模型組合到一起。爲了可視化到底哪些區域是可烘焙區域,我們可以在空間中根據minBox和maxBox這兩個參數繪製一個線框,在線框外的區域是不予烘焙的。調整好各個模型的位置之後,就可以調用該接口進行烘焙。

在這裏插入圖片描述

在這裏插入圖片描述

在烘焙時還需要注意的一點是,烘焙好的三維形狀紋理在渲染體積雲時會被連續採樣,所以佈置三維模型時,需要在水平的四個與boundingBox相交的位置上放置相同的三維模型,使其被循環採樣時保持連續性。

烘焙結束之後就可以使用自定義的三維形狀紋理進行體積雲渲染,用法與官方提供的shape.bin用法一樣。

Demo獲取

emo以unitypackage的形式打包並上傳至百度雲盤,地址請戳:

https://pan.baidu.com/s/1JEt_aaC70ARgWmX3cl-n9w 提取碼: yx8a

從Assets->Import Package->Custom Package中導入該unitypackage,其中Plugin文件夾中有適配層和CGKit體積雲插件的dll和so。volumeCloud文件夾中有相關的腳本、shader以及搭建的場景。StreamingAssets文件夾中存放了相關的資源文件(shape.bin和noise.bin)。volumeCloud/Readme.txt中註明了Demo運行的一些注意事項,在運行前請按說明進行配置。


原文鏈接:https://developer.huawei.com/consumer/cn/forum/topic/0202500645526020389?fid=18

原作者:轉來轉去

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章