• Unity Shader波纹效果


    我们今天来模拟一下波纹效果,当一颗石头投入水面时,在水中会形成向外扩散的一圈波纹,本质上就是一个向四周扩散的波。根据我们日常生活的经验可以知道,当一个物体投入水中时,中心的振幅时比较大的,而随着波向边缘运动,振幅越来越小,而波的频率在中心总体时很小的,而在边缘时波频率很大。

    那么我们可以先试着用正弦波来模拟。根据我们上述的波的性质,可以简单的将公式写为

     

     这样,一个以(0,0,0)为中心点的正弦波就构造出来了,前面振幅之所以要在分母上加1是为了防止分母为0.

    然后上代码:

    Shader "Unlit/VertWave"
    {
        Properties
        {
            _MainTex ("Texture", 2D) = "white" {}
            _Color("Color",Color) = (1,1,1,1)
            _WaveIntensity("Intensity",float) = 0.2
            _Speed("Speed",float) = 1
            _Frequency("Frequency",Range(0.1,1)) = 1
            
        }
        SubShader
        {
            Tags { "RenderType"="Opaque" }
            LOD 100
    
            Pass
            {
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                // make fog work
                #pragma multi_compile_fog
    
                #include "UnityCG.cginc"
                #include "UnityLightingCommon.cginc"
    
                struct appdata
                {
                    float4 vertex : POSITION;
                    float2 uv : TEXCOORD0;
                    float3 normal:NORMAL;
                };
    
                struct v2f
                {
                    float2 uv : TEXCOORD0;
                    UNITY_FOG_COORDS(1)
                    float4 vertex : SV_POSITION;
                    float3 normalWS : TEXCOORD2;
                };
    
                sampler2D _MainTex;
                float4 _MainTex_ST;
                float _WaveIntensity;
                float _Speed;
                float _Frequency;
                half4 _Color;
                v2f vert (appdata v)
                {
                    v2f o;
                    float4 positionWS = mul(UNITY_MATRIX_M,v.vertex);
                    float distanceSqr = dot(positionWS.xz,positionWS.xz);
                    positionWS.y = _WaveIntensity*rcp(distanceSqr+1) * sin(_Frequency*distanceSqr -_Time.y*_Speed);
                    o.vertex = UnityWorldToClipPos(positionWS);
                    o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                    o.normalWS = UnityObjectToWorldNormal(v.normal);
                    UNITY_TRANSFER_FOG(o,o.vertex);
                    return o;
                }
    
                half4 frag (v2f i) : SV_Target
                {
                    half4 col = tex2D(_MainTex, i.uv)*_Color;
                    float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                    float3 normalWS = normalize(i.normalWS);
                    col.rgb *= _LightColor0.rgb*saturate(dot(normalWS,lightDir));
                    UNITY_APPLY_FOG(i.fogCoord, col);
                    return col;
                }
                ENDCG
            }
        }
    }

    结果如下:

     我们会看到因为只移动了顶点(不要直接用plane那个unity自带的网格,顶点数太少,出来的效果很丑,吼吼吼~),没有调整法线导致的光照一致,如果不是用了一张mainTex可能都看不出来里面有波在运动。所以我们接下来就修正法线。

    法线怎么求呢?我们可以通过求每个点x向的切线和z向的切线,然后通过叉乘构造出法线。那么问题就回到了如何求切线。我们知道切线的斜率其实就是一个函数在某一点的导数,在3维空间中的偏导数。那么我们就开始求偏导数:

      

     代码:

    float distanceSqr = dot(positionWS.xz,positionWS.xz);
                    positionWS.y = _WaveIntensity*rcp(distanceSqr+1) * sin(_Frequency*distanceSqr -_Time.y*_Speed);
                    
                    float3 xTangent = float3(1,0,0);
                    xTangent.y = (2*positionWS.x*(distanceSqr+1)*cos(distanceSqr)-sin(distanceSqr))/((distanceSqr+1)*(distanceSqr+1));
                    float3 zTangent = float3(0,0,1);
                    zTangent.y = (2*positionWS.z*(distanceSqr+1)*cos(distanceSqr)-sin(distanceSqr))/((distanceSqr+1)*(distanceSqr+1));
    
                    o.normalWS =  normalize(cross(zTangent,xTangent));

    效果如下:

    gif图前半部分是法线错误的效果,后半部分是计算法线后的效果。

    但是这个算出来的结果真的正确吗?别忘了我们还在shader里面公开了一些参数,这些参数的变化都会影响法线,也就是我们要把这些变动因素也要计入。

    公式修改为:

    代码修改为:

    float3 xTangent = float3(1,0,0);
                    xTangent.y = (2*_Frequency*positionWS.x*_WaveIntensity*(distanceSqr+1)*cos(distanceSqr)-sin(distanceSqr))/((distanceSqr+1)*(distanceSqr+1));
                    float3 zTangent = float3(0,0,1);
                    zTangent.y = (2*_Frequency*positionWS.z*_WaveIntensity*(distanceSqr+1)*cos(distanceSqr)-sin(distanceSqr))/((distanceSqr+1)*(distanceSqr+1));

    最终结果:

    其实和水纹一点都不像,哈哈哈,主要是正弦波函数是随便一拍脑门拟合的,没去细究真实世界的波函数究竟是什么,但是这里提供这样一个思路,换一个波函数也可以解决~~

    终于写完了,睡午觉~~~

  • 相关阅读:
    javascript命名规范
    angularjs指令参数transclude
    angular中的compile和link函数
    angularjs中的directive scope配置
    sublime text3同时编辑多行
    jquery中on/delegate的原理
    defered,promise回顾
    导航栏滚动到顶部后固定
    angularjs揭秘
    $stateParams
  • 原文地址:https://www.cnblogs.com/shenyibo/p/14158729.html
Copyright © 2020-2023  润新知