【Shader基础】UnityShader 透视扫描效果

一、shader效果原理分析(ASE实现) 

透视扫描效果可分为三个部分:一是主体半透明,二是边缘自发光,三是上下扫光效果。

先用ASE实现,接着用代码复现。(ASE插件下载,点击此处)

1.半透明效果

半透明效果只需要在ASE编辑面板左栏,点击几个选项即可。(详细参数看图片红框)

2.边缘自发光

要做到边缘自发光,最直接的方式就是使用菲涅尔反射系数,在UnityShader中,我们常用单位化的顶点法线N点乘视线向量V来模拟(单位化的两个向量点乘的结果,是夹角的cos余弦值)。

WorldNormal代表顶点法线,ViewDir代表视线向量

θ从0-90度的过程中,cosθ由1-0,所以我们用(1-N·V)来模拟边缘自发光效果(与视线夹角越大的地方越亮)。

cos曲线
Cos(x)曲线图,便于理解
onemius代表(1-N·V)

得到菲涅尔系数后(1-N·V)后,我们将其作为一个遮罩的效果,黑色部分显示主体颜色,白色部分显示边缘颜色。

使用lerp节点,A节点插入主体颜色,B节点插入边缘颜色(乘以一个浮点数,控制边缘亮度),Alpha节点插入上面得到的菲涅尔系数(同时使用smoothstep平滑阶梯函数控制系数大小)。

加上主体纹理颜色,在边缘发光的基础上,在加一点纹理。

以上效果我们统称为边缘遮罩rim_mask

3. 扫光效果

扫光效果的话,只是在实现了边缘自发光和半透明效果的基础上,加上一层上下/左右平移的高光纹理即可。

首先找到得到世界空间下,模型顶点相对于轴心点的xy值。

接着使用Time节点并乘以平移速度FlowSpeed,加上上图中得到的xy,传入高光纹理中的UV节点中。

红框中的参数需要改为变量,公开在材质编辑面板上

上述的边缘自发光和扫光效果,可以输出为一个节点。

最后整理为几个参数节点,分别输入材质的发光节点emission和透明通道opacity。

二、Shader代码

1.半透明效果

在Shader代码的subshader代码块中输入红框内几行。

2.边缘自发光效果

在片元函数frag中计算菲涅尔色值。

根据上述代码,我们可以看出,结构体v2f中需要包含法线向量和顶点坐标。

3.扫光效果

完整代码如下:

Shader "MyShader/Scan"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _RimMin("_RimMin",Range(-1,1)) = 0.0
        _RimMax("_RimMax",Range(0,2)) = 1.0
        _TexPow("_TexPow",Float) = 5.0
        _InnerColor("_InnerColor",Color) = (0,0,0,0)
        _RimColor("_RimColor",Color) = (1,1,1,1)
        _RimIntensity("_RimIntensity",Float) = 1.0
        _FlowTex("_FlowTex",2D) = "white" {}
        _FlowTilling("_FlowTilling",Vector) = (1,1,0,0)
        _FlowSpeed("_FlowSpeed",Vector) = (0,0,0,0)
        _FlowIntensity("_FlowIntensity",Range(0,1)) = 0.5
        _InnerAlpha("_InnerAlpha",Range(0,1)) = 1.0
    }
    SubShader
    {
        Tags { "Quene"="Transparent" }
        LOD 100
        Pass
        {
            ZWrite Off
            Blend SrcAlpha One
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 texcoord : TEXCOORD0;
                float3 normal:NORMAL;
            };

            struct v2f
            {
                float2 uv:TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
                float3 normal_world:NORMAL;
                float3 pos_world:TEXCOORD1;
                float3 pivot_world:TEXCOORD2;
            };

            sampler2D _MainTex;
            sampler2D _FlowTex;
            float4 _MainTex_ST;
            float _RimMax;
            float _RimMin;
            float _TexPow;
            float4 _InnerColor;
            float4 _RimColor;
            float _RimIntensity;
            float4 _FlowTilling;
            float4 _FlowSpeed;
            float _FlowIntensity;
            float _InnerAlpha;            

            v2f vert (appdata v)
            {
                v2f f;
                f.vertex = UnityObjectToClipPos(v.vertex);
                f.uv = v.texcoord;
                f.normal_world = mul(float4(v.normal,0.0),unity_WorldToObject);     //获取世界空间的法线
                f.pos_world = mul(unity_ObjectToWorld,v.vertex).xyz; //获取世界空间的顶点坐标

                f.pivot_world = mul(unity_ObjectToWorld,float4(0.0,0.0,0.0,1.0)).xyz;  //获取世界空间的轴心坐标 

                return f;
            }

            fixed4 frag (v2f f) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, f.uv);
                
                fixed3 normal_world = normalize(f.normal_world);             //单位化的顶点法线向量
                fixed3 view_world = normalize(_WorldSpaceCameraPos.xyz - f.pos_world);        //单位化的视线向量
                fixed NV = saturate(dot(normal_world,view_world));            //顶点法线与视线向量点乘
                fixed fresnel = 1.0 - NV;                     
                fresnel = smoothstep(_RimMin,_RimMax,fresnel);  //获取平滑过渡的菲涅尔色值

                half emiss = tex2D(_MainTex,f.uv).r;    //获取初始纹理的色值
                emiss = pow(emiss,_TexPow);

                half final_fresnel = saturate(emiss + fresnel);   //初始色值加上菲涅尔透光色值
                half3 rim_color = lerp(_InnerColor.xyz,(_RimColor.xyz*_RimIntensity),final_fresnel);    //得到边缘发光效果

                half2 uv_flow = (f.pos_world.xy - f.pivot_world.xy)* _FlowTilling.xy;    //计算扫光的UV
                uv_flow = uv_flow + _Time.y * _FlowSpeed.xy;
                
                half4 flow_color = tex2D(_FlowTex,uv_flow)*_FlowIntensity;     //将扫光UV传入扫光纹理

                half3 final_color = flow_color.rgb + rim_color;          
                half final_alpha = (flow_color.a + final_fresnel)*_InnerAlpha;      //最终色值乘以一个自定义的alpha参数

                return float4(final_color,final_alpha);
            }
            ENDCG
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿木呀

创作不易,感谢鼓励,嘻嘻☺

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值