Demo 開源丨Cocos Creator 快速實(shí)現(xiàn) 2D 動(dòng)態(tài)光照
腦海中有個(gè)炫酷的效果,卻不知道怎么實(shí)現(xiàn)?茫茫互聯(lián)網(wǎng)竟然找不到自己需要的 Cocos Creator 教程?
其實(shí),許多熱心開發(fā)者、Cocos 引擎官方技術(shù)團(tuán)隊(duì),一直在論壇、GitHub、公眾號(hào)等地方持續(xù)輸出著自己對(duì) Cocos Creator 的探索成果。Cocos 布道師 好巧啊c & 哲鋒 收集了部分優(yōu)質(zhì)開源 Demo 工程,將陸續(xù)同大家分享一些 Cocos Creator 3.x 游戲常用效果技術(shù)實(shí)現(xiàn)方案。
今天先從 2D 動(dòng)態(tài)光照說起,下一章再來講講「陰影」。

「八方旅人」光照效果,圖源網(wǎng)絡(luò)
在游戲里面,良好的光照是烘托氣氛的絕佳方式。以前的 2D 游戲大部分是不帶任何動(dòng)態(tài)光照的,而隨著圖形學(xué)和硬件的進(jìn)步,現(xiàn)在,越來越多的 2D 游戲也開始采用動(dòng)態(tài)光照來豐富游戲的表現(xiàn),比如大名鼎鼎的《八方旅人》、《泰拉瑞亞》等等。
恰好最近 Cocos Creator 的技術(shù)支持團(tuán)隊(duì)發(fā)布了基于 Coocos Creator 3.3.2 的?2D 動(dòng)態(tài)光照?Demo,今天我們就基于這個(gè) Demo,一起看看如何為自己 2D 游戲添加動(dòng)態(tài)光照。
2D 動(dòng)態(tài)光照的 Demo 及資源貼見文末,請(qǐng)往下拉↓
法線貼圖
首先看看 Demo 的預(yù)覽效果。可以看到,當(dāng)我們?cè)?Demo 中移動(dòng)光源的位置,小人和場(chǎng)景也會(huì)隨之表現(xiàn)出不同光照的情景(案例素材源于CODEWEB)

那么它和普通的 Sprite 有什么區(qū)別呢?
通過對(duì)比不難看出它們的差別主要是集中在材質(zhì)上面。小人使用了自定義的 mat_normal 材質(zhì)。

在 Cocos Creator 里,我們可以通過不同的材質(zhì)來定義物體渲染的樣式。
而對(duì)于帶光照的 Sprite,他多出了一個(gè)叫做 USE_2D_NORMAL 和 USE_2D_LIGHT 的選項(xiàng),同時(shí)多了一張叫做 normal 的法線貼圖。

僅僅只增加這么一張小小的法線就能實(shí)現(xiàn)光照效果,這就是圖形學(xué)的神奇之處了。
那么該如何制作法線貼圖呢?
如果你的項(xiàng)目是 3D 轉(zhuǎn) 2D,法線貼圖的制作也不是太困難的事情,只要讓項(xiàng)目組的美術(shù)大佬在導(dǎo)出圖片的同時(shí)導(dǎo)出對(duì)應(yīng)的法線貼圖就可以;
但如果你的項(xiàng)目是手繪的,想要光照效果就比較麻煩了,畢竟你需要手繪法線貼圖;
當(dāng)然現(xiàn)在也有可以通過圖片生成法線的算法[2],只不過效果畢竟沒有手調(diào)的好。要好的效果的話,還是需要手調(diào)法線。
準(zhǔn)備好了自己的法線貼圖,就可以去啟用 2D 光照了。
添加動(dòng)態(tài)光照
第一步,為 Sprite 創(chuàng)建一個(gè)新的材質(zhì)。

第二步,在材質(zhì)中選擇 light/eff 作為材質(zhì)的 Effect。

第三步,勾選 USE_2D_NORMAL 和 USE_2D_LIGHT 并將準(zhǔn)備好的法線貼圖賦予給 Light Normal 這個(gè)屬性。

最后我們需要?jiǎng)?chuàng)建一個(gè)光源。3D 的光源是不行的,我們需要?jiǎng)?chuàng)建的是一個(gè) Demo 中自帶的光源組件?light/Light.ts。

