2016年2月3日 星期三

Unity 5 全域光照使用小技巧

作者:柳振東 原文:上篇 下篇

距離Unity 5發佈已經一段時間了,或許大家在使用Unity 5時或多或少都會碰到一些問題,今天我們Unity的官方技術工程師 - 柳振東 - 將在本文中針對Unity 5中全新的Enlighten全域光照系統的一些問題幫大家做解答。

為什麼Unity 5的全域光照系統會讓大家望之怯步呢? 因為Unity 5取代了Unity 4用的Beast光照系統改用了全新的Enlighten。而這兩套光照系統的演算法並不一樣,使用上就是各種參數與操作細節也不同。

對於沒有系統學習過電腦圖學的開發者,非常建議大家去瞭解一下全域光照明有哪些模型,因為大家可能對局部光照明模型(就是我們平常所說的即時光照)比較熟悉,有時可能會把局部光照明的一些知識用在全域光照系統上,導致做白工。

下面挑選一些常見問題與大家分享。



為什麼場景烘焙出來lightmap上有Realitime燈光的顏色?


相信不少開發者都為這樣的問題煩惱過,因為燈光明明是Realtime的,但卻被烘焙到lightmap去?但這在Unity 5並不是一個bug。

首先我們要知道Unity 5中新增一種全域光照明的使用方式:Precomputed Realtime GI(預計算即時全域光照明),簡稱:即時GI。而另一種Bake GI就是大家在Unity 4中一直用過來的ligtmap烘焙光照。

即時GI與Bake GI一樣也需要預先計算,但與Bake GI不同的是即時GI並不預計算場景光線的反射資訊,而是預計算場景靜態物體表面所有可能的反射路徑,然後在遊戲執行時和燈光的位置、方向等資訊一起即時計算出全域光照的結果。

這個機制使預計算GI有非常大的優勢,讓設定為預計算GI的燈光,在位置、方向、強度和顏色等資訊都可以在執行時即時調整。

例如,使用預計算GI我們就可以在場景中實現非常真實的日光變化效果(別忘了我們其實是使用全域光照明模型,比即時光照效果強多了)。

問題來了,預計算GI有一點非常容易讓人搞混,就是預計算GI必需燈光 type設定為Realtime。Realtime難道不是即時燈光嗎?怎麼變成全域光照明模型的燈光了?

Unity 5的燈光屬性中增加了一項Bounce Intensity,如下圖:



Bounce Intensity是指全域光照中的間接光強度,可以簡單地把全域光照理解為直接光照與間接光照兩部分。直接光照指直接從光源射到物體上的光,而間接光照指從其他物體表面反射而來的光。直接光照其實就是局部光照模型,也就是即時光照計算出來的結果。

回到正題,Bounce Intensity在Unity 5 中預設值是1,代表一盞Realtime燈光預設使用預計算GI,且間接光照的強度不做改變(大於1是強制增大間接光照,在一些室外光照射入洞穴這類的場合中可能會用到)。而lighting視窗中的預計算GI選項也是預設打開的,如下圖所示: 



所以如果你不更改這些設定,即使看起來你好像只使用了Realtime的燈光,但是真正執行的時候其實是使用全域光照系統!
要說明的一點是,只要一盞燈的Bounce Intensity大於0,Unity就會認為你需要使用全域光照系統。那麼即使你在lighting視窗中取消勾選Precomputed Realtime GI,Unity依然會嘗試使用全域光照。

既然已經取消了預計算GI的選項,Unity會去使用另外一種全域光照的方式,Bake GI(只要你還勾選著Bake GI)。而結果就是烘焙出來的lightmap裡也有那盞Bounce Intensity大於0的Realtime燈光的資訊。

總結:Unity 5中燈光有個新屬性Bounce Intensity,這個值只要大於0,系統就會認為你需要使用預計算GI來計算這盞燈,而如果此時Precomputed Realtime GI沒有被勾選,而Bake GI勾選了那麼Unity會把這個燈光也烘焙到lightmap中去。



為什麼在Unity 5中動態更換lightmap沒有作用?



在場景中動態更換lightmap是挺常用的需求。例如:同一個場景需要白天與黑夜兩個時間的效果,那麼我們就會烘焙兩張不同的lightmap,然後用腳本在執行時切換。在Unity 4中可以通過把lightmap資源載入到Texture2D中,然後給予一個LightmapData結構,最後給LightmapSettings.lightmaps來更換lightmap。

但是在Unity 5中無法這麼換。是因為lightmap沒有正確更換嗎?NO! 其實lightmap已經更換了,問題是使用lightmap的物體並不知道自己應該用哪張lightmap,也不知道要從lightmap的哪個地方開始採樣,而這其實就是每個Renderer上的兩個參數,lightmapIndex與lightmapScaleOffset。
現在大家會問為什麼在Unity 4中沒這問題呢?那是因為這些資訊在Unity 4中已經被序列化進場景中,當場景載入的時候這兩個值就被賦予到每個Renderer中。而在Unity 5,lightmapIndex與lightmapScaleOffset因為多場景編輯的邏輯需求,不再被序列化到場景中去了,而是存在於一個跟著lightmap烘焙產生的檔案叫Lighting Data Asset裡(這個檔在初期的5.x中叫做lightmapSnapShot)。

這個檔案與lightmap在同層目錄中,並且可以在Editor中隨時更換LightingData檔,如下圖所示: 



那既然LightingData檔存儲著lightmapIndex與lightmapScaleOffset,是否只要保證LightingData檔最終能被呼叫不就沒問題了嗎?理論上是,如果我們只需要更換不同燈光條件下的lightmap,場景本身沒有更改的話,那麼多套lightmap對於場景中的靜態物體Renderer而言也只需要同一套lightmapIndex與lightmapScaleOffset資訊而已。

