본문 바로가기
Unity - Surface Shader

3. Vertex Shader를 사용하여 Toon Shading 해보기

by SimonLee 2023. 11. 14.

만화케릭터를 강조하기 위하여 외곽 효과를 주는 것을 toon shading, cell shding 이라고 합니다.

이번에 해볼 예제는 외곽선 그리기(toon shading) 입니다.

 

외곽선을 그리는 방법에는 림라이트 방법을 사용해도 되지만, 2 pass 렌더링 사용하는 방법도 가능합니다.

2 pass란 동일한 픽셀에 2번 렌더링 한다는 의미 입니다.

렌더링 한다고 해서 반드시 컬러값을 업데이트 하는 것은 아닙니다.

프래그먼트 쉐이더에서 아무일도 하지 않으면 변화가 없어 보입니다.

 

그러면 왜 하느냐? 

백버퍼에 특정 값을 기록하여 진짜 렌더링하는 패스에서 해당 값을 사용하기도 하며,

이번 예제처럼 덧그리기도 할수도 있습니다.

 

그럼 2 pass 렌더링을 해보러 가봅시다.

Vertex shader를 사용한 Toon shading

 

 

원리를 먼저 생각해보면요

사각형이 그려져 있는데, 사각형 보다 약간 큰 사각형이 바로 뒤에 있으면

앞에 있는 사각형이 뒤에 있는 사각형의 가장자리는 가리게 되고, 끝부분만 남게 될 것입니다. 

빨간색 체크된 부분이 외곽선 처럼 보임

 

아래 처럼 2 번 렌더링 합니다.

1) 모델을 약간만 크게 만들어서 뒤에 그립니다.

2) 모델을 원래 크기로 앞에 그립니다.

 

약간만 크게 만드는 방법은 어떻게 할까요 ?

버텍스 셰이더에서 정점을 노말방향 * coeff 값을 더해 주면 됩니다.

void vert(inout appdata_full v) 
{
    v.vertex.xyz = v.vertex.xyz + v.normal.xyz * 0.01;
}

 

뒤에 그리는 방법은? 

컬링 방법을 사용합니다.

프론트 페이스 컬링 (cull front) 을 하게 되면 앞면이 없어지고, 백페이스 컬링(cull back) 을 하면 뒷면이 없어집니다.

보통 렌더링 할때 불투명한 오브젝트의 뒷면은 그리지 않기 때문에 디폴트로 백페이스 컬링이 설정되어 있습니다.

 

예를 들어 A 조금 큰 모델 (전부 검정), B 원래 크기 모델 (원래 색상) 이라고 가정해보면,

A - cull back, B - cull front : 

--> A 렌더링 vertex의 영역이 넓기 때문에 검은색으로 B를 다 가려버립니다.

A - cull front, B - cull back : 

--> A 검은색은 뒷면만 렌더링 되고, B  원래 크기 모델 색상은 앞면에 그려지면서 외곽 효과가 잘 그려집니다.

Shader "Custom/Toon"
{
    Properties
    {
       ......
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }        
        
       	// 1nd pass
        cull back
        CGPROGRAM
        ..........
        ENDCG
		
        // 2nd pass
        cull front
        CGPROGRAM
        .........
        ENDCG
    }
    FallBack "Diffuse"
}

 

첫번째 렌더링은 fragment shader에서 검은색만 리턴하면 되고,

두번째 렌더링은 custom lighting을 적용시켜 봅니다.

 

디퓨즈 텀 음영을 자연스럽게 보간 하지 않고 영역별로 끊어지게 만들고 싶으면

n dot l 조명 값에 따라 범위를 지정하고 대표 음영 값을 넣어주면 됩니다.

float ndotl = dot(s.Normal, lightDir) * 0.5 + 0.5;

if (ndotl > 0.4) 
{
    ndotl = 1;
}
else if (ndotl > 0.2 && ndotl <= 0.4) 
{
    ndotl = 0.7;
}
else 
{
    ndotl = 0.5;
}

 

물론 if문을 쓰지 않고 다른 방법을 써도 됩니다.

3개의 대표 음영을 값을 얻어 내기도 합니다. ( 1 / 3,  2 / 3, 3 / 3 )

ndotl *= 3;
ndotl = ceil(ndotl) / 3;

 

 

전체 코드 입니다.

Shader "Custom/Toon"
{
    Properties
    {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}         
        _BumpMap ("BumpMap", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }        
        cull back

        CGPROGRAM
        #pragma surface surf Nolight Lambert vertex:vert noshadow noambient

        #pragma target 3.0

        void vert(inout appdata_full v) 
        {
            v.vertex.xyz = v.vertex.xyz + v.normal.xyz * 0.01;
        }

        struct Input
        {
            float4 color:Color;
        };

        void surf (Input IN, inout SurfaceOutput o)
        {

        }

        float4 LightingNolight(SurfaceOutput s, float3 lightDir, float atten)
        {
            return float4(0, 0, 0, 1);
        }
        ENDCG

        cull front
        // 2nd pass
        CGPROGRAM
        #pragma surface surf Toon

        #pragma target 3.0
        
        sampler2D _MainTex;
        sampler2D _BumpMap;
        struct Input
        {
            float2 uv_MainTex;
            float2 uv_BumpMap;
        };

        void surf(Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
            o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
        float4 LightingToon (SurfaceOutput s, float3 lightDir, float atten) {
            float ndotl = dot(s.Normal, lightDir) * 0.5 + 0.5;
            if (ndotl > 0.4) 
            {
                ndotl = 1;
            }
            else 
            {
                ndotl = 0.5;
            }
            float4 final;
            final.rgb = s.Albedo * ndotl * _LightColor0.rgb;
            final.a = s.Alpha;
            return final;
        }

        ENDCG
    }
    FallBack "Diffuse"
}

 

'Unity - Surface Shader' 카테고리의 다른 글

5. 알파블렌딩과 컷아웃  (3) 2023.11.20
4. Cubemap을 사용하여 반사매질을 표현해보자.  (0) 2023.11.19
2. Blin - phong Specular + Rim Lighiting  (0) 2023.11.11
1. Hologram  (0) 2023.11.10
시작하면서...  (0) 2023.11.10