炸殺
카테고리
작성일
2020. 4. 28. 13:06
작성자
炸殺

이번 포스트에서는 UV에 대해 조금 더 심도 있게 다뤄보겠습니다.

 

 

 

UV 매핑란 2차원 그림을 3차원 모델로 만들기 위한 프로세스로,

간단하게 말하면 3D 모델링의 '전개도'를 만드는 것입니다.

 

 

 

이전 포스트에서

우리가 모델링을 하고, UV를 펴고 저장을 하게되면 그 모양 그대로 보존되는 것이 아니라

각각의 버텍스에 위치, UV, Normal, Vertex Color등의 데이터가 저장되고,

엔진에 불러왔을 때 이 데이터를 바탕으로 엔진에서 '재구성'이 이루어진다고 이야기 했었죠?

 

버텍스에 저장되어있는 데이터를 기반으로 엔진에서 다시 만들기 때문에

버텍스를 겹쳐두거나 쓰레기 버텍스가 있을 경우 엔진에 불러왔을 때 오류가 생기기도 합니다.

 

 

 

 

UV는 이러한 버텍스에 들어있는 정보 값 중 하나로 struct Input에서 float2를 주고 받아옵니다.

UVW=XYZ=RGB......

float3(4)인 컬러가 비트에 상관없이 백분률로 환산되어 비슷한 결과값을 가져왔던 것이 기억 나시나요?

UV역시 0~1사이의 값이 들어가며 백분율로 환산되어 오브젝트, 텍스쳐의 크기와 상관없이

UV의 좌표는 변하지 않고 일정한 비율로 적용됩니다.

 

 

 

 

 

이러한 UV는 사용하는 그래픽 API에 따라 기준점의 차이가 있습니다.

 

OpenGL의 경우 기준점이 왼쪽 하단에,

DirectX의 경우 기준점이 왼쪽 상단에 위치합니다.

 

유니티는 OpenGL, 언리얼은 DirectX를 기본으로 사용하고 있습니다.

 

 

 

 

 

 

UV연산

 

 

색상의 경우 0이하의 값이나 1이상의 값이 적용되었을 때 HDR의 영역으로 넘어갔는데요,

UV에 1이상의 값을 준다면 어떻게 될까요?

 

이 텍스쳐의
UV에 2를 곱해주었다.

 

 

 

각각의 플롯에 2를 곱해주게 되면 (0, 0)은 그대로 (0, 0). (1, 1)은 (2, 2). (0.5, 0.5)은 (1, 1)이 될 것입니다.

이렇게 말이죠.

 

기존의 영역은 OpenGL기준 왼쪽 하단 1/4의 영역을 차지하게 되겠죠. 이제 세이브를 하고 엔진을 보면.

 

 

 

 

빈 영역에 텍스쳐가 반복되고 있다.

곱셈은 타일링이라는 것을 확인할 수 있습니다.

 

 

 

 

 

물론 타일링이 되지 않도록 하는 것 역시 가능합니다.

텍스쳐의 Inspector를 살펴보면 Wrap Mode라는 항목이 있습니다.

 

Repeat : 기본적으로 셋팅되어있는 상태. 텍스쳐가 타일링 된다.
Clamp : 가장 끝에 위치한 픽셀을 집어서 늘립니다.
Mirror : 말 그대로 거울처럼 뒤집어져 반복됩니다.
Per-axis : 커스텀 모드. U(X)축과 V(Y)축을 따로따로 설정해줄 수 있습니다.
위 이미지에선 U-Mirror, V-Clamp를 주었습니다.

 

Mirror Once의 경우 Clamp와 차이가 없는데요,

Mirror Once기능은 OpenGL ES에서는 항상 지원되는 것이 아니라고 합니다.(?)

자세한 내용은 유니티 메뉴얼을 참고합시다. https://docs.unity3d.com/ScriptReference/TextureWrapMode.MirrorOnce.html

 

 

 

 

 

이제 여기서

'한 방향으로만 타일링 하는 건 안될까?'

라는 의문이 드는데요,

 

U,V 두 개 다에 2를 곱했으니까... U나 V하나에만 곱해준다면....? 

에러가 났습니다. (유니티가 노력했지만 일단 에러. 노란 경고창이 뜬다.)

 

 

tex2D 함수의 괄호 안에는 (Tex, TexUV)의 값만 가져올 수 있습니다.

저 안에 들어가는 값을 멋대로 늘이고 줄일 수 없는데요.

TexUV가 들어가는 곳에는 UV, 반드시 float2의 값만 들어가야합니다.

(UV가 아니더라도 float2값만 들어가면 에러는 나지 않습니다.)

 

 

 

눈치채셨나요...? 코드를 보면 제가 float2라고 안 말해줬어요.....

(0,0)이라고 쳤다고 엔진이 아~ float2구나~ 알아주지 않습니다. float2라고 앞에 정확히 명시를 해주어야합니다.

 

 

 

괄호 앞에 float2를 쓰자 제대로 작동한다.

 

 

 

 

곱해줄 숫자를 프로퍼티스로 빼서 엔진에서 조작할 수 있게도 만들어 봅시다.

 

곱셈이기 때문에 초기값에 0을 넣으면 안된다!

 

 

아주 잘 작동한다.

 

위에 작성한 코드만이 정답인 것은 아닙니다. 

U와 V에 각각의 플롯이 곱해진다는 것만 성립이 되면 되는데요,

 

 

 

같은 플롯끼리는 곱할 수 있는 성질!

 

x는 x끼리, y는 y끼리 곱해진다.

float4 maintex = tex2D(_MainTex, float2(IN.uv_MainTex.x * _U, IN.uv_MainTex.y *_V));
float4 maintex = tex2D(_MainTex, fIN.uv_MainTex * float2(_U, _V));

두 개 모두 같은 결과가 출력됩니다!

 

 

 


 

+의문점

다음과 같이 x,y 혹은 r,g를 입력했을 때는 정상적으로 값이 출력되지만
다음과 같이 u,v를 입력하였을 땐
해당 오류가 뜬다. uv는 왜 입력할 수 없을까..?

 

 

 


 

 

 

이 타일링.... 어디서 많이 봤는데......

또이잉

그렇습니다. 이미 2D 프로퍼티에 내장 되어있는 기능이죠.

그럼 그 아래 있는 Offset을 만들려면 어떻게 해야할까요? Offset은 텍스쳐를 '이동'시켜줍니다.

 

 

'곱셈'을 했을 땐 (1, 1)이 (0.5, 0.5)자리에 오면서 타일링이 되었었죠.

그러면 UV에 '덧셈'을 해준다면?

 

각 플롯에 -0.5를 더해주면 (0,0)이 정 가운데로 온다.

 

0.5를 더한 것과 -0.5를 더한 것과 결과는 같다.

 

 

 

UV에 플롯을 더해주자 입력한 값만큼 대각선으로 '이동' 한 것을 확인할 수 있습니다.

 

오프셋의 이동 한 장 요약

 

 

 

오프셋 역시 타일링처럼 가로 세로 각각 제어하는 것이 가능합니다. 방법 역시 동일.

야호!

 

 

 

 

 

 

 

Time 변수

 

텍스쳐가 좀 더 자연스럽게 흘러갈 수는 없을까? 라는 고민을 해결해줄 'Time 변수'를 소개합니다.

float4의 값을 가지고 있는 Time 변수는 엔진 안에서 이미 선언되어있는 변수입니다.

 

 

 

Time과 같이 엔진안에 내장 되어 미리 선언 되어있는 변수를 '빌트인 셰이더 변수'라고 부르는데요,

Time 뿐만 아니라 오브젝트 변환 매트릭스, 광원 파라미터 등의 다양한 변수가 포함되어있습니다.

 

이 변수들은 자동으로 포함되는 UnityShaderVariables.cginc 인클루드 파일에 모두 선언되어있기 때문에

따로 변수를 선언하지 않고 변수명만 쏙 가져다 써도 에러가 나지 않는 것입니다!

 

 

 

시간 변수는 총 4가지 종류가 있다.

한국어 메뉴얼에는 _Time과 _CosTime만 적혀있지만

영문 메뉴얼을 확인해보면 _SinTime과 unity_DeltaTime까지 총 4가지가 표시됩니다.

 

 

지금 이용해볼 시간 변수는 가장 상단에 있는 '_Time'변수!

 

앞에서부터 차근차근 읽어보면 변수명 '_Time', float4의 값을 가지고 있으며

설명에 (t/20, t, t*2, t*3)이 유독 눈에 들어오네요. (나머지는 영어 문장이라 머리에서 무시한 것도 있다.)

 

 

 

 

 

괄호 안의 값은 각각의 '속도'값이며 이는 앞에서 부터 x,y,z,w와 대응합니다.

저희는 여기서 원하는 속도를 쏙 꺼내서 사용하면 되는 것입니다.

 

 

 

Animated materials를 켜면 Scene에서도 움직인다.

 

 

여기에 float 변수 하나를 만들어 프로퍼티에 올리고 Time 변수에 곱해준다면

Inspector에서 속도 조절도 할 수 있게 됩니다.

 

예~~~

 

 

 

 

 

 

 

 

UV '보기'

 

 

지난 포스트에서 UV를 컬러로 나타내면 어떤 색을 띄게 되는지 설명했었습니다. 

셰이더 코드를 통해 이 색을 직접 확인해 봅시다.

 

 

 

방법은 간단합니다. Emission이나 Albedo에 각각 U, V의 데이터를 넣어주면 됩니다!

 

 

1의 값을 가지고 있는 곳은 흰색, 0의 값을 가지고 있는 곳은 검은 색으로 표시 됩니다.

(그라데이션 지는 이유는 lerp가 들어가서이다.)

 

 

 

이 둘을 동시에 보는 법 역시 간단합니다. 숫자=색이라는 것을 잊지 맙시다!

 

 

 

비는 3번째 자리는 쿨하게 0을 넣어주자.

안녕 UV!

 

U의 자리엔 붉은 색이, V의 자리엔 초록색이 들어오면서

좌표 (0,0)인 곳에는 검은색, (1,1)인 곳에는 노란색으로 표시되는 것을 확인할 수 있습니다!

 

 

 

 

 

 

 

 

 

 

 

불 쉐이더 만들기

 

UV의 타일링과 오프셋, Time을 이용한 애니메이션까지 알게 되었습니다.

이제 이를 이용하여 불 쉐이더를 만들어 봅시다.

 

 

불은 불규칙적인 움직임을 가지며 타오릅니다. 이걸 어떻게 구현할 수 있을까요?

 

 

 

위에서 알아보았던 내용들 중 UV가 0과 1의 값을 가지고 있던 것에 주목해 봅시다.

0과 1의 값을 가진 텍스쳐를 UV로 넣어버리면???

 

 

실험에 참여할 텍스쳐

 

실험을 위해 텍스쳐 2개를 불러옵니다. 텍스쳐2의 r값을 텍스쳐1의 UV값에 더해봅시다.

 

반드시 maintex2부터 선언해주자. 코드는 위에서 아래로 읽기 때문에 사용할 선언문을 아래다 쓰면 못찾아서 에러가 난다..
마스킹 텍스쳐의 사이즈를 줄이고 압축을 하지 않으면 자동으로 블렌딩 되어 깔끔한 이미지를 얻을 수 있다.

 

가운데가 푹 찌그러졌습니다. 왜 이런 모양이 나오는 걸까요?

 

 

 

Tex2의 r채널을 보면 (사실 흑백 이미지라 그냥 봐도)

 

중앙으로 올 수록 점점 하얀색을 띄고 있습니다.

이를 숫자로 바꿔본다면 가장자리는 0의 값을, 중앙은 1의 값을 가지고 있는 것이죠.

 

이러한 값을 가지고있는 텍스쳐를 UV에다 더해버리니... 

0을 더한 가장자리는 움직이지 않고 1을 더한 중앙만 대각선으로 이동해 저런 모양이 되어버린 것입니다.

 

 

여기서 Tex2에게 Time 함수를 적용한다면?

 

 

 

꿀렁꿀렁

 

 

 

 

여기에서 0과 1이 난잡하게 뒤섞인

 

이런 텍스쳐를 넣는다면????

본 모습을 알아볼 수 없게 완전 구겨졌다.

 

 

분명 0을 더했을 땐 이동하지 않고, 1을 더했을 땐 많이 이동한 것으로 보아....

텍스쳐를 어둡게 하면 '덜'구겨지지 않을까요?

 

 

 

텍스쳐를 어둡게하는 방법은 2가지가 있는데

 

첫번째. 포토샵에서 텍스쳐를 어둡게 만든다.

두번째. 노드에서 텍스쳐를 곱해준다.

 

가 있습니다. 우리는 노드를 배웠으니 노드에서 곱해줍시다!

 

 

 

물 표면이 출렁이는 것 같기도 하다.

 

실제로 물의 굴절을 표현할 때 물아래를 캡쳐하여 텍스쳐를 구겨 출력하기도 합니다.

0.2(float)이 들어간 자리를 프로퍼티스로 빼 외부에서 구김의 정도를 조절하게끔 하는 것 역시 가능합니다.

 

어? 불규칙한 물의 흔들림....... 불규칙한 불의 흔들림.....

 

 

 

 

 

 

본격적으로 불 쉐이더를 만들기 전에

알파를 불러오는 것부터 해봅시다.

 

알파를 불러와도 그대로다...

 

분명 알파를 불러왔음에도 텍스쳐엔 아무런 변화가 없습니다. 이를 해결하기 위해선 이 친구의 호적을 파야합니다.

 

 

쉐이더 코드를 거슬러 올라가다 보면....

 

아니 이럴수가 Rander Type이 "Opaque"로 되어있습니다.

Opaque는 '불투명'이라는 뜻으로 알파값을 가지고 있어도 Opaque로 랜더링 할 시 알파 값은 싹 날아갑니다.

 

여기에 Opaque대신 "Transparent(투명)"을 적어줍시다.

이제 알파가 적용된다면 정말 좋겠지만 한 번 더 허락을 받아야합니다.

 

 

첫번째 줄 #pragma surface surf Standard 뒤에 적혀있는 fullforwardshadows를 지우고

그 자리에 alpha:blend를 적어넣어줍니다.

 

빛을 내는 불에 그림자가 연산되면 안되니까 Emission을 넣어주자.

 

알파가 적용되었다.

 

드디어 알파가 예쁘게 들어갔습니다.

이제 이 불을 '타오르게' 만들어야하는데요,

 

불에게 생명을 불어넣어주기 위해 텍스쳐를 한장 더 추가한 뒤

아까처럼 Tex1의 UV에 Tex2의 값을 더한 뒤 _Time 변수를 넣어봅시다!

 

 

불 텍스쳐를 Clamp로 변경해주자

 

 

분명 불은 아래에서 위로 타오르는데 대각선 아래로 타오르고 있습니다...

Tex2의 UV가 아래에서 위로 흐르도록 세로축인 V(Y)에만 Time을 줘볼까요?!

 

_Time을 더할경우엔 위에서 아래로 UV가 흐른다.
불꽃이 타오른다!!!!

 

 

 

 

이 밖에도 알파가 있는 텍스쳐들의 알파를 서로 곱하거나,

컬러를 서로 곱하는 등 텍스쳐에 따라 다양한 불꽃을 만들어낼 수 있습니다. (한 번 해보자!)

 

 

알파도 곱하고 컬러도 곱하니 색다른 연출이 나왔다.

 

 

 

 

좀 더 편하게 값을 조절해볼 수 있도록 구김의 정도와, 속도를 조절할 수 있는 프로퍼티를 만들어봅시다.

 

값을 조정해주니 느낌이 확 달라졌다!