到這里我們已經(jīng)成功添加了動(dòng)態(tài)光照,接下來簡(jiǎn)單了解一下光照的實(shí)現(xiàn)原理,這能幫助我們更好理解光照效果的實(shí)現(xiàn)。
實(shí)現(xiàn)原理
在物理世界中,我們看到的物體的顏色,其實(shí)是物體本身反射光線的顏色,因?yàn)槲矬w的材質(zhì)不同,會(huì)吸收部分不同的顏色分量而導(dǎo)致我們看到的物體顏色不同。
模擬光照的過程實(shí)際上就是模擬整個(gè)光的傳播過程。

I 為入射光線??
R 為反射光線??
n 為法線
v 為物體表面某個(gè)位置到視點(diǎn),也就是攝像機(jī)的向量
一般來說我們可以將光照簡(jiǎn)單的分為以下三類:
漫反射(Diffuse)。漫反射描述了粗糙表面、無光澤表面對(duì)光的反射。常見的模型為 Lamber模型,它描述了入射光在射入粗糙物體表面以后所產(chǎn)生的反射物理現(xiàn)象。其反射光的方向由入射光和法線的夾角決定,當(dāng)夾角越大,損失的能量就越大,看到的物體就越暗。同時(shí)離光源越遠(yuǎn),能量損失也越大。
鏡面反射(Specular)。鏡面反射描述了光線在光滑表面對(duì)光的反射。
環(huán)境光(Ambient)。環(huán)境光表示光線在場(chǎng)景內(nèi)進(jìn)行復(fù)雜的傳播以后,形成的彌漫整個(gè)空間的光線。
最終的光線顏色計(jì)算滿足下面的公式:

I 為最終的光顏色
Id 為漫反射光
Is 為鏡面反射光
Ia 為環(huán)境光
在圖形學(xué)的光照模型中,幾乎大部分的光照算法都來源于對(duì)上述公式的擴(kuò)展。
Shader 解析
讓我們回到 Demo 中看看光照是怎么實(shí)現(xiàn)的。
針對(duì)漫反射,我們需要定義光源的位置,因此在 light/Light.ts 腳本將光源的位置傳遞給著色器:
????spr.getMaterial(0).setProperty('light_worldpos',?lightPos);
其中 lightPos 是計(jì)算了光源節(jié)點(diǎn) light 在世界坐標(biāo)位置。
在著色器內(nèi),通過計(jì)算位置和光源的距離,對(duì)光線的能量衰減進(jìn)行計(jì)算。

之后使用光源到位置點(diǎn)的矢量和法線的夾角計(jì)算反射光的強(qiáng)度。
?//?計(jì)算光照反射系數(shù),向量點(diǎn)積
?float?normalDot?=?max(0.0,?dot(normal,?-normalize(vec3(object_direction.x,?object_direction.y,?-60))));
?//?反射光?*?法向量衰減?+?環(huán)境光??????
?return?col?*?(diffuse?*?light_brightness?*?normalDot?+?vec3(light_ambientColor));
?
通常環(huán)境光不會(huì)有大的變化,因此使用了常量來代替,代碼中使用 light_ambientColor 來描述。

最后將物體本身的顏色 col 和光線的顏色進(jìn)行相乘得到該片元的顏色。

在 Demo 中我們可以看到通過計(jì)算入射光和物體法線的夾角來計(jì)算光照明亮變化的過程。
而這里的法線則來自從法線貼圖內(nèi)采樣的值,這也就是為什么在實(shí)現(xiàn) 2D 光照時(shí),我們需要提供法線貼圖。畢竟對(duì)于普通的 Sprite 來說,它本身是不含有任何法線信息的。
?//?獲取法向量
????vec3?normal?=?texture(light_normal,?uv0).rgb;
?normal?=?normal?*?2.0?-?1.0;
資源鏈接
點(diǎn)擊文末【閱讀原文】下載 Demo
https://github.com/cocos-creator/CococsCreator-public-technology-solutions.git
論壇地址
https://forum.cocos.org/t/topic/124637
參考文章
2D 中的法線貼圖
https://zhuanlan.zhihu.com/p/111211259
基礎(chǔ)法線貼圖生成算法
https://zhuanlan.zhihu.com/p/111419293



