简单的勾边算法

   下述代码列出了几种勾边的实现思路,实际游戏中在屏幕空间扩展法线的方案,基本上就够用了:

Shader "Shader/Outline" 
{
    Properties
    {
        _MainTex("MainTex", 2D) = "white" {}
        _Color("Color", Color) = (1, 1, 1, 1)
        _OutLine("OutLine", Float) = 0.1
        _OutLineColor("OutLineColor", Color) = (1, 1, 1, 1)
        _Factor("Factor", Range(0, 1)) = 0.5
    }

    SubShader 
    {
        Tags { "Queue" = "Opaque" }
        Pass
        {
            Cull Front
            ZWrite on
            CGPROGRAM
            
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            
            struct v2f
            {
                float4 pos : SV_POSITION;
                float3 color : COLOR0;
                float2 uv : TEXCOORD0;
            };
            
            sampler2D _MainTex;
            float4 _MainTex_ST;
            float4 _Color;
            float _OutLine;
            float4 _OutLineColor;
            float _Factor;

            v2f vert(appdata_base v)
            {
                /* // 沿法线方向扩展(问题:近粗远细 深度覆盖 发现分离)
                v2f o;
                v.vertex.xyz += v.normal * _OutLine;
                o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                return o;
                */

                /* // 在视空间挤出(问题:法线分离问题)
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

                float3 norm = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);
                float2 offset = TransformViewToProjection(norm.xy);
                o.pos.xy += offset * o.pos.z * _OutLine;
                return o;
                */

                /*
                // 使用顶点位置作为挤出方向(问题:等于从几何中心缩放,不一致)
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

                float3 dir = normalize(v.vertex.xyz);
                dir = mul((float3x3)UNITY_MATRIX_IT_MV, dir);

                float2 offset = TransformViewToProjection(dir.xy);
                o.pos.xy += offset * o.pos.z * _OutLine;
                return o;
                */

                // 在发现和顶点方向间插值
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

                float3 dir = normalize(v.vertex.xyz);
                float3 dir2 = v.normal;
                dir = lerp(dir2, dir, _Factor);

                dir = mul((float3x3)UNITY_MATRIX_IT_MV, dir);

                float2 offset = TransformViewToProjection(dir.xy);
                o.pos.xy += offset * o.pos.z * _OutLine;
                return o;
            }

            half4 frag(v2f i) : COLOR
            {
                return _OutLineColor;
            }
            
            ENDCG
        }
  Pass {...}
}

  通过offset操作,来实现描边的效果:

Shader "Shader/OutLine_Offset" 
{
    Properties
    {
        _MainTex("MainTex", 2D) = "white" {}
        _Color("Color", Color) = (1, 1, 1, 1)
        _OutLine("OutLine", Float) = 0.1
    }
    SubShader 
    {
        Tags { "Queue" = "Opaque" }
        Pass
        {
            Cull Front
            Offset -1,-1
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            struct v2f
            {
                float4 pos : SV_POSITION;
            };
            float4 _OutLine;
            v2f vert(appdata_base v)
            {
                v2f o;
                o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
                return o;
            }
            half4 frag(v2f i) : COLOR
            {
                return _OutLine;
            }
            ENDCG
        }
        Pass
        {
            Offset 2,-1
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            struct v2f
            {
                float4 pos : SV_POSITION;
                float3 color : COLOR0;
                float2 uv : TEXCOORD0;
            };
            sampler2D _MainTex;
            float4 _MainTex_ST;
            float4 _Color;
            v2f vert(appdata_base v)
            {
                v2f o;
                o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                return o;
            }
            half4 frag(v2f i) : COLOR
            {
                float4 mainColor = tex2D(_MainTex, i.uv);
                float4 finalColor = mainColor * _Color;
                return finalColor;
            }
            ENDCG
        }
    } 
    FallBack "VertexLit"
}

  这个方法不是太实用,只是一种实现思路。

  使用的方案是UnityPro提供的AssetsPackage中现成的ToonShader。

  http://wiki.unity3d.com/index.php/Silhouette-Outlined_Diffuse

posted @ 2017-02-27 21:05  斯芬克斯  阅读(1315)  评论(0编辑  收藏  举报