Unity Advaced Mesh API – 胡亂摸經驗 part I (animation curve for job)

這幾天開始摸索Unity Advanced Mesh API來處理Mesh Derform的功能,因為原本使用的asset是利用純CPU來處理mesh,asset的效果不錯,只是在iOS上時,大約同時處理8~10個點雲物件的變形FPS開始就不行了。

所以開始摸索Advanced Mesh API,基本上摸到一個程度,發現有些細節官方文件也沒有說的很清楚,有點經驗後,回頭看Keijiro大神的範例(https://github.com/keijiro/DanmakuBenchmark),發現好到一個不行,自己目前寫出來的code根本還沒有很完整地用到最新的mesh 直接由這邊學習比較清楚,之後有空再來寫一篇。

在自己摸索的過程中,因為要把原來純CPU版本simple mesh api的版本轉換成job來運算Vertices的位置,需要用到animation curve來自定變形的動畫參數,但因為job並不支援animation curve當作輸入參數類型,所以得先將animation curve切割為native array,再傳入job來EvaluateLerp

我參考這邊的code來用(https://github.com/5argon/E7Unity/blob/master/SampledAnimationCurve/SampledAnimationCurve.cs)

iOS 上使用timeline

在嘗試製作AR MV時,使用Timeline來控制歌詞的出現

但發現輸出到手機後,按播放都沒有反應,檢查了按鈕的設定跟一堆東西後,發現是timeline並沒有被觸發,查詢之下,發現是Unity Timeline目前的bug之一。

解決辦法是在Unity Assets資料夾下新增一個link.xml檔案,並在裡面寫下:

<linker>    
<assembly fullname="UnityEngine.Timeline" preserve="all"/>    <assembly fullname="Unity.Timeline" preserve="all"/>    
<assembly fullname="UnityEngine">      
<namespace fullname="UnityEngine.Playables" preserve="all"/>    </assembly>
</linker>

這樣在輸出的時候,會將其註冊成AssetBundle,而不會被Unity濾掉,Timeline就可以正常運作。

Reference:
https://www.yui-tech-blog.com/entry/2019/12/09/【Unity】Unityを2019にアップグレードしたらTimelineが上手く再

ComputerShader處理後由CPU後續處理的方法

在最近的專案中,必須透過ComputerShader(CS)處理大量的點雲判斷,並回傳處理後的點來後續處理。

通常的狀態下,是使用GetData來將CS處理完的buffer資料塞回陣列再處理

_kernelIndex = Shader.FindKernel("CS_PointCloudMove");
_buffer = new ComputeBuffer(Count, sizeof(float));
Shader.SetBuffer(_kernelIndex, "floatBuffer", _buffer);
int groupX = (Count / _num);         
Shader.Dispatch(_kernelIndex, groupX, 1, 1);

var data = new float[Count]; 
_buffer.GetData(data);

但GPU到CPU的處理時間相當耗時,會使得FPS大幅地降低,我研究了一下後發現Unity有新的語法AsyncGPUReadback.Request(COMPUTE_BUFFER, METHOD),可以非同步地處理回傳的資料,能大大提升GPU<->CPU的運算效能

using System.Runtime.InteropServices;

...
AsyncGPUReadback.Request(_buffer, OnParticleCallback);
...

void OnParticleCallback() 
{
   if (request.hasError)
      return;
   // CPU particle data process
   _tmpParticles = request.GetData().ToArray();
   int pCount = 0;
   for (int i = 0; i < _tmpParticles.Length; i++)
   {
      if (_tmpParticles[i].hit > 0f)
      {
         _interactiveCount++;
         hitPoints.Add(_tmpParticles[i].position);
         pCount++;
      }
      if (pCount > touchCheckMaxNums)
         break;
      }
}