水面漩涡改进版

最后都变了- 提交于 2020-10-24 10:22:42

针对之前的水面漩涡效果做了些改进。之前的漩涡效果参考:

就是爱折腾:Unity Shader实现一个水面漩涡的效果zhuanlan.zhihu.com图标

主要是加了透明折射的效果,看起来更接近真实的漩涡。如图:

修改版使用了顶点片元着色器,也添加了曲面细分。利用GrabPass实现水的折射效果。

之前实现的漩涡效果是没有考虑这种折射效果的。所以采用了比较简单的方式,直接给水加个光照就完事了。后面想想,还是想做的更真实些。于是我开始考虑使用GrabPass这种方式,但是貌似Surface Shader中不怎么好实现这个效果(可能也是我水平有限,如果有大佬知道的话,望指出),所以我把前面的surface shader改写成了基于顶点片元的。

改完后,遇到了第一个问题便是曲面细分效果没了,毕竟之前用surface shader弄起来是so easy啊,但现在是顶点片元了,刚开始还是一脸懵逼。后面我查了些资料,摸索出了解决办法:

就是爱折腾:Unity曲面细分的原理与应用2zhuanlan.zhihu.com图标

看现实当中的水,漩涡处的折射效果(扭曲)是比其它地方强的。这个效果,我直接简单的使用网格的局部空间的顶点位置来设置(这里肯定有更好的办法,不过我为了偷懒):顶点偏移越大,扭曲越厉害。

通过这些修改,上面的效果就完成了!下面是完整代码:

