강의 필기/3-1

<2> 셰이더 기초(1)

YujinK 2020. 4. 12. 19:19


1. Float(부동 소수점) 이란?


프로그래밍에서 계산할 때의 단위. 

(부동 소수점.. 단어부터 어렵다)

그래픽 작업자인 저는 "소수점이 있는 수" 정도로 이해하면 됩니다. 


- Float으로 색상 표현하기


*실제 작업 시에는 대문자를 사용하지 않습니다 float으로 작성.


색상을 float으로 계산할 때는 float을 3개 이용합니다. 


(float,float,float)

float 하나가 32bit의 색상값, 숫자를 담는 상자라고 생각합시다.


float3(1,1,1) 은 24bit에서 (255,255,255) 와 같습니다. 즉, 흰 색이겠네요.

float3(0,0,0) 은 24bit (0,0,0) 와 같습니다. 검정색입니다!


그렇다면 float4는 무엇일까요? 마지막에 알파 채널이 추가된 것입니다.

float4(0.5,0.5,0.5,0.5) -> 32bit (128,128,128,128)와 같습니다. 반투명한 회색인 것입니다.


= float값은 8bit color값인 255와 같습니다. 0은 0, 1은 255인 것이죠.

셰이더 코딩을 위해선 색을 이런 식으로 이해하고 표현해야 합니다...


- Unity shader에서 사용하는 단위


1) float : 32비트

2) half : 16비트

3) fixed : 11비트


가 있지만 현재로썬 float만 신경쓰도록 합니다! flaot을 이용한 코딩도 익숙하지 않은데 다른 것까지 고려했다가는 헷갈리고 머리 아프니까요...


float이 숫자를 담는 가장 큰 상자, half가 그의 절반 크기의 상자, fixed는 그보다 더 작은 상자. 정도로만 이해하고 있습니다.



2. Shader


게임에서 사용하는 Shader는 몇 가지 언어로 분화되어 있습니다.


(1) HLSL (High Level Shader Language) : 가장 보편적, 정통 / Unity URP부터 사용, Unreal도 이 쪽

(2) GLSL (OpenGL 기반)

(3) CG (NVIDIA에서 제작한 언어) : Unity에서 사용 ...


복잡해 보이지만 하나만 배우면 나머지도 사용할 수 있습니다.(ㅠ)


- Unity Shader 

ShaderLab이라는 자체 문법 스크립트를 사용합니다. 


(1) ShaderLab만을 이용

호환성은 좋지만 이걸로 할 수 있는 게 없다...이젠 안 씁니다.


(1) Surface Shader (그래픽 작업자는 이것을 주로 사용! 적절한 난이도!)

- 기본적으로 ShaderLab을 사용하되, 일부 CG코드를 사용합니다. 

- 가장 쉽고 멀티 플렛폼에 잘 대응되는 Shader

- 약간의 C문법을 사용합니다.

: 기초문법 // = ; , 변수, 함수, 조건문...


(2) Vertex & Fragment Shader 

ShaderLab을 사용하긴 하지만, CG의 비중이 굉장히 큽니다. 기본적인 라이트, 노말, 버텍스, 행렬 등...을 모두 수동으로 설정해주어야 합니다. 좀 더 디테일한 설정이 가능합니다. (Surface Shder에서는 자동으로 처리해주는 부분)



3. Unity에서의 Shader 작업 기초


- 스크립트(ShaderLab) 에서는 따옴표(;) 를 사용하지 않습니다. 실제 코딩 부분에서는 꼭 사용해줍시다.

- 중괄호{} 안에 있는 것을 하나의 방이라고 생각해 봅시다!


생성 및 이름 설정 



Create - Shader- Standard Surface Shader : 우리가 사용할 기본적인 셰이더입니다.


주의할 점! : 생성과 동시에 이름을 설정해주세요. 

생성 후에 이름을 바꾸는 것과, 생성과 동시에 이름을 설정하는 것은 서로 다르게 작동합니다.


이제 셰이더 파일을 열어봅시다. 다음은 처음 열었을 때의 화면입니다.




