前言

小时候最喜爱看西游记,总是空想着本人能像孙悟空那样,脚踏筋斗云穿梭云海间,生存在仙境中。长大后做了图形程序,始终想做一个真正的云海进去,但因为挪动端的计算瓶颈,始终没能做出一个兼顾性能和成果的体积云(体积云是基于物理的云渲染零碎,在游戏中模拟出具备半透明、无规则的体现成果的云)。

自己是一个游戏开发爱好者,常常会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下载下来,目录构造如下:

process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDcwODI0MA==,size_16,color_FFFFFF,t_70)

通过官网的阐明文档可知: 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_... 提取码: 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

原作者:转来转去