Unity - 簡單製作自己的3D圖像化音樂顯示器(Simple 3D Audio Visualization)

  這次動手做自己的3D圖形化音樂撥放器,使用Unity提供的API來取得聲音檔的資訊,這邊的網格Mesh,使用的是前一篇(Unity - 動態製作一張平面網格)所建立的平面網格來製作這個3D圖形化的效果。


  簡單的使用一個Mesh來顯示波形,使用一個List來保存舊資料,就可以達到這樣類似流動的效果了。




實作測試






  首先預備一些參數使用
    public CreatePlane plane; //準備好的Mesh
    public AudioSource source; //音源
    private int samples = 1024; //採樣的層數,需要2的次方,看需求設定,越大當然越耗效能
    private List<float[]> datas = new List<float[]>(); //為了讓Mesh有波動的效果,所以用個List保留採樣過的資料
    private int minLength; //這邊網格的寬只有50,所以計算每個點之間間格的採樣層數 
 

  網格寬的邊上50個點,每個點要代表一個Hz參數,如果只有用0~49的話,採樣的陣列資料float[1024]只有取得前50個,這不是我所想要的情況,所以稍微計算一下間隔。
  
  Mathf.FloorToInt(1024f / 50f); 使用Floor取得整數可以避免造成index超出陣列。


  接著把 float[] 丟入Unity提供的GetSpectrumData(),取得採樣後,把這個 float[] 資料存下來,存到 List<float[]> datas裡面,之後要把這個List裡面的每個資料作為Mesh的每一個橫斷面上點的Y座標來用。

  最後把這個List的數值作為Y軸座標,計算調整後設定到整張Mesh各個點上,接著更新整張Mesh就完成了。






簡單製作3D圖形化音樂顯示器Code
public class AudioVisualization : MonoBehaviour
{
    //這邊我使用前一篇製作的Mesh,可以看自己的需求,使用內建的Plane或自己製作一個來用
    public CreatePlane plane;

    public AudioSource source; //音源
    private int samples = 1024; //採樣的層數,需要2的次方,
    private List<float[]> datas = new List<float[]>(); //為了讓Mesh有波動的效果,所以用個List保留採樣過的資料
    private int minLength; //這邊網格的寬我只有使用50,所以計算每個點之間間格的採樣層數

    void Start()
    {
        minLength = Mathf.FloorToInt((float)samples / (float)plane.lengthX); //計算間隔

        for(int i = 0; i < plane.lengthY; ++i)
            datas.Add (new float[samples]); //先準備完整數量的空白List
    }

    void Update ()
    {
        GetFreq();
        UpdateMesh();
    }
 
    void GetFreq()
    {
        float[] newFreqData = new float[samples];
        source.GetSpectrumData(newFreqData, 0, FFTWindow.BlackmanHarris); 

        //把取得的採樣加入List然後簡單做個判斷避免List過長,刪除舊的資料
        datas.Add (newFreqData);
        if(datas.Count > plane.lengthY)
            datas.RemoveAt(0);
    }

    void UpdateMesh()
    {
        for(int y = 0; y < plane.lengthY; ++y)
        {
            float[] lineSamples = datas[y]; //從List當從取得橫斷面的採樣

            for(int x = 0; x < plane.lengthX; ++x)
            {
                float sample = lineSamples[x*minLength]; //取得採樣其中一點的數值
                plane.matrix[(y*plane.lengthX) + x].y = Mathf.Clamp(sample * (0.5f*((x+1)*(x+1))), 0f, 5f); //簡單做個放大,並且設定最大值
            }
        }
        plane.mesh.vertices = plane.matrix; //Update mesh
    }
}


No comments:

Post a Comment