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 Shader나 Surface 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 변수로 값을 활용 할 수 있다.
-텍스처 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개의 텍스처를 합쳐서 출력 했다.
-텍스처 알파 블랜드
블랜딩이란 투명, 반투명 오브젝트를 표현하는데 사용된다. 위 사진을 보면 버텍스쉐이더, 레스터라이저, 프래그먼트 쉐이더의 모든 과정이 끝나고 마지막에 블랜딩 과정이 들어있다.
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"} <- 와 같이 임의로 렌더 순서를 정할 수도 있다.
'Unity > Shader & VFX' 카테고리의 다른 글
Unity Shader Properties 응용 & multi_compile (0) | 2019.12.29 |
---|---|
[Unity Shader Effect] Soft Particles Shader (0) | 2019.12.28 |
이펙트 쉐이더 3강 - 기술 응용 : 홀로그램 쉐이더 (0) | 2019.12.27 |
이펙트 쉐이더 2강 - Alpha / Blending (0) | 2019.12.15 |
이펙트 쉐이더 1강 - Shader 기초 개념 (0) | 2019.11.30 |
WRITTEN BY
- CatDarkGame
Technical Artist dhwlgn12@gmail.com