溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Unity Shader如何實(shí)現(xiàn)基于光照圖的簡易晝夜變化

發(fā)布時(shí)間:2022-01-05 15:17:05 來源:億速云 閱讀:511 作者:小新 欄目:大數(shù)據(jù)

這篇文章主要介紹Unity Shader如何實(shí)現(xiàn)基于光照圖的簡易晝夜變化,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!

Unity Shader如何實(shí)現(xiàn)基于光照圖的簡易晝夜變化

Unity Shader如何實(shí)現(xiàn)基于光照圖的簡易晝夜變化


好,還是先上一張最終效果的動(dòng)態(tài)圖,變化過程有點(diǎn)卡頓,是因?yàn)镚IF壓縮抽幀導(dǎo)致的,實(shí)際沒有這個(gè)問題。不過應(yīng)該可以表現(xiàn)出一天的時(shí)段變化了。

可能有的朋友發(fā)現(xiàn)問題了,哎呀,怎么影子木有變化.....是的,變不了,因?yàn)槲覀冞@是基于烘焙過得光照圖做的變化,所以,陰影是固定的。如果我們有實(shí)時(shí)光,就不是這個(gè)教程所涉及的了,直接調(diào)整方向光就可以了。

這個(gè)方案最早是基于我以前做的一個(gè)MMO項(xiàng)目,在幾年前,實(shí)時(shí)光并不現(xiàn)實(shí)(現(xiàn)在對于很多項(xiàng)目來說也不現(xiàn)實(shí)),光線幾乎都是由烘焙后的光照圖來決定。但是我們在做一些劇情動(dòng)畫時(shí),又需要一些晝夜變化,通常這些變化還是很快速的。譬如,劇情中寫道“就這么,一夜過去了.....”,如果這時(shí)候能加一個(gè)快速變化的光照系統(tǒng),還是很有畫面感的,對吧?然后,就有了這個(gè)暴力簡陋版晝夜系統(tǒng)。

為什么說它暴力簡陋版呢?因?yàn)樗娴暮鼙┝?,我們最終想到的性能最優(yōu)辦法,就是直接修改所有材質(zhì)的顏色,當(dāng)然最終如果實(shí)現(xiàn)出好的效果的話,還是需要很多方面考量的,這里只提供一個(gè)簡單的解題思路。

修改材質(zhì)的顏色,總不能讓我們寫代碼去挨個(gè)遍歷場景中所有的材質(zhì)來修改顏色吧,累死了,而且一想就知道性能堪憂。好在Unity為我們提供了一個(gè)便利的修改方法,就是全局修改shader屬性。我們來看看官方的這個(gè)API,修改全局shader顏色的,(Shader.SetGlobalColor)。

Unity Shader如何實(shí)現(xiàn)基于光照圖的簡易晝夜變化

用起來很簡單,這個(gè)API可以直接的修改場景里所有叫這個(gè)名字的屬性,前提是這個(gè)屬性沒有被暴露在編輯器里,也就是說你只能在Pass通道里聲明它,如果你在屬性塊里聲明了它,對不起,這個(gè)API無效,它會(huì)優(yōu)先使用屬性塊里定義的參數(shù)。

熟悉了這個(gè)API,接下來就簡單了,我們只需要在每一個(gè)需要變化的shader里,加入一個(gè)天光的顏色屬性,直接乘上去就行了,最后再全局修改這個(gè)顏色??纯聪旅娴膕hader重點(diǎn)代碼,多出來的代碼,用手指頭都能數(shù)的過來。

    //這里就是需要修改的全局顏色---天光顏色    fixed4 _SkyColor;
   void surf (Input IN, inout SurfaceOutputStandard o) {      // 直接乘上去就好了      fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color*_SkyColor;

好,還沒完,重點(diǎn)是我們需要用一個(gè)腳本來控制修改這個(gè)顏色。

這里我寫了四個(gè)時(shí)段的顏色,分別是,早晨,中午,下午,晚上(事實(shí)上如果不計(jì)較細(xì)節(jié),早晨和中午可以合并)。并把這四個(gè)顏色暴露出來,供場景人員修改。還有一個(gè)一整天循環(huán)一次所需要的時(shí)間,單位為秒。

Unity Shader如何實(shí)現(xiàn)基于光照圖的簡易晝夜變化

接下來就是在代碼里去推進(jìn)時(shí)間了,下面是代碼,還是比較簡單的,每一行我都寫了詳細(xì)注釋,但我代碼寫的并不好,請諒解。

public class DayAndNightManager : MonoBehaviour{    public float oneDayTime = 10; //一整天循環(huán)一次的時(shí)間,這里默認(rèn)的十秒    public Color morningColor = Color.white; //早晨顏色    public Color noonColor = Color.white; //中午顏色    public Color afterNoonColor = Color.white; //傍晚顏色    public Color nightColor = Color.white; //晚上顏色
   private Color[] colors;//所用的所有時(shí)間段的顏色    private Color currentColor;//當(dāng)前顏色    private float dayTimer; //時(shí)間    // Use this for initialization    void Start () {        //把所有時(shí)段的顏色收集起來,這里多了一個(gè),是因?yàn)檎麄€(gè)是一個(gè)循環(huán),結(jié)束的時(shí)候要回到凌晨時(shí)段,所以最后一個(gè)顏色是早晨的顏色        colors = new Color[5];        colors[0] = morningColor;        colors[1] = noonColor;        colors[2] = afterNoonColor;        colors[3] = nightColor;        colors[4] = morningColor;        //初始化        Shader.SetGlobalColor("_SkyColor",Color.white);        currentColor = morningColor;        dayTimer = 0;    }
   // Update is called once per frame    void Update()    {        //時(shí)間開始推進(jìn),乘以4是因?yàn)槲覀冇兴膫€(gè)時(shí)段,這樣再乘以后面的一整天時(shí)間才是準(zhǔn)確的。        dayTimer += Time.deltaTime*4/oneDayTime;        //一個(gè)循環(huán)結(jié)束了,重新開始        if (dayTimer >= (float)colors.Length - 1)        {            dayTimer = 0;        }        //采樣兩個(gè)顏色,第一個(gè)是即將過去的時(shí)間顏色,第二個(gè)是即將到來的時(shí)間顏色,我們通過兩個(gè)數(shù)學(xué)方法向下和向上取整。        Color color01 = colors[Mathf.FloorToInt(dayTimer)];        Color color02 = colors[Mathf.CeilToInt(dayTimer)];        //兩個(gè)顏色的占比,我們通過取時(shí)間的小數(shù)部分,就可以了。        float weight = dayTimer - Mathf.FloorToInt(dayTimer);        //利用權(quán)重來對顏色進(jìn)行融合        currentColor = Color.Lerp(color01, color02, weight);        //修改全局顏色,_SkyColor已經(jīng)寫到所有的shader里了        Shader.SetGlobalColor("_SkyColor", currentColor);        //修改全局時(shí)間,這個(gè)主要是控制天空盒的貼圖融合        Shader.SetGlobalFloat("_DayAndNightChange", dayTimer);


   }

代碼的最后一行是修改天空盒的貼圖融合,是的,光變了,天空盒怎么能不變,所以我用了四張?zhí)炜蘸械馁N圖來做過度,聽著內(nèi)存有點(diǎn)吃緊是吧?如果想節(jié)約的話,可以用一張貼圖來做顏色變化就可以了,但是效果可能沒有這樣好一點(diǎn),美術(shù)同學(xué)可以把四張圖都給你畫的很美吆。

