2014年11月10日 星期一

Unity:如何搬移 Lightmapping 資料?

在 3D 遊戲中,如果不是動態光影,通常為了改善效能,會為場景 bake 出 Lightmaps,來使遊戲執行時的靜態光影不需要進行即時的光影演算,而在 Unity 中也可以很方便地為場景 Lightmapping,可是 Lightmapping 資料卻不像 GameObject 一樣做成 Prefab 就可以供任何場景重複使用,甚至提供給其他專案使用;正因為 LightmapSettings 資料是存在於個別的場景的 Scene 檔中,所以要搬移就比較沒那麼方便。

團隊的專案合作中,美術可能會不希望直接去更動到正式場景的內容,所以會另建新場景將場景的內容配置好並做好 Lightmapping 之後才將整個場景地形及物件做成 Prefabs 提供給正式場景使用,或是提供給多個場景使用,可是,這時候如果把 Prefabs 放置到場景中會發現,跟預期的不同,並沒有原本預期的光影;當然,使用 Unity 的老手可能會在 Project view 裡面找出 bake 好的 Lightmap 圖片,並開啟 Lightmapping view 的 Maps tab 來手動設置好 Lightmap 的圖,但這麼做既費時又容易出錯,原本美術為了避免本身工作會不小心影響到正式場景內容的美意,在此反而會造成更多工作上的困擾,所以在這裡,程式人員就可以製作一個簡單的儲存及載入 Lightmapping 的小工具來協助處理。

在開始製作這個工具之前,我們需要一些相關的知識,就是在做 Lightmapping 時,有設定 Lightmap Static 的 GameObject 就能被 bake,而這些 GameObject 的 Renderer 將會在 lightmapIndex、lightmapTilingOffset 記錄相關的資料;而場景中的 Lightmapping 相關資料則是儲存在場景本身的 Scene 檔中的 LightmapSettings 之中;由於 GameObject 製作成 Prefab 後,它的 Renderer 資料也會跟隨記錄在 Prefab 裡面,所以不用另外處理,那麼只要我們製作的小工具能夠把場景的 LightmapSettings 另外儲存下來,在別的場景需要用到時再把它載入到場景的 LightmapSettings 就可以做到 Lightmapping 資料的搬移。



首先,我們要讓我們的小工具有個執行的地方,所以可以利用「自訂 Unity 工具列選單處理專案內容」文中提到的做法來為選單列加入新的選單命令。所以,我們可以先撰寫這樣的程式碼:

public class ToolsMenu{

    [MenuItem(“Tools/Lightmapping/儲存 Lightmap Settings”)]
    static void SaveLightmapSettings(){

    }

    [MenuItem(“Tools/Lightmapping/載入 Lightmap Settings”)]
    static void LoadLightmapSettings(){

    }
}

那麼,點一下選單列,就會發現多了 Tools 選單,並可從中選擇我們定義的這兩個選單命令。

選單列多出 Tools 選單
接下來,希望將 LightmapSettings 的資料儲存成檔案,所以需要撰寫一個繼承 ScriptableObject 的 class 來給之後儲存資料時製作 DataAsset 使用。

public class LightmapSettingsHolder : ScriptableObject {

    public ColorSpace bakedColorSpace;
    public LightmapDataHolder[] lightmaps;
    public LightmapsMode lightmapsMode;
    public LightProbes lightProbes;
}

[System.Serializable]
public class LightmapDataHolder{

    public Texture2D lightmapFar;
    public Texture2D lightmapNear;
}

準備好儲存資料的 class 之後,就可以開始來為前面的 ToolsMenu 撰寫內容如下...

public class ToolsMenu {

