老實講,這個需求是老闆提的。
遊戲嘛,很多東西都可以做,但是做不做往往不是做的人可以決定的。這個效果雖然沒見過有遊戲實現過(一般實現的都是無方向的邊緣光),但是在一些2d動畫裡是有的——比如一款叫輪舞曲duo!的遊戲,在一些過場和戰鬥畫面裡面出現了很正確的光照效果,比如一盞燈從胸口平移過去可以看到非常合理的溝渠明暗變化過程。
剛看到的時候很驚豔,想想也就是明白了。在2d畫面上加乙個法線圖不就好了,變形後的影象,法線雖然會有一定偏移,但大方向是正確的,並不容易暴露。
所以實現這個並沒有什麼難度,難的是製作上的問題。搞來搞去,法線圖最後只給了5種顏色,分別對應中間,左,右,上,下。所以基本上沒法獲得正確的光照效果,只能做到將物體的不同區域做出區分單獨讓光線生效,如果是平行光,各區域之間是完全沒有過渡的,但如果用點光源,倒是可以靠衰減和角度體現出過渡來,勉強說的過去。
實現方式就是將2d物體看作是個佈告板,然後加上法線圖直接用和法線貼圖一樣的方式處理。
但這樣的話,就需要乙個正常的光照系統了。在不用srp的情況下,unity多光源的方式是多pass,顯然沒這必要。頂點光照又不可能用在法線貼圖上。普通的forward shader裡,因為是用於多pass的,能取到光照資料並不完整。不過好在unity還保留了最早的vertex lit用的光照,可以獲得最近8個光源的資料。
unity_lightcolor 燈光顏色
unity_lightposition 燈光位置,用w可以區分平行光和頂點光
unity_lightatten 點光源範圍,衰減引數
unity_spotdirection spot的資料
要注意的是,這些資料都是在檢視空間內的,而且需要把lightmode設定成vertex才會生效。
而利用的方法在unity自己的shader裡已經提供了,照抄就好:
float3 tolight = unity_lightposition[k].xyz - i.viewposition * unity_lightposition[k].w;
float lengthsq = dot(tolight, tolight);
lengthsq = max(lengthsq, 0.000001);
tolight *= rsqrt(lengthsq);
float atten = 1.0 / (1.0 + lengthsq * unity_lightatten[k].z);
//if (false) //spotlight
//float diff = max (0, dot (viewn, tolight));
lightcolor += unity_lightcolor[k].rgb * (diff * atten);
同時處理了點光源和平行光,並用乙個可選分支來支援spot光,不需要支援可以刪掉。
需要說明的是,這段**本身是用在頂點上的,直接移動到frag上是可以,但就需要靠考慮下效能問題。這組光源事先按距離排序過,所以取前幾個就行了,也可以中途用break提前退出(不存在的光源會被填充為0,0,0,0,unity_lightcolor[k].a == 0就表示後面沒資料了),不過我也不清楚用break有沒有提公升,雖然這種用法同warp肯定在同一分支不會有浪費,但畢竟會多乙個break的成本,而且會阻礙迴圈展開。
由於都是用在較小的2d人物上,即使光源多也不需要做精確的光照裁剪,就不用考慮那些麻煩事了,但是大塊物體比如牆體和路面絕對不能這樣用。現階段由於手機上還無法放棄不支援compute shader的裝置,基本沒啥合適的方案,目前的foward+的光照裁剪部分基本無法脫離compute shader(compute buffer也是其中一部分),傳統的tbdr也不清楚和手機的硬體tbr有沒有衝突。srp我沒認真看過,不清楚它的hd管線(傳統tbdr實現)是否能脫離compute shader,但不管怎麼說,那畢竟也是個延遲渲染,很麻煩的。不支援compute buffer恐怕是下乙個阻礙手遊畫面提公升的節點。
扯遠了,反正現在這做法只能處理人物,場景是不能受光照影響的,效能上浪費太多。控制光源上限就能限制成本,一般單物體最多3個光也差不多夠了。
法線資料直接取樣就行了,畢竟是佈告板。
嚴格來講,應該根據uv方向旋轉貼圖,這樣物體旋轉的時候才不會有問題。但是因為當初沒商量好,都是在單個部件模型上繪製的法線方向,然後打圖集的時候部件會被旋轉,而shader取不到這個旋轉資訊,就無法還原。如果法線圖是在打圖集後,根據圖集裡的方向來繪製本來是沒問題的。
不過旋轉不常見,也就無所謂了,應用了旋轉還可能出現預料之外的情況。但完整的方案應該是要旋轉的,需要乘乙個切線空間到檢視空間的矩陣,和普通的法線貼圖一樣。
但依然需要乘乙個物體空間到檢視空間的矩陣。這裡遇到了乙個坑。按說這裡直接用normalize(mul((float3x3)unity_matrix_it_mv, norm))處理方向就好,但實際執行的時候會在特定位置和角度會出現頻閃現象,我查了下unityshader的原始碼,發現它所有轉換角度的地方都是這樣的
inline float3 unityobjecttoviewnormal( in float3 norm )
用了個巨集來區分uniform和非uniform的情況,而且全走的下面那行,雖然我的模型確實是uniform的。下面那行……看上去完全沒有道理,所以我只能認為unity_matrix_mv被做了什麼手腳,先不管它。
替換了之後就正確了。但實際執行的時候,在某些特定機型執行下面那條shader會直接崩掉而且沒有任何反饋,fallback也無法捕獲,表現出來的是整個麵片大幅度隨機擾動(一段寫在frag的**竟然能影響頂點你敢信?)
也沒辦法只能改回上面那行偶爾錯誤的**。
我也不清楚這算是unity還是裝置的錯,但輸入再怎麼錯,裝置也不應該讓frag影響頂點吧?所以以下機型能不能主動去世?
oppo r9s,華為 榮耀暢玩5x(kiw-al10),vivo x9,華為 榮耀暢玩5a(cam-al00),oppo a57,華為 公尺蘭;麥芒5(mla-al10),三星 galaxy a700(sm-a7000),小公尺 -(hm 4x),vivo xplay5a,vivo x6splus d,樂視 樂max 2(x820),努比亞 z11 mini(nx529j),三星 galaxy a7(sm-a7100),三星 galaxy note 4(sm-n9108v),三星 galaxy note 4(sm-n9109w),樂視 樂1 pro(x800),三星 -(sm-g6100),中興 新axon天機(a2017),錘子 smartisan t2(sm801),谷歌 hamu;xt1100,moto nexus 6(nexus 6),三星 galaxy note 4(sm-n9106w),奇酷 q5(1515-a01),vivo pd1516a(xplay5s),努比亞 z17 mini(nx569j),中興 天機7 max(c2017),酷派 鋒尚n1s(5380ca),海信 a1 ivvi i3p-02(i3p-02),oppo r9st,華為 nova(caz-tl10),樂視 樂2(x528)總得來講,這個效果表現尚可。設計的時候是在夜間低亮度下使用的,但是高亮度下也不是一點效果都沒有,只是效果差很多罷了。光照本來也就是這樣的東西。
實現的時候用的都是unity自己的東西,倒是挺簡單的,不費啥工夫。主要的人力在於畫法線圖那邊的人。但是圖那麼難的都畫了,劃分5個方向的朝面塗色也不算太麻煩吧。
如果量少,繪製更細緻的法線就能獲得更高的品質。不過要這樣做的話,可能3d建模生成法才反而才是效率最高的做法。
厲害的人還可以考慮立繪或者cg繪製法線。講真的,那個效果才是真的牛批。(不要找我要圖,你覺得我能發嗎?)
一句題外話,現在日式**的npr的可參考例子太少了,其實真想做日式**的npr,除了mmd,真有必要翻下日本那邊的**渲染的3d動畫,嗯,我指的就是那種。
遊戲也有,也是那種的。
誰讓日本遊戲業不行了呢,他們也只能做這個了。
2D遊戲開發(2)
每次給遊戲新增新功能時,通常也會引入一些新設定。為了讓所有的設定進行統一管理,我們可以配置乙個名為 setting的模組,這個模組中包含乙個setting的類,用來儲存所有的設定。usr bin env python3.5 filename setting 儲存所有的設定 class setting...
2D遊戲vs3D遊戲
前幾天,小李子一直在糾纏3d,偽3d遊戲等。雖說小李子效力於大公司,但是該公司畢竟不是專業的遊戲公司。美工xx等一概不全,要使用個3d引擎做個ipad,android的遊戲。這年頭是怎麼了。遊戲最主要的是什麼?是畫面?還是互動的畫面?還是互動的立體畫面?接觸過這麼多遊戲,還沒有見過只以最精緻的畫面獲...
2D遊戲vs3D遊戲
前幾天,小李子一直在糾纏3d,偽3d遊戲等。雖說小李子效力於大公司,但是該公司畢竟不是專業的遊戲公司。美工xx等一概不全,要使用個3d引擎做個ipad,android的遊戲。這年頭是怎麼了。遊戲最主要的是什麼?是畫面?還是互動的畫面?還是互動的立體畫面?接觸過這麼多遊戲,還沒有見過只以最精緻的畫面獲...