所以這時只要保證lighting視窗的Ligtmaps頁面中Light Data Asset裡選了正確的LightingData檔即可(因為很多開發者習慣性刪除掉這個檔,因此也就遺失了lightmapIndex與lightmapScaleOffset資訊)。

簡單的情況下使用LightingData檔即可。但是還有一種情況,如果場景本身也有所改變怎辦?

例如:我需要原始的場景lightmap與一個被炸彈炸過的場景lightmap,由於場景中的靜態物件改變了,這時兩次烘焙會產生兩個資料不同LightingData檔,無法簡單使用其中一個。 可惜的是現在Unity還不能在執行階段時切換LightingData檔,設計的時候僅僅作為Editor使用。

要解決這問題,我們需要自己記錄每個Renderer上的lightmapIndex與lightmapScaleOffset資訊,然後在更換lightmap的時候把這個資訊還原回去。

總結:具體做法有很多種,基本原理都是把lightmapIndex與lightmapScaleOffset序列化起來。比如說我們可以給每個靜態物件掛一個腳本,在裡面定義一個包含Render,lightmapIndex與lightmapScaleOffset的結構,然後一個Save函數用來在每次烘焙lightmap之後把lightmapIndex與lightmapScaleOffset序列化進結構中,一個Load函數用來在切換lightmap時還原每個Renderer的lightmapIndex與lightmapScaleOffset資訊。這樣問題就解決了。



為何烘焙的Point光源會有一圈色階變化?


大家在烘焙lightmap的時候可能會發現類似下圖的奇怪效果。


圖中我們使用了一盞Baked的point light,烘焙之後發現光照效果會出現一圈圈的色階突變。

原因是我們使用的是壓縮版本的lightmap。如下圖所示:



壓縮貼圖雖然可以幫我們減少遊戲最後的包裝大小,但是對場景的光照效果也會產生影響,所以要追求高品質光照效果的場景建議不要勾選此選項。

取消壓縮後的效果,色階變化不存在了。 




為什麼在Realtime燈光下的效果和改為Baked烘焙的效果差別很大?



這個問題美術經常發生。美術一般在烘焙場景之前會先把燈光都設為Realtime, 來即時查看調整燈光參數後的效果。一旦調整到滿意的效果之後,就會把燈光類型改為Baked然後烘焙。

在Unity 4中這樣做一般來說能達到烘焙之後的效果與即時燈光的效果比較接近,但是在Unity 5中可能會發現差別很大。原因主要有兩點:

1、 在上面我們已經提到,新的燈光參數Bounce Intensity,這個參數的調整會影響最終烘焙效果間接光的比例。

2、 Unity 5中對Gamma和Linear顏色空間的實現做了調整以更符合真實情況,但這也導致即時燈光效果與烘焙效果在兩種顏色空間中存在差異。

所以,建議美術在Linear顏色空間中進行Realtime燈光的調整。因為在Linear顏色空間下,即時燈光與烘焙效果最為接近。不過由於目前手機平台並不支援顏色空間(Color space)切換,我們可以讓專案在PC平台下切換為Linear顏色空間。

選項在Player Settings中的Other Settings裡,如下圖所示:



另外,為了讓Bounce Intensity參數調整也能反映在Realtime燈光中,建議美術在即時調整燈光參數時,在Lighting視窗中勾選Precomputed Realtime GI,如下圖所示: 



當然Precomputed Realtime GI也是需要在場景靜態物體的位置固定之後烘焙一次的,所以如果怕會忘記烘焙的話也可以勾選Build旁邊的Auto。

最後,在調整好燈光效果之後我們把燈光類型改為Baked(或Mixed),取消勾選Precomputed Realtime GI,改勾選Baked GI,然後進行烘焙。

下面展示了兩種顏色空間下Realtime燈光與烘焙效果的對比:



我們可以看到Linear顏色空間下兩種光照方式的效果是最為接近的。

有開發者問到,專案的場景在手機上的烘焙效果與編輯器中看到的即時燈光效果差異非常大,遠遠比上面對比圖中的差別大是為何?

如果大家也有這樣的問題,那麼請注意:不同的螢幕對於同一種顏色的表現都會有或多或少的差別,要對比手機與電腦上看到的顏色差別不能直接進行對比,而應該讓兩個效果在同一個螢幕中對比。一個簡單的辦法是在手機中抓圖然後傳到電腦中進行對比。有興趣的朋友可以試試,保證你一定會因為手機與電腦螢幕的顏色解析差異感到驚訝。

怎麼讓不同物體佔用不同大小的lightmap解析?



這個問題經常會有開發者碰到,主要目的是為了最大限度縮減lightmap的大小,通過讓場景中次要物體的lightmap效果粗糙一點來節省遊戲的空間。
其實做法非常簡單,我們可以通過lighting視窗中Object頁面裡的Scale In Lightmap來設定某個Object在lightmap中佔用多少
像素,預設是1,對於重要的物體提高這個數值來增加此物體使用的lightmap解析,反之我們可以把這個值設為0到1之間來減少此物體所使用的lightmap解析。選項如下圖所示:


有關Unity 5的Enlighten全域光照系統中的相關問題就介紹到這裡,如果大家在開發過程中遇到類似問題,或有相應的解決方法,歡迎給我們留言或是在官方論壇:http://forum.china.unity3d.com 與我們交流!

2 則留言:

  1. 您好,想請問一下。有沒有辦法利用 lighting data asset 中的 data,取得某些沒有出現在畫面上的頂點的全域光照打光顏色? 想問一問有沒有相關的 api ?

    回覆刪除
    回覆
    1. 顏色資訊本身是不在這個lighting data裡面的,而是在lightmap裡面,所以沒辦法這樣取得

      刪除

著作人