前言

继透明贴图之后,今天再来说一下UV动态效果之UV流动和UV扰动的实现方式。

所谓的UV动态指的是UV随着时间进行特定的变化,因此在shader中需要获取游戏运行的时间,将时间数值进行一定的处理后作为偏移量加到UV上,再用偏移后的UV对Noise贴图进行采样,最后叠加到颜色贴图上。

在Unity中获取到的时间参数_Time,为float4类型,其中x、y、z、w四个分量分别代表t/20、 t、 t*2、 t*3,可根据需要选择使用。

 

UV流动

UV流动效果也被称为GhostFlow效果,如前面所说,需要对UV根据时间进行某个方向的偏移,再对Noise贴图进行采样。

Unity中的shader代码如下:

// shader的路径和名称
Shader "Shader Forge/UVflow" {
    // 暴露属性到面板
    Properties {
        _MainTex("Main Texture", 2d) = "white"{}
        _Opacity("Opacity", range(0.0, 1.0)) = 0.5
        _NoiseTex("Noise Texture", 2D) = "white"{}
        _NoiseMul("Noise Multiply", range(0.0,5.0)) = 1.0
        _FlowSpeed("Flow Speed", range(-10.0,10.0)) = 5.0
    }
    SubShader {
        Tags {
            "Queue"="Transparent"                //调整渲染顺序
            "RenderType"="Transparent"           //透贴
            "ForceNoShadowCasting"="True"        //关闭阴影投射
            "IgnoreProjector"="True"             //不响应投射器
        }
        Pass {
            Name "FORWARD"
            Tags {
                "LightMode"="ForwardBase"
            }
            Blend One OneMinusSrcAlpha           //修改混合模式
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #pragma multi_compile_fwdbase_fullshadows
            #pragma target 3.0
            
            // 声明变量
            uniform sampler2D _MainTex;
            uniform half _Opacity;
            uniform sampler2D _NoiseTex; uniform float4 _NoiseTex_ST;
            uniform half _NoiseMul;
            uniform half _FlowSpeed;
            
            // 输入结构
            struct VertexInput {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };
            
            // 输出结构
            struct VertexOutput {
                float4 pos : SV_POSITION;
                float2 uv0 : TEXCOORD0;   //采样MainTex
                float2 uv1 : TEXCOORD1;   //采样NoiseTex
            };
            
            // 顶点shader
            VertexOutput vert (VertexInput v) {
                VertexOutput o = (VertexOutput)0;
                o.pos = UnityObjectToClipPos( v.vertex );
                o.uv0 = v.uv;
                o.uv1 = TRANSFORM_TEX(v.uv, _NoiseTex);    //UV1支持TillingOffset
                o.uv1.y = o.uv1.y + frac(-_Time.x * _FlowSpeed);   //V轴方向随时间流动,frac()取余数
                return o;
            }
            
            // 像素shader
            float4 frag(VertexOutput i) : COLOR {
                half4 var_MainTex = tex2D(_MainTex, i.uv0);
                half4 var_NoiseTex = tex2D(_NoiseTex, i.uv1);

                half3 finalRGB = var_MainTex.rgb;
                half noise = lerp(1.0, var_NoiseTex * 2.0, _NoiseMul);   //remap范围
                noise = max(0.0, noise);                                 //截掉负值
                half opacity = var_MainTex.a * _Opacity * noise;
                
                return half4(finalRGB * opacity, opacity);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

效果如下:

如果感觉流动的效果比较呆板,纹理重复率较高,可以通过两张Noise贴图分别用不同的速度叠加成最终Noise贴图再使用的方式得到比较自然的Noise效果。

 

UV扰动

UV扰动也被称为GhostWarp,实现思路是在UV流动的基础上对UV进行扰动,先根据时间对UV进行某个方向的偏移,之后UV通过Warp贴图的R、G通道叠加上U、V轴向的扰动偏移量,最后也是再将偏移后的UV对Noise贴图进行采样。

而且为了减少采样贴图的数量,可以把Noise贴图整合到Warp贴图的B通道中使用。

// shader的路径和名称
Shader "Shader Forge/UVwarp" {
    // 暴露属性到面板
    Properties {
        _MainTex("Main Texture", 2d) = "white"{}
        _Opacity("Opacity", range(0.0, 1.0)) = 0.5
        _WarpTex("Warp Texture", 2D) = "white"{}    //rg通道控制UV方向的扭曲,b通道为noise,这样可以少采样一张贴图
        _WarpMul("Warp Multiply", range(0.0, 5.0)) = 1.0
        _NoiseMul("Noise Multiply", range(0.0,5.0)) = 1.0
        _FlowSpeed("Flow Speed", range(-10.0,10.0)) = 5.0
    }
    SubShader {
        Tags {
            "Queue"="Transparent"                //调整渲染顺序
            "RenderType"="Transparent"           //透贴
            "ForceNoShadowCasting"="True"        //关闭阴影投射
            "IgnoreProjector"="True"             //不响应投射器
        }
        Pass {
            Name "FORWARD"
            Tags {
                "LightMode"="ForwardBase"
            }
            Blend One OneMinusSrcAlpha           //修改混合模式
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #pragma multi_compile_fwdbase_fullshadows
            #pragma target 3.0
            
            // 声明变量
            uniform sampler2D _MainTex;
            uniform half _Opacity;
            uniform sampler2D _WarpTex; uniform float4 _WarpTex_ST;
            uniform half _WarpMul;
            uniform half _NoiseMul;
            uniform half _FlowSpeed;
            
            // 输入结构
            struct VertexInput {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };
            
            // 输出结构
            struct VertexOutput {
                float4 pos : SV_POSITION;
                float2 uv0 : TEXCOORD0;   //采样MainTex
                float2 uv1 : TEXCOORD1;   //采样WarpTex
            };
            
            // 顶点shader
            VertexOutput vert (VertexInput v) {
                VertexOutput o = (VertexOutput)0;
                o.pos = UnityObjectToClipPos( v.vertex );
                o.uv0 = v.uv;
                o.uv1 = TRANSFORM_TEX(v.uv, _WarpTex);    //UV1支持TillingOffset
                o.uv1.y = o.uv1.y + frac(-_Time.x * _FlowSpeed);   //V轴方向随时间流动,frac()取余数
                return o;
            }
            
            // 像素shader
            float4 frag(VertexOutput i) : COLOR {
                half3 var_WarpTex = tex2D(_WarpTex, i.uv1).rgb;
                float2 uvBias = (var_WarpTex.rg - 0.5) * _WarpMul;  //计算UV偏移值,让扰动区间在-0.5~0.5
                float2 uv0 = i.uv0 + uvBias;                        //应用UV偏移值
                half4 var_MainTex = tex2D(_MainTex, uv0);           //使用偏移UV采样MainTex

                half3 finalRGB = var_MainTex.rgb;
                half noise = lerp(1.0, var_WarpTex.b * 2.0, _NoiseMul);    //remap范围
                noise = max(0.0, noise);                                 //截掉负值
                half opacity = var_MainTex.a * _Opacity * noise;
                
                return half4(finalRGB * opacity, opacity);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

效果如下:

 

本文同样参考自B站庄懂的技术美术入门课,感谢。

 


对待生命,你不妨大胆一点,
因为我们终究要失去它。

《不合时宜的沉思》
——尼采