반응형

Shader Lab이란?

Unity에서 작성하는 Shader는 기본적으로 ShaderLab이라는 Unity 자체 언어를 기반으로 만들어진다.

Unity에서 쉐이더를 만드는 방식은 크게 3가지가 존재 한다.

 

- 1. Fixed Function Shader

고정 랜더 파이프라인에서 작동하는 쉐이더로 구형 방식의 체계이며 Unity에서는 Shader Lab언어를 통해 작성이 가능하다. 반대로 HLSL이나 CG 등 흔히 쉐이더 스크립트라고 하는 언어들은 Programmable Render Pipeline에서

동작한다.

 

- 2. Vertex & Fragment Shader

고정 랜더 파이프라인 방식이 아닌 우리가 흔히 쉐이더라 부르는 Programmable Render Pipeline에서 동작하는

쉐이더이다. 파이프라인 중 가장 핵심인 레스터라이저 단계의 전 후로 Vertex쉐이더, Fragment(Pixel)쉐이더를 작성하여 사용한다. Shader Lab 스크립트 내부에서 CGPROGRAM~ENDCG 명령어를 통해 작성 할 수 있다. Unity에서는 CG라는 nVidia에서 만든 언어를 사용 한다.

 

- 3. Surface Shader

Untiy에서 만든 간편성 쉐이더 체계로 Vertex&Fragment Shader에서 직접 구현 해야할 기본적인 라이트, 노말 계산,

버텍스 계산, 행렬 계산 등이 캡슐화 되어있고 자동으로 처리해 준다.

 

이 포스팅은

Unity에서 빈 쉐이더 부터 시작하여 ShaderLab의 기본을 배워 Fixed Function Shader를 작성을 한다.

Fixed Function Shader는 고정랜더파이프라인에서 사용되던 기능들을 Shader Lab언어를 통해 작성 할 수 있으며

가볍고 구형 하드웨어에서 동작한다. 그리고 Unity에서 사용되는 Vertex & Fragment ShaderSurface Shader

기본적으로 ShaderLab 위에서 작동 되기 때문에 이후 Multi Pass 기법으로 다른 방식의 쉐이더를 연결 하여 사용

할 수 있다. 1장. Shader Lab & Fixed Function Shader으로 시작으로 Unity Shader의 기초를 포스팅한다.

 

ShaderLab : 기본 문법

[1. Unity 세팅]

이 포스팅은 Unity 2018.3.0f2 버전을 사용하고 있다. 우선 위 사진 처럼 Unlit Shader를 만든다.

1
2
3
4
5
6
7
8
9
10
11
Shader "Unlit/TutorialShader"
{
    SubShader
    {
        Pass 
        {
            
        }
    }
}
 
cs

 

기본 코드가 나오는데 위 코드와 같이 싹다 지운다.  최소 코드로 하나하나 기능을 집어가면서 설명할 예정이다.

 

[2. SubShader]

ShaderLab의 맨 처음 구문으로 Tag, CommonState 등 옵션을 통해 랜더 순서, 다른 SubShader선택 등 옵션을 설정 할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 
Shader "Unlit/TutorialShader"
{
   SubShader
   {    
        // OpenGL3.0
 
      }
      SubShader 
    {
        // OpenGL2.0
      }
      SubShader 
    {
        // Other
      }
}
 
cs

위 코드와 같이 한 개의 쉐이더에 SubShader를 여러개 구성하여 디바이스 별로 어떤 코드를 사용 할 것인지 선택 가능하다.

 

[3. Fallback]

1
2
3
4
5
6
7
8
9
10
11
Shader "Unlit/TutorialShader"
{
    SubShader
    {
        Pass 
        {
            
        }
    }
    FallBack "Diffuse"
}
cs

앞서 설명한 조건에 맞는 SubShader를 찾지 못 했을 경우 대비책으로 사용할 쉐이더를 선택하는 명령어다.

위 코드에서 예시로 기본으로 있는 Diffuse 쉐이더를 선택했지만 

이렇게 자체 쉐이더를 경로를 지정하여 선택이 가능하다.

 

[4. Pass]

Pass 구문은 쉐이더의 렌더링에서 한번 거쳐가는 단위로서 SubShader에서는 여러개의 Pass를 작성 할 수 있고

맨 위의 Pass부터 순차적으로 돌아간다. 

Unity에서는 Normal Pass, Use Pass, Grab Pass로 총 3가지 종류의 Pass가 있다.

- Normal Pass  :  Pass라고 작성된 코드이며 일반 적인 렌더링에 사용된다.

- Use Pass       : 는 다른 쉐이더에서 작성 된 Pass가져와서 사용한다.

- Grab Pass     : 특수한 Pass로 화면 오브젝트를 캡처할때 등에 사용된다.(Render To Texture)

 

