본문 바로가기
SHADER

유니티 셰이더 - Plane에서 아웃라인(outline)을 이쁘게 만들기 위한 고민..

by tartist 2016. 8. 23.

유니티의 아웃라인 셰이더는 구글에 돌아다니는 레퍼런스가 많아 손쉽게 구현이 가능합니다.

하지만, 자세하게 여러가지를 따지다 보면 불편한 사항이나 제약이 많이 뒤따른다는 것을 쉽게 발견할 수 있습니다.

 

 

일반적인 공식으로 알려져있는 아래 수식대로 셰이더를 만들어 아래 오브젝트에 연결해 보면..

 

        v2f o;
        o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
        fixed3 norm   = mul ((fixed3x3)UNITY_MATRIX_MVP, v.normal);
        fixed2 offset = TransformViewToProjection(norm.xyz);
     
        o.pos.xy += offset * o.pos.z * _Outline;

        o.pos.z += 0.01 * _Outline;


        o.color = _OutlineColor;
        return o;

 

 

 

3D형태의 메쉬를 가진 오브젝트에는 잘 적용되지만 플랜 상태의 오브젝는 제대로 출려되지 않는 문제가 발생합니다. 아래 그림 참조

 

 

 

이 문제를 해결하기 위해서는 버텍스의 노멀 값 뿐 아니라 사이즈자체도 커지도록 한번 더 계산해 주는 수식을 넣어줘야 합니다.

 

fixed3 norm   = mul ((fixed3x3)UNITY_MATRIX_MVP, v.normal); //요 수식에서

 

fixed3 norm   = mul ((fixed3x3)UNITY_MATRIX_MVP, v.vertex + v.normal); //버텍스 값이 더해지도록 수식을 추가해 준다.

 

 

이렇게해도 제대로 출력되지 않을땐 아웃라인 패스 부분에 Cull back 을 추가해 줍니다.

 

 

 

그러면 아래 그림처럼 플랜에서도 정상적인 아웃라인을 만들어 지게 됩니다.

 

 

 

 

하지만 이 셰이더를 사용할 때 주의해야할 점이 있는데 바로 배칭이 안된다는 것입니다.

 

스태틱 배칭을 키고나면 버텍스 연산이 다르게 되어 아래그림처럼 엉뚱한 형태로 출력되게 됩니다.

 

 

 

위 셰이더를 사용하고자 할 때는 메터리얼을 따로 만들어 관리하는 것이 최적화에 도움이 될겁니다.

 

 

 

아래는 위 오브젝트의 아웃라인을 구현한 셰이더 코드입니다.

 

{
    Properties {
        _MainColor ("Main Color"Color) = (.5,.5,.5,1)
        _OutlineColor ("Outline Color"Color) = (0,0,0,1)

        _Outline ("Outline width"Range (010)) = 1
        _MainTex ("Base (RGB)"2D) = "white" { }
        _Rim ("Rim"Float) = 0.0
        _scale("Z Position(Only Plane)"FLoat) = 1.0

        //Sorting
        _ZTest ("ZTest Less = 4 / Always = 6 "FLoat) = 1
    }
 
    CGINCLUDE
    #include "UnityCG.cginc"
    #pragma target 3.0

    struct v2f {
        fixed4 pos : SV_POSITION;
        fixed4 color : COLOR;

    };
     
    uniform fixed _Outline;
    uniform fixed4 _OutlineColor;
    uniform fixed _Rim;
    uniform fixed _scale;
     
    v2f vert(appdata_base v) {
        
        v2f o;
        o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
        v.vertex.xyz += v.normal.xyz * _scale;
        fixed3 norm   = mul ((fixed3x3)UNITY_MATRIX_IT_MV, v.normal + v.vertex * _Outline);
        fixed2 offset = TransformViewToProjection(norm.xyz);
     
        o.pos.xy += (offset * o.pos.z * _Outline) * 0.001;

        o.pos.z += 0.01 * _Outline;


        o.color = _OutlineColor;
        return o;
    }
    ENDCG
 
    SubShader {
        Tags {"Queue"="Geometry+200" "IgnoreProjector"="True" "RenderType"="Opaque"}
         Blend One Zero
//         ZTest NotEqual
//         ZWrite on
        Pass
        {
            ColorMask 0
        }
//
        Pass
        {
            Name "OUTLINE"
            Tags { "LightMode" = "Always" }
            Cull off
            Blend SrcColor OneMinusSrcColor
            ZTest Always
//            ZWrite off

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            fixed4 frag( v2f i ) : COLOR
            {
                fixed4 c = i.color;

                return i.color;
            }
            ENDCG
        }         
             
            CGPROGRAM
            #pragma surface surf Lambert

            struct Input {
                fixed2 uv_MainTex;
                fixed3 worldPos;
            };


            sampler2D _MainTex;
            //sampler2D _BumpMap;
            uniform fixed3 _MainColor;
            void surf(Input IN, inout SurfaceOutput o) {
//                IN.worldPOS.z;
                o.Emission = tex2D(_MainTex, IN.uv_MainTex).rgb * _MainColor * _Rim;
            //    o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
            }
            ENDCG
 
    }

}