Shader "Water/VotexExtendPure"
{
    Properties
    {
        _BaseColor ("BaseColor", Color) = (1,1,1,1)
        [Normal]_NormalTex ("Normal", 2D) = "white" {}
        _FoamTex("FoamTex",2D) = "white"{}
        _FoamStrengh("FoamStrengh",Range(0,1)) = 0.2
        _XSpeed("XSpeed",Range(0,1)) = 0.2
        _YSpeed("YSpeed",Range(0,1)) = 0.2
        _Gloss("Gloss",Range(0,0.1)) = 0.1
        _SpecStrengh("SpecStrengh",Range(0,0.01)) = 1
        _RefractionAmount("RefractAmount",Range(0,100)) = 5
        _TessellateAmount("TessellateAmount",float) = 4

        _Votex_Para("Center",vector) = (0.5,0.5,0.2,0)  //x,y:漩涡中心  z:漩涡半径
        _Votex_depth("Votex Depth",Range(0,4)) = 0.1    //漩涡深度
        _Votex_distortAmount("Votex_distortAmount",Range(0,20)) = 2 //扭曲力度

        _Shape_distortAmount("Shape_distortAmount",Range(0,1)) = 0.5//漩涡塑形

    }

    CGINCLUDE
    
    #include "Assets/Shaders/1UPUtility.cginc"
    #include "Assets/Shaders/1UPMath.cginc"

    sampler2D _NormalTex,_FoamTex;
    half4 _NormalTex_ST,_FoamTex_ST;
    float _XSpeed,_YSpeed,_Gloss,_SpecStrengh,_TessellateAmount,_FoamStrengh;
    fixed4 _BaseColor,_Votex_Para;
    float _Votex_depth,_Votex_distortAmount,_Shape_distortAmount,_RefractionAmount;
    sampler2D _GrabTexture;
    float4 _GrabTexture_TexelSize;

    
    struct v2f
    {
        float4 uv : TEXCOORD0;
        UNITY_FOG_COORDS(1)
        float4 vertex : SV_POSITION;
        float3 normal:NORMAL;
        float4 screenPos:TEXCOORD2;
        float4 localPosition:TEXCOORD3;
    };

    v2f vert (appdata_base v)
    {
        v2f o;

        float3 center = float3(_Votex_Para.x,0,_Votex_Para.y);
        float radius = _Votex_Para.z;
        float dis = distance(v.vertex.xyz,center);
        float s = saturate((radius - dis)) / radius;
        float ss = s * s;
        //越靠近漩涡中心,顶点偏移量越大。ss = s*s,能够产生漩涡的弧形
        float offsetN = _Votex_depth * ss;
        
        //进行顶点的偏移
        v.vertex.xyz = v.vertex.xyz - v.normal * offsetN;

        //扭曲(三步:将漩涡中心移到零点->绕y轴旋转->将漩涡中心移回原处)
        float3 tempVertex = v.vertex.xyz -center;
        tempVertex += ss * _Shape_distortAmount;
        tempVertex = mul(MATRIX_3_Y(ss * _Votex_distortAmount),tempVertex);
        v.vertex.xyz = tempVertex+center;

        o.uv.xy = v.texcoord.xy * _NormalTex_ST.xy + _NormalTex_ST.zw;
        o.uv.zw = v.texcoord.xy * _FoamTex_ST.xy + _FoamTex_ST.zw;
        o.localPosition = v.vertex;
        o.vertex = UnityObjectToClipPos(v.vertex);
        o.normal = v.normal;
        o.screenPos = ComputeGrabScreenPos(o.vertex);

        return o;

    }

    fixed4 frag (v2f i) : SV_Target
    {
        fixed4 mainColor = _BaseColor;
        float4 worldPosition = mul(unity_ObjectToWorld,i.localPosition);

        float3 n1 = UnpackNormal(tex2D(_NormalTex,i.uv.xy + float2(_XSpeed,_YSpeed) * 0.1 * _Time.y));
        float3 n2 = UnpackNormal(tex2D(_NormalTex,i.uv.xy - float2(_XSpeed,_YSpeed) * 0.1 * _Time.y));
        float3 offset = BlendNormalRNM(n1,n2);

        //泡沫
        fixed4 foamColor = tex2D(_FoamTex,i.uv.zw);
        mainColor += foamColor * _FoamStrengh;

        //透明
        i.screenPos.xy /= i.screenPos.w;
        //折射(漩涡处折射加大)
        i.screenPos.xy += offset.xy * (_RefractionAmount + abs(i.localPosition.y) * 400) * _GrabTexture_TexelSize.xy;
        fixed4 backColor = tex2D(_GrabTexture,i.screenPos.xy);
        mainColor *= backColor;

        //光照(高光部分没有法线只是随便加了以下,偷懒)
        half3 worldViewDir = normalize(WorldSpaceViewDir(worldPosition));
        half3 worldLightDir = normalize(WorldSpaceLightDir(worldPosition));
        half3 objectWorldNormal = UnityObjectToWorldNormal(i.normal);
        half3 specular = Highlights(worldPosition.xyz,_Gloss,normalize(objectWorldNormal - offset),worldViewDir,worldLightDir);
        mainColor.rgb += specular * _SpecStrengh * (0.05 + abs(i.localPosition.y) * 400);

        // mainColor.rgb = specular;
        return mainColor;
    }

    //曲面细分
    // tessellation vertex shader
    struct InternalTessInterp_appdata_base {
        float4 vertex : INTERNALTESSPOS;
        float3 normal : NORMAL;
        float4 texcoord : TEXCOORD0;
    };
    InternalTessInterp_appdata_base tessvert_surf (appdata_base v) {
        InternalTessInterp_appdata_base o;
        o.vertex = v.vertex;
        o.normal = v.normal;
        o.texcoord = v.texcoord;
        return o;
    }

    // tessellation hull constant shader
    UnityTessellationFactors hsconst_surf (InputPatch<InternalTessInterp_appdata_base,3> v) {
        UnityTessellationFactors o;
        float4 tf;
        tf = _TessellateAmount;
        o.edge[0] = tf.x; o.edge[1] = tf.y; o.edge[2] = tf.z; o.inside = tf.w;
        return o;
    }

    // tessellation hull shader
    [UNITY_domain("tri")]
    [UNITY_partitioning("fractional_odd")]
    [UNITY_outputtopology("triangle_cw")]
    [UNITY_patchconstantfunc("hsconst_surf")]
    [UNITY_outputcontrolpoints(3)]
    InternalTessInterp_appdata_base hs_surf (InputPatch<InternalTessInterp_appdata_base,3> v, uint id : SV_OutputControlPointID) {
        return v[id];
    }

    // tessellation domain shader
    [UNITY_domain("tri")]
    v2f ds_surf (UnityTessellationFactors tessFactors, const OutputPatch<InternalTessInterp_appdata_base,3> vi, float3 bary : SV_DomainLocation) {
        appdata_base v;UNITY_INITIALIZE_OUTPUT(appdata_base,v);
        v.vertex = vi[0].vertex*bary.x + vi[1].vertex*bary.y + vi[2].vertex*bary.z;
        v.normal = vi[0].normal*bary.x + vi[1].normal*bary.y + vi[2].normal*bary.z;
        v.texcoord = vi[0].texcoord*bary.x + vi[1].texcoord*bary.y + vi[2].texcoord*bary.z;
        v2f o = vert (v);
        return o;
    }


    ENDCG

    SubShader
    {
        Tags { "RenderType"="Transparent" "Queue"="Transparent" }
        LOD 100
        Blend SrcAlpha OneMinusSrcAlpha

        GrabPass{
            "_GrabTexture"
        }

        Pass
        {
            CGPROGRAM
            #pragma vertex tessvert_surf
            #pragma fragment frag
            #pragma hull hs_surf
            #pragma domain ds_surf
            #pragma target 5.0
            // make fog work
            #pragma multi_compile_fog
            #pragma nodynlightmap nolightmap

            #include "UnityCG.cginc"

            ENDCG
        }
    }
}

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!