團隊的專案合作中,美術可能會不希望直接去更動到正式場景的內容,所以會另建新場景將場景的內容配置好並做好 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(){
}
}
[MenuItem(“Tools/Lightmapping/儲存 Lightmap Settings”)]
static void SaveLightmapSettings(){
}
[MenuItem(“Tools/Lightmapping/載入 Lightmap Settings”)]
static void LoadLightmapSettings(){
}
}
那麼,點一下選單列,就會發現多了 Tools 選單,並可從中選擇我們定義的這兩個選單命令。
選單列多出 Tools 選單 |
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;
}
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;
}
}
[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 的資料都被儲存在資源檔中 |
沒有 LightmapSettings 資料的場景,相同的 Prefab 也無法呈現 Lightmapping 效果 |
選擇「載入 Lightmap Settings」 |
載入 LightmapSettings 資料後,終於呈現 Lightmapping 效果 |
Lightmapping view 可以看到圖都被放置好了 |
這個小工具,在載入 Lightmapping 資料時,必須先選擇儲存下來 LightmapSettings 資源檔才可載入,所以工具程式碼裡面最好還是要另外做些判斷,以免未選擇或是選擇錯誤檔案發生錯誤。這部分就不在此補充了....
另外,如果有多個場景需要儲存 LightmapSettings 資料,記得將已儲存好的 LightmapSettings 資源檔名更改為其他名稱或是搬移到其他路徑,以免儲存下一個 LightmapSettings 資料時,將原本儲存好的資料覆蓋過去。
P.S. 使用 Unity 版本為 4.5.5f1。