<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>

          Cocos Creator 卡通水體渲染教程

          共 5541字,需瀏覽 12分鐘

           ·

          2021-01-12 12:23

          本篇教程轉(zhuǎn)載自 Cocos 中文社區(qū),作者:regocxy。通過(guò)本篇教程你將學(xué)到如何做風(fēng)格化水體的渲染,包含的知識(shí)點(diǎn)有如何使用天空立方體貼圖作反射,如何巧用噪聲貼圖作紋理擾動(dòng)并順便做出浮沫效果,如何巧用uv做出邊沿霧效果。

          水體渲染是游戲中比較有挑戰(zhàn)的一種效果,實(shí)現(xiàn)難度也有深有淺,這里筆者希望使用一種簡(jiǎn)單高效的方法實(shí)現(xiàn)一個(gè)簡(jiǎn)單美觀的風(fēng)格化水體效果,最終實(shí)現(xiàn)效果如下,性能非常優(yōu)秀,對(duì)移動(dòng)設(shè)備非常友好。

          b6c61fa70719c9635799891e0b622a59.webp79303ba4cdabf98004000bd1b24e4490.webp

          下面是實(shí)現(xiàn)過(guò)程:1、天空反射

          天空的反射需要:

          • 環(huán)境立方體貼圖
          • 噪聲貼圖
          • 菲涅爾反射

          1.1 環(huán)境立方體貼圖

          想要做出水體流動(dòng)的感覺有非常多的方法,其中使用 uv 偏移是最簡(jiǎn)單并且性能最好的方法,該方案絕大多數(shù)的做法都是對(duì)一張法線貼圖作 uv 縮放和偏移,并作光影計(jì)算從而表現(xiàn)出流動(dòng)的水面。

          該方案的確能做出相當(dāng)不錯(cuò)的風(fēng)格化水體效果,但是筆者這次不想這么做,因?yàn)榉ň€貼圖的采樣還原在筆者看來(lái)還是不夠精簡(jiǎn),甚至水體對(duì)光源的明暗變化筆者也不想計(jì)算。

          筆者選擇了直接對(duì)環(huán)境立方體貼圖做采樣,表現(xiàn)一個(gè)簡(jiǎn)單的水面反射效果。

          代碼如下:

          vec3??v?=?normalize(v_view);
          vec3??r?=?-v;
          vec3??reflectColor?=?texture(envTexture,?r).rgb;

          以上代碼中,筆者對(duì)反射做了一個(gè)計(jì)算優(yōu)化,直接對(duì)視角向量取反,即 r = -v

          常規(guī)做法是 r = reflect(v, n),其中 reflect(v, n) = v - 2.0 * dot(n, v) * n

          reflect 表達(dá)式就能看出筆者的寫法效率要遠(yuǎn)高于常規(guī)做法,少了 2 次的乘法計(jì)算和 1 次點(diǎn)成計(jì)算。

          而筆者的計(jì)算優(yōu)化成立的原因是對(duì)于天空的反射,如果僅僅讓視覺上看起來(lái)像反射,我們其實(shí)可以不用關(guān)心反射方向的正確性,讀者可以自己作個(gè)圖細(xì)品下。

          完整代碼如下:

          vec4?frag?()?{
          ????vec3??n?=?normalize(v_normal);
          ????vec3??v?=?normalize(v_view);
          ????vec3??r?=?-v;
          ??????
          ????vec3??reflectColor?=?texture(envTexture,?r).rgb;
          ????return?vec4(reflectColor,?mainColor.a);
          }

          此時(shí)讀者應(yīng)該能得到一個(gè)鏡子一般的水面,毫無(wú)美感,并且絲毫也感受不出這是水。

          de57bc63c3581cabd1cf2fdd8fdf77e7.webp

          1.2 噪聲貼圖

          表現(xiàn)水體的核心有兩點(diǎn):一個(gè)是流動(dòng)感,另一個(gè)是扭曲感。

          而這兩點(diǎn)都可以通過(guò)對(duì)噪聲貼圖進(jìn)行 uv 偏移實(shí)現(xiàn)。

          本文使用的噪聲貼圖:

          90bed01326131e6dbee593ffbc9bf9b6.webp

          代碼如下:

          vec4?vert()?{
          ????StandardVertInput?In;
          ????CCVertInput(In);
          ????mat4?matWorld,?matWorldIT;
          ????CCGetWorldMatrixFull(matWorld,?matWorldIT);
          ????vec4?worldPos?=?matWorld?*?In.position;
          ????v_uv.xy?=?worldPos.xz?*?0.1?+?cc_time.x?*?0.05;
          ????...
          }

          vec4?frag?()?{
          ????float?t?=?texture(noiseTexture,?v_uv.xy).r;
          ????vec3??n?=?normalize(v_normal);
          ????vec3??v?=?normalize(v_view);
          ????vec3??r?=?-v?+?t?*?0.03;
          ????????
          ????vec3??reflectColor?=?texture(envTexture,?r).rgb;
          ????return?vec4(reflectColor,?mainColor.a);
          }

          以上代碼中,筆者作 uv 偏移是基于世界坐標(biāo)偏移的,而不是簡(jiǎn)單的 v_uv.xy += cc_time.x * 0.05,這里的原因是基于世界坐標(biāo)作偏移可以隨意調(diào)整水面大小,而不會(huì)拉伸噪聲貼圖,造成失真。

          這里還有一點(diǎn)需要注意的是:我們的噪聲貼圖的 wrap mode 需要設(shè)置為 repeat 即重復(fù)模式。

          f257db81004cd60073876a66643b8ee0.webp

          1.3 菲涅爾反射

          加入流動(dòng)感和扭曲感后,我們的水體終于看起來(lái)像水了。

          目前還存在一個(gè)問(wèn)題:水面任何視角的反射表現(xiàn)都是一樣的,這是不正確的。

          這里需要引出一個(gè)現(xiàn)象叫菲涅爾反射(fresnel),簡(jiǎn)單的講,就是視線垂直于表面時(shí),反射較弱,而當(dāng)視線非垂直表面時(shí),夾角越小,反射越明顯。

          代碼如下:

          float?fresnel?=?mix(0.15,?1.0,?pow(1.0?-?dot(n,?v),?3.0));

          常規(guī)的 fresnel 反射公式為 fresnel = pow(1.0 - dot(n, v), x),x 為指數(shù)系數(shù)。

          而這里筆者使用了 mix 函數(shù),將 fresnel 的數(shù)值映射到 0.151.0 之間,確保視角與水面垂直時(shí),也是存在反射的。

          mix(x, y, a)是一個(gè)混合函數(shù),等價(jià)于 x×(1?a)+y×a.

          完整的代碼如下:

          vec4?frag?()?{
          ????float?t?=?texture(noiseTexture,?v_uv.xy).r;
          ????vec3??n?=?normalize(v_normal);
          ????vec3??v?=?normalize(v_view);
          ????vec3??r?=?-v?+?t?*?0.03;
          ?????
          ????vec3??reflectColor?=?texture(envTexture,?r).rgb;
          ????float?fresnel?=?mix(0.15,?1.0,?pow(1.0?-?dot(n,?v),?3.0));
          ????vec3??color?=?mix(mainColor.rgb,?reflectColor,?fresnel);
          ????return?vec4(color,?mainColor.a);
          }

          加入菲涅爾反射前后對(duì)比:

          110fd4ef5683c65083655e2c1d2ea3bb.webpefe11e5b7ff6285867a3fbf223712f2a.webp


          2、浮末

          目前我們的水面雖然有了些許流動(dòng)感,但還不夠明顯,所以我們需要在水面上制造一些浮沫,突出水的流動(dòng)。

          浮沫有幾個(gè)特點(diǎn):

          • 位置不固定;
          • 大小也不固定。

          觀察我們的噪聲貼圖,你會(huì)發(fā)現(xiàn)噪聲貼圖上的一些白色圖案剛好符合我們需求,我們只要想個(gè)辦法將它提取出來(lái)就可以了。

          所以我們對(duì)上述代碼做如下改動(dòng):

          vec4?frag?()?{
          ????float?t?=?texture(noiseTexture,?v_uv.xy).r;
          ????...
          ????color?=?mix(color,?vec3(1.0),?step(0.9,?t));
          ????...
          }

          首先我用 step 函數(shù)對(duì)噪聲作了一次過(guò)濾,將大于 0.9 的噪聲提取了出來(lái),并用 mix 混合函數(shù),將水的顏色和白色(vec3(1.0) 是白色)進(jìn)行混合得到帶有白色浮沫的水面。

          step(edge, x)是一個(gè)階躍函數(shù),等價(jià)于x < edge ? 0: 1。

          另外大多時(shí)候我們使用 step,提取出來(lái)的圖案,都是有鋸齒感的,所以需要作抗鋸齒,這時(shí)就需要使用 smoothstep 函數(shù)。修改如下:

          vec4?frag?()?{
          ????float?t?=?texture(noiseTexture,?v_uv.xy).r;
          ????...
          ????color?=?mix(color,?vec3(1.0),?smoothstep(0.9,?0.91,?t));
          ????...
          }

          改動(dòng)非常少,僅僅是將 step(0.9, t)替換為 smoothstep(0.9, 0.91, t)smoothstep(0.9, 0.91, t) 的作用是將 t[0.9, 0.91] 的范圍內(nèi)作平滑處理,當(dāng) t < 0.9 時(shí),取 0;當(dāng) t > 0.91 時(shí),取1

          smoothstep(edge0, edge1, x)是一個(gè)三次平滑階躍函數(shù),可以將x在[edge0, edge1]之間做一個(gè)平滑過(guò)渡,大多時(shí)候都用來(lái)消除鋸齒。

          完整的代碼如下:

          vec4?frag?()?{
          ????float?t?=?texture(noiseTexture,?v_uv.xy).r;
          ????vec3??n?=?normalize(v_normal);
          ????vec3??v?=?normalize(v_view);
          ????vec3??r?=?-v?+?t?*?0.03;
          ?????
          ????vec3??reflectColor?=?texture(envTexture,?r).rgb;
          ????float?fresnel?=?mix(0.15,?1.0,?pow(1.0?-?dot(n,?v),?3.0));
          ????vec3??color?=?mix(mainColor.rgb,?reflectColor,?fresnel);
          ????color?=?mix(color,?vec3(1.0),?smoothstep(0.9,?0.901,?t));
          ????return?vec4(color,?mainColor.a);
          }

          效果如下:

          63b735417acec0d052a713db0b21bafe.webp

          3、加入天空盒

          這里的天空盒沒用skybox,因?yàn)槭褂?skybox,水面的邊沿線消除有些困難,所以筆者使用了一個(gè)球體。

          效果如下:

          b266a75c23755fb7eb60f2bd4a234e89.webp2d3572a3674f8b7eafd19dd51870d949.webp


          讀者可以看到在水面和天空的交界處有一個(gè)明顯的邊沿線,下面我們就要消除掉這個(gè)邊沿線。

          消除這個(gè)邊沿線,我們首先想到的是使用,將遠(yuǎn)處的水面與天空盒用霧來(lái)模糊掉。

          但是使用常規(guī)的霧效會(huì)帶來(lái)一個(gè)問(wèn)題:當(dāng)相機(jī)進(jìn)行遠(yuǎn)近移動(dòng)時(shí),霧的效果會(huì)產(chǎn)生變化,水面的邊沿線還是沒解決。

          034ed70511a4a7f9bbad66e6bc69d5d4.webp

          所以我們要換個(gè)思路實(shí)現(xiàn)。

          其實(shí)消除這個(gè)邊沿線的思路很簡(jiǎn)單,我們只要讓遠(yuǎn)處水面的顏色與天空盒一致就好了。

          于是筆者寫了下面這段代碼:

          vec4?vert()?{
          ????...
          ????v_uv.zw?=?a_texCoord;
          ????...
          }

          vec4?frag()?{
          ????...
          ????vec2?d?=?v_uv.zw?-?vec2(0.5,?0.5);
          ????color?=?mix(color,?rimColor.rgb,?rimColor.a?*?smoothstep(0.0,?0.27,?dot(d,d)));
          ????...
          }

          效果如下:

          b75bac62273f310e34ed14e314c880fb.webp

          完美解決問(wèn)題,原理是這樣的:我們將與水面中心距離大于一定范圍內(nèi)的區(qū)域顏色設(shè)置成 rimColor(rimColor 的顏色基本與天空盒的顏色一致) 并且用 smoothstep,對(duì)一定范圍內(nèi)的距離值做了平滑處理。

          但是在實(shí)際計(jì)算中,筆者作了一個(gè)計(jì)算優(yōu)化,筆者沒有直接使用距離值即 sqrt(dot(d,d)),而是使用了距離的平方值即 dot(d, d),原因是求平方根比較廢性能,如果僅僅是比大小,其實(shí)沒必要開根號(hào)。

          完整的代碼如下:

          vec4?vert()?{
          ????StandardVertInput?In;
          ????CCVertInput(In);
          ????mat4?matWorld,?matWorldIT;
          ????CCGetWorldMatrixFull(matWorld,?matWorldIT);
          ????vec4?worldPos?=?matWorld?*?In.position;
          ????v_normal?=?normalize((matWorldIT?*?vec4(In.normal,?0.0)).xyz);
          ????v_view?=?cc_cameraPos.xyz?-?worldPos.xyz;
          ????v_uv.xy?=?worldPos.xz?*?0.1?+?cc_time.x?*?0.05;
          ????v_uv.zw?=?a_texCoord;
          ????return?cc_matProj?*?cc_matView?*?worldPos;
          }

          vec4?frag?()?{
          ????float?t?=?texture(noiseTexture,?v_uv.xy).r;
          ????vec3??n?=?normalize(v_normal);
          ????vec3??v?=?normalize(v_view);
          ????vec3??r?=?-v?+?t?*?0.03;
          ?????
          ????vec3??reflectColor?=?texture(envTexture,?r).rgb;
          ????float?fresnel?=?mix(0.15,?1.0,?pow(1.0?-?dot(n,?v),?3.0));
          ????vec3??color?=?mix(mainColor.rgb,?reflectColor,?fresnel);
          ????color?=?mix(color,?vec3(1.0),?smoothstep(0.9,?0.91,?t));
          ????vec2?d?=?v_uv.zw?-?vec2(0.5,?0.5);
          ????color?=?mix(color,?rimColor.rgb,?rimColor.a?*?smoothstep(0.0,?0.27,?dot(d,d)));
          ????return?vec4(color,?mainColor.a);
          }

          最后在相機(jī)前擺上一些粒子烘托下氣氛:

          79303ba4cdabf98004000bd1b24e4490.webpc5720a290cb47d09b05f1fccd17517af.webp3、結(jié)語(yǔ)

          非常感謝?regocxy 帶來(lái)的技術(shù)分享,歡迎各位開發(fā)者點(diǎn)擊「閱讀原文」查看原貼,為作者點(diǎn)贊,與作者進(jìn)行交流學(xué)習(xí)!作者會(huì)在社區(qū)中繼續(xù)講解如何制造岸邊泡沫以及浪花喲!


          瀏覽 134
          點(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>
                  欧美成人三级视频 | 免费超踫| 蜜桃臀久久久蜜桃臀久久 | 操逼视频网站网址 | 亚洲免费在线播放 |