본문 바로가기
Shader

4주차, UV를 다뤄보자.

by JDCM 2020. 10. 5.

좀 더 깔끔하게 보고 싶으신 분들은, 구글 도큐먼트 링크를 추천합니다.

docs.google.com/document/d/1F-HqqxzrMuYFUdj8KL5oPY9tRep8xD6SMBHEmw9VWyc/edit?usp=sharing

 

4주차, UV를 다뤄보자.

4주차, UV를 다뤄보자. 텍스쳐를 배웠다면 텍스쳐를 보여주는 UV도 공략해야 제맛! 3주차 돌아보기 그래, 분명 텍스처 두 개를 Lerp해서... ( 둘 다 좋은데 어떻게 고르라고! ) 우리는 우선 Shader 코드�

docs.google.com

 

4주차,

UV를 다뤄보자.

텍스쳐를 배웠다면 텍스쳐를 보여주는 UV도 공략해야 제맛!

1. 3주차 돌아보기

그래, 분명 텍스처 두 개를 Lerp해서...

 

( 둘 다 좋은데 어떻게 고르라고! )

 

우리는 우선 Shader 코드를 만들면 “완전 초기화”를 해줘야 한다는 사실을 알고 있다.

기본적으로 들어간 값들이 있기 때문에 싹 치워준 다음, 코드를 작성하는 방법을 배웠다.

(응? 잘 모르겠으면 역시 3주차로 회귀해야지 : https://docs.google.com/document/d/1wJvxya6Wts9d4404O_js6wajwYbVPjv3l23wF6Hv_s4/edit?usp=sharing)

 

또한 텍스처를 불러올 때에는 주어진 ‘형식’이 있는 것도 배웠다. 그 형식은 … 

 

_Name(“display name”, 2D) = “name” { options } 를 통해 선언하고


sampler2D _Name으로 Sampling(샘플링)하며


Struct(구조체) Input에 float2 uv_Name 을 넣어 UV를 불러와


void surf란에 float4 texResult = tex2D( texture, IN. textureUV );만 넣어준다.

 

이 외에도 회색으로 만들기, 선형 보간(lerp)를 배웠지만…

lerp를 배우는 과정에서 다른 한 텍스쳐만의 Tiling(타일링)을 바꿔줄 수 있다는 장점을 알았다.

 

근데, 타일링도 쉐이더다. 도대체 어떻게 되는 것일까?

오늘 한 번 알아보도록 하자.

 

2. UV와 Time 알아보기

1. UV 일단 알아보기

 

(UV하면 테트리스 밖에 생각 안나는데...)

(https://3dtotal.com/tutorials/t/an-in-depth-look-at-uvw-mapping-object-waylon-brinck-max)

 

우리가 텍스쳐를 가져올 때, UV도 따로 가져왔던 기억이 있다.

“Struct(구조체) Input에 float2 uv_Name 을 넣어 UV를 불러와”ㅆ...었다.

즉 엔진에게 ‘우리가 텍스쳐를 가져오긴 했는데, UV 값 좀 주라’하고 요청하는 것이다. 

 

UV는 결국 float2의 값으로 가져오게 된다. 즉 UV를 다르게 보자면 XY와 다를 것이 없다는 것이다. 


그럼 U와 V의 값에도 수치가 들어가고 있고 텍스쳐의 위치가 0~1사이 값으로 출력되고 있다는 것도 알 수 있다.

 

사진으로 나타내자면 이런 형태인데,

만약 저 값에다가 2를 곱해준다면…?



(0,0) 자리에는 2가 곱해져도 동일하게 (0,0) 이지만…

(0.5, 0.5) 자리는 (1, 1)

(1, 1) 자리에는 (2, 2) 값이 들어가게 된다.

 

그럼 이미지는 작아지게 된다…

문제는 남은 자리는 뭐가 나오게 될까?


일단 노드로 해볼 수밖에 없다. 유니티는 내가 시키면 해주니까!

 

우선 초기화는 언제나 기본이다. 지금 코드는 완벽한 코드는 아니다.

Texture 설정을 넣은 리셋 코드이다. 초기화는 습관으로 만들어두도록 하자!

 

UV에만 2를 곱하게 되면 이런 결과가 나온다….



 아무 텍스쳐가… 4개가 되었다.

어라? 아까 남았던 부분을 대신해서 타일링이 되었다.

 

이건 바로 이 설정에서 알 수 있다.

Material이 아니라, Texture 자체의 Inspector를 살펴보자.

 

Texture 자체의 설정에 Wrap Mode를 설정 할 경우…

 

 

  • Repeat : 상단처럼 ‘반복’되어 나온다.

  • Clamp : 위치를 범위에 고정시킨다. (위치를 제일 가까운 사용 가능한 값으로 옮긴다.)

  • Mirror : 거울처럼 각각 위치를 뒤집는다.

  • Mirror Once : Clamp와 큰 차이가 없었다. (추후 알아봐야지!) 

  • Per-Axis : U, V 따로 설정을 줄 수 있다. (이건 재밌으니 직접 해보세요!)

 

아무튼 이렇게 했다면 UV를 타일링 하면서… 값이 있고 그걸 수정할 수 있다는 점도 알았다. 그렇다면 Tiling 하단의 Offset도 알아볼 수 있다.

 

곱하기를 통해 Texture가 4/1(4분의 1)로 줄어들었다면, 값을 더하게 되었을 때에는 위치를 변경할 수 있는 것이다.

 

Tiling : 텍스쳐에 곱하기

 

와! 그럼 가로, 세로로도 “늘일 수” 있나요!?

물론 가능하다. UV가 들어가는 값에 float 값으로 _U, _V를 정의하고 U와 V에 따로 곱해준다면…잘 모르겠으니 우선 코드를 보자.

그럼 결과는 …

 

 

U와 V를 따로따로 값을 주어 곱해준다면 이런 형태로 나타나게 되는 것이다!

대신 더 가벼운 방법도 있다.

 

float4 Tex = tex2D(_MainTex, IN.uv_MainTex * float2(_U, _V));

 

어차피 uv_MainTex가 UV, 즉 float2의 값을 가지므로 동일한 float2 값에 _U, _V를 곱해주는 것과 동일한 것이다.

이제 타일링도, 늘이는 것도 가능하다면 … “이동” 할 때가 되었다.

 

(아, 이건 발진인가?)

타일링을 하려면 각각 UV에 값을 곱하면 된다는 것을 알았다. 

만약 “더한다면” 어떤 일이 일어날까? 

상단에 있었던 코드의 곱하기를 그저 더하기로 바꾸게 되면 이렇게 된다.

 

 

U(X)에 더할 경우 가로로, V(Y)에 더할 경우 세로로 움직이는 것을 알 수 있다.


(0,0) ~ (1,1) 사이의 값에서 더해지면서

(1,1) ~ (2,2) 의 값도 보여지게 되는 것이다.

 

Wrap 설정이 Repeat가 되어있어야지 자연스럽게 이미지가 이어져 보였다.



와! 이제 이동까지 알았다. 

여기서 우리의 지금까지의 지루함을 해결해 줄 친구가 나왔다.

 

(아니, 얘는 멈추는 애라서...안됩니다.)

 

2. Time 사용해보기 

(나도 시간을 조종할 수 있다 이거야!)

 

시간(Time)이다. 결과물을 보려면 직접 수치를 조절해줘야 했던 우리의 부담을 Time을 통해 해결하게 될 것이다! 문제는 …

 

우리가 시간을 넣으려는 텍스쳐의 정의 값은 float4이므로, Time의 값을 넣을 때

Time.x / Time.y / Time.z / Time.w 등 각각 나눠지는데, 우리는 우선 “Time.y”를 써보자. 

Time은 선언할 필요도 없는 내장 변수이므로 걱정 없이

어렵지 않다. _U, _V를 썼던 자리에 _Time.y 만 작성해 넣어주면 된다. 결과는 …

 

어...너무 느려요, 또는 너무 빨라요. 하는 경우라면 원하는 값을 곱해주면 된다. (곱하기 먼저)

만약 _Time.y의 값이 정말 맘에 들지 않는다면 … 곱할 값을 따로 Properties에 빼어 속도 조절 기능을 만들 수도 있는 일이다. 

(이제 진짜 시간을 조절할 수 있다.)

 

3. 진짜 UV 보기 

UV를 어떻게 다루는지도 봤고, 시간으로 UV를 움직이게도 해봤다.

근데 문제는… 진짜 UV의 모습을 모른다. 우리는 UV의 값을 다루기만 했지 UV의 진짜 모습을 모르는 것이다. 한번 그 얼굴을 만나러 가보자.

 

우선 쉐이더를 리셋하고, 해당 코드를 집어넣어 보았다.

어라? 안 나온다.

잘 찾아보니 UV의 경우 half3 경로로 되어 있어서 float4에 값에 넣을 수 없다고 한다. (정확한지 잘 모르겠다.)

그럼 UV의 U만 float 형태로 가져와보자.

그랬더니 빼꼼, U가 고개를 내밀었다. 동일한 방식으로 y도 볼 수 있다.

그라데이션으로 나왔지만 결국 0~1 값 뿐, 중간의 회색은 자동적으로 lerp되어 나온 것이다.

아… 근데 계속 이렇게 따로 봐야만 하나요? 오작교를 만들어주자.

 

o.Emission = float3(IN.uv_MainTex.x, IN.uv_MainTex.y, 0);

 

어차피 float 값이라면 float3 값으로 추출해주어도 가능한 것이다. 그리고 이 결과가 나왔다.

float(1,1,0)처럼, U는 붉은 색, Y는 녹색으로 서로 색의 수치를 가지고 나오게 되었다.




(번외) Time 변수 보기

상단에서 UV Animation 을 넣을 때 Time이 각각 float4 형식으로 나뉘어진다고 했었다.

그런 _Time에도 갖가지 방식이 있다.

 

_Time

float4

Time(t/20, t, t*2, t*3) (각각 R, G, B, A 값이다.) 

_SinTime

float4

사인 함수를 쓴다. Sine of time(t/8, t/4, t/2, t)

_CosTime

float4

코사인 함수를 쓴다. Cosine of time(t/8, t/4, t/2, t)

unity_DeltaTime

float4

프레임 단위 수의 시간을 잰다.

Delta time: (dt, 1/dt, sommthDt, 1/smoothDt)

 

보기 힘들겠지만, 대다수 우리가 다룰 수 있는 t 값만 들어가있는 _Time.y만 쓸 뿐이지, _SinTime이나 _CosTime같은 경우는 사용되는 일이 없다. 

 

  1. 불 쉐이더 만들어보기

(불타는 것은 컴퓨터 뿐.)

 

불은 항상 테두리가 불특정적으로 흐려져야 하기 때문에 알파(Alpha)를 가지고 있다.

일전부터 계속 이야기를 나누던 ‘알파 호적파기’의 기본적인 부분을 시도할 수 있겠다.

자, 시도하기 전에 꼭 쉐이더 코드를 리셋하고 따라해보자.



기본적인 형태는 완료했다. 자! 이제 알파를 작동시켜 보자!

void surf란에 o.Alpha = Tex.a; 를 넣어도 알파는 나오지 않는다.

심지어 o.Albedo = Tex.a; 로 설정해도 알파는 보이지도 않는다. 호적을 파보자.



호적 파기는 다음과 같다.

 

  • 단계 1번

 

SubShader 하단 란을 보면 “Opaque”를 Render하세요~ 란 태그 명령어가 보인다.

Opaque란 ‘불투명한’임으로 아무리 투명한 알파를 나타내려 하더라도 보여주지 못한다.

그럼, 우리는 ‘불투명(Opaque)’ 대신에 ‘투명(Transparent)’를 넣을 필요가 있다.

 

  • 단계 2번

 

CGPROGRAM 하단의 fullforwardshadows를 지우고 대신에 alpha:blend (이대로 써야한다!) 를 바꿔준다.

 

  • 단계 3번

 

불이니까 빛나야 하므로 Albedo가 아닌 Emission으로… 그리고 우리의 주인공을 빼먹지 말자. 이렇게 값을 넣게 된다면 …

 

 

잘 타오르고 있다. 알파 값에 맞춰서 불 테두리가 잘 잘려나왔다.

Emission이 되어서 빛까지 난다. 근데 … 진짜 불 같은가? 솔직히 대박 허접하다.

일렁이는 느낌도 없고, Emission으로 빛난다 하더라도 한계가 있는 법이다.

 

우린 이 상태에서 하나의 Texture를 더 심어보자...



그럼 알파 값은 Tex의 값으로, Emission의 출력 값으로는 Tex2가 나오고 있는 것을 확인할 수 있다. 만약 Tex2의 알파를 쓰고 싶다면 Tex2의 것도 쓸 수 있다.

 

근데 Tex2에 들어간 이미지는 위, 아래가 이어져있다. 잠시 Time 값으로 놀아보자.

 

(번외) 불기둥 만들기

솔직히 엄청 간단하다. Emission, Alpha 둘다 Tex2의 값을 가져오고 Tex2의 UV값의 V(Y)에 _Time 변수만 넣어주면 되는 것이다. 

 

다만 이렇게 되면 문제점이 하나 있는데, 값이 상승(올라감)으로써 불기둥이 상단에서 하단으로 내려가는 듯한 느낌이 된다.

불기둥이라면 하단에서 상단으로 올라가야 한다.
+값을 주었을 때 내려가는 느낌이었다면, -값을 주었을 때야말로 올라가는 느낌이 된다.


그럼, 이제 불기둥이 쑥쑥 올라갈 것이다.

 

열심히 가지고 놀았다 한다면 이제는 두 텍스쳐를 어떻게 사용할 것인가? 이다.

간단하게 포토샵의 Multiply처럼 Texture 두개를 곱해보도록 하자.

 

그럼 하단은 밝아지고 상단은 어두운 채로 Texture들이 존재하게 된다. 이 상태로 V값에 주었던 _Time값을 넣으면 상단으로 올라갈수록 어두워지는 Texture를 확인할 수 있다.

 

어? 어두운 부분이 어두워졌다는 것은 곱하기를 하면 검은 부분이 검게 남는다는 것을 알 수 있었다. 그럼… 알파에도 이 행동을 하면 똑같이 나오려나? 

알파도 동일하게 어두워졌다. 그럼 이제 Emission으로 보지 말고, Alpha에 직접 넣어서 _Time까지 넣어보자!



 

현재 상태를 보면 Tex는 고정, Tex2는 -_Time.y 형태로 흘러가게 된다!

 

1. Alpha로 왜곡해보기

(뭘 할지 일단 스포일러를 해둔다.)

텍스처 두개의 알파값을 곱하고 확인해보면서 검은 부분이 이기는(?)점을 확인할 수 있었다.

알파를 쓸 텍스쳐의 A의 0~1, 즉 희고 검은 부분에 따라서 계산이 된다는 것이다.

그럼… UV에 우리가 숫자를 더했을 때처럼, 숫자 값을 가진 텍스쳐를 더해본다면?

 

좌측이 Tex, 우측이 Tex2 이므로 Tex2의 값을 Tex의 UV값에 “더해보았다”! 그랬더니 ...

 

(텍스쳐가 깨지는 것은 압축의 문제.)

짜그라졌다. 

Tex2가 Tex의 UV를 구기고 있다면, 만약, Tex2의 UV에 _Time을 넣었을 때에는…

 

꿀렁꿀렁.

그럼 다른 텍스쳐를 넣었을 때에는요? 상단의 스포일러 텍스처는요?

 

쯔와아아아악!

자…이제 뭘 할 수 있나면요…

 

2. 모두 응용해보기!

 

 

물을 만들었습니다.

 

아직 살펴봐야 할 점은

  1. Tex 코드에서 Tex2, Tex3을 더할 때 왜곡이 너무 심하게 져서 /8 (나누기8) 값을 했더니 많이 나아졌는데… 이게 옳은 방법인지.

  2. Texture를 무려 4개를 썼는데 이게… 괜찮은 것인지? (용량을 알 수 있다면 참 좋을텐데)

  3. 심지어 Emission에 더할때도 1.5를 감소시킬 정도였습니다. (노출이 엄청 심해짐)

 

'Shader' 카테고리의 다른 글

6주차, 빛을 알아보자.  (0) 2023.01.30
5주차, Vertex Color를 주자.  (0) 2020.10.08
3주차, 함수 조작을 배워보자.  (0) 2020.09.17
2주차, 쉐이더 코드를 배워보자.  (1) 2020.09.15
1주차, 쉐이더를 배워보자.  (0) 2020.09.14