첫 줄부터 살펴봅니다. 따옴표(") 안에 있는 것은 일반적인 글씨로 인식합니다.

여기선 셰이더의 경로와 이름을 설정합니다. 슬래쉬(/)를 통해 경로를 설정해줄 수 있습니다.

이 셰이더는 Custom의 경로에 위치하였고, 셰이더의 이름은 TestShader 입니다.




머테리얼의 셰이더 설정에서 확인할 수 있네요!


저 첫줄을 수정하면 셰이더의 경로 및 이름을 설정할 수 있습니다. 

"Project/Cha/TestShader" 로 작성한다면 Project 안의 Cha 안에 위치한 TestShader가 되겠습니다.


이 때 코딩에서 셰이더의 이름은 '파일을 생성했을 때 처음 지은 이름'이 되어있습니다.

생성 후에 유니티에서 파일 명을 바꿔봤자 셰이더명은 바뀌지 않습니다...스크립트 파일을 열어 따로 수정해주어야 하는 번거로움이 생깁니다.


따라서 웬만하면 생성 후 셰이더 이름을 바로 지어줍시다. (헷갈리지 않기 위해..)


셰이더의 구조

여기서 잠깐 셰이더의 전체 구조를 살펴보면 다음과 같습니다.



Properties


- 셰이더의 인터페이스 부분만을 다룹니다.


현재 Properties의 상태.


Test 머테리얼을 생성해 TestShader를 입힌 후 확인해 보았습니다. 

properties는 위 사진에 보이는 부분, 인터페이스를 설정하는 부분입니다.


여기서 모든 부분을 주석화(//)하여 스크립트를 날려주면...


셰이더 인터페이스가 모두 사라져 버리는 모습을 확인할 수 있습니다.


그러면 본격적으로 Properties를 작성해 봅시다.

이와 같이 스크립트를 작성했을 때


셰이더에 인터페이스가 작성되는 것을 확인할 수 있습니다.


Properties에 쓰이는 것을 정리하자면 다음과 같습니다.


이렇게 작성해보았습니다.


셰이더 인터페이스에 적용된 모습!



SubShader



subshader를 살펴봅시다. 다른 건 아직 잘 모르겠지만 "surf"가 가장 중요하다, 당장 이것을 만져볼 것이다. 하는 것은 기억합시다. 일단 surf안을 전부 지워주고 새로 작성해보겠습니다.


surf에는 SurfaceOutputStandard라는 구조체(struct)가 포함되어 있습니다.

구조체를 가방이라고 생각하고, 안에 있는 필요한 기능을 하나씩 꺼내서 사용한다 생각합시다!


surf의 구조체를 정리하자면 다음과 같습니다.




struct

SurfaceOutputStandard

{

fixed3 Albedo; //fixed3는 float과 같습니다. RGB

fixed3 Normal;

fixed3 Emissoin;

half Metallic;

half Smoothness;

half Occlusion;

half Alpha;

};




오늘은 Albedo와 Emission 정도만 익숙해집시다.



void surf (Input IN, inout SurfaceOutStandard o) 는


surf에서 SurfaceOutStandard라는 구조체를 사용하겠다는 뜻입니다. 동시에 SurfaceOutStandard를 'o'라고 선언하였네요. SurfaceOutStandard를 일일이 쓰기에는 너무 길긴 합니다...


다음으로

o.Albedo = float3(1,0,0); 라고 적어주었습니다.


여기서'.'은 '~의' 정도로 생각합시다.

o.Albedo면 o(SurfaceOutStandard)의 Albedo를 가져온다. 라고 이해하면 되겠습니다.


o.Albedo = float3(1,0,0);은 

float3(1,0,0)의 값 즉, 붉은색을 Albedo에 넣어주겠다는 뜻입니다.


이대로 저장하고 유니티를 확인해보면



셰이더에 Albedo로 붉은색이 적용되어 있습니다.


앞선 코드를 주석화 한 뒤 Emission을 활용한 코드를 새로 작성해 보겠습니다.


o.Emission = float3(0,1,0);



Emisson(음영이 없는, 빛계산을 하지 않는 컬러)으로 녹색이 출력됩니다.



이미지 연산


o.Emission = float3(1, 0, 0) + float3(0, 0, 1);

을 입력하면 어떻게 될까요?



결과적으로 float3(1,0,1);이 적용되어 RGB로 (255,0,255)인 마젠타 컬러가 출력됩니다.



o.Emission = float3(1, 0, 0) + float3(0, 0, 1) + float3(0,1,0);



은 float3(1,1,1)이 되어 흰 색의 컬러가 적용됩니다.


그렇다면 o.Emission = float3(1, 0, 0) + 1; 은 무엇이 될까요?

각 자리의 모든 값에 1을 더하게 됩니다. 따라서



o.Emission = float3(2,1,1)이 되는 것입니다. 엔진 화면 상으로는 '흰 색'이 출력됩니다.



위 컬러는 (2,1,1)이지만 사실은 흰 색이 아닙니다...모니터가 출력하지 못할 뿐입니다. 

HDR상태가 된 것인데요, 여기에 Bloom넣으면 흰 색보단 붉은 색으로 빛날 것입니다. 

(순수한 흰 색이 아닙니다!)


o.Emission = float3(1, 0, 0) - 1; 은요?



float3(0,-1,-1)로 검정색으로 표현됩니다. (순수한 검정 색은 아니지만요)


o.Emission = float3(1, 0, 0) + float2(0,1); 은 연산 가능할까요?



안 됩니다. 에러가 납니다...


따라서 이미지 연산은

- 같은 자릿수, 또는 float과 연산이 가능합니다 (float3, 그리고 float2는 같이 연산하지 못합니다)

- 색을 더하면 밝아집니다.

- 색을 빼면 어두워집니다.

- 색을 나누거나 곱하는 것은 경우에 따라 다릅니다. (*0.5를 하면 어두워지고, *2를하면 밝아집니다.)



이미지 연산 추가 


1-x는 반전, Inverse


o.Emission = 1 - float3(1, 0, 0);

o.Emission = float3(1,1,1) - float3(1, 0, 0); 와 같습니다. 따라서 float3(0,1,1)인 하늘색이 됩니다.


float3(1,0,0) 붉은색을 반전한 float3(0,1,1)이 되었네요!


이미지 색상 반전의 공식(포토샵에서도)은 모두 셰이더에서의 1-x입니다.



변수 선언과 초기화


float3 magam = float3 (1,1,1);


은 magam이라는 변수(값을 저장하는 컵 혹은 상자라고 생각합시다!)를 선언한 것입니다. 동시에 magam 변수에 float3(1,1,1) 즉, 흰 색의 값을 넣어주었네요.


위의 것을 더 쉽게 표현하는 방법이 있는데요


바로 

float3 magam =1;

입니다. float3의 각 자리수에 1을 넣어주기 때문에 float3(1,1,1)와 같은 값이 됩니다.


magam이라는 변수가 흰 색으로 태어났습니다...



변수 활용(1)


o.Emission=magam;


을 입력해주면 어떻게 될까요?



흰 색의 에미션이 적용되었습니다. 앞으로 흰 색 에미션을 넣으려면 코드를 길게 적을 필요 없이 magam 변수를 불러오면 됩니다!



변수 활용(2) : 연산


    float3 magam = float3(1, 0, 0);

    float3 saveme = float3(0, 1, 0);

    float3 why = float3(0, 0, 1);


세 개의 변수를 선언하겠습니다. 이후


o.Emission = magam+saveme; 를 하면 



magam인 float3(1,0,0)과 saveme float3(0,1,0)의 값을 더한 (1,1,0) 의 노란색이 나옵니다.



변수 활용(3)


magam = saveme; 를 하면 어떻게 될까요?


이는 saveme의 값을 magam에 넣는다, 라는 뜻입니다. 

saveme는 float3(0,1,0)의 값을 가지고 있으므로, 이제부터 magam은 float3(0,1,0)이 되는 것입니다.



(0,1,0)인 녹색이 출력됩니다.



변수활용(4)



magam = magam+saveme; 를 선언하면 magam은 (1,1,0)의 값이 들어가겠죠?



노란색이 나왔습니다!



변수활용(5)


*float4를 기준, 색상값을 (r,g,b,a)로 표현합니다. 이는 (x,y,z,w)로도 바꿔 표현할 수 있습니다.



o.Emission = magam.rrr; 라는 값을 써보았습니다. 

.는 경로, '의'를 뜻한다고 했죠.


위 코드는 magam의 r에 들어있는 값을, 1을 불러오므로

o.Emission = float3(1,1,1); 과 같습니다. 따라서 흰 색이 됩니다.


다르게

o.Emission = magam.grb; 라는 문장을 쓸 경우

o.Emission= float(0,1,0); 이 되어 초록색이 나옵니다!



o.Emission = magam.r; 을 사용하는것도 가능합니다.

이는 o.Emission = float3(1,1,1)과 같습니다. r의 1값을 불러와 각 값에 넣어주는 것입니다.




float3(1,1,1)이 적용되어 흰 색이 나왔습니다.