<4> 셰이더 기초 (3) : UV 컨트롤
1. UV 컨트롤
- UV?
텍스쳐의 좌표계를 의미한다. U는 X로, V는 Y로 이해하면 됩니다.
주의할 점!
엔진에서 차용하는 API에 따라 UV 좌표의 기준이 다릅니다.
언리얼에서는 DirectX를, 유니티에서는 OpenGL의 기준을 따르니 이 점 꼭 생각하고 작업하도록 합시다.
모델링 데이터를 엔진에 가져가면, UV의 데이터값이 float2(U,V) 로 들어갑니다.
데이터의 버텍스 위치에 UV좌표값을 입력하여 계산합니다. 이를 활용하여 UV를 컨트롤할 수 있 수 있습니다.
셰이더를 한 번 열어보자. 이 안에 UV 정보는 어디에 있을까요?
Struct Input 에서 엔진에 입력된 UV값을 받아오고 있습니다. UV이기 때문에 역시나 float2 값을 쓰고 있네요.
- UV 컨트롤
float2 UV 값에 연산을 해봅시다. 어떤 결과가 나올까요?
기본 상태
(1) 곱하기
fixed4 c = tex2D (_MainTex, IN.uv_MainTex*2);
를 해봅니다.
모든 UV 좌표값에 2를 곱하니
(0.0)*2 = (0.0)
(1.1)*2 = (2.2)
결과적으로 해당 텍스쳐는 작아지게 됩니다.
유니티에 적용시킨 상태입니다. 텍스쳐가 작아지고, 타일링 되었습니다.
타일링이 된 이유는 텍스쳐의 설정이 Repeat으로 되어있어 빈 공간에 텍스쳐를 반복하여 출력하기 떄문입니다.
텍스쳐의 설정을 Clamp로 바꿔주면,
작아진 상태 하나만 출력되는 것을 확인할 수 있습니다.
변수를 활용하면 다음과 같이 활용할 수 있습니다.
텍스쳐의 설정을 다시 Repeat 으로 바꾸고 _UV변수값을 조절해보면
위와 같은 결과를 알 수 있습니다.
여기서 알 수 있는 것은 다음과 같습니다:
Tilling은 곱하기(*) 연산으로 이루어진다. UV 좌표값이 커질 수록 텍스쳐는 작아진다.
하지만,
유니티의 Tiling 기능은 가로(X)와 세로(Y)의 값을 각기 조절할 수 있는 것에 비해, 지금의 코드는 XY가 동시에 연산되어 아쉽지요. U와 V의 좌표값을 각기 연산 후 출력할 수 있도록 해보겠습니다.
기능을 만들기 앞서 필요한 인터페이스를 설정해주고, 변수를 선언해 연결해주었습니다.
void surf에 위와 같은 코드를 입력해주었습니다.
fixed4 c = tex2D (_MainTex, IN.uv_MainTex * float2(_U,_V));
uv는 (U,V)로 나뉘어 float2의 값을 받아온다 했었죠.
따라서 uv데이터에 float2 값을 연산, (U,V)의 각 자리에 연산할 float 변수를 다르게 설정해주면 원하는 결과를 얻을 수 있습니다.
(2) 더하기
UV 좌표값에 덧셈 연산을 하면 어떤 결과나 나올까요?
기본 상태
fixed4 c = tex2D (_MainTex, IN.uv_MainTex +1);
을 해봅시다.
모든 UV 좌표값에 1을 더하니
(0.0)+1 = (1.1)
(1.1)+1 = (2.2)
그대로입니다...
텍스쳐의 좌표가 한 번 돌아 원상태의 모습을 띠고 있습니다.
더하는 값을 0.5로 바꾸어보겠습니다.
텍스쳐가 옮겨졌습니다.
위와 같은 방식으로 U,V에 더할 변수를 나누어 각기 계산해줄 수 있습니다.
타일링에 쓴 코드에서 곱셈을 더하기로만 바꿔주었습니다.
이와 같이 구현 됩니다.
여기서 알 수 있는 것은 다음과 같습니다:
Offset은 더하기(+) 연산으로 이루어진다. 이를 통해 텍스쳐를 옮길 수 있다.
(3) _Time 함수를 이용하여 UV 애니메이션을 넣기
_Time 함수는 내장되어있는 함수로, 이를 이용하면 시간의 흐름에 따른 애니메이션을 줄 수 있습니다.
(_Time 함수에 자세히 더 알아보고 싶어 검색해 보았지만...못 알아들었습니다ㅠ 당장은 이 함수를 사용하면 UV를 움직일 수 있다, 정도로 이해하고 있습니다.)
위와 같이 IN.ux_MainTex.x 값에 _Time함수를 사용해보았습니다. (+Time.y)
적용하고 구현된 것을 살펴보면
특별한 조작 없이도 텍스쳐가 X축의 방향으로 흐르는 것을 볼 수 있습니다.
(4) 전체 코드
타일링, 옵셋, UV 애니메이션 기능을 모두 넣은 전체 코드입니다.
현재는 Emission으로 _Time이 출력되도록 해두었습니다.
2. 활용(1): 불 만들기
새로운 셰이더파일을 생성하여 불 텍스쳐(A라 명명하겠습니다.)를 하나 넣어줍니다.
알파가 있는 텍스쳐임에도, 현재로썬 적용되지 않습니다. 먼저 알파를 적용시키도록 합니다.
(1) 알파 적용
알파를 적용시키기 위해서 먼저 만져주어야 하는 부분이 있습니다.
SubShader 블록에서
1. RanderType을 Transparent (투명) 으로 바꿔 입력해주어야 합니다.
2. #pragma surface surf Standard의 뒤에 alpha:blend 를 입력해주어야 합니다.
이렇게 하고나면
텍스쳐의 알파가 적용되는 모습을 확인할 수 있습니다.
셰이더에 불 텍스쳐(B라 명명하겠습니다)를 하나 더 추가했습니다. 두 불을 만들어보도록 합시다!
(2) B 텍스쳐를 y축 방향, 위쪽으로 흐르게 만들기
_Time 함수를 활용해 B텍스쳐를 위쪽으로 흐르게 해봅시다.
불길이 타오르는 것을 의도해 만들도록 합니다!
우선 B텍스쳐의 x와 y축을 분리하고, y축에 _Time.y를 더합니다.
흐르긴 흐르는데 위에서 아래로 흘러내립니다. 용암처럼 되어버렸네요..
흐르는 방향을 바꾸어주려면 B텍스쳐의 y축과 _Time함수의 연산을 더하기(+)에서 빼기(-)로 바꾸어주면 됩니다. 흐르는 방향이 반전 되도록 합니다.
빼기로 바꾸어준 모습.
(3) A와 B 텍스쳐에 멀티플라이 적용
이제 A와 B 텍스쳐를 섞어봅시다. 멀티플라이 연산, 곱하기(*)를 활용할 것입니다.
먼저, Emission에서 rgb끼리 곱해봅니다.
텍스쳐 A와 B의 rgb를 곱한 모습
알파에도 역시 곱하기 연산이 가능합니다. A와 B의 알파값을 곱해봅니다.
그러면 이와 같은 결과물이 나옵니다!
알파쪽을 조금 살펴보자면
텍스쳐a의 알파
텍스쳐b의 알파
텍스쳐 a와 b의 알파를 곱한 버전입니다. 곱셈 연산이기 때문에 검은 부분이 밝은 부분을 덮어버립니다.
위의 형태에서 텍스쳐 b에 _Time함수를 주어 위쪽으로 흐르게 만든 것입니다.
이렇게요. 여기서 알파을 적용하여 출력합니다. 알파끼리도 곱셈 연산을 해줍니다.
텍스쳐의 알파끼리도 곱셈 연산이 작용하고, 잘 구현되는 모습을 확인할 수 있습니다.
Emission의 출력값을 rgb로 바꾸어주면 다시 원래의 결과물이 구현됩니다.
그럴듯해 보이지만 사실 이 불은 쓸 수 없는 리소스입니다. 물리기반으로 모든 연산이 돌아가고 있기 때문에 몹시 무겁습니다. 가장 잘 보이는 예시로
조명을 키면 그림자가 보입니다..그림자 연산까지 되고 있네요.
사용할만한 것, 필요한 연산만 이루어지는 셰이더를 만들기 위해선 더 많은 것을 만져야 합니다. (그렇다고..합니다. 도대체 뭘까요...)
3. 활용(2) : UV 조작, 불 만들기
텍스쳐를 새로 넣어주었습니다.
텍스쳐를 한 장 더 추가해서 회색 컬러를 넣어줍니다.
이후 이렇게, 불 텍스쳐의 rgb값에 회색의 r값을 더해주면 어떻게 될까요?
텍스쳐가 이동합니다. 회색 텍스쳐의 r값, 0.5가 각각 더해진 것입니다.
여기서 회색 텍스쳐는 UV 위치를 조정하는 용도로 쓰이고 있습니다.
색상은 곧 데이터, 숫자이고 이를 알고 활용하면 더 다양한 것을 구현할 수 있겠습니다!
회색 텍스쳐를 빼고 다음의 텍스쳐를 넣어보겠습니다.
가운데, 흰 색일수록 1에 가까운 값을 가지고 있어 UV가 이동할테고,
바깥쪽, 검은 색은 0의 값을 가지고 있기 때문에 UV가 이동하지 않을 것입니다.
적용하면, 흰 색인 중앙 부분만 UV가 이동한 모습을 확인할 수 있습니다. 텍스쳐가 일그러졌네요!
여기서 UV 조작용 텍스쳐에 _Time 함수를 적용시키면 어떻게 될까요?
값이 높은 곳은 쪽으로 반복해 이동하는 UV 애니메이션이 구현되었습니다. 텍스쳐가 계속해서 일그러집니다.
다음으로, 이 텍스쳐를 사용해 UV를 움직여보겠습니다.
지나치게 정신없이 움직입니다. UV가 덜 움직이도록 만들고싶어요. 어떻게 해야할까요?
일단 밝은 부분을 줄여야겠죠. 두 가지 방법이 있습니다.
(1) 텍스쳐를 수정해 명도를 낮춘다. (포토샵 등, 엔진 외부에서 작업)
(2) 엔진 내에서 텍스쳐의 색상값을 곱하기(*)연산하여 텍스쳐를 어둡게 만든다.
이렇게 d.r에 0.3 값을 곱해주었습니다. 텍스쳐의 명도가 0.3만큼 낮아질 것입니다.
적용하고 보면, 이전보다 훨씬 진정된....UV 애니메이션이 구현된 것을 볼 수 있습니다.
Time 함수값을 조절하면 속도를 느리거나, 더 빠르게 만들 수도 있습니다.
여기서 텍스쳐가 위로 흐르도록 바꾸어보겠습니다.
float2에서 첫 번째 값, x는 0으로 두고 y에는 _Time함수를 적용하여 텍스쳐가 y축으로 흐르게 합니다.
이 float2 값을 uv데이터에서 빼기 연산을 해주면, 텍스쳐가 아래에서 위로 으르게 되겠죠!
구현된 모습입니다.
여기서 첫 번째 텍스쳐를 불 텍스쳐로 교체하고, 알파를 적용해보겠습니다.
그럴 듯한 불 셰이더가 되었습니다!
마지막으로, UV 조작용 텍스쳐의 명도(최종 결과물이 구겨지는 정도)와 _Time 속도 값을 인스펙터서 조절할 수 있도록 인터페이스를 제작하고 변수를 정리해줍니다.
전체 코드
조작에 따라 UV가 움직이거나, (변수 Br)
속도를 조절할 수 있습니다. (변수 _TimeSpeed)