对表面着色器中的顶点使用动画

我们现在知道了如何访问每个顶点数据的一些基础知识,这次让我们更进一步的了解一些其他类型的数据和顶点的位置。

使用顶点函数,我们可以访问网格中每个顶点位置。具体来说就是可以在着色器处理渲染的过程中,这些函数可以让我们单独对每一个顶点进行修改。

这个知识点当中,我们会创建一个着色器,并且用三角函数的正弦函数(sine wave)来修改网格当中的每一个顶点。该技术可以用来创建旗子飘动或者海浪等物体动画。


  • 始前准备 我们把资源都放一块儿,这样方便我们为顶点着色器( Vertex Shader )编写代码:

    • 1.创建一个新的场景,并且创建一个平面网格( plane mesh ),把它放在场景正中央,位置归零。
    • 2.然后创建一个新的材质和着色器。
    • 3.最后,把着色器挂到材质上,在把材质挂到平面网格上。

    最终,你的场景看起来应该跟下图一样:
    diagram


  • 操作步骤
    场景创建好后,鼠标双击打开刚刚我们创建的着色器:

    • 1.让我们给着色器的属性块下面的预设值:
    Properties
    {
        _MainTex ("Base (RGB)", 2D) = "white" {}
        _tintAmount ("Tint Amount", Range(0,1)) = 0.5
        _ColorA ("Color A", Color) = (1,1,1,1)
        _ColorB ("Color B", Color) = (1,1,1,1)
        _Speed ("Wave Speed", Range(0.1, 80)) = 5
        _Frequency ("Wave Frequency", Range(0, 5)) = 2
        _Amplitude ("Wave Amplitude", Range(-1, 1)) = 1
    }
    
    • 2.接下来我们通过下面的声明告诉Unity,我们需要使用顶点函数了,添加声明的位置在: #pragma statement
    CGPROGRAM
    #pragma surface surf Lambert vertex:vert
    
    • 3.为了访问属性块中的值,我们还要在 CGPROGRAM 块中声明与之对应的变量:
    sampler2D _MainTex;
    float4 _ColorA;
    float4 _ColorB;
    float _tintAmount;
    float _Speed;
    float _Frequency;
    float _Amplitude;
    float _OffsetVal;
    
    • 4.我们也将使用顶点位置的变化作为 vert 颜色。因为这样我们可以顺便修改物体的颜色了:
    struct Input
    {
        float2 uv_MainTex;
        float3 vertColor;
    }
    
    • 5.此时,我们要修改顶点的表现了,我们要使用到下面的正选函数和顶点函数,在我们的 Input Struct 后面添加如下代码:
    void vert(inout appdata_full v, out Input o)
    {
        float time = _Time * _Speed;
        float waveValueA = sin(time + v.vertex.x * _Frequency) * _Amplitude;
        v.vertex.xyz = float3(v.vertex.x, v.vertex.y + waveValueA, v.vertex.z);
        v.normal = normalize(float3(v.normal.x + waveValueA, v.normal.y,v.normal.z));
        o.vertColor = float3(waveValueA,waveValueA,waveValueA);
    }
    
    • 6.最后,我们要使用一个 lerp() 函数对两个颜色进行一个插值,这样我们就可以对新网格的波峰和波谷应用不同的颜色,为了获得这种表现,添加下面的代码让顶点函数起作用:
    void surf (Input IN, inout SurfaceOutput o)
    {
        half4 c = tex2D (_MainTex, IN.uv_MainTex);
        float3 tintColor = lerp(_ColorA, _ColorB, IN.vertColor).rgb;
        o.Albedo = c.rgb * (tintColor * _tintAmount);
        o.Alpha = c.a;
    }
    

    当你完成了着色器的编写后,回到Unity并且等待着色器编译完成。当编译完成后,你就会看到如下图所示的情形:

    diagram


  • 原理介绍
    这个特定的着色器使用了我们上个知识点的一些概念,但不同的是,我们这里改变的是网格上那些顶点的位置。有时候你不想用骨骼结构( skeleton structure )或者是节点结构( hierarchy of transforms )来扣动画, 然后在简单的拼接它们,比如旗帜,在旗子上做动画然后跟旗杆组合拼接成旗帜。这个时候你使用这个着色器就特别有用了。

    我们简单的使用了正弦函数 sin() 来创建正弦波,这个函数式Cg语言内建的。当计算好正弦值后,我们把这个值赋值给了每个顶点位置的 y,然后就产生了波动的效果。

    当然我们还顺便稍微修改了网格上面的法线,让它针对正弦波有更真实的着色。

    从这里你也可以知道,通过利用表面着色器内建的顶点参数,来获得一些比较复杂的顶点效果,还是比较容易的。