이것저것

[UNITY] 쉐이더 1장 - 기본 이론 본문

[유니티] Unity3D/Unity Shader

[UNITY] 쉐이더 1장 - 기본 이론

Patch_JA 2019. 3. 23. 18:09
728x90






저번시간에 HLSL, GLSL, CG같은 용어를 배운적이 있는데, 한번더 간단히 요약 하자면


HLSL -> DirectX에서 제작

GLSL -> OpenGL에서 제작

CG -> NVIDIA에서 제작


위 3가지 쉐이딩 언어들은 거어어어어어의 비슷하기 때문에 하나만 잘 공부해도 금방 옮겨갈수 있다고 한다.

이번장은 유니티에서 SurfaceShader를 이용하여 CG/HLSL를 살짝(?) 맛보는 시간을 가져본다.



유니티 쉐이더

유니티 쉐이더는 멀티플랫폼을 위한 문법으로 3가지 종류의 작성법이 일반적으로 존재한다.


ShaderLab

곧 지원이 중지된다고 한다..


Surface Shader

- 기본적으로 프로그래머가 아니여도 아티스트들이 쉬운개념으로 배울수 있고 

  Matrix같은 행렬연산이 자동으로 되며 아쉬운 부분이라면 고급기술들은 아쉽지만 자동이라는 장점이 있다.


Fragment Shader

- Surface Shader와 비슷하지만 완전 수동방식으로 Matrix 행렬연산으로 시작해서 정통으로 풀문법이 필요하다.



Unity Shader Graph


- SRP에서만 작동되는 비주얼 쉐이더 제작툴인데

  아직 SRP가 미완성인데다 Surface Shader를 공부하고나면 이해하기 어렵지 않다.




유니티에서 Surface Shader 만들기.


우선 본격적으로 쉐이더를 가지고 놀아보기전에 기본적으로 몇가지 세팅 및 준비를 해본다.


쉐이더와 마테리얼을 생성하여 마테리얼에 쉐이더를 적용후 모델에 넣는 과정이다.


[ 1. 쉐이더 생성 ]



1. 폴더를 만든다.


2. Create -> Shader -> Standard Surface Shader 생성



일단은 이름을 Hello 라고 지어놓았다.




[ 2. 마테리얼 생성 ]




1. 폴더를 만든다.


2. Create -> Material 생성



이것도 임의로 이름을 지어놓는다.



[ 3. 마테리얼에 쉐이더 넣기 ]



 만들어놓은 쉐이더를 드래그하여 마테리얼에 놓는다.




그러면 위처럼 Shader 부분이 만들어 놓았던것으로 바뀌는것이 확인된다.





[ 4. 3D모델 만들어서 적용시키기 ]



1. Hierarchy -> 3D Object -> Sphere 생성


2. Sphere오브젝트 Inspector에 만들어놓은 마테리얼을 드래그&드롭 하여 집어넣기



여기까지 잘 따라왔다면 이제 본격적으로 쉐이더 코드를 확인해볼 시간이다.


쉐이더 코드를 보기위해선 두가지 정도 방법이 있다. 상황에 따라 선택해서 쓰면 될듯 하다.



[1번 방법] 마테리얼에 우측상단 톱니 -> Edit Shader




[2번 방법] 만들어놓은 쉐이더 더블클릭





비쥬얼 스튜디오가 있다면 아래와같이 코드가 나온다..


참고로 쉐이더는 스크립트 언어 방식으로 일반적인 컴파일하는 언어와는 다르게 바로바로 수정하여 확인해볼수 있다.



처음 보는사람들은 코드가 너무 길고 많아보여서 부담감이 있을수있다..


다장에 지워도 될부분들은 빨간색으로 표시를 해두었다. 참고로 // 로 시작하는부분은 '주석' 이라고 부르고


코멘트를 달아놓는곳이라고 생각하면 된다. 해당 코드가 무엇을 하는건지 또는 주의해야할 점같은 것들을 주석을 통하여


작성해두면 다른사람이나 나중에 시간이 흘러 본인이 까먹어도 주석을 통해 확인할 수 있다.



혹시나 코드가 눈에 익지 않은 사람이라면 아래 사진처럼 { } 사이에 값들이 있는데 Shader "Custom/HelloShader" { } 가 


큰 집이라고 생각하면 그안에 Properties {} , SubShader {}  거실과 큰방 작은방등등이 있다고 일단은 생각하자..





코드 1번째줄을 보면 Shader "Custom/Hello" 라는 구문이 있다.


해당 구문은 에디터상에서 이름을 표시하는 역할을 한다.


큰따옴표 두개 " " 가 있다면 그 안에 값은 사용자가 임의적으로 값을 입력할수 있는 문자열 상태가 된다.


즉 "이제 이 안에서는 제 마음대로 작성할수 있는겁니다." 


그럼 한번 아래 Shader "Custom/Hello"를 Shader "원하는문자" 대로 수정해보자.





위에서 눈치챘을수 있겠지만 "Custom/HelloShader"에서 "Custom/"을 빼고 "HelloNoFolder"라고 이름을 수정했더니


쉐이더 리스트에서 폴더처럼 구분짓는 생기거나 사라지는 것을 볼수 있다.


"/" 슬래시를 넣으면서 폴더처럼 구분짓게 되고, 나중에 쉐이더가 많아질때 Hero나 Enemy 같이 구분지어놓고 사용하면 가독성이

 높아질것 같다.



다음은 Properties 부분이다. 이부분은 사용자 인터페이스를 다룬다고 생각하면 편하다.



_Color ("Color", Color) = (1, 1, 1, 1) 라는 부분을 먼저 보자.


프로그래밍적으로 보자면

_변수 ("텍스트", 자료형) = 초기값 이다.


좀더 쉽게 설명하자면

_이름 ("표시될 이름", 종류) = 기본값 정도 될것같다.



위에서 색상을 주황색으로 칠해놓은 부분이 있는데


그부분을 제외한 나머지 값은 임의적으로 수정할수 있다는것이다.


즉 주황색 부분은 정해져있는 종류를 사용해야한다.



"그래서 위의 값이 뭔데?" 라고 한다면 일단 마테리얼 부분을 한번 봐보자.



아까전 _이름 ("표시될 이름"종류) = 기본값


표시될 이름 부분이 마테리얼 값에 보이는 이름과 같은걸 볼수 있다.


Range, float, int, Color 등등 종류(자료형) 부분은 기본적으로 사용할수 있는것을 


제공해주는것이기 때문에 가능하면 외워서 사용하는것이 좋다.



말보단 실전.. 직접 추가해봐서 어떤느낌인지 실습해보자.



fVal ("NonRange Test", float) = 0.5

fVal2 ("Range Test", Range(0, 1)) = 0.5


float형Range형 두가지 버전을 작성해보았다.

* Range(min, max) 최소값과 최대값을 설정할수 있다.


작성이 완료 되었으면 Ctrl+S 눌러서 저장후 마테리얼 부분을 확인해보자.




추가한 값 2개를 확인할수 있다. 결국은 둘다 float 1개 값을 가지고 있지만,


float 으로 작성할시 사용자 마음대로 임의 숫자값을 넣을수 있는 반면


Range를 이용하여 Min, Max를 설정해둔다면 작성자의 의도한 값내에서만 설정할수 밖에 없다.


프로퍼티를 사용자 인터페이스를 다룰수 있게되었으니, 다음 실세인 SubShader쪽을 한번 확인해보자.



CGPROGRAM으로 시작해서 ENDCG끝나는 부분이 있다.


그사이에 있는 코드들이 CG언어 코드들이다.



CGPROGRAM ~ ENDCG 사이에 코드들은 프로퍼티나나 다른 부분과 달리 모두 세미콜론(;)이 들어가 있는데


여기서부터는 특별취급 영역이라고 생각 하자.. 세미콜론이 없는곳을 특히 특별취급 영역이라고 생각하자.

 프로그래밍을 모르는 입문자라면 굉에에에엥장히 많이 실수하는 부분이므로


항상 주의하고 에러가 난다면 가장 먼저 체크해봐야할 부분이다.





빨간색 부분은 일단 당장 쓰진 않을것이니 지워도 된다. ( 참고로 빨간부분을 지우고 실행하면 마테리얼이 검정으로만 나온다. )


36번째줄을 보면 void surf(Input In, inout SurfaceOutputStandard o) 라는 부분이 있다.


이것을 '함수'라고 하는데 surf라는 함수안에 Input은 in, SurfaceOutputStandard는 o로 된 무언가 


선언되어 값을 집어넣고 뺄수 있다고 생각하자.



여기서 출력구조 SurfaceOutputStandard 라는 구조체(struct)가 있다. 무슨 소리인지 모르겠다면


간단하게 바구니가 있고 그 안에 여러가지 물품이 담겨져 있어서 꺼내쓰고 한다고 생각하자.


그럼 그 출력구조에는 무엇이 있는지 한번 확인해보자.

자세한 내용은 유니티 매뉴얼에도 있으니 참고만 하자https://docs.unity3d.com/kr/current/Manual/SL-SurfaceShaders.html


몇가지 표준 출력구조가 있는데 우리는 SurfaceOutputStandard를 사용할것이다.


struct SurfaceOutputStandard { fixed3 Albedo; fixed3 Normal; half3 Emission; half Metallic; half Smoothness; half Occlusion; fixed Alpha; };


우선 두가지만 기억해두면 된다.


fixed3 Albedo;


half3 Emission;


아니 근데.. Albedo, Emission이 뭐지..?


Albedo = 빛을 영향을 받는다.

Emission = 빛 영향을 받지않는다.


Emission의 경우 사전의 뜻이 다른것 같아 생각한것을 작성하여 정확하지 않을수 있다.

자세한건 아래 실습으로 눈으로 확인해보자.


* fixed와 half는 float이라고 기억하자!




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


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


각각의 값을 입력해보고 결과를 확인해보면!



Albedo는 빛을 받아 그림자가 생기는 반면 Emission은 무시하는것 같다.


코드를 확인해보면 float3를 사용하여 1, 0, 0의 값을 입력했더니 레드가 나왔다.


그밖에도 변수도 같이 섞어서 응용하면



이런식으로 값을 더해서 색을 입힐수 있고


float 1만 넣으면 float3 ( 1, 1, 1 ) 같은 효과를 낼수도 있다.




그런데.. 프로그래머라면 엄창난 충공깽 방식이 등장한다..







점(.)이 붙으면 그 속에 내용을 가져올수있는데... 


.rgb, .rb 같은 순서에 상관없이 값을 맘대로 꺼내 사용할수 있다.


굉장히 깨름칙하고 소름이 돋는 부분이였다... ( 어떻게 보면 굉장히 좋다..? )



surf 함수 사용에 대해서 알아보았는데 


이번엔 Properties에서 값을 서로 연동하는법을 알아보자.



프로퍼티에서 fR, fG, fB 라는 변수에 Range(0, 1)값으로 지정해두고 fR에만 1.0 으로 값을 초기화 했다.


그후 struct Input 구조체 아래 프로퍼티와 같이 float fR, float fG, float fB 그대로 이름을 사용하였다.


그런후 surf함수에서 o.Emission에 RGB 3개값 float3 형에다 (fR, fG, fB)값을 넣어주었다.



[ 결과값 ]




RGB값을 직접 조절해서 사용할수 있는 간단한 쉐이더가 완성되었다.




[참고]

https://docs.unity3d.com/kr/530/Manual/SL-SurfaceShaders.html

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




Comments