Shader Lab : 기본 Shader 만들기

[1. Color]

1
2
3
4
5
6
7
8
9
10
11
12
Shader "Unlit/TutorialShader"
{
    SubShader
    {
        Pass 
        {
            Color(1, 0, 0, 1)
        }
    }
    FallBack "Diffuse"
}
 
cs

Pass 내부에 Color함수에 float4 값을 넣었더니 단순히 Mesh에 색깔만 출력하는 Shader가 되었다.

 

[2. Material(재질) 설정]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Shader "Unlit/TutorialShader"
{
    SubShader
    {
        Pass 
        {
            Material
            {
                Diffuse(1,0,0,1)
            }
            Lighting On
        }
    }
    FallBack "Diffuse"
}
 
cs

일반적인 Color가 아닌 실제 라이팅에 반응 쉐이더를 만들기 위해서는 Material 구문을 이용하여

Diffuse(난반사 색) 함수를 사용해야 한다. Diffuse는 말 그대로 빛에 의한 난반사이므로 빛이 필요하다.

Lighting On 명령어를 사용하면 게임 월드에 존재하는 라이팅에 영향을 받게 된다.(빛 방향만)

 

ShaderLab의 Material 구문에 들어가는 명령어 리스트이다.

- Diffuse : 난반사 색

- Ambient : 환경광 색

- Shininess : 광택 강도(Specular강도)

- Specular :  정반사 색

- Emission : 자체발광 색(조명 영향없는 색)

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Shader "Unlit/TutorialShader"
{
    SubShader
    {
        Pass 
        {
            Material
            {
                Diffuse(1,0,0,1)
                Specular(1,1,1,1)
                Shininess 1
            }
            Lighting On
            SeparateSpecular On
        }
    }
    FallBack "Diffuse"
}
 
cs

SeparateSpecular On 명령어를 통해 Specular를 활성화 시키고 Material 구문에 Specular색과 Shininess값을 넣어서

Specular 효과를 구현했다.

 

[3. Properties(인터페이스 변수)]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Shader "Unlit/TutorialShader"
{
    Properties
    {
        _DiffuseColor ("DiffuseColor", Color) = (1,1,1,1)
    }
 
    SubShader
    {
        Pass 
        {
            Material
            {
                Diffuse [_DiffuseColor]
                Specular(1,1,1,1)
                Shininess 1
            }
            Lighting On
            SeparateSpecular On
        }
    }
    FallBack "Diffuse"
}
 
cs

Properties 구문을 통해 에디터, C#코드 등 외부에서 조작이 가능한 변수를 만들었다.

 Diffuse [_DiffuseColor]

인터페이스 변수를 쉐이더 코드에 적용했다.

 

Properties변수는 여러 종류가 존재하며 각 종류마다 용도와, 표기 방법이 각각 있다.

 

[4. Texture 입히기]

-텍스처 1개 적용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Shader "Unlit/TutorialShader"
{
    Properties
    {
        _DiffuseColor ("DiffuseColor", Color) = (1,1,1,1)
        _MainTex ("Texture", 2D) = "white"
        
    }
         
    SubShader
    {
        Pass 
        {
            Material
            {
                Diffuse[_DiffuseColor]
                Specular(1,1,1,1)
                Shininess 1
            }
 
            Lighting On
            SeparateSpecular On
 
            SetTexture[_MainTex]
            {
            }
        }
    }
    FallBack "Diffuse"
}
 
cs

우선 인터페이스 변수에 2D 유형의 텍스처 변수를 만든다.

Lighting On 명령어 다음 줄로 SetTexture 명령어를 사용하여 [](대괄호)에 인터페이스에서 선언한 2D변수를 넣는다.

 

결과물로 텍스처는 출력 되었지만 조명연산이 되지 않은채 텍스처가 출력됬다. 위 코드는 조명연산은 돌아가고 있지만

결과물로 나오지 않고 있다.

 

-텍스처에 조명연산 넣기 - SetTexture 전용 명령어

SetTexture[_MainTex]
{
     combine texture * Primary
}

이전 코드에서 SetTexture명령어 안에 한 줄의 코드를 넣었다.

SetTexture에서 사용 할 수 있는 combine 명령어를 통해 texture와 Primary 변수를 곱하는 연산을 했다.

 

SetTexture에는 2가지 명령이 존재한다.

- combine 명령

이 명령은 이후 넣은 값을 텍스처에 합치는 명령어이다. 넣는 값에 대해서는 정해진 변수가 몇개 있다.

texture : SetTexture[텍스쳐]에서 지정한 텍스처 변수

Primary : lighting calculation(조명연산)정보 또는 색상 값

Previous : 이전 연산 정보, SetTexture연산 정보 결과

Constant : ConstantColor 명령으로 지정한 색상 값

 

