【V7投稿】我为COCOS立Flag

flag

我觉得对于完3D的新手来说,最大的成就感就是自己建模,然后让他动起来,如果觉得有问题,就再加一个自己学会写Shader。那么这个教程就是为了让大家能够快速的上手,创建自己的3D模型,并让通过Shader它动起来。

快速开始

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

  2. 了解下创建网格的接口:官方文档


utils.MeshUtils.createMesh

这个接口是用来创建网格的,你可以通过这个接口创建一个网格,绑定到节点的MeshRender的mesh属性上,就可以看到你的模型了。

为了方便,我封装了一个网格创建的工具类(MeshCreator),我们可以直接使用这个工具类来创建网格,文档最后会附加项目文件,你可以直接下载下来,然后直接使用。

起点:显示一个三角形

三角形是3D世界的基本单位,我们先从一个三角形开始,然后再慢慢的扩展到更多的三角形,最后就是一个完整的模型了。

triangle


private createTriangle() {

    this._meshCreator.reset();

    let p0 = vec3Pool.alloc().set(0, 0, 0);

    let p1 = vec3Pool.alloc().set(1, 0, 0);

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

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

    let uv0 = vec2Pool.alloc().set(0, 0);

    let uv1 = vec2Pool.alloc().set(1, 0);

    let uv2 = vec2Pool.alloc().set(0, 1);

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

    this._meshCreator.indices.push(0, 2, 1);

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

}

代码看起来是不是很简单,我们只需要创建三个点,然后创建三个uv,最后创建一个三角形,然后就可以看到一个三角形了,如果你不用显示贴图的话,甚至于可以不用计算uv,乱写一个就行了。
其实创建网格还有很多参数,这里我们都可以先忽略它们!

进阶:显示一个正方形

正方形是两个三角形组成的,我们只需要创建两个三角形,然后组合在一起,就可以显示一个正方形了。

quad


private createQuad() {

    this._meshCreator.reset();

    let p0 = vec3Pool.alloc().set(0, 0, 0);

    let p1 = vec3Pool.alloc().set(1, 0, 0);

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

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

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

    let uv0 = vec2Pool.alloc().set(0, 0);

    let uv1 = vec2Pool.alloc().set(1, 0);

    let uv2 = vec2Pool.alloc().set(1, 1);

    let uv3 = vec2Pool.alloc().set(0, 1);

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

   

    this._meshCreator.indices.push(0, 2, 1, 0, 3, 2);    

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

}

与三角形类似,我们只需要创建四个点,然后创建四个uv,最后创建两个三角形,然后就可以看到一个正方形了。

聪明的你可能已经发现了,正方形虽然是两个三角形组成的,但是我们只需要创建一个正方形的四个点,然后通过索引来组合两个三角形,这样就可以减少重复的点了。

看代码还是太累,这里用图片来表示一下顶点:

v

终极:显示一个飘动的旗帜

旗子其实就是一个长方形,然后通过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();

}

网格的创建没什么再说的了,但是这里要注意需要精确计算uv坐标,保证纹理显示正确;

而且在shader中需用通过uv坐标来控制顶点的位置,从而让旗帜动起来,简单的来说就是通过uv的横坐标控制顶点偏移的强度,毕竟靠近旗杆的地方是不会动的,而靠近旗尾的地方是会动的。

我们通过shader显示一下旗帜UV水平方向上的变化,可以看到离旗杆越远,旗帜就会动的越强烈:


  vec4 SurfacesFragmentModifyBaseColorAndTransparency()

  {

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

  }

uv

至此,给网格添加材质普通的材质球后,我们就有了一个静态的旗帜,接下来就是通过Shader让它动起来了。

s-flag

进化:让旗帜动起来

让旗子动起来很简单,只需要在Cocos Surface Shader的函数SurfacesVertexModifyWorldPos中修改顶点的位置即可,最简单的方式就是给一个sin,让他随时间的变化平移顶点的位置,这样就可以让旗帜动起来了。


  vec3 SurfacesVertexModifyWorldPos(in SurfacesStandardVertexIntermediate In)

  {

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

    return In.worldPos;

  }

效果如下:

sin-flag

好吧,一言难尽,确实是动起来了,但是也飞起来了,旗杆完全没达到约束他的效果,我们用上文提到的uv来控制顶点的偏移,再试试效果:


  vec3 SurfacesVertexModifyWorldPos(in SurfacesStandardVertexIntermediate In)

  {

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

    return In.worldPos;

  }

效果如下:

uv-flag

这次的效果明显好多了,但是还是有点问题,旗帜的波动不够自然,我们可以通过增加一张噪声贴图来让它更自然一些,这里我用了一个简单的噪声贴图,你可以用更复杂的噪声贴图来让它更自然一些,当然这里需要再顶点着色器中做纹理采样,可能有些低端手机或者平台不支持,所以需要注意一下。

noise

当然,你也可以通过更复杂的算法来模拟噪声:

curve


  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;

  }

效果如下:

noise-flag

有了噪声贴图,旗帜的波动就更自然了, 但是有些小问题,我再次修改下代码,形成最终的效果:


   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;

    }

效果如下:

flag

效果其实还是有问题,在增加旗帜的接收投影后,旗子上有些奇怪的阴影,这个问题我还没有解决,如果你有解决方案,欢迎留言。

总结

看到没有,从头到尾,我们只关注两件事:

  1. 创建网格

    网格我已经通过工具类封装好了,你只需要调用接口,传入参数,就可以创建一个网格了。

  2. 编写Shader

    而Shader我们从头到尾值关注了一个函数SurfacesVertexModifyWorldPos,这个函数是用来修改顶点的位置的,我们只需要在这个函数中修改顶点的位置,就可以让模型动起来了。

所以,3D绘制其实是很简单的,只要你掌握了这两个点,你就可以做出很多很多的东西了,这里只是一个简单的例子,你可以通过这个例子,去尝试更多的东西,比如更复杂的模型,更复杂的Shader,更复杂的动画,这些都是你可以尝试的,希朥这个教程能够帮助到你,也希望你能够享受这个过程。

不知道你有没有发现,从头绘制一个3D模型,并且给予它生命一般的动画,这个过程是多么的有趣,这个过程就像是一个创造者,你可以创造出你想要的一切,这种感觉是多么的美妙,希望你也能够感受到这种美妙。

项目文件

项目文件 (282.1 KB)
文件下载后,直接解压放到你的工程资源文件夹下即可。

15赞

牛的啊啊啊啊啊!沙发

这尼玛秀啊,必须内定为第一名!!!

感谢支持!

:wink:

牛逼!!!

厉害厉害 :+1:

w哥流弊!!!!

你也流弊 :wink:

厉害厉害,能运用好这些知识实现成品,就是厉害

感谢支持 :grinning:

是真滴6,牛逼

牛,赞赞赞赞

这个工具能分享下地址不 :+1:
image

https://www.geogebra.org/geometry

2赞

Desmos | 图形计算器

2赞

:pray:感谢呀。。

沉下去了,再顶上来

哈哈哈,还得是你啊

没时间整其他的,先占占位置,哈哈,:wink: