유니티의 3가지 쉐이더 제작 방법 by 김윤정

기본적으로 유니티는 3가지의 쉐이더 제작 방법을 지원한다고 볼 수 있습니다.
그걸 다시 크게 두 개로 나누면

1. Script로 제작하는 방법
2. CG로 제작하는 방법

이라고 볼 수 있겠는데요



1. Script로 제작하는 방법

가장 심플하고 호환성이 높은 방법이라고 할 수 있겠습니다.
방식은 기존의 hlsl 같은 렝귀지와 전혀 다른 방식으로 제작되어 지며, 자체적 언어로 제작됩니다.
아마도 각종 플렛폼에 공용으로 사용 가능하게끔 엔진단계에서 컴파일 해 주기 위해 그런 것 같습니다만.

방식은 대략 이런 방식으로 제작됩니다.


Shader "쉐이더 이름" {
Properties {
  _Color ("Main Color", Color) = (1,1,1,1)
  _MainTex ("Base (RGB)", 2D) = "white" {}
               }

SubShader {
    Pass {
     Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
        Material { }
  Blend SrcAlpha OneMinusSrcAlpha
        Lighting Off
        Zwrite on
        Fog { Mode Off }
        SetTexture [_MainTex] {
   constantColor [_Color]
            Combine texture* constant , texture
        }
    }
}
}

이 방식은 호환성이 높고 굉장히 간단해서 상대적으로 제작하기 쉽지만,
채널당 이미지를 다루기도 쉽지 않고 아주 기본적인 것 외에 조금 복잡한 수준의 연산을 불가능하다는 단점이 있습니다.
라이팅도 건드릴 수 없구요.


2. CG로 제작하는 방법

스크립트로 제작하는 방식이 기능적 제한이 너무 많다보니, 결국 CG로 제작하는 방법이 사용됩니다. CG는 hlsl과 유사한 언어이며, 엔비댜에서 제작되었습니다. (맞던가?)

유니티에서는 이 CG로 짜는 쉐이더가 2개로 다시 갈라질 수 있는데요.
- fragment (hlsl에서는 픽셀쉐이더) 로 짜는 방법
- surface 로 짜는 방법
으로 갈라질 수 있습니다.

이 방법들은 스크립트로 짜는 쉐이더보다 훨씬 무궁무진한 기법을 만들 수 있다는 장점이 있습니다만, 동시에 제약도 가지고 있습니다.


2.1.  fragment로 짜는 법

대략 아래와 같은 방식으로 이루어집니다. 기존의 스크립트 쉐이더와 같은 구조에, pass 안에 CGPROGRAM을 선언하고
vertex와 fragment 가 들어 있는 함수명을 선언한 후 vertex쉐이더와 fragment 쉐이더를 CG로 만들어 Color로 출력하는 방식입니다. 당연히 기존 존재하는 라이팅이나 커스텀 라이팅을 만들 수도 있긴 합니다

방식은 아래와 같습니다.

Shader "쉐이더 이름" {
Properties {
  _Color ("Main Color", Color) = (1,1,1,1)
  _MainTex ("Base (RGB)", 2D) = "white" {}
               }
SubShader
  {
    Pass 
       {
            CGPROGRAM

            #pragma vertex vert_full
            #pragma fragment frag_full 

struct v2f {
float4 vertex : POSITION;
}

v2f vert_full (appdata_full v){
 v2f_aniOnly o;
 o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);    

 return o;
}

half4 frag_full (v2f i) : COLOR{
 return half4(0,0,0,1);
}
          ENDCG
        
   }
      }
}


이렇게 제작하면 2pass로도 제작할 수 있고, CG의 여러 가지 기능을 짜서 커스터마이징을 할 수도 있습니다.

그렇지만 치명적인 단점으로, 이렇게 짜면 디퍼드 렌더링을 사용하지 못합니다 ;ㅅ;
디퍼드 렌더링은 완성되지 않은 데이터를 받아서 최종 픽셀을 결정하는건데 이 방법은 아예 최종 픽셀을 결정해 버리는 방식이라서 말이죠. (라고 스승님이 말씀)


2.2. surface 로 짜는 법

fragment로 짜는 법과 비슷하지만, surface로 짜는 방식은 약간의 차이가 있습니다. 일단 pass가 없습니다. 그리고 최종 결과물이 color로 나오는 것이 아니라 구조체로 나옵니다. 아래와 같은..

void surf (Input IN, inout SurfaceOutput o)
{
o.Albedo;
o.Normal; 
o.Emission;
o.Gloss;
o.Specular;
o.Alpha;
}

때문에 이런 구조가 됩니다.
Shader "쉐이더 이름" {
Properties {
  _Color ("Main Color", Color) = (1,1,1,1)
  _MainTex ("Base (RGB)", 2D) = "white" {}
               }
SubShader 
{
        CGPROGRAM

 #pragma surface surf BlinnPhong
 #include "UnityCG.cginc"

 struct Input {
}

void surf (Input IN, inout SurfaceOutput o)
{
o.Albedo;
o.Normal; 
o.Emission;
o.Gloss;
o.Specular;
o.Alpha;
}
          ENDCG
      }
}


이 구조에서는  #pragma surface surf BlinnPhong 대신에 커스텀 라이팅 구조를 만들어서 넣을 수도 있기 때문에 다양한 빛처리가 가능해 집니다. 또한 디퍼드 렌더러일때의 빛처리와 포워드 렌더러일때의 빛처리를 다르게 할 수도 있지요. 
기능상으로 가장 강력하고 만들기도 편한 방법이긴 합니다만, 애석하게도 자체적으로 pass를 만들어 쓰기 때문에 2pass 쉐이더 제작을 할 수 없는 단점을 가지고 있습니다. 

* 최근에 유니티 코리아 분과 함께 이 쪽 연구를 잠깐 해봤는데, 서피스에서도 2pass를 하는 법이 있긴 하다고 합니다. 힌트는 #debug  ..  이 쪽 좀 더 연구해 봐야겠어요


* 4.x때부터 공식적으로 서피스 쉐이더에서 2pass가 가능해 졌습니다. CG쪽을 그냥 두 번 쓰면 됩니다.



3. 결론 

때문에 아이패드나 다양한 기기의 호환, 빠른 속도를 위해서는 1번의 스크립트 방식으로,

2pass 이상 연산을 요하는 PC 전용의 반투명 (이펙트 등. 반투명은 어차피 디퍼드로 돌아가지 않기 때문에 ) , 그리고 포워드 렌더러 전문 쉐이더는 2.1번 방식으로 (디퍼드 렌더러로도 짤 수 있음. 단 귀찮음)
(2015년 수정합니다 :빠릅니다. 각종 기법을 사용할 수 있는 정통 방법입니다. 단 모든게 수동으로 제작되기 때문에 모든 경우의 수를 다 작성해 줘야 합니다 )


2pass는 할 수 없지만 (할 수 있음) 디퍼드 렌더러를 포함하여 가장 고급스러운 효과를 내기 위한 PC (혹은 고사양 게임기) 용 쉐이더(캐릭터 쉐이더등) 에서는 2.2 방식으로 쉐이더를 작성하는 것이 적합할 것으로 보입니다.
(2015년 수정합니다 : 편하게 짤 때 좋습니다. 굴절 같이 배경의 이미지를 캡쳐해서 다시 연산한다던가 깊이값 받아오는등의 고급 기법이 힘듭니다. )


핑백

덧글

※ 로그인 사용자만 덧글을 남길 수 있습니다.


MyADD

<script> (adsbygoogle = window.adsbygoogle || []).push({}); </script>