    [MenuItem("Tools/Lightmapping/儲存 Lightmap Settings")]
    static void SaveLightmapSettings(){

        LightmapSettingsHolder holder = ScriptableObject.CreateInstance<LightmapSettingsHolder>();

        holder.bakedColorSpace = LightmapSettings.bakedColorSpace;
        holder.lightmapsMode = LightmapSettings.lightmapsMode;
        holder.lightProbes = LightmapSettings.lightProbes;

        holder.lightmaps = new LightmapDataHolder[LightmapSettings.lightmaps.Length];
        for(int i = 0 ; i < LightmapSettings.lightmaps.Length ; i++){

            holder.lightmaps[i] = new LightmapDataHolder();
            holder.lightmaps[i].lightmapFar = LightmapSettings.lightmaps[i].lightmapFar;
            holder.lightmaps[i].lightmapNear = LightmapSettings.lightmaps[i].lightmapNear;
        }

        AssetDatabase.CreateAsset(holder , "Assets/LightmapSettings.asset");
    }

    [MenuItem("Tools/Lightmapping/載入 Lightmap Settings")]
    static void LoadLightmapSettings(){

        LightmapSettingsHolder holder = (LightmapSettingsHolder)Selection.activeObject;

        LightmapSettings.bakedColorSpace = holder.bakedColorSpace;
        LightmapSettings.lightmapsMode = holder.lightmapsMode;
        LightmapSettings.lightProbes = holder.lightProbes;

        LightmapData[] lightmaps = new LightmapData[holder.lightmaps.Length];
        for(int i = 0; i < holder.lightmaps.Length; i++){

            lightmaps[i] = new LightmapData();
            lightmaps[i].lightmapFar = holder.lightmaps[i].lightmapFar;
            lightmaps[i].lightmapNear = holder.lightmaps[i].lightmapNear;
        }

        LightmapSettings.lightmaps = lightmaps;
    }
}

撰寫好了以上程式碼之後,我們可以開啟已經 Lightmapping 的場景,直接在選單列上面選擇「Tools/Lightmapping/儲存 Lightmap Settings」,之後在 Project view 的根目錄可以看到多了一個名為 LightmapSettings 的資源檔,點選這個檔案的話,可以在 Inspector view 看到 LightmapSettings 的資料都儲存在這個檔案的欄位中了。

選擇「儲存 Lightmap Settings」
儲存完畢的 LightmapSettings 資源檔
LightmapSettings 的資料都被儲存在資源檔中
當開啟一個全新的場景,如果把做好 Lightmapping 場景儲存下來的 Prefab 放到場景中,會沒有 Lightmapping 的效果,此時選擇已儲存好的 LightmapSettings 資源檔,並在選單列選擇「Tools/Lightmapping/載入 Lightmap Settings」,就可以在場景中看到有 Lightmapping 的效果了,查看 Lightmapping view 的 Maps tab 也可以看到 Lightmapping 的圖也都被放進去了。

沒有 LightmapSettings 資料的場景,相同的 Prefab 也無法呈現 Lightmapping 效果
選擇「載入 Lightmap Settings」
載入 LightmapSettings 資料後,終於呈現 Lightmapping 效果
Lightmapping view 可以看到圖都被放置好了
如果要將這些儲存的資料移到其他專案使用,可以先選擇儲存下來的 LightmapSettings 資源檔,然後點選滑鼠右鍵選擇 Select Dependencies,就可以找出相關的其他檔案,這樣就可以利用 Export Package 打包匯出再匯入到其他專案中。

這個小工具,在載入 Lightmapping 資料時,必須先選擇儲存下來 LightmapSettings 資源檔才可載入,所以工具程式碼裡面最好還是要另外做些判斷,以免未選擇或是選擇錯誤檔案發生錯誤。這部分就不在此補充了....

另外,如果有多個場景需要儲存 LightmapSettings 資料,記得將已儲存好的 LightmapSettings 資源檔名更改為其他名稱或是搬移到其他路徑,以免儲存下一個 LightmapSettings 資料時,將原本儲存好的資料覆蓋過去。

P.S. 使用 Unity 版本為 4.5.5f1。