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

          用 Shader 實現(xiàn)旗幟飄揚動畫效果

          共 8864字,需瀏覽 18分鐘

           ·

          2024-04-26 12:07

          覺得對于剛?cè)腴T 3D 編程的朋友來說,如果能夠完成代碼創(chuàng)建模型數(shù)據(jù)->創(chuàng)建材質(zhì)->編寫Shader動畫這一系列,想必會有滿滿的成就感。

          今天就用 Cocos Creator 的 utils.MeshUtils.createMesh 接口,帶大家感受一下這個流程。

          這個流程不僅可以用于新手學(xué)習(xí),也是一些高端特效的必備技巧。

          準(zhǔn)備:基礎(chǔ)

          1. 你需要一個Cocos Creator,我這里用的是3.8.2版本

          2. 了解下創(chuàng)建網(wǎng)格的接口:查看官方文檔

          utils.MeshUtils.createMesh

          這個接口是用來創(chuàng)建網(wǎng)格的,你可以通過這個接口創(chuàng)建一個網(wǎng)格,綁定到節(jié)點的 MeshRendermesh 屬性上,就可以看到你的模型了。

          起點:顯示一個三角形

          三角形是 3D 世界的基本單位,我們在屏幕上看到的三維模型,也是通過三角形構(gòu)成的。三角形數(shù)量越多,能夠表示的模型細節(jié)就越多。

          我們先從一個三角形開始,然后再慢慢的擴展到更多的三角形,最后就是一個完整的模型了。


          private createTriangle() {

              this._meshCreator.reset();

              let p0 = vec3Pool.alloc().set(000);

              let p1 = vec3Pool.alloc().set(100);

              let p3 = vec3Pool.alloc().set(0-10);

              this._meshCreator.verticles.push(p0 , p1, p3);

              let uv0 = vec2Pool.alloc().set(00);

              let uv1 = vec2Pool.alloc().set(10);

              let uv2 = vec2Pool.alloc().set(01);

              this._meshCreator.uvs.push(uv0, uv1, uv2);

              this._meshCreator.indices.push(021);

              this._meshRenderer.mesh = this._meshCreator.buildMesh();

          }

          代碼看起來是不是很簡單?

          我們只需要創(chuàng)建三個頂點,然后創(chuàng)建三個uv,最后創(chuàng)建一個三角形,然后就可以看到一個三角形了。如果你不用顯示貼圖的話,甚至于可以不用計算uv,亂寫一個就行了。其實創(chuàng)建網(wǎng)格還有很多參數(shù),這里我們都可以先忽略它們!

          進階:顯示一個正方形

          正方形是兩個三角形組成的,我們只需要創(chuàng)建兩個三角形,然后組合在一起,就可以顯示一個正方形了。


          private createQuad() {

              this._meshCreator.reset();

              let p0 = vec3Pool.alloc().set(000);

              let p1 = vec3Pool.alloc().set(100);

              let p2 = vec3Pool.alloc().set(1-10);

              let p3 = vec3Pool.alloc().set(0-10);

              this._meshCreator.verticles.push(p0, p1, p2, p3);

              let uv0 = vec2Pool.alloc().set(00);

              let uv1 = vec2Pool.alloc().set(10);

              let uv2 = vec2Pool.alloc().set(11);

              let uv3 = vec2Pool.alloc().set(01);

              this._meshCreator.uvs.push(uv0, uv1, uv2, uv3);

             

              this._meshCreator.indices.push(021032);    

              this._meshRenderer.mesh = this._meshCreator.buildMesh();

          }

          與三角形類似,我們只需要創(chuàng)建四個點,然后創(chuàng)建四個uv,最后創(chuàng)建兩個三角形,然后就可以看到一個正方形了。

          聰明的你可能已經(jīng)發(fā)現(xiàn)了,正方形雖然是兩個三角形組成的,但是我們只需要創(chuàng)建一個正方形的四個點,然后通過索引來組合兩個三角形,這樣就可以減少重復(fù)的點了。

          看代碼還是太累,這里用圖片來表示一下頂點:

          終極:顯示一個飄動的旗幟

          旗子其實就是一個長方形,然后通過Shader來讓它動起來,唯一不同的是,我們需要給它多個頂點,然后通過Shader控制各個頂點,讓它動起來,從而看起來像是飄動的旗幟。


          private buildMesh() {

              this._dirty = DirtyType.None;        

              this._meshCreator.reset();

              const width = this._size.x;

              const height = this._size.y;

              const verticePreUnit = this._verticePreUnit;

              const widthSegments = Math.floor(width * verticePreUnit);

              const heightSegments = Math.floor(height * verticePreUnit);

              for(let j = 0; j <= heightSegments; j++) {

                  const y = - j / heightSegments * height;

                  for(let i = 0; i <= widthSegments; i++) {

                      const x = i / widthSegments * width;

                      const v = vec3Pool.alloc();

                      v.set(x, y, 0);

                      this._meshCreator.verticles.push(v);

                     

                      const uv = vec2Pool.alloc();

                      uv.set(i / widthSegments, j / heightSegments);

                      this._meshCreator.uvs.push(uv);

                  }

              }

              for(let j = 0; j < heightSegments; j++) {

                  for(let i = 0; i < widthSegments; i++) {

                      const a = i + (widthSegments + 1) * j;

                      const b = i + (widthSegments + 1) * (j + 1);

                      const c = (i + 1) + (widthSegments + 1) * (j + 1);

                      const d = (i + 1) + (widthSegments + 1) * j;

                      this._meshCreator.indices.push(a, b, c);

                      this._meshCreator.indices.push(a, c, d);

                  }

              }

              this._meshRenderer.mesh = this._meshCreator.buildMesh();

          }

          網(wǎng)格的創(chuàng)建沒什么再說的了,但是這里要注意需要精確計算uv坐標(biāo),保證紋理顯示正確;

          而且在shader中需用通過uv坐標(biāo)來控制頂點的位置,從而讓旗幟動起來,簡單的來說就是通過uv的橫坐標(biāo)控制頂點偏移的強度,畢竟靠近旗桿的地方是不會動的,而靠近旗尾的地方是會動的。

          我們通過shader顯示一下旗幟UV水平方向上的變化,可以看到離旗桿越遠,旗幟就會動的越強烈:


          vec4 SurfacesFragmentModifyBaseColorAndTransparency()

          {

          return vec4(ALBEDO_UV.x, 0.0, 0.0, 1.0);

          }

          至此,給網(wǎng)格添加材質(zhì)普通的材質(zhì)球后,我們就有了一個靜態(tài)的旗幟,接下來就是通過Shader讓它動起來了。

          進化:讓旗幟動起來

          讓旗子動起來很簡單,只需要在Cocos Surface Shader的函數(shù)SurfacesVertexModifyWorldPos中修改頂點的位置即可,最簡單的方式就是給一個sin,讓他隨時間的變化平移頂點的位置,這樣就可以讓旗幟動起來了。


          vec3 SurfacesVertexModifyWorldPos(in SurfacesStandardVertexIntermediate In)

          {

          In.worldPos += a_normal * sin(cc_time.w + In.worldPos.x);

          return In.worldPos;

          }

          效果如下:

          好吧,一言難盡,確實是動起來了,但是也飛起來了,旗桿完全沒達到約束他的效果,我們用上文提到的uv來控制頂點的偏移,再試試效果:


          vec3 SurfacesVertexModifyWorldPos(in SurfacesStandardVertexIntermediate In)

          {

          In.worldPos += a_normal * sin(cc_time.w + In.worldPos.x) * a_texCoord.x;

          return In.worldPos;

          }

          效果如下:

          這次的效果明顯好多了,但是還是有點問題,旗幟的波動不夠自然,我們可以通過增加一張噪聲貼圖來讓它更自然一些,這里我用了一個簡單的噪聲貼圖,你可以用更復(fù)雜的噪聲貼圖來讓它更自然一些,當(dāng)然這里需要再頂點著色器中做紋理采樣,可能有些低端手機或者平臺不支持,所以需要注意一下。

          當(dāng)然,你也可以通過更復(fù)雜的算法來模擬噪聲:


          vec3 SurfacesVertexModifyWorldPos(in SurfacesStandardVertexIntermediate In)

          {

          float noise = texture2D(a_noiseMap, a_texCoord).r;

          In.worldPos += a_normal * sin(cc_time.w + In.worldPos.x) * a_texCoord.x * noise;

          return In.worldPos;

          }

          效果如下:

          有了噪聲貼圖,旗幟的波動就更自然了, 但是有些小問題,我再次修改下代碼,形成最終的效果:


          vec3 SurfacesVertexModifyWorldPos(in SurfacesStandardVertexIntermediate In)

          {

          float t = sin(cc_time.w * speed + In.worldPos.x);

          vec3 oldPos = In.worldPos;

          In.worldPos += a_normal * t * height * a_texCoord.x;

          vec2 uv = a_texCoord + vec2(t * 0.3, 0.0);

          vec4 noise = texture(noiseMap, uv);

          In.worldPos -= noise.r * height * a_texCoord.x;

          return In.worldPos;

          }

          最后,把圖片換成 Cocos 的標(biāo)準(zhǔn)模板 LOGO。


          得到的效果如下:


          總結(jié)

          看到?jīng)]有,從頭到尾,我們只關(guān)注兩件事:

          1. 創(chuàng)建網(wǎng)格

          網(wǎng)格我已經(jīng)通過工具類封裝好了,你只需要調(diào)用接口,傳入?yún)?shù),就可以創(chuàng)建一個網(wǎng)格了。

          2. 編寫Shader

          而Shader我們從頭到尾值關(guān)注了一個函數(shù)SurfacesVertexModifyWorldPos,這個函數(shù)是用來修改頂點的位置的,我們只需要在這個函數(shù)中修改頂點的位置,就可以讓模型動起來了。

          所以,3D 繪制其實是很簡單的,只要你掌握了這兩個點,你就可以做出很多很多的東西了,這里只是一個簡單的例子,你可以通過這個例子,去嘗試更多的東西,比如更復(fù)雜的模型,更復(fù)雜的Shader,更復(fù)雜的動畫,這些都是你可以嘗試的,希朥這個教程能夠幫助到你,也希望你能夠享受這個過程。

          不知道你有沒有發(fā)現(xiàn),從頭繪制一個3D模型,并且給予它生命一般的動畫,這個過程是多么的有趣,這個過程就像是一個創(chuàng)造者,你可以創(chuàng)造出你想要的一切,這種感覺是多么的美妙,希望你也能夠感受到這種美妙。

          項目文件

          點擊【閱讀原文】,可在文章末尾獲得項目文件,直接解壓放到你的工程資源文件夾下即可。

          瀏覽 317
          2點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          2點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  日韩毛片| 91av在线观看视频五月天 | 欧美福利 | 国产美女被操 | 国产色情-搜索 |