至于天空盒的shader,也很簡單,我使用了一個(gè)float屬性來融合四張貼圖,重點(diǎn)代碼如下。

    //重點(diǎn)是這個(gè)變量,通過全局修改這個(gè)變量的參數(shù)來對四個(gè)時(shí)段的貼圖采樣,并融合。    //這個(gè)變量的范圍與腳本的時(shí)間一致,都是0-4循環(huán),最后一個(gè)時(shí)段與第一個(gè)一樣,都是凌晨    float _DayAndNightChange;
   half4 frag (v2f i) : SV_Target    {      //基本就是一些融合算法,先利用時(shí)間來算出每個(gè)貼圖的顏色占比,然后統(tǒng)一加到一起。      //這只是計(jì)算了天空盒的一面,后面的Pass算法相同     half4 col01 = skybox_frag(i,_Tex01, _Tex01_HDR)*(saturate(1- _DayAndNightChange));     half4 col02 = (skybox_frag(i,_Tex02, _Tex02_HDR)*(saturate(2- _DayAndNightChange)))-(skybox_frag(i,_Tex02, _Tex02_HDR)*(saturate(1- _DayAndNightChange)));     half4 col03 = (skybox_frag(i,_Tex03, _Tex03_HDR)*(saturate(3- _DayAndNightChange)))-(skybox_frag(i,_Tex03, _Tex03_HDR)*(saturate(2- _DayAndNightChange)));     half4 col04 = (skybox_frag(i,_Tex04, _Tex04_HDR)*(saturate(4- _DayAndNightChange)))-(skybox_frag(i,_Tex04, _Tex04_HDR)*(saturate(3- _DayAndNightChange)));     half4 col05 = skybox_frag(i,_Tex01, _Tex01_HDR)*(saturate(_DayAndNightChange -3));     half4 c = col01 +col02+col03+col04+col05;     return c;     }

代碼有些冗余,有很大的修改空間........但是這樣看著比較清楚,其實(shí)就是利用_DayAndNightChange這個(gè)屬性對每張貼圖進(jìn)行采樣,然后融合。

好,這里我只是實(shí)現(xiàn)了一個(gè)簡易版的效果,如果想在項(xiàng)目中使用,并實(shí)現(xiàn)好的效果,還有很多需要考慮的,譬如,全局修改霧的顏色,還有晚上開啟一些燈光效果,火把效果,還可以寫一個(gè)方法,用于跳轉(zhuǎn)到某個(gè)時(shí)間。還有如果這種屬性多了的話,最好重新修改一下編輯窗口,否則美術(shù)同學(xué)看著會(huì)很亂。

至于性能方面,這種系統(tǒng)還是很好控制的,個(gè)人推薦時(shí)間的變化不要像我寫的這樣每幀一次,可以控制一下,譬如0.1s一次。

上面這個(gè)簡單工程我也附在這里,如果有什么不對的地方,望指正。

對了,這個(gè)工程在打開的時(shí)候可能場景是黑的,運(yùn)行一次就好了。原因是,全局修改的變量,也就是那個(gè)_SkyColor,在沒有賦值的時(shí)候,是黑色的。后期這個(gè)東西可能需要改一下,

以上是“Unity Shader如何實(shí)現(xiàn)基于光照圖的簡易晝夜變化”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI