하지만 Standard Surface Shader에서는 이 시맨틱이 많이 축약되어있습니다.
굳이 시맨틱을 안달아줘도 되게 말이죠. (근데 어째서인지 칼라는 남았다)
하지만 Surface Shader가 아닌 Fragment Shader에서는 이렇게 시맨틱을 이용한 형태의 변수들을 사용합니다.
잠깐 주제와 벗어나지만 하나만 짚고 넘어가자!
원래 UV를 불러오기 위해선 :TEXCOORD를 적어줘야되는 것도 알겠는데,
뒤에 [n]은 뭐고, UV는 분명 float2인데 왜 Type이 float4로 되어있을까요?
float4 TEXCOORD; float2 UV;
UV = TEXCOORD.xy
즉 uv는 float4 Texcoord의 xy를 가져온 샘입니다. 그렇다면 uv2는? Texcoord.zw를 사용하여 가져올수 있겠죠.
그럼 뒤에 붙어있는 [n]은 무엇일까요?
이건 Texcoord0, Texcoord1처럼 '숫자'를 붙일 수 있다는 의미입니다.
보통 Texcoord0은 uv1, uv2로 쓰이지만,
Texcoord1부터는 '그냥 float4짜리 변수'로 취급해서 uv를 더 넣어줘도 되고 심지어 컬러를 넣을 수도 있습니다.
(가끔 넣어준다고...)
Particle System의 Custom Vertex Streams. 저 괄호 안에 있던게 바로 시멘틱이었다.
다시 본론으로 돌아와서...
이제 Vertex Color도 받아왔으니 두 눈으로 확인해봐야겠죠.
UV를 불러왔을 때와 마찬가지로 Vertex Color를 꺼내옵시다.
Vertex Color 고유색을 보기 위해 Emission으로 불러왔다.
마구잡이로 그렸던 Vertex Color가 드러났다.
이렇게 그려진 Vertex Color를 텍스쳐에 곱해준다면?
파릇파릇한 텍스쳐를 곱해주면짜잔!
마치 포토샵의 곱하기 레이어를 씌운 것처럼 자연스럽게 색상이 얹어졌습니다.
이를 이용하면 단조롭게 반복되는 텍스쳐에 불규칙한 변수를 더해주어 자연스러운 연출도 가능하겠네요!
[왼쪽: 쌩 텍스쳐] [오른쪽: 버텍스 컬러를 곱해줌]
Texture Splatting
계속해서 반복해서 말하고 있지만 컬러는 곧 숫자라는 것을 자연스럽게 떠올리셔야합니다.
Vertex Color는 버텍스가 공짜로 주는 '색상' 보다
Vertex Color는 버텍스로 공짜로 주는 'float4'로 생각하는 순간 갑자기 할 수 있는 것들이 막 생각나실 겁니다.
출처 - 유니티 공식 블로그
잠시 Terrain의 이야기를 해봅시다.
넓은 지형을 만들 때 널리 쓰이는 Terrain은 지형을 만들고, 텍스쳐를 섞거나 오브젝트를 분산 배치 하는데 용이하지만
너무 무겁다는 단점과 함께 부유섬, 동굴, 밑부분이 더 깎여있는 절벽 등을 만들 수 없고 부분적으로 버텍스 밀도를 조절할 수 없다는 단점이 있습니다.
지금 부터 저희가 할 건 Vertex Color를 이용해 Texture Splatting을 해보는 겁니다!
Ploy brush의 매시 조형 기능을 사용한다면 지형의 높낮이도 표현할 수 있고 아니 이럴 수가 오브젝트 분산 배치 기능도 있습니다. 그래요! 우리가 Terrain을 직접 만드는 겁니다!
Paint texture on Mesh를 선택하니 경고창이 떴다.
텍스쳐 블렌드 기능 역시 있지만 텍스쳐 블렌드 쉐이더를 따로 짜줘야합니다. 지금은 Vertex Color를 이용해봅시다.
가장 먼저 할건 Vertex Color를 모두 '검정색'으로 칠해주는 것.
알파를 제외하면 (1,1,1)의 값을 이미 가지고 있기 때문에 (0,0,0)으로 초기화 해주는 겁니다.
(완벽한 검정색이 아닌 이유는 GI때문. Emission이여도 약간의 연산은 들어간다.Directional Light를 꺼주면 깨끗한 검정색을 볼 수 있다.)
이제 각 채널의 값을 마스킹 레이어로써 사용하는 겁니다.
포토샵으로 생각한다면 각각의 채널이
포토샵의 레이어 마스크
이 친구의 역할을 수행하는 겁니다. 그래요....포토샵은 쉐이더.......
r 채널만 출력했다.
RGB도 R따로 G따로 B따로 보면 1과 0. 흑백으로 표시 되잖아요?
저희가 수도 없이 사용했던 레이어 마스크는.....float이었던 겁니다................쟤 흑백만 되잖아요....이유가 있었던 거임....
때문에 노란색, 마젠타처럼 2채널 이상 1의 값을 가지고 있는 색상이 아닌 빨강, 초록, 파랑으로 칠해줍시다.
그럼 이제 마스킹할 텍스쳐를 불러 올 수 있도록 Properties를 생성해주어야겠죠.
배경 레이어에 들어갈 거 하나,
R에 마스킹할 거 하나,
G에 마스킹할 거 하나,
B에 마스킹할 거 하나.
해서 총 4개의 텍스쳐를 불러올 수 있도록 만들어줍시다.
텍스쳐 4개를 불러올 수 있다!
위의 코드 상태에서는 나머지 칸에도 텍스쳐를 불러와도 Baxe Tex에 넣은 텍스쳐만 표시가 될 것입니다.
그럼 어떻게 해야 Vertex Color의 RGB채널을 이용해 마스킹 할 수 있을까요?
우리는 이전 포스트에서 이미 마스킹을 해봤습니다. 기억 안나신다고요...? 네....여기를 클릭해주세요.....
float!
각각의 채널도 float. Lerp함수의 중간값 역할을 하는 s값도 float! 이 점을 이용해 텍스쳐를 블랜딩 해봅시다!
lerp(텍스쳐1, 텍스쳐2, 마스킹 채널)Rtex에 넣었던 텍스쳐가 표시 되었다.
Vertex Color를 빨갛게 칠해준 영역만큼 두번째 텍스쳐가 표시되었습니다!
lerp를 좀 더 간단하게 생각하면
lerp(이 텍스쳐를 바탕으로, 이 텍스쳐를, 얘가 가진 값만큼 표시하겠습니다)
입니다.
이제 이어서 G와 B채널에도 텍스쳐를 마스킹해봅시다.
각각 RGB채널에 해당하는 텍스쳐가 마스킹 되었다.
나머지 텍스쳐 들도 각각 초록색과 파란색을 칠해주었던 영역만큼 마스킹이 되었습니다.
하지만 여기서 한가지 의문점이 듭니다.
o.Emission을 저렇게 많이 사용해도 되나? 중복해서 사용했는데 오류가 왜 안나지?
코딩은 오른쪽부터 읽고 위에서부터 아래로 읽습니다.
가장 아래 적힌 친구가 절대적이죠.
예를 들어서 40번째 줄에 float3 A = float3(1,0,0);이라고 변수 선언을 하고,
그 아래 41번째 줄에 A = A + float3(0,1,0); 라고 작성한하면
42번째 줄에 o.Emission = A; 로 출력했을 때 빨간색이 아닌 노란색이 나오게 됩니다.
즉,
A와 B를 섞고 A와 B를 섞은 거에 C를 섞고 A와 B를 섞은 거에 C를 섞은 거에 D를 섞은 것을 출렸했다.
물론 이것보다 더 복잡하게 짤 수도, 더 명료하게 짤 수도 있습니다. lerp를 사용하지 않고 마스킹을 할 수도 있습니다.
Normal map, Smoothness
텍스쳐만 있으면 아무래도 밋밋합니다. Normal map과 Smoothness를 넣어봅시다.
노말....이 친구는 무진장 특이한 녀석입니다. 뭔가....특별한...그런....
너무 길어져서 조금 정리해줬다
위에서 부터 차근차근 알아봅시다.
bump
Properties를 만드는 건 지금까지 텍스쳐를 불러왔던 방법과 같습니다. 단 기본 값을 'bump'로 넣어줍시다.
white를 넣는다고 해서 에러가 나거나 그런 것은 아니지만 Normal에 흰색이 들어갈 경우엔 빛 연산은 이상하게 받을 수가 있습니다. 어차피 텍스쳐를 넣어주면 없어질 값이지만 Normal 텍스쳐를 넣기 전까지 이상하게 연산되고 있음 작업에 차질이 생길 수도 있으니깐요.
#pragma target 3.0
사실 이 친구는 이전까지 지워도 상관 없었습니다. 지워도 쉐이더가 정상적으로 잘 작동했죠.
하지만 지금 부터 지우면 에러가 납니다. 바로 Shader 2.0의 한계에 도달한 것.
이 친구를 지울 경우 기본으로 OpenGL ES 2.0으로 작동을 하게 되는데,
이 버전은 인터폴레이터를 최대 8개까지만 지원합니다.
현재 이미 텍스쳐를 4개나 사용하고 있는 상황에서 노말맵까지 추가를 못하는 겁니다. 능력오버!
때문에 CGPROGRAM을 시작하면서 #pragma target 3.0을 선언함으로써 더 최신 버전을 사용하겠다 말하는 것.
대신 이걸 사용하면 구버전의 기기에선 돌아가지 않습니다. 구버전이라고 해도 갤럭시3 정도지만.
현재#pragma target 3.0를 사용하지 않는 곳은 없다라고 생각해도 될 정도로 범용적으로 사용되고 있지만
중국이나 동남아쪽의 시장을 생각한다면 완전히 배제할 수는 없는 상황입니다. 물론 이거보다 더 높은 버전도 있습니다.
메뉴얼에서도 최소한으로 사용하길 권장하고 있다.
sampler 2D와 uv, tex2D 변수 선언은 이전과 같으니 넘어가고~~
지금까지와 하나 더 다른 것이 바로 Normal을 출력할 때의 선언입니다.
UnpackNormal
Albedo와 Emission의 경우엔 별도의 함수 없이 알맞는 변수명만 가져와도 결과를 출력해 주었습니다.
하지만 Normal은 따로 전용 함수를 붙여줘야합니다.
그냥 변수명만 입력하면 오류는 나지 않지만 제대로 출력이 되지 않습니다.
그냥 까맣게 되어버렸다.
그렇게 적용한 Normal map은...
적용이 되긴 했는데... 뭔가 약하다.
모든 물체는 적던 많던 스펙큘러(Speculer)를 가지고 있습니다.
특정 물체에 '질감'을 결정짓는 것이 바로 이 Speculer인데 Speculer가 없기 때문에 뭔가 어색해 보이는 것.
Normal map은 Speculer가 없으면 잘 느껴지지 않습니다.
그럼 Speculer도 연산해달라고 요청합시다!
유니티에선 Speculer를 Smoothness라는 이름으로 부릅니다.
SurfaceOutputStandard에 선언되어있다.
Normal이 더 잘 보인다!
Normal이 좀 더 선명해짐과 동시에 '젖은' 느낌이 나는 이유는
물체가 물에 젖었을 때 Speculer의 비율이 더 높아지기 때문입니다.
Normal을 출력하는 방법은 몇가지 더 있는데,
변수를 먼저 선언한 뒤 노말을 불러온다면 노말의 '강도'를 조절하는 것이 가능해집니다.
UnpackNormal함수를 사용하기 위해선 반드시 float3여야한다.Normal이 강해졌다!
각각 R과 G채널에 20을 곱해주었더니 노말이 강력해졌습니다.
여기서 주의할 점은 R과 G채널만 건들여주어야 한다는 것. B채널을 건들이게 되면 에러가 발생됩니다.