- ConstantColor 명령

SetTexture함수 내에서 색상 값을 저장하기 위한 변수이다. 

ConstantColor(1,1,1,1)을 통해서 색상을 저장하고 Constant 변수로 값을 활용 할 수 있다.

Constant Color 사용 예시

-텍스처 2개 블랜드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
Shader "Unlit/TutorialShader"
{
    Properties
    {
        _DiffuseColor ("DiffuseColor", Color) = (1,1,1,1)
        _MainTex ("Texture", 2D) = "white"
        _SubTex("Texture Sub", 2D) = "white"
        
    }
         
    SubShader
    {
        Pass 
        {
            Material
            {
                Diffuse[_DiffuseColor]
                Specular(1,1,1,1)
                Shininess 1
            }
 
            Lighting On
            SeparateSpecular On
 
            SetTexture[_MainTex]
            {
                combine texture
            }
 
            SetTexture[_SubTex]
            {
                combine texture * previous
            }
        }
    }
    FallBack "Diffuse"
}
 
cs

인터페이스

SetTexture[_MainTex]
{
     combine texture
}

SetTexture[_SubTex]
{
     combine texture * previous
}

 

-텍스처 알파 마스크

알파채널이 있는 이미지가 있다.

이번엔 알파채널을 이용하여 텍스처 마스킹해보겠다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Shader "Unlit/TutorialShader"
{
    Properties
    {
        _DiffuseColor ("DiffuseColor", Color) = (1,1,1,1)
        _MainTex ("Texture", 2D) = "white"
    }
         
    SubShader
    {
        Pass 
        {
            Material
            {
                Diffuse[_DiffuseColor]
                Specular(1,1,1,1)
                Shininess 1
            }
 
            Lighting On
            SeparateSpecular On
 
            SetTexture[_MainTex]
            {
                 constantColor(1,0,1,1)
                combine texture lerp(texture) constant
            }
 
        }
    }
    FallBack "Diffuse"
}
 
cs

핵심은 lerp함수이다.

 

lerp함수에 넣은 텍스처에서 알파값을 기준으로 알파값이 1에 가까울 수록 texture가 출력되고 

0에 가까울 수록 constant가 출력된다.

 

이 기능을 이용해서 두 개의 텍스처를 한번에 출력해보겠다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Shader "Unlit/TutorialShader"
{
    Properties
    {
        _DiffuseColor("DiffuseColor", Color) = (1,1,1,1)
        _MainTex("Texture", 2D) = "white"
        _SubTex("Texture Sub", 2D) = "white"
 
    }
 
        SubShader
    {
        Pass
        {
            Material
            {
                Diffuse[_DiffuseColor]
                Specular(1,1,1,1)
                Shininess 1
            }
 
            Lighting On
            SeparateSpecular On
 
            SetTexture[_MainTex]
            {
                combine texture
            }
 
            SetTexture[_SubTex]
            {
                combine texture lerp(texture) previous
            }
        }
    }
        FallBack "Diffuse"
}
 
cs

 

첫 번째 SetTexture명령어에서 벽 텍스처를 적용하고 두 번째 SetTexture명령어에서

lerp 함수를 통해 불 텍스처의 알파 마스킹으로 2개의 텍스처를 합쳐서 출력 했다.

 

 

-텍스처 알파 블랜드

https://docs.unity3d.com/kr/current/Manual/SL-Blend.html

블랜딩이란 투명, 반투명 오브젝트를 표현하는데 사용된다. 위 사진을 보면 버텍스쉐이더, 레스터라이저, 프래그먼트 쉐이더의 모든 과정이 끝나고 마지막에 블랜딩 과정이 들어있다.

 

ShaderLab코드에서는 위와 같이 작성한다. 블랜드 세팅법은 유니티 쉐이더 모두 공통이다.

위 코드는 반투명 처리하기 위한 Blend 세팅 코드로 

기본값(Default)는 Blend Off이다. (작성하지 않으면 기본값으로 설정됨)

Blend 옵션값을 조정하여 이 쉐이더에서 어떤 Blend 속성을 사용 할 것 인지 선택 할 수 있다.

 

-Blend Properties(옵션)-

Source는 계산되어 지는 색상을 의미하고 Desination은 이미 스크린에 있는 색상값을 의미한다.

