Shader实例:边缘发光和描边

效果图:

1.边缘发光
思路:用视方向和法线方向点乘,模型越边缘的地方,它的法线和视方向越接近90度。点乘越接近0
那么用 1-减去上面点乘的结果,来作为颜色分量,来反映边缘颜色强弱。

Shader "Custom/OutLine1" 
{
    Properties
    {
        _MainTex("main tex",2D) = "black"{}
        _RimColor("rim color",Color) = (1,1,1,1)//边缘颜色
        _RimPower ("rim power",range(1,10)) = 2//边缘强度
    }
 
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include"UnityCG.cginc"
 
            struct v2f
            {
                float4 vertex:POSITION;
                float4 uv:TEXCOORD0;
                float4 NdotV:COLOR;
            };
 
            sampler2D _MainTex;
            float4 _RimColor;
            float _RimPower;
 
            v2f vert(appdata_base v)
            {
                v2f o;
                o.vertex = mul(UNITY_MATRIX_MVP,v.vertex);
                o.uv = v.texcoord;
                float3 V = WorldSpaceViewDir(v.vertex);
                V = mul(_World2Object,V);//视方向从世界到模型坐标系的转换
                o.NdotV.x = saturate(dot(v.normal,normalize(V)));//必须在同一坐标系才能正确做点乘运算
                return o;
            }
 
            half4 frag(v2f IN):COLOR
            {
                half4 c = tex2D(_MainTex,IN.uv);
                //用视方向和法线方向做点乘,越边缘的地方,法线和视方向越接近90度,点乘越接近0.
                //用(1- 上面点乘的结果)*颜色,来反映边缘颜色情况
                c.rgb += pow((1-IN.NdotV.x) ,_RimPower)* _RimColor.rgb;
                return c;
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

2.描边(一)
思路:两次渲染,第一次渲染背面,剔除正面,把模型顶点沿法线方向扩伸一定距离(用来表现描边的粗细)
第二次渲染,渲染正面,剔除背面

Shader "Custom/OutLine2"
{
    Properties
    {
        _MainTex("main tex",2D) = ""{}
        _Factor("factor",Range(0,0.1)) = 0.01//描边粗细因子
        _OutLineColor("outline color",Color) = (0,0,0,1)//描边颜色
    }
 
    SubShader 
    {
        Pass
        {
            Cull Front //剔除前面
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
 
            struct v2f
            {
                float4 vertex :POSITION;
            };
 
            float _Factor;
            half4 _OutLineColor;
 
            v2f vert(appdata_full v)
            {
                v2f o;
                //v.vertex.xyz += v.normal * _Factor;
                //o.vertex = mul(UNITY_MATRIX_MVP,v.vertex);
 
                //变换到视坐标空间下,再对顶点沿法线方向进行扩展
                float4 view_vertex = mul(UNITY_MATRIX_MV,v.vertex);
                float3 view_normal = mul(UNITY_MATRIX_IT_MV,v.normal);
                view_vertex.xyz += normalize(view_normal) * _Factor; //记得normalize
                o.vertex = mul(UNITY_MATRIX_P,view_vertex);
                return o;
            }
 
            half4 frag(v2f IN):COLOR
            {
                //return half4(0,0,0,1);
                return _OutLineColor;
            }
            ENDCG
        }
 
        Pass
        {
            Cull Back //剔除后面
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
 
            struct v2f
            {
                float4 vertex :POSITION;
                float4 uv:TEXCOORD0;
            };
 
            sampler2D _MainTex;
 
            v2f vert(appdata_full v)
            {
                v2f o;
                o.vertex = mul(UNITY_MATRIX_MVP,v.vertex);
                o.uv = v.texcoord;
                return o;
            }
 
            half4 frag(v2f IN) :COLOR
            {
                //return half4(1,1,1,1);
                half4 c = tex2D(_MainTex,IN.uv);
                return c;
            }
            ENDCG
        }
    } 
    FallBack "Diffuse"
}

3.描边(二)
思路:两次渲染。第一次渲染背面,剔除正面。利用Offset指令,向离摄像机更近的方式偏移
第二次正常渲染物体(不剔除),(或者可以渲染正面,剔除背面)。

Offset:深度偏移
Offset Factor,Units
Factor参数表示 Z缩放的最大斜率的值。
Units参数表示可分辨的最小深度缓冲区的值。

我的理解:深度偏移,Unity是左手坐标系,Z的正方向是朝屏幕里面。
沿Z的负方向偏移就是离摄像机更近,
沿Z的正方向偏移就是离摄像机更远。

作用:可以强制使位于同一位置上的两个几合体中的一个几何体绘制在另一个的上层
比如:几何体A的Z值比几何体B更远,此时B是在A的上层。
但是,给A设置了Offset 往Z的负方向偏移,此时A比B更近了,A就在B上层了。
在深度测试(Z test)的时候,会去比较Z值,近的会绘制在远的上面。

所以上面的思路,要么第一次渲染背面,往Z的负方向偏移
要么第二次渲染的,往Z的正方向偏移,都是可以的。

Shader "Custom/OutLine3" 
{
    Properties
    {
        _MainTex("main tex", 2D) = ""{ }
        _OutLineColor("outline color",Color) = (0,0,0,1)//描边颜色
    }
 
    SubShader
    {
        //描边
        pass
        {
            Cull Front
            Offset -5,-1 //深度偏移
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
 
            sampler2D _MainTex;
            half4 _OutLineColor;
 
            struct v2f
            {
                float4  pos : SV_POSITION;
            };
 
            v2f vert(appdata_base v)
            {
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
                return o;
            }
 
            float4 frag(v2f i) : COLOR
            {
                return _OutLineColor;
            }
            ENDCG
        }
 
        //正常渲染物体
        pass
        {
            //Cull Back
            //Offset 5,-1
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
 
            sampler2D _MainTex;
            float4 _MainTex_ST;
 
            struct v2f
            {
                float4  pos : SV_POSITION;
                float2  uv : TEXCOORD0;
            };
 
            v2f vert(appdata_base v)
            {
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
                return o;
            }
 
            float4 frag(v2f i) : COLOR
            {
                float4 c = tex2D(_MainTex,i.uv);
                return c;
            }
            ENDCG
        }
    }
}
posted @ 2016-05-13 15:59  Joe师傅  阅读(38548)  评论(0编辑  收藏  举报