生活随笔
收集整理的這篇文章主要介紹了
Cocos Creator 3D 材质系统:曲面效果如何实现?
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
引言
前不久發(fā)布的 Cocos Creator 1.0.2 版本中正式加入了對 OPPO 小游戲、vivo 小游戲以及華為快游戲平臺的支持,在諸多 Creator 3D 制作的小游戲案例中,《豬豬俠:極速狂飆》已上線 OPPO 小游戲平臺。
這款休閑跑酷小游戲,采用了曲面材質(zhì)效果來使跑酷賽道更加多變有趣,今日,Cocos 引擎開發(fā)工程師 ChiaNing 將為各位開發(fā)者來解析這種曲面效果的實現(xiàn)思路和方案,在閱讀完本文之后,大家便可以將這種效果應(yīng)用在自己的游戲中。
1、使用背景
在固定背后視角的跑酷游戲中,玩家面對的始終是前方布滿障礙的賽道,除去有趣的障礙設(shè)計可以一直吸引玩家的注意外,許多游戲還會添加一些視覺效果來使游戲畫面看起來更豐富有趣,比如今天我們要分享的這款《豬豬俠:極速狂飆》就采用了曲面的效果使得賽道看起來更多變,也更有立體感和縱深感。
?
?
那么,這樣的曲面效果在 Cocos Creator 3D 中是如何實現(xiàn)的呢?
2、實現(xiàn)方案分析
要實現(xiàn)曲面的效果,我們有幾種方案可選擇:
1. 直接使用曲面模型
這是最直觀最容易想到的實現(xiàn)方案,從模型層面直接將效果做好,省去了其他處理,但這種方案也存在著很多嚴(yán)重的問題:
(1)模型復(fù)用不便,模型生成時的狀態(tài)幾乎決定了它的使用場合,這對于游戲開發(fā)中需要大量復(fù)用資源以減小包體來說有嚴(yán)重的問題。
(2)對于跑酷游戲這種物理需求并不復(fù)雜的游戲來說,大部分的游戲邏輯都可以直接通過計算直接完成而并不需要依賴物理引擎實現(xiàn),對于正常的模型來說,規(guī)則的形狀對于邏輯實現(xiàn)是很友好的,但是啟用曲面模型就會對這種計算帶來很多困難,幾乎只能通過使用物理引擎來實現(xiàn),過多的物理計算對性能是會有較大的影響的。
包體不友好,性能不友好,異型模型還會對制作帶來麻煩,對于只是為了實現(xiàn)顯示效果來說,這些損耗得不償失。
2. 使用材質(zhì)系統(tǒng)實現(xiàn)
我們需要明白一點,要實現(xiàn)的曲面的效果,實際上影響的只有顯示效果,與其他的任何系統(tǒng)是不相關(guān)的,它不應(yīng)當(dāng)影響到其他無關(guān)的模塊,既然只想改變顯示,那采用材質(zhì)系統(tǒng)相較于采用曲面模型的方案有著諸多好處:
(1)不必使用物理引擎,簡單的物理效果可以通過計算來實現(xiàn),效率更優(yōu)。
(2)模型可復(fù)用,想要實現(xiàn)不同的彎曲效果也很方便,只要使用帶有曲面效果的不同參數(shù)的材質(zhì)即可實現(xiàn)同一模型的不同效果。相較于方案一的多重模型來說,只需要幾個材質(zhì)即可解決問題。
(3)參數(shù)可配置,可以通過參數(shù)調(diào)節(jié)來得到不同的效果。
分析看來,相較于直接使用曲面模型的方案來說,使用材質(zhì)系統(tǒng)實現(xiàn)的方案優(yōu)勢很明顯,沒有額外的開銷,也沒有太大的包體負(fù)擔(dān)。
綜上所述,使用材質(zhì)系統(tǒng)實現(xiàn)更能滿足我們的需求,因此采用材質(zhì)系統(tǒng)來實現(xiàn)這個效果。
3、方案思路分析
從需求來看,我們的目的是實現(xiàn)一個與我們的觀察點相關(guān)的模型變形,既然只是變形,并不涉及到顏色的變化和處理,那么需要處理的就只有頂點著色器部分,并不涉及片段著色器。對于不太清楚渲染管線各個階段的讀者,可以參考 [LearnOpenGL]的渲染管線介紹。
在 Shader 中,通過頂點著色器即可完成對模型頂點位置的操作。明確了是對頂點位置進(jìn)行操作后,我們將攝像機所在的點定為原點。
由于我們的攝像機是固定在人物背后,且賽道始終保持向 Z 軸負(fù)方向延伸,所以可以將模型與攝像機的 Z 軸方向的距離看作函數(shù)的輸入值,想要得到曲面的效果,模型的點的變化規(guī)律如下:
?
- 距離原點越遠(yuǎn)的點產(chǎn)生的偏置值越大(函數(shù)在區(qū)間內(nèi)為增函數(shù))
- 距離原點越遠(yuǎn)的點偏置的變化速度越快(函數(shù)的導(dǎo)數(shù)為一次函數(shù))
- 由上述兩條規(guī)律不難得出,二次函數(shù)的變化規(guī)律與我們想要實現(xiàn)的曲面效果的規(guī)律契合,所以我們的頂點著色器的運算為一個關(guān)于頂點位置 Z 值的二次函數(shù)運算。
我們剛剛得出的規(guī)律是建立在一個特定空間下的,即以攝像機為原點的空間,這個空間正是空間變換中的觀察空間階段,所以我們之后對頂點的操作正是在這個空間中進(jìn)行才能夠得到正確的結(jié)果。
4、材質(zhì)系統(tǒng)簡介
Cocos Creator 3D 提供了完備的材質(zhì)系統(tǒng),基于這套材質(zhì)系統(tǒng),我們能夠很方便地在引擎中創(chuàng)建使用編輯材質(zhì),并且在場景預(yù)覽窗口能夠隨時觀察到材質(zhì)更改所帶來的變化。
在 Cocos Creator 3D 編輯器中與材質(zhì)系統(tǒng)相關(guān)的資源有兩種,分別為:
Effect 資源
此類型資源為符合 Cocos Effect 語法標(biāo)準(zhǔn)的渲染流程描述文件,由 YAML 格式的流程控制清單和基于 GLSL 300 ES 語法的 Shader 片段共同組成。
Material 資源
此資源可看做是 Effect 資源在場景中的資源實例,其本身除了 Effect 資源的引用外,還包括很多可配置參數(shù)以決定 Material 的狀態(tài)。在實際使用中,我們的模型是需要使用 Material 資源的,這樣就可以實現(xiàn)使用同一個 Effect 但參數(shù)不同以實現(xiàn)不同效果的需求了。
材質(zhì)的使用也非常的方便,在 Cocos Creator 3D 編輯器的資源管理器中右鍵即可新建出 Effect 資源和 Material 資源。
?
在 Material 資源可選擇需要使用的 Effect 還可對其他參數(shù)進(jìn)行配置,完成配置并保存后,選中需要使用材質(zhì)的模型,選中需要的 material 或者直接將 material 拖入框中即可完成材質(zhì)的設(shè)置。
?
具體詳盡的材質(zhì)介紹和參數(shù)表請參看官方文檔 [材質(zhì)系統(tǒng)]。
5、具體實現(xiàn)
思路已經(jīng)很清晰了,那么現(xiàn)在開始著手實現(xiàn) Shader。下面是具體的實現(xiàn)步驟:
1、啟動 Cocos Creator 3D 編輯器(以下簡稱編輯器),為實驗方便,使用最簡單的場景即可,新建場景后,在場景編輯器中新建一個 Plane 模型,之后以此對象作為查看 Shader 效果的對象。
2、在編輯器的資源管理器中右鍵新建 Effect,將其命名為 curved 或者符合要求的名字。
3、這時新建的 Effect 文件為編輯器內(nèi)置的 Effect 模板,其包含了最基礎(chǔ)的 shader 結(jié)構(gòu),我們需要在這個基礎(chǔ)上添加我們需要的功能,關(guān)于 Effect 的具體介紹請參看我們的 [材質(zhì) Effect 文檔],在里我只對我們需要的更改做出介紹。
4、先來看 CCEffect 部分:
?
<p>CCEffect %{</p><p><span style="white-space:pre">? ? ? ? </span>techniques:</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>- name: opaque</p><p><span style="white-space:pre">? ? ? ? </span>passes:</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>- vert: general-vs:vert # builtin header</p><p><span style="white-space:pre">? ? ? ? </span>frag: unlit-fs:frag</p><p><span style="white-space:pre">? ? ? ? </span>properties: &props</p><p><span style="white-space:pre">? ? ? ? </span>mainTexture: { value: white }</p><p><span style="white-space:pre">? ? ? ? </span>mainColor: { value: [1, 1, 1, 1], editor: { type: color } }</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>- name: transparent</p><p><span style="white-space:pre">? ? ? ? </span>passes:</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>- vert: general-vs:vert # builtin header</p><p><span style="white-space:pre">? ? ? ? </span>frag: unlit-fs:frag</p><p><span style="white-space:pre">? ? ? ? </span>blendState:</p><p><span style="white-space:pre">? ? ? ? </span>targets:</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>- blend: true</p><p><span style="white-space:pre">? ? ? ? </span>blendSrc: src_alpha</p><p><span style="white-space:pre">? ? ? ? </span>blendDst: one_minus_src_alpha</p><p><span style="white-space:pre">? ? ? ? </span>blendSrcAlpha: src_alpha</p><p><span style="white-space:pre">? ? ? ? </span>blendDstAlpha: one_minus_src_alpha</p><p><span style="white-space:pre">? ? ? ? </span>properties: *props</p><p>}%</p> 復(fù)制代碼
在默認(rèn)的 Effect 模板中,我們需要更改的是 vert 字段所使用的 Shader 片段,默認(rèn)模板中提供的 general-vs:vert 是內(nèi)置的頂點著色器,所以我們需要將其替換為我們即將實現(xiàn)的頂點著色器的名字(暫定為 unlit-vs).
接下來,需要對 properties 部分進(jìn)行修改,property 列表將會將屬性暴露在編輯器面板上方便我們的編輯和更改。
此時需要決定哪些數(shù)據(jù)是需要作為 uniform 傳入 shader 中對效果做出影響的了,結(jié)合之前分析的需求:需要有一個決定模型點在各個分量軸上偏置值的偏置位置信息,我們使用一個 vec4 來存儲這個偏置值(allOffset);需要有一個決定偏置變化的系數(shù)的值,使用一個 float 即可(dist);還可以添加模型的主貼圖等(mainTexture)
經(jīng)過以上更改之后,Effect 的 CCEffect 部分看起來是這個樣子的:
?
<p>CCEffect %{</p><p><span style="white-space:pre">? ? ? ? </span>techniques:</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>- name: opaque</p><p><span style="white-space:pre">? ? ? ? </span>passes:</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>- vert: unlit-vs:vert</p><p><span style="white-space:pre">? ? ? ? </span>frag: unlit-fs:frag</p><p><span style="white-space:pre">? ? ? ? </span>properties: &props</p><p><span style="white-space:pre">? ? ? ? </span>mainTexture: { value: grey }</p><p><span style="white-space:pre">? ? ? ? </span>allOffset: { value: [0, 0, 0, 0] }</p><p><span style="white-space:pre">? ? ? ? </span>dist: { value: 1 }</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>- name: transparent</p><p><span style="white-space:pre">? ? ? ? </span>passes:</p><p><span style="white-space:pre">? ? ? ? </span>- vert: unlit-vs:vert</p><p><span style="white-space:pre">? ? ? ? </span>frag: unlit-fs:frag</p><p><span style="white-space:pre">? ? ? ? </span>depthStencilState:</p><p><span style="white-space:pre">? ? ? ? </span>depthTest: true</p><p><span style="white-space:pre">? ? ? ? </span>depthWrite: false</p><p><span style="white-space:pre">? ? ? ? </span>blendState:</p><p><span style="white-space:pre">? ? ? ? </span>targets:</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>- blend: true</p><p><span style="white-space:pre">? ? ? ? </span>blendSrc: src_alpha</p><p><span style="white-space:pre">? ? ? ? </span>blendDst: one_minus_src_alpha</p><p><span style="white-space:pre">? ? ? ? </span>blendDstAlpha: one_minus_src_alpha</p><p><span style="white-space:pre">? ? ? ? </span>properties: *props</p><p>}%</p> 復(fù)制代碼
5、由于默認(rèn)的 Effect 模板中使用了內(nèi)置的頂點著色器,游戲賣號所以這里需要實現(xiàn)自己的頂點著色器,可以參考內(nèi)置的 builtin-unlit 的實現(xiàn)來編寫此段 shader:
添加需要的 uniform :
<p></p><p>uniform Constants {</p><p>??vec4 allOffset;</p><p>??float dist;</p><p>};</p> 復(fù)制代碼
編寫入口函數(shù):vert
按照引擎要求對接骨骼動畫和數(shù)據(jù)解壓,直接在開頭調(diào)用 CCVertInput 工具函數(shù)。
模型資源在場景中可能出現(xiàn)很多重復(fù)的,這樣就需要對模型進(jìn)行動態(tài)合批,對接引擎的動態(tài)合批流程,在包含 cc-local-batch 頭文件后,通過 CCGetWorldMatrix 函數(shù)獲取世界矩陣。
?
<p>vec4 position;</p><p>CCVertInput(position);</p><p></p><p>highp mat4 matWorld;</p><p>CCGetWorldMatrix(matWorld);</p><p></p> 復(fù)制代碼
在分析時提到,需要在觀察空間下對頂點進(jìn)行處理,所以需要將坐標(biāo)轉(zhuǎn)換到觀察空間下。曲面效果是和 Z 坐標(biāo)直接相關(guān)的,所以系數(shù)也是直接影響 Z 坐標(biāo)的。
dist 系數(shù)為影響變化的系數(shù),所以在和 vpos.z 的運算時,可以使用乘法也可以使用除法,但這個改變會直接影響 dist 的取值,所以在決定是使用除法還是乘法后,需要對值進(jìn)行對應(yīng)修改,且注意使用除法時 dist 的值不可為 0。
對于各軸分量的修改,需要 allOffset 參與運算然后造成影響,此處的 zOff 的平方運算即為分析中的二次函數(shù)符合變化規(guī)律的實現(xiàn)。
在處理完成之后,按照正常的變換邏輯繼續(xù)將觀察空間通過投影矩陣變換為裁剪空間下的坐標(biāo)之后繼續(xù)傳遞給片段著色器即可。
?
<p>highp vec4 vpos = cc_matView * matWorld * position;</p><p>highp float zOff = vpos.z / dist;</p><p>vpos += allOffset * zOff * zOff;</p><p>highp vec4 pos = cc_matProj * vpos;</p><p></p><p>v_uv = a_texCoord;</p><p>return pos;</p> 復(fù)制代碼
6、對于片段著色器,我們并未做特殊的操作,所以直接使用默認(rèn)提供的就可以。
7、最終的 effect 如下:
?
<p>CCEffect %{</p><p><span style="white-space:pre">? ? ? ? </span>techniques:</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>- name: opaque</p><p><span style="white-space:pre">? ? ? ? </span>passes:</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>- vert: unlit-vs:vert</p><p><span style="white-space:pre">? ? ? ? </span>frag: unlit-fs:frag</p><p><span style="white-space:pre">? ? ? ? </span>properties: &props</p><p><span style="white-space:pre">? ? ? ? </span>mainTexture: { value: grey }</p><p><span style="white-space:pre">? ? ? ? </span>allOffset: { value: [0, 0, 0, 0] }</p><p><span style="white-space:pre">? ? ? ? </span>dist: { value: 1 }</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>- name: transparent</p><p><span style="white-space:pre">? ? ? ? </span>passes:</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>- vert: unlit-vs:vert</p><p><span style="white-space:pre">? ? ? ? </span>frag: unlit-fs:frag</p><p><span style="white-space:pre">? ? ? ? </span>depthStencilState:</p><p><span style="white-space:pre">? ? ? ? </span>depthTest: true</p><p><span style="white-space:pre">? ? ? ? </span>depthWrite: false</p><p><span style="white-space:pre">? ? ? ? </span>blendState:</p><p><span style="white-space:pre">? ? ? ? </span>targets:</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>- blend: true</p><p><span style="white-space:pre">? ? ? ? </span>blendSrc: src_alpha</p><p><span style="white-space:pre">? ? ? ? </span>blendDst: one_minus_src_alpha</p><p><span style="white-space:pre">? ? ? ? </span>blendDstAlpha: one_minus_src_alpha</p><p><span style="white-space:pre">? ? ? ? </span>properties: *props</p><p>}%</p><p></p><p>CCProgram unlit-vs %{</p><p><span style="white-space:pre">? ? ? ? </span>precision highp float;</p><p></p><p><span style="white-space:pre">? ? ? ? </span>#include <cc-global></p><p><span style="white-space:pre">? ? ? ? </span>#include <cc-local-batch></p><p><span style="white-space:pre">? ? ? ? </span>#include <input></p><p></p><p><span style="white-space:pre">? ? ? ? </span>in vec2 a_texCoord;</p><p><span style="white-space:pre">? ? ? ? </span>out vec2 v_uv;</p><p></p><p><span style="white-space:pre">? ? ? ? </span>uniform Constants {</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>vec4 allOffset;</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>float dist;</p><p><span style="white-space:pre">? ? ? ? </span>};</p><p></p><p><span style="white-space:pre">? ? ? ? </span>highp vec4 vert () {</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>vec4 position;</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>CCVertInput(position);</p><p></p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>highp mat4 matWorld;</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>CCGetWorldMatrix(matWorld);</p><p></p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>highp vec4 vpos = cc_matView * matWorld * position;</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>highp float zOff = vpos.z / dist;</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>vpos += allOffset * zOff * zOff;</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>highp vec4 pos = cc_matProj * vpos;</p><p></p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>v_uv = a_texCoord;</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>#if FLIP_UV</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>v_uv.y = 1.0 - v_uv.y;</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>#endif</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>return pos;</p><p><span style="white-space:pre">? ? ? ? </span>}</p><p>}%</p><p></p><p>CCProgram unlit-fs %{</p><p><span style="white-space:pre">? ? ? ? </span>precision highp float;</p><p><span style="white-space:pre">? ? ? ? </span></p><p><span style="white-space:pre">? ? ? ? </span>#include <output></p><p></p><p><span style="white-space:pre">? ? ? ? </span>in vec2 v_uv;</p><p><span style="white-space:pre">? ? ? ? </span>uniform sampler2D mainTexture;</p><p></p><p><span style="white-space:pre">? ? ? ? </span>vec4 frag () {</p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>vec4 o = vec4(1, 1, 1, 1);</p><p></p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>o *= texture(mainTexture, v_uv);</p><p></p><p><span style="white-space:pre">? ? ? ? ? ? ? ? </span>return CCFragOutput(o);</p><p><span style="white-space:pre">? ? ? ? </span>}</p><p>}%</p> 復(fù)制代碼
8、在完成了 effect 之后,我們可以在編輯器中新建一個材質(zhì),在材質(zhì)的 Effect 中選擇剛剛完成的 curved 之后傳入想要的貼圖,填入 dist 和 AllOffset 參數(shù),保存之后將這個材質(zhì)賦予剛剛提到的 plane 對象,調(diào)整參數(shù)可以看到我們的片面就能出現(xiàn)偏置效果了,移動攝像機可看到偏置效果是與頂點距離攝像機的距離相關(guān)的:
?
9、請注意,Shader 的參數(shù)與模型的尺寸是相關(guān)的,上圖所示效果是在 dist 為 100,AllOffset 的 Y 值為 10 時的效果,各位開發(fā)者可嘗試不同的組合來達(dá)到想要的效果。
10、似乎上圖的效果還不是太直觀,所以我用一些建筑模型和一些路面模型簡單搭建了一段賽道來模擬游戲可能會出現(xiàn)的場景。建議最好是能夠顯示出縱深效果的連續(xù)模型段,更能顯示出效果,當(dāng)然這個效果并不只限于 Y 軸方向,還可以同時滿足 X 軸方向的偏置需求,下圖所示即為 Dist 為 100,X 為 -20,Y 為 -10 時的效果圖:
?
6、總結(jié)
相信各位對 [ShaderToy] 并不陌生,之后我們將提供從 ShaderToy 中遷移 Shader 的方法,海量 Shader 等你來學(xué)習(xí)!目前在我們的 [官方案例倉庫] 的 demo02 中,已經(jīng)實現(xiàn)了一個遷移好的場景案例 (ShaderToy),感興趣的小伙伴們趕快去試試吧!
有很多小伙伴在社區(qū)分享了炫酷的材質(zhì)效果,在這里感謝 @yans 做出的分享,歡迎有想法的開發(fā)者多做分享,共同促進(jìn)社區(qū)成長。
希望通過這個簡單的案例為各位提供一個了解材質(zhì)系統(tǒng)的入口。材質(zhì)系統(tǒng)功能十分豐富,能夠?qū)崿F(xiàn)的效果也是多種多樣的,各位快快打開腦洞,用 Cocos Creator 3D 來實現(xiàn)各種炫酷的效果吧!
歡迎小伙伴們繼續(xù)通過論壇、GitHub、Cocos 企業(yè)服務(wù)等渠道向我們提交 Cocos Creator 3D 使用反饋!
以上,謝謝!
與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖
總結(jié)
以上是生活随笔為你收集整理的Cocos Creator 3D 材质系统:曲面效果如何实现?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。