One 1의 값 : Source또는 Destination 색상이 온전히 받는다.
Zero 0의 값 : Source또는 Destination 값을 삭제한다.
SrcColor 이 스테이지의 값은 Source 색상 값에 의 해 곱해진다.
SrcAlpha 이 스테이지의 값은 Source 알파 값에 의해 곱해진다.
DstColor 이 스테이지의 값은 프레임 버퍼 Source 색상 값에 의해 곱해진다.
DstAlpha 이 스테이지의 값은 프레임 버퍼 Source 알파 값에 의해 곱해진다.
OneMinusSrcColor 이 스테이지의 값은 (1 - Source 색상값)에 의해 곱해진다.
OneMinusSrcAlpha 이 스테이지의 값은 (1 - Source알파값)에 의해 곱해진다.
OneMinusDstColor 이 스테이지의 값은 (1 - Destination 색상값)에 의해 곱해진다.
OneMinusDstAlpha 이 스테이지의 값은 (1 - Destination 알파값)에 의해 곱해진다.

 

-Blend 옵션 조합-

위 Blend 옵션을 조합해서 원하는 쉐이더의 블랜드를 사용 할 수있는데 아래는 가장 흔하게 사용하는 조합과 용도이다.

Blend SrcAlpha OneMinusSrcAlpha Alpha Blend
Blend One One Additive 
Blend One OneMinusDstColor Soft Additive
Blend DstColor Zero Multiplicative
Blend DstColor SrcColor 2x Multiplicative

 

-반투명 쉐이더 만들기(Alpha Blend)-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
Shader "Unlit/TutorialShader"
{
    Properties
    {
        _DiffuseColor("DiffuseColor", Color) = (1,1,1,1)
        _MainTex("Texture", 2D) = "white"
        _SubTex("Texture Sub", 2D) = "white"
 
    }
 
    SubShader
    {
        Pass
        {
            Blend SrcAlpha OneMinusSrcAlpha
        
            Material
            {
                Diffuse[_DiffuseColor]
                Specular(1,1,1,1)
                Shininess 1
            }
 
            Lighting On
            SeparateSpecular On
 
            SetTexture[_MainTex]
            {
                combine texture
            }
 
            SetTexture[_SubTex]
            {
                ConstantColor[_DiffuseColor]
                combine texture lerp(texture) previous, constant
            }
        }
    }
        FallBack "Diffuse"
}
 
cs

 

Alpha Blend가 가능하게 Blend 옵션을 설정한다.

 

기존에 사용했던 lerp함수에 한 가지 더 인자 값이 들어간다.

Properties인터페이스 변수에 Color 변수를 추가하여 ConstantColor에 세팅하여 

lerp의 마지막 인자 값에 넣으면 해당 Color 변수의 알파값을 통해 투명도 조절이 가능하다.

 

-Render Queue-

알파블랜드 기능을 만들어 반투명화 시켰지만 한 가지 이슈가 발생한다.

위 gif처럼 카메라움직임에 따라서 랜더 순서가 뒤바뀐다.

반투명을 그리는 원리는 간단하게 먼저 그려진 뒤 오브젝트와 반투명 오브젝트와 알파값만큼 색이 섞이는 원리이다.

그래서 반투명 오브젝트는 맨 나중에 그려야하는데 하지만 작성한 쉐이더에서는 어디에도 그리는 순서를

설정하는 부분은 없다.

그래서 Tag 명령어를 통해서 강제로 랜더링 순서를 정해 줘야한다.

 

Tags{"Queue" = "Transparent"}

을 추가하면 위 문제를 해결 할 수 있다.

"Queue"는 Tag에서 렌더링 순서를 정하는 명령어이고 

"Transparent"는 Queue의 렌더 순서 중에서 정해진 변수 같은 것이다.

 

아래는 Transparent처럼 정해진 변수 목록이다.

Background 1000 이 렌더링 큐는 다른 것들보다 먼저 렌더링됩니다. 정말 배경으로 있어야 할 것들을 위해서 사용합니다.
Geometry(Default) 2000 이 큐는 대부분의 오브젝트에 사용됩니다. 불투명 오브젝트가 이 큐를 사용합니다.
AlphaTest 2450 알파 테스트 지오메트리에 이 큐를 사용합니다. 모든 불투명 오브젝트 뒤에 그리는 편이 효율적이므로 Geometry와는 별도의 큐로써 존재합니다.
Transparent 3000 이 렌더 큐는 _Geometry_와 AlphaTest 후에 뒤부터 순서대로(back-to-front) 렌더링됩니다. 알파 블렌딩하는 것 (즉, 깊이 버퍼에 기록하지 않는 쉐이더)은 모두 여기에 있어야 합니다(유리, 파티클 효과).
Overlay  4000 이 렌더 큐는 오버레이 효과를 위해 사용합니다. 마지막으로 렌더링하는 것은 여기에 있어야 합니다 (예: 렌즈 플레어 등).

 

위 처럼 정해진 변수를 통해 렌더 순서를 정할 수 있지만

Tags{"Queue" = "Transparent + 1"}  <- 와 같이 임의로 렌더 순서를 정할 수도 있다.

 

 

 

 

 

반응형

WRITTEN BY
CatDarkGame
Technical Artist dhwlgn12@gmail.com

,