<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          這樣學(xué)Shader可以少走幾年彎路,看完我信了……

          共 15054字,需瀏覽 31分鐘

           ·

          2022-01-20 09:59

          推薦理由:

          感覺到,看過(guò)很多的Shader教程,還是無(wú)法上手。


          看到一個(gè)特效,連思路都沒有,只能拿別人做好的Shader干點(diǎn)調(diào)參數(shù)的活。


          我看了麒麟子的這篇教程才知道,原來(lái)連很多概念都沒弄清楚,只是在語(yǔ)法層面打轉(zhuǎn)轉(zhuǎn)!


          要想學(xué)好CocosShader麒麟子的教程絕對(duì)是最佳路徑,而且還有免費(fèi)的配套源碼,真的是大愛了!


          我堅(jiān)信!只要跟隨大神們的腳步,一路同行,定能少走許多的彎路,達(dá)到夢(mèng)想的巔峰!


          本文1.46W字,先奉上源碼免費(fèi)下載:
          https://store.cocos.com/app/detail/3521

          一、花花世界,從本文開始

          本文真正的標(biāo)題為:

          《Cocos Shader入門基礎(chǔ)五:是紋理給了你這個(gè)花花世界》

          不瞞大家說(shuō),終于要續(xù)寫這個(gè)系列了,麒麟子想想都激動(dòng)。

          時(shí)光飛逝,離上一篇文章?《Cocos Shader入門基礎(chǔ)四:Uniform與材質(zhì)參數(shù)控制》?的發(fā)布,仿佛就在15個(gè)月前。

          本以為,大家學(xué)完前面四篇基礎(chǔ),就可以上手Cocos Shader,后面的內(nèi)容就算鴿掉也無(wú)所謂。

          然而就在這周,麒麟子接到了一個(gè)小姑娘的咨詢:她需要使用Cubemap、多層紋理混合、UV流動(dòng)以及邊緣光等技術(shù)組合出一個(gè)渲染效果。

          在交流的過(guò)程中,感覺她對(duì)于一些概念沒有完全理清,對(duì)Cocos Shader著色器(Effect)文件語(yǔ)法以及Cocos Creator材質(zhì)系統(tǒng)不熟悉,導(dǎo)致在編寫Shader的時(shí)候,無(wú)法干凈利落的排查故障和實(shí)現(xiàn)效果。

          補(bǔ)充說(shuō)明:?
          她在網(wǎng)上也看了不少相關(guān)的文章和教程,但由于不是使用Cocos Creator進(jìn)行教學(xué),在學(xué)習(xí)完之后,使用過(guò)程中依然需要大量的時(shí)間進(jìn)行摸索和驗(yàn)證。

          這更印證了麒麟子之前的想法:使用Cocos Creator來(lái)教Shader編寫,是十分有必要的。

          教一個(gè)人也是教,教一群人也是教。

          索性,補(bǔ)上這個(gè)基礎(chǔ)篇的最后一篇。

          基礎(chǔ)篇結(jié)束之后,會(huì)按效果進(jìn)行文章組織,由一篇或者多篇文章循序漸進(jìn)完成一個(gè)效果的實(shí)現(xiàn)。

          不是麒麟子吹,在掌握本文的基礎(chǔ)知識(shí)后,只需幾行Cocos Shader代碼,就可以寫出上面這些效果。

          話不多說(shuō),我們進(jìn)入正題。

          二、主要內(nèi)容

          15個(gè)月前發(fā)布的上一篇Cocos Shader文章中,麒麟子就預(yù)告了本文內(nèi)容。

          但是15個(gè)月前定的內(nèi)容,顯然是不適合這個(gè)高速發(fā)展的世界,況且Cocos Creator3.4版本了,且即將發(fā)布3.4.1。

          從本文開始,所有文章內(nèi)容編寫會(huì)切換到Cocos Creator 3.4+版本。

          最終內(nèi)容規(guī)劃如下:

          • Cocos Shader中的紋理
            • 紋理相關(guān)基礎(chǔ)知識(shí)
            • Cocos Shader中使用smapler2D
            • Cocos Shader中使用smaplerCube
          • 紋理基本屬性
            • 采樣方式
            • 過(guò)濾方式
            • 尋址方式
          • 常見效果實(shí)現(xiàn)
            • 紋理UV流動(dòng)動(dòng)畫
            • 紋理UV切換動(dòng)畫
            • 多重紋理混合
            • 紋理擾動(dòng)效果
          • 一個(gè)能量環(huán)繞的3D角色效果

          三、Cocos Shader中的紋理

          3.1 紋理相關(guān)基礎(chǔ)知識(shí)

          什么是紋理?

          GPU渲染流程中使用的圖片,我們稱之為紋理,美術(shù)領(lǐng)域稱其為貼圖。

          紋理的英文為texture貼圖的英文為map。

          所以,當(dāng)你在Shader中看到參數(shù)為mainTexture或者albedoMap的時(shí)候,請(qǐng)記得它們就是紋理。

          什么是紋理坐標(biāo)?

          試想一下,當(dāng)我們要渲染一個(gè)三角面的時(shí)候,三角面中某個(gè)位置與紋理的顏色如何對(duì)應(yīng)呢?

          圖形學(xué)前輩們發(fā)明了紋理坐標(biāo)這個(gè)概念,它用于指定每個(gè)頂點(diǎn)與紋理的對(duì)應(yīng)關(guān)系,而頂點(diǎn)之間,則使用插值進(jìn)行處理。

          紋理坐標(biāo)是一個(gè)頂點(diǎn)上屬性,它和頂點(diǎn)位置、頂點(diǎn)法線、頂點(diǎn)顏色一樣,屬于常見的頂點(diǎn)信息。

          有了紋理紋理坐標(biāo),就可以對(duì)紋理進(jìn)行采樣,獲得紋理上對(duì)應(yīng)貼圖位置的信息。

          Shader中,通常使用uv來(lái)表示紋理坐標(biāo),u表示水平方向,v表示豎直方向。

          紋理坐標(biāo)紋理的關(guān)系是什么呢?請(qǐng)看下圖:

          如上圖所示,我們使用[0.0,0.0]來(lái)表示一張紋理的左上角,使用[1.0,1.0]來(lái)表示一張紋理的右下角。

          紋理坐標(biāo)紋理的實(shí)際尺寸無(wú)關(guān),這樣可使相關(guān)運(yùn)算與紋理分辨率脫離關(guān)系。

          不難發(fā)現(xiàn):

          • 當(dāng)uv為(0.5,0.5)的時(shí)候,剛好獲取到中間的像素。
          • 當(dāng)uv為(0.0,0.0)的時(shí)候,剛好取到左上角的像素。
          • 當(dāng)uv為(1.0,1.0)的時(shí)候,剛好取到右下角的像素。

          什么是紋理采樣?

          紋理采樣是指獲取給定紋理坐標(biāo)處的紋理顏色的過(guò)程。

          紋理采樣是一個(gè)非常復(fù)雜且耗時(shí)的操作 ,會(huì)涉及到放大采樣,縮小采樣Mipmap采樣等內(nèi)容,本文后面會(huì)講相關(guān)知識(shí)。

          雖然過(guò)程很復(fù)雜,但這些事情都是GPU硬件做的,完全不需要操心。

          Shader中,我們能夠感知到的紋理采樣只有下面這個(gè)函數(shù):

          vec4 texture(sampler,uv)

          若想要在Shader對(duì)一個(gè)紋理進(jìn)行采樣,只需要像下面這樣的代碼即可:

          vec4 color = texture(textur,euv);

          Cocos Shader支持哪些紋理類型?

          打開Cocos Creator官方文檔https://docs.cocos.com/creator/manual/zh/。

          進(jìn)入圖形渲染->材質(zhì)系統(tǒng)->Pass Params頁(yè)面,并滾動(dòng)到底部。

          可以找到Cocos Shader編寫時(shí)所支持的Uniform類型,如下圖所示:

          從文檔中可以看出,Cocos Shader支持sampler2DsamplerCube。

          3.2 在Cocos Shader中使用smapler2D

          smapler2D顧名思義,它可以用來(lái)聲明一個(gè)2D紋理采樣器。

          Cocos Shader中,如果聲明了這個(gè)類型的uniform,就可以將一個(gè)2D紋理資源傳遞給它。

          2D紋理資源理解起來(lái)非常簡(jiǎn)單,常見的JPG,PNG圖片,都是2D紋理資源。

          也可以簡(jiǎn)單的理解為普通紋理

          下面來(lái)看看,如何在Cocos Shader中,使用2D紋理。

          步驟一、創(chuàng)建一個(gè)默認(rèn)的著色器(Effect)

          新建一個(gè)Cocos Creator 3.4的項(xiàng)目,并在assets目錄下,點(diǎn)擊鼠標(biāo)右鍵,在彈出的菜單中選擇創(chuàng)建著色器(Effect)。如下圖所示:

          麒麟小貼士
          新建項(xiàng)目的時(shí)候,建議選擇HelloWorld模板。
          此模板提供了靜態(tài)模型帶動(dòng)畫的模型、Cubemap等素材,方便大家學(xué)習(xí)使用。如下圖所示:

          步驟二、創(chuàng)建一個(gè)材質(zhì),并使用剛創(chuàng)建的著色器(Effect)

          如下圖所示,使用右鍵菜單創(chuàng)建一個(gè)材質(zhì)

          有朋友問:?物理材質(zhì)是不是基于物理渲染的材質(zhì)?
          答:?不是的,物理材質(zhì)物理系統(tǒng)使用的材質(zhì),用于描述物體的物理屬性。

          選中新建的材質(zhì),在右邊的屬性面板(Inspector)中,將Effect切換為剛剛新建的。如下圖所示:

          麒麟小貼士:?
          切換后,右邊的材質(zhì)面板會(huì)多出一個(gè)綠色的勾,點(diǎn)擊將保存更改。

          步驟三、使用2D紋理

          將一個(gè)2D紋理拖到MainTexture參數(shù),會(huì)發(fā)現(xiàn)材質(zhì)預(yù)覽中的模型也會(huì)跟著改變外觀。

          本文DEMO使用了HelloWorld模板項(xiàng)目中自帶的盾牌貼圖,如下圖所示:

          Shader解析

          仔細(xì)閱讀剛剛創(chuàng)建的著色器(Effect),可以發(fā)現(xiàn),在其properties區(qū)域定義了一個(gè)mainTexture屬性,此屬性會(huì)出現(xiàn)在屬性面板(Inspector)上。如下圖所示:

          mainTexture的默認(rèn)值為white,表示在不被賦值的時(shí)候,它將是白色。

          unlit-fs中,可以找到如下語(yǔ)句:

          uniform sampler2D mainTexture

          它定義了一個(gè)類型為sampler2Duniform。

          這個(gè)就是標(biāo)準(zhǔn)2D紋理屬性定義,這個(gè)mainTexture就是一個(gè)2D紋理。

          frag函數(shù)中,可以找到如下語(yǔ)句:

          vec4 col = mainColor * texture(mainTexture,v_uv)。

          這句話中的texture函數(shù),用于獲取紋理在指定uv坐標(biāo)處的像素顏色值(RGBA),也就是我們所說(shuō)的紋理采樣。如下圖所示:

          可能有人會(huì)疑惑,v_uv是什么?它從哪里來(lái)的?

          這個(gè)v_uv就是紋理坐標(biāo),它是vs傳遞給fs的。

          可以查看本文下一節(jié)中的general-vs.chunk內(nèi)容看看v_uv的來(lái)源。

          可能還有朋友會(huì)疑惑,為什么新建的默認(rèn)Effect沒有unlit-vs呢?

          這是因?yàn)?,大部分情況下,不需要改動(dòng)vs,所以Cocos Creator引擎提供了內(nèi)置的general-vs供大家使用。

          general-vs的路徑為?internal/chunks/general-vs.chunk。

          新建的著色器(Effect)general-vs:vert #builtin header就是對(duì)它的引用。如下圖所示:

          如果想要自定義vs,只需將其復(fù)制過(guò)來(lái),再按需修改即可。

          為了照顧?“沒有時(shí)間”?的朋友們,麒麟子還是展示一下general-vs.chunk的內(nèi)容。


          precision?highp?float;
          #include?
          #include?
          #include?
          #include?
          #include?
          #include?

          in?vec4?a_color;
          #if?HAS_SECOND_UV
          ??in?vec2?a_texCoord1;
          #endif

          out?vec3?v_position;
          out?vec3?v_normal;
          out?vec3?v_tangent;
          out?vec3?v_bitangent;
          out?vec2?v_uv;
          out?vec2?v_uv1;
          out?vec4?v_color;

          vec4?vert?()?{
          ??StandardVertInput?In;
          ??CCVertInput(In);

          ??mat4?matWorld,?matWorldIT;
          ??CCGetWorldMatrixFull(matWorld,?matWorldIT);

          ??vec4?pos?=?matWorld?*?In.position;

          ??v_position?=?pos.xyz;
          ??v_normal?=?normalize((matWorldIT?*?vec4(In.normal,?0.0)).xyz);
          ??v_tangent?=?normalize((matWorld?*?vec4(In.tangent.xyz,?0.0)).xyz);
          ??v_bitangent?=?cross(v_normal,?v_tangent)?*?In.tangent.w;?//?note?the?cross?order

          ??v_uv?=?a_texCoord;
          ??#if?HAS_SECOND_UV
          ????v_uv1?=?a_texCoord1;
          ??#endif
          ??v_color?=?a_color;

          ??CC_TRANSFER_FOG(pos);
          ??CC_TRANSFER_SHADOW(pos);

          ??return?cc_matProj?*?(cc_matView?*?matWorld)?*?In.position;
          }

          general-vs.chunk的代碼中可以看出,引擎內(nèi)置的這個(gè)vs默認(rèn)就對(duì)tangentbitangent進(jìn)行了計(jì)算。

          基于對(duì)性能的講究,如果有項(xiàng)目確實(shí)用不上的,請(qǐng)記得移除。

          同時(shí),還可以發(fā)現(xiàn),默認(rèn)的vs對(duì)于常見的position、normal、uv、uv1、color頂點(diǎn)信息都進(jìn)行了輸出,在fs中可直接使用。如下圖所示:

          3.3、在Cocos Shader中使用smaplerCube

          smaplerCube對(duì)應(yīng)的是Cubemap(立方體貼圖),用一個(gè)更貼切的描述叫:立方體盒子貼圖。

          可以想象一下,使用6張正方形的2D紋理,拼接成一個(gè)中空的立方體盒子,就得到了一個(gè)Cubemap。

          為了便于查看,Cubemap的顯示方式,通常是以六面展開方式出現(xiàn)的。如下圖所示:

          這個(gè)特征非常好記,下次再看到這樣的圖的時(shí)候,大家肯定都能識(shí)別啦。

          接下來(lái),我們看看在Cocos Shader中,如何使用Cubemap。

          步驟一、在properties中新增一個(gè)cubeTexture屬性

          使用上一節(jié)講到的方法,創(chuàng)建一個(gè)著色器(Effect)

          mainTexture下方添加一個(gè)cubeTexture屬性,如下圖所示:

          步驟二、在unlit-fs中新增一個(gè)samplerCube類型的uniform

          uniform sampler2D mainTexture的下方,增加uniform samplerCube cubeTexture,見下圖中紅色方框標(biāo)記:

          步驟三、引入法線數(shù)據(jù)

          在前面的知識(shí)介紹中,我們講到了,Cubemap其實(shí)就是一個(gè)空心的立方體盒子。那如何獲取它的信息呢?

          假如有一個(gè)很大的立方體盒子,在盒子中的某個(gè)點(diǎn),朝某個(gè)方向發(fā)射出一條射線。

          射線最終會(huì)與立方體盒子形成交點(diǎn),而交點(diǎn)處的顏色值,就是我們想要的顏色值。

          不難發(fā)現(xiàn),如果將一個(gè)物體放入環(huán)境中,如果想讓這個(gè)物體的表面,映射出Cubemap的內(nèi)容,所使用的射線肯定和物體的面朝向是分不開的。

          在3D渲染流程中,決定物體面朝向的信息,叫法線(Normal)。因此,需要先引入法線信息

          由于general-vs已經(jīng)輸出了v_normal,所以在unlit-fs中只需要引入就可使用。

          in vec3 v_position;下方添加一行in vec3 v_normal;即可引入并使用v_normal。請(qǐng)見下圖綠色方框標(biāo)記。

          步驟四、采樣Cubemap

          Cubemap采樣函數(shù)的原型為vec4 texture(samperCube cubemap,vec3 coord)。

          麒麟小貼士:?
          Cocos Shader中的texture函數(shù)有許多個(gè)重載,它會(huì)根據(jù)傳入的數(shù)據(jù)類型挑選適合的函數(shù)原型。
          所以采樣2D紋理Cube紋理均使用texture函數(shù)就行了,只是傳入的參數(shù)不同。

          最終采樣函數(shù)見下圖黃色標(biāo)記。

          為了測(cè)試效果,我們先新建一個(gè)材質(zhì)。

          選擇剛寫好的著色器(Effect)作為Effect。

          assets/skybox/sunnySkyBox拖到材質(zhì)的CubeTexture參數(shù)上。如下圖所示:

          在場(chǎng)景中新建一個(gè)球,并使用此材質(zhì),最終得到的效果如下:

          步驟五、正確的反射計(jì)算

          當(dāng)你在編輯器場(chǎng)景中轉(zhuǎn)動(dòng)攝像機(jī)的時(shí)候會(huì)發(fā)現(xiàn),天空盒與球面的關(guān)系是固定的。

          而現(xiàn)實(shí)世界中,物體反射的內(nèi)容在環(huán)境不變的情況下,與物體轉(zhuǎn)動(dòng)是無(wú)關(guān)的。

          常見的Cubemap采樣,一般是采用視線基于法平面的反射方向去采樣。

          因此,需要根據(jù)normal計(jì)算出視線的反射方向,并利用反射方向進(jìn)行采樣。

          設(shè)視線方向?yàn)?code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">V,法線方向?yàn)?code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">N,反射方向?yàn)?code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">R(V,N,R均為單位向量)。

          R推導(dǎo)過(guò)程如下圖所示:

          最終可得?R = V - 2*dot(V,N)*N

          根據(jù)上述公式,我們修改一下Shader中對(duì)CubeTexture的采樣,如下圖所示:

          在未轉(zhuǎn)動(dòng)的情況下,很難感受到與直接使用v_normal進(jìn)行采樣的區(qū)別。

          但當(dāng)我們轉(zhuǎn)動(dòng)攝像機(jī)的時(shí)候,可以明顯感知到反射帶來(lái)的變化。

          如果把球體換成其他非規(guī)則模型這個(gè)感受會(huì)更明顯。

          步驟六、反射強(qiáng)度控制

          完成步驟五后,我們可以得到一個(gè)全反射的Shader,但這個(gè)Shader的適用性不高。

          在現(xiàn)實(shí)世界中,全反射的情況非常少見,因此需要添加一些控制反射強(qiáng)弱的因子,使物體表面的反射更加真實(shí)。

          首先,在properties中,添加兩個(gè)屬性:

          • maskTexture用于控制物體表面某個(gè)部位的反射強(qiáng)弱
          • reflectionStrengthen?用于控制整體強(qiáng)弱

          然后添加相應(yīng)的uniform,如下圖所示:

          最后,在frag中使用剛剛添加的屬性,如下圖所示:

          紅框中的代碼,用于采樣maskTexture

          藍(lán)框中的代碼,我們將maskTexturer通道作為反射強(qiáng)度因子,并與全局強(qiáng)度因子相乘,得到最終的反射因子。

          麒麟小貼士:?
          這里簡(jiǎn)單的使用了r通道,是非常常見的mask紋理做法。r通道即便是在單通道紋理或者灰度紋理的情況下也是生效的。當(dāng)然,也可以根據(jù)實(shí)際情況,使用g、b、a通道或者通過(guò)各類公式計(jì)算得出反射強(qiáng)度。

          綠框中的代碼,是將底色與反射做一個(gè)線性插值。當(dāng)反射越強(qiáng)的時(shí)候,底 色越弱,當(dāng)反射越弱的時(shí)候,底色越強(qiáng)。

          黃框中的代碼,會(huì)在材質(zhì)面板上會(huì)出現(xiàn)一個(gè)SHOW_REFECTION_STRENGTHEN的開關(guān),開啟這個(gè)開關(guān)就可以查看反射強(qiáng)度。

          DEMO中渲染了4個(gè)球用于展示效果,如下圖所示:

          • 第1個(gè)球使用了盾牌圖作為maskTexture??梢钥吹皆搅恋牡胤剑瓷鋸?qiáng)度越大(比如椰子頭部),越暗的地方反射強(qiáng)度越?。ū热缢{(lán)色部分)。
          • 第2個(gè)球是顯示的第1個(gè)球的材質(zhì)中最終的各部分的反射強(qiáng)度。整體偏 暗是因?yàn)槿謴?qiáng)度reflectionStrengthen設(shè)置為了0.5
          • 第3個(gè)球是maskTexture為空的情況。由于maskTexture默認(rèn)值為white,所以maskColor.r總是為1.0,即全反射。而reflectionStrengthen為0.5,所以原色與反射各占一半,且各部分反射都一樣。
          • 第4個(gè)球是顯示的第3個(gè)球的材質(zhì)中最終的各部分的反射強(qiáng)度。整體為灰色是因?yàn)槿謴?qiáng)度reflectionStrengthen設(shè)置為了0.5。

          DEMO中本小節(jié)相關(guān)資源

          Effect:assets/tutorial/effect-cubemap.effect

          Scene:assets/tutorial/tutorial-cubemap.scene

          四、紋理屬性

          assets窗口中,選擇任意紋理,可在右邊屬性窗口(Inspector)中看到紋理相關(guān)的屬性。如下圖所示:

          4.1、紋理采樣方式

          下圖中MinFilterMagFilterMipFilter就是需要關(guān)注的采樣方式,它們能夠有效的提升渲染效果。

          它們的含義如下:

          MinFilter

          • 縮小采樣,用于紋理分辨率大于實(shí)際所需時(shí)

          MagFilter

          • 放大時(shí)采樣,用于紋理分辨經(jīng)小于實(shí)際所需時(shí)

          MipFilter

          • Mipmap采樣,用于在Mipmap開啟時(shí),從各級(jí)Mipmap中獲取像素信息時(shí)的使用。

          然而,光有概念依然是較難理解的,接下來(lái)用一個(gè)示例讓大家細(xì)細(xì)體會(huì)一下它們的作用。

          下圖中

          • 圖1?- 正常視角
          • 圖2?- 攝像機(jī)拉遠(yuǎn)的視角
          • 圖3?- 攝像機(jī)拉近的視角。

          現(xiàn)在大家把目光集中到角色上,可以很明顯的發(fā)現(xiàn),圖2中,角色變小了,圖3中角色變大了。

          但是,圖2圖3的角色,使用的是同一張貼圖。

          也就是說(shuō),同樣的紋理區(qū)域,在屏幕上顯示大小是不同的。

          假如角色紋理尺寸為512x512圖1是它的最佳視角。

          那么在圖2中,由于紋理所占屏幕比例縮小了,為了保證較好的效果,紋理采樣的時(shí)候就需要做縮小采樣處理。

          圖3中,紋理所占屏幕比例變大了,紋理采樣的時(shí)候就需要放大采樣處理。

          4.2、紋理過(guò)濾方式

          紋理過(guò)濾方式有兩種

          • 最近點(diǎn)過(guò)濾(nearest)
          • 線性過(guò)濾(linear)。

          最近點(diǎn)過(guò)濾就是選擇最近的像素使用。如下圖所示:

          最近點(diǎn)過(guò)濾(圖片來(lái)源于網(wǎng)絡(luò))

          可以把紋理想象成一個(gè)由像素組成的格子,采樣的時(shí)候,uv坐標(biāo)落到哪個(gè)格子,就取哪個(gè)格子。

          最近點(diǎn)過(guò)濾采樣的優(yōu)點(diǎn)是:性能好,沒有額外運(yùn)算。

          有得必有失,最近點(diǎn)過(guò)濾采樣的缺點(diǎn)是:不管是放大采還是縮小采,效果都不好。

          線性過(guò)濾就是選擇最近像素的2x2像素區(qū)域,進(jìn)行加權(quán)混合。

          線性過(guò)濾(圖片來(lái)源于網(wǎng)絡(luò))

          線性過(guò)濾(Linear)相較于最近點(diǎn)過(guò)濾(Nearest)的優(yōu)缺點(diǎn),剛好相反:線性過(guò)濾效果較好,但性能較低。

          麒麟小貼士:?
          雖然線性過(guò)濾比最近點(diǎn)過(guò)濾性能低一點(diǎn),但由于最近點(diǎn)過(guò)濾在很多時(shí)候是滿足不了視覺要求,所以大部分情況下都是采用線性過(guò)濾方式。

          兩種過(guò)濾方式比較(圖片來(lái)源于網(wǎng)絡(luò))

          麒麟小貼士:?
          以上內(nèi)容僅針對(duì)紋理放大和縮小采樣,即MinFilterMagFilter。
          Mipmap采樣過(guò)濾請(qǐng)看下面內(nèi)容。

          既然有了MinFilterMagFilter存在,那Mipmap的意義何在呢?

          說(shuō)來(lái)也是尷尬,Mipmap出現(xiàn)的主要原因,是因?yàn)?code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">縮小過(guò)濾采樣不能達(dá)到很好的效果。

          當(dāng)縮小不多的時(shí)候,MinFilter可以工作得很好。

          但當(dāng)縮小達(dá)到一定比例的時(shí)候,MinFilter就無(wú)能為力了。

          縮小過(guò)多的像素會(huì)因?yàn)椤當(dāng)D壓’而產(chǎn)生摩爾紋。

          Mipmap則是通過(guò)預(yù)先的較好的過(guò)濾采樣算法,逐級(jí)生成小分辨率的紋理,從而避免像素‘?dāng)D壓’問題。

          下圖展示了Mipmap過(guò)濾方式分別為None、Nearest、Linear三種情況下的效果對(duì)比:

          從上圖中可以看出,不管Mipmap是否開啟,近處的像素都是可以接受的。這是因?yàn)榻幍南袼夭粫?huì)被擠壓。

          • 當(dāng)Mipmap FilterNone時(shí),不開啟Mipmap。
          • 當(dāng)Mipmap FilterNearest時(shí),會(huì)先選擇最接近的Mipmap層級(jí)。
          • 當(dāng)Mipmap FilterLinear時(shí),會(huì)選擇接近的兩個(gè)Mipmap層級(jí),并做線性插值。

          麒麟小貼士:?
          一般用于2D?Sprite的紋理不開啟Mipmap,用于3D模型渲染的紋理需要開啟Mipmap。

          4.3、紋理尋址方式

          紋理尋址方式,主要是為uv坐標(biāo)大于1.0的時(shí)候,提供相應(yīng)的尋址策略。

          Cocos Creator引擎提供了以下三種紋理尋址方式:

          • 重復(fù)(repeat
            • 大于1.0的部分會(huì)被模除,僅保留小數(shù)部分。
          • 邊緣約束(clamp-to-edge
            • 小于0.0時(shí)取0.0。
            • 大于1.0時(shí)取1.0。
          • 鏡像重復(fù)(mirrored-repeat)
            • 整數(shù)部分為偶數(shù)時(shí),取小數(shù)部分。
            • 整數(shù)部分為奇數(shù)時(shí),取 1.0減去小數(shù)部分。

          三種紋理尋址的效果如下圖所示:

          左:repeat,中:clamp-to-edge,右:mirrored-repeat

          麒麟小貼士:
          1、當(dāng)需要大面積的地磚、玻璃窗等重復(fù)效果時(shí)。使用repeat模式,并且調(diào)節(jié)uv?tiling,可以有效降低紋理分辨率,節(jié)省顯存。
          2、clamp-to-edge可確保邊緣干凈。
          3、mirrred-repeat是一個(gè)特殊的repeat,使用場(chǎng)合不多,可按需使用。
          4、3D紋理默認(rèn)為repeat
          5、repeat在一些低端API上(如WebGL?1.0)要求紋理尺寸為2冪。所以,3D模型渲染盡可能使用2冪紋理,以增強(qiáng)兼容性。

          這里補(bǔ)充說(shuō)明一個(gè)標(biāo)準(zhǔn)材質(zhì)上與尋址有關(guān)的屬性TilingOffset,如下圖所示:

          材質(zhì)面板上的TilingOffset

          TilingOffset顧名思義,它負(fù)責(zé)調(diào)節(jié)UVTiling(縮放)Offset(偏移)

          其中,xy分量用于調(diào)節(jié)Tilingxy的值會(huì)和uv相乘,使得uv值變大或者變小。

          如上圖所示,將它們?cè)O(shè)置為3,在repeat模式下,就會(huì)重復(fù)3次。

          zw分量用于調(diào)節(jié)Offset,zw的值會(huì)和uv相加,值得uv值產(chǎn)生偏移。

          internal/effects/builtin-standard中可看到具體的計(jì)算公式,如下圖所示:

          下圖是x=10,y=10,z=0.3,w=0.3的效果。

          可能一些認(rèn)真學(xué)習(xí)的小伙伴就要問了:那在Cocos Creator中,如何修改一個(gè)紋理的尋址方式呢?

          如果是用于3D模型的紋理,在assets中選中某張紋理后,右邊的屬性窗口(Inspector)就會(huì)顯示出紋理相關(guān)屬性。

          在屬性窗口中修改Wrap Mode SWrap Mode T即可。

          這個(gè)ST就是我們常說(shuō)的UV,如下圖所示:

          有朋友可能會(huì)問,UVST是什么關(guān)系呢?

          • UVW空間中,U表示水平方向,V表示豎直方向 ,W表示深度方向(其實(shí)除了2DCube紋理,還有3D紋理。只是使用得較少,本文沒有展開講。)
          • UVW是一種用于均勻紋理坐標(biāo),在3D建模領(lǐng)域中被廣泛應(yīng)用。
          • STQ空間中,S表示水平方向,T表示豎直方向 ,Q表示深度方向
          • STQ可以表示非均勻紋理坐標(biāo),STQ能夠用于需要處理紋理透視矯正的情況。因此,在紋理相關(guān)領(lǐng)域 ,STQ被廣泛應(yīng)用。
          • U=S/Q,?V=T/Q。因此,當(dāng)Q=1.0時(shí)(即不需要透視矯正),UV=ST。
          • UVW,STQXYZ的三個(gè)分量都分別表示正交坐標(biāo)系中的水平,豎直,深度。只是在不同的領(lǐng)域,為了更好的區(qū)分概念,而采用了不同的字母表示。

          麒麟小貼士:?
          如果想要修改sprite的紋理尋址方式,只需在assets窗口中展開sprite的資源內(nèi)容,并選中sprite內(nèi)容中的texture即可。

          五、常見效果實(shí)現(xiàn)

          5.1、紋理UV流動(dòng)動(dòng)畫

          終于,可以開始讓Shader從靜態(tài)走向動(dòng)態(tài)了。

          Shader上的一小步,效果上的一大步。

          麒麟小貼士:?
          要想實(shí)現(xiàn)紋理UV流動(dòng)動(dòng)畫,有一個(gè)前提是:紋理尋址方式為repeat或者mirroed-repeat。

          上面的動(dòng)畫中,左邊的是repeat,右邊的是mirrored-repeat,可以從Cocos Logo頭頂?shù)姆较蚩闯霾町悺?/p>

          紋理UV流動(dòng)的原理非常簡(jiǎn)單,只需要在紋理采樣的時(shí)候,給uv加一個(gè)與時(shí)間相關(guān)的偏移量即可。如下圖所示:

          其中?cc_time?是引擎提供的內(nèi)置Shader變量,x分量表示從項(xiàng)目啟動(dòng)開始到現(xiàn)在經(jīng)過(guò)的時(shí)間。?yzw分量暫時(shí)未使用。

          更多內(nèi)置變量可直接查看:引擎官方文檔->圖形渲染->材質(zhì)系統(tǒng)->Builtin Shader Uniforms。

          麒麟小貼士:?
          若想在Cocos Creator編輯器內(nèi)查看紋理流動(dòng)效果,只需要在編輯器窗口中同時(shí)按下鼠標(biāo)左鍵和右鍵即可。沒有鼠標(biāo)的筆記本,長(zhǎng)按觸摸板即可。

          5.2、紋理UV切換動(dòng)畫

          像上面這樣的圖片,大部分人應(yīng)該不陌生了,它是一個(gè)24列的紋理圖集。

          有兩種方式實(shí)現(xiàn)紋理UV切換動(dòng)畫:

          • 方式一、在TypeScript中計(jì)算出對(duì)應(yīng)的紋理坐標(biāo),然后修改材質(zhì)中TilingOffset屬性的xyzw參數(shù)。
          • 方式二、直接在Shader中計(jì)算對(duì)應(yīng)的紋理坐標(biāo)。

          對(duì)于24列的紋理圖集,可以很容易算出,每一個(gè)子圖占用的UV比例。水平方向每個(gè)子圖占0.25,豎直方向每個(gè)子圖占0.5。因此,將TilingOffsetx設(shè)置為0.25,y設(shè)置為0.5即可。

          對(duì)于此紋理圖集,也可以很容易的算出所有子圖的偏移參數(shù):

          第一行:[0.0,0.0],[0.25,0.0],[0.5,0.0],[0.75,0.0]
          第二行:[0.0,0.5],[0.25,0.5],[0.5,0.5],[0.75,0.5]

          設(shè)row=2,?col=4,可得:

          • 每一個(gè)子圖在水平方向的占比為1.0 / col?(即0.25)。
          • 每一個(gè)子圖在豎直方向的占比為1.0 / row?(即0.5)。

          計(jì)算公式為:

          • z = (index % col) * 1.0 / col
          • w = floor(index / col) * 1.0 / row

          大家可帶入驗(yàn)算一下。

          有了上面的公式,新建一個(gè)腳本并寫下對(duì)應(yīng)的代碼即可。這個(gè)使用本系列的上一篇教程內(nèi)容就可以搞定,在此不再敷述。

          下面我們就來(lái)一步步實(shí)現(xiàn)基于Shader的紋理UV切換效果。

          步驟一、新建一個(gè)著色器(Effect)

          參考本文3.1中的步驟二,新建一個(gè)著色器,命名為:effect-texture-anim.effect。

          步驟二、在properties中添加屬性

          properties區(qū)域,添加兩個(gè)屬性:

          • cells: 類型為vec2,用于標(biāo)記紋理圖集中水平和豎直方向的子圖數(shù)量。
          • fps: 類型為float,用于控制子圖切換速度。

          步驟三、添加Uniforms

          unlit-fs中,添加兩個(gè)uniform,如下圖所示:

          步驟四、根據(jù)上述公式,編寫Shader

          ??vec4?frag?()?{
          ????float?index?=?floor(cc_time.x?*?fps);

          ????float?row?=?cells.x;
          ????float?col?=?cells.y;

          ????vec2?offset?=?vec2(mod(index,col)/col,floor(index/col)/row);

          ????vec4?color?=?mainColor?*?texture(mainTexture,?v_uv?/?cells.yx?+?offset);

          ????float?gray?=?color.r?*?0.299?+?color.g?*?0.587?+?color.b?*?0.114;
          ????color.a?=?gray;?

          ????CC_APPLY_FOG(color,?v_position);
          ????return?CCFragOutput(color);
          ??}

          步驟五、新建一個(gè)材質(zhì),使用此Shader

          在場(chǎng)景中創(chuàng)建一個(gè)Plane或者Quad。

          新建一個(gè)材質(zhì),使用此Effect,并設(shè)置好對(duì)應(yīng)的紋理參數(shù),最終可看到如下效果:

          DEMO中本小節(jié)相關(guān)資源:

          Effect:assets/tutorial/effect-texture-anim.effect

          Scene:assets/tutorial/tutorial-anim.scene

          5.3、多重紋理混合

          多重紋理混合在Shader編寫中,是一個(gè)使用頻率非常高的點(diǎn)。

          常見的應(yīng)用場(chǎng)景有:多層地表混合物體表面細(xì)節(jié),流光特效消散特效,鏤空特效石化效果等等。

          多重紋理的混合的實(shí)現(xiàn),在Shader中只需要三步:

          步驟一、添加必要的紋理參數(shù)

          可以基于之前的著色器(Effect)來(lái)改,也可以新建一個(gè)。

          首先在properties里面添加一個(gè)紋理屬性

          然后在unlit-fs中添加相應(yīng)的sampler2D

          步驟二、取出所有需要參與的紋理像素值

          這個(gè)太簡(jiǎn)單了,簡(jiǎn)單得就像普通紋理采樣。如下圖所示:

          步驟三、根據(jù)想要的混合方式混合

          公式是可以隨便造的,但有一些用于實(shí)現(xiàn)常見效果的公式可以快速使用。如下圖所示:

          對(duì)應(yīng)渲染結(jié)果如下圖所示:

          如果結(jié)合UV切換動(dòng)畫,就可以呈現(xiàn)文章開頭的效果:

          DEMO中本小節(jié)相關(guān)資源 :

          Effect:assets/tutorial/effect-texture-anim-blending.effect

          Scene:assets/tutorial/tutorial-anim-blending.scene

          5.4、紋理擾動(dòng)效果

          紋理擾動(dòng)效果的核心思想,就是在紋理采樣前對(duì)uv進(jìn)行干擾。

          干擾的方式一般是使用另一張紋理作為干擾紋理。

          面為了更直觀的看出擾動(dòng)效果,肯定是要和紋理動(dòng)畫結(jié)合的。

          接下來(lái)我們分別實(shí)現(xiàn)基于UV流動(dòng)動(dòng)畫UV切換動(dòng)畫的兩種紋理擾動(dòng)效果。

          5.4.1 基于紋理UV流動(dòng)的擾動(dòng)效果

          基于紋理流動(dòng)的擾動(dòng)效果,效果如下所示:

          基于紋理流動(dòng)的擾動(dòng)效果.GIF

          要想達(dá)到上面的效果,需要實(shí)現(xiàn)以下幾個(gè)功能:

          • 使用一張干擾圖干擾底圖的采樣
          • 干擾圖UV流動(dòng)
          • 控制干擾強(qiáng)度
          • 干擾圖與底圖的混合時(shí)的比例

          接下來(lái),手把手教大家做一次。

          步驟一、新建一個(gè)著色器(Effect)

          給這個(gè)著色器文件命名為:effect-texture-move-distortion(大家也可根據(jù)愛好自行命名)。

          步驟二、在properties中添加參數(shù)

          • detailTexture?細(xì)節(jié)圖,在這里也充當(dāng)擾動(dòng)圖
          • strengthen?擾動(dòng)強(qiáng)度控制因子
          • speed?紋理UV流動(dòng)速度,這里用了vec2,用于單獨(dú)調(diào)節(jié)uv的流動(dòng)速度
          • detailColorFactor?細(xì)節(jié)圖在向原圖疊加時(shí)的混合比例

          步驟三、在unlit-fs中添加相關(guān)的uniform

          沒看錯(cuò),這里只加了一個(gè)叫paramsuniform。

          大家請(qǐng)注意看,在properties中,添加strengthen等屬性時(shí),比之前多加了一個(gè)target。

          target的意義在于重定向,它可以決定屬性使用哪一個(gè)uniform的哪幾個(gè)分量。

          也可以反過(guò)來(lái)解釋:target可以使我們給uniform的分量在屬性面板上起一個(gè)別名,方便我們理解各個(gè)分量的含義。

          比如,在本例中:

          • params.x的別名為strengthen
          • params.yz的別名為speed
          • params.w的別名為detailColorFactor

          麒麟小貼士:
          出現(xiàn)這個(gè)機(jī)制的主要原因,是由于uniform要占用內(nèi)存空間的。

          而不同的平臺(tái),在編譯Shader的時(shí)候,uniform內(nèi)存布局規(guī)則不同,可能會(huì)造成內(nèi)存浪費(fèi)。

          因此建議大家在寫Shader的時(shí)候,uniform盡量以vec4為主。

          步驟四、讓干擾圖流動(dòng)起來(lái)

          黃框標(biāo)記的部分,是為了方便Shader的編寫,我們使用了幾個(gè)臨時(shí)變量將params的分量根據(jù)properties中的定義存儲(chǔ)下來(lái)。

          紅框標(biāo)記部分則是對(duì)uv做一個(gè)時(shí)間相關(guān)的偏移。這是本文前面紋理UV流動(dòng)動(dòng)畫中講過(guò)的內(nèi)容 ,不再敷述。

          最后采樣得到的detailColor就是一個(gè)不停流動(dòng)的紋理。

          步驟五、擾動(dòng)處理

          灰度計(jì)算

          上圖中,黃色方框標(biāo)記的語(yǔ)句:

          float gray = detailColor.r * 0.299 + detailColor.g * 0.587 + detailColor.b * 0.114

          是由于本示例中用的細(xì)節(jié)圖片沒有ALPHA通道,所以這里使用了心理學(xué)灰度公式來(lái)計(jì)算出圖片的灰度值,用于參與強(qiáng)度運(yùn)算。

          如果是帶有ALPHA通道的圖片,直接使用ALPHA通道值即可。

          也可以簡(jiǎn)單使用detailColor.r作為擾動(dòng)強(qiáng)度去算。

          麒麟小貼士:?
          在表示灰色的時(shí)候,大家應(yīng)該會(huì)發(fā)現(xiàn)有的人用gray,有的人用grey。
          不必糾結(jié),在表示顏色時(shí),兩個(gè)都是可以的。
          gray是美式寫法,grey是英式寫法。

          偏移計(jì)算

          大家注意上圖中的紅框標(biāo)記的代碼,如下所示:

          vec2 offset = (detailColor.rg - 0.5) * 2.0 * strengthen * gray

          這里使用了rg通道來(lái)作為uv擾動(dòng)因子,但rg是一個(gè)[0.0,1.0]區(qū)間的值。

          想讓uv在一個(gè)范圍內(nèi)擾動(dòng),而不是朝某一個(gè)方向偏轉(zhuǎn),期望的擾動(dòng)因子就需要在[-1.0,1.0]這個(gè)區(qū)間。

          大家一起來(lái)回憶一下中學(xué)數(shù)學(xué)的內(nèi)容:

          • 若函數(shù)f(x)的值域?yàn)?code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">[0.0,1.0]

          • 設(shè)g(x)=(f(x) - 0.5) * 2.0。

          • 可得g(x)的值域?yàn)?code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">[-1.0,1.0]。

          最終我們可以得到一個(gè)x,y分量在[-1.0,1.0]區(qū)間的偏移量。

          強(qiáng)度控制

          句末的?* strengthen * gray,是對(duì)擾動(dòng)的強(qiáng)度進(jìn)行控制。

          • strengthen是對(duì)offset整體的縮放,用于整體調(diào)節(jié)擾動(dòng)強(qiáng)度。
          • gray是對(duì)單個(gè)像素進(jìn)行強(qiáng)度控制,從而產(chǎn)生波紋效果。

          采樣與混合

          有了之前的準(zhǔn)備工作,只需要將offset參與到mainTexture的采樣即可。如下所示:

          vec4 color = mainColor * texture( mainTexture, v_uv + offset );

          如果只做到這一步的話,我們會(huì)發(fā)現(xiàn),像素會(huì)被一個(gè)看不見的東西擾動(dòng),如下所示:

          不管是游戲、動(dòng)漫還是電影里,帶空間擾動(dòng)的特效或多或少都有一些顏色。所以我們還需要將擾動(dòng)特效的顏色疊加到底圖上,代碼如下:

          color.rgb += detailColor.rgb * detailColorFactor;

          其中detailColorFactor用于控制特效圖顯示的強(qiáng)弱。

          最終的效果如下所示:

          DEMO中本小節(jié)相關(guān)資源

          Effect:assets/tutorial/effect-texture-move-distortion.effect

          Scene:assets/tutorial/tutorial-move-distortion.scene

          5.4.2 基于紋理UV切換的擾動(dòng)效果

          基于紋理UV切換動(dòng)畫的擾動(dòng)效果,與上一節(jié)基于UV流動(dòng)的擾動(dòng)效果大同小異。本質(zhì)的區(qū)別就是detailTexture的運(yùn)動(dòng)方式問題。

          本節(jié)要實(shí)現(xiàn)的UV切換動(dòng)畫,只需要將UV流動(dòng)動(dòng)畫部分,改為UV切換動(dòng)畫即可。

          接下來(lái),我們說(shuō)一下差異的地方。

          差異一、properties

          • speed替換為了cells,用于標(biāo)記圖集中的子圖數(shù)量
          • 新增fps,用于控制子圖切換頻率

          差異二、uniforms

          fps是新增的屬性,而params4個(gè)分量都用完了,所以只能給fps新增一個(gè)uniform。

          差異三、animUV

          animUV的計(jì)算方式替換為了UV切換方式。

          其余部分均與上一小節(jié)保持一致,最終效果如下所示:

          DEMO中本小節(jié)相關(guān)資源

          Effect:assets/tutorial/effect-texture-anim-distortion.effect

          Scene:assets/tutorial/tutorial-anim-distortion.scene

          六、一個(gè)能量環(huán)繞的3D角色效果

          通過(guò)本文的Shader組合,即可實(shí)現(xiàn)上面的效果,編輯好材質(zhì)參數(shù)就行。

          DEMO中本小節(jié)相關(guān)資源

          Scene:assets/tutorial/tutorial-soldier.scene

          七、結(jié)束

          呼~~~,終于寫完了!本想著是一篇簡(jiǎn)單的入門文章,結(jié)果內(nèi)容越講越多,碼字器統(tǒng)計(jì)已9500多字。

          是時(shí)候收手了!

          本文DEMO源碼可在Cocos Store免費(fèi)獲取,地址如下:

          https://store.cocos.com/app/detail/3521

          也可點(diǎn)擊閱讀原文直接跳轉(zhuǎn)到Cocos Store頁(yè)面。

          關(guān)注**麒麟子隨筆**
          一個(gè)3D圖形渲染愛好者的公眾號(hào)
          瀏覽 117
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  99热在线观看一区 | 蜜桃视频操B网 | 操B强奸毛片国产 | 欧美成人三级在线 | 少妇bd|