炸殺
카테고리
작성일
2020. 4. 19. 23:44
작성자
炸殺

 

 

이전 게시글에서 '변수 선언'에 대하여 설명을 했었는데요.

이를 응용하여 RBG 색상을 각각 따로따로 조절할 수 있도록 'RGB 슬라이더'를 만들어 가볍게 워밍업을 해봅시다.

 

 

우선 유저가 컨트롤 할 수 있는 '슬라이더'가 필요하겠죠?

가장 먼저 프로퍼티스(Properties)에서 인터페이스를 만들어줍니다.

슬라이더가 필요하니 'Range'를 사용하는 것이 적절하겠네요.

 

 

오류가 없는지 중간중간 저장하여 꼭 확인하자.

 

 

 

 

 

SubShader를 3가지 구역으로 나눴을 때 남는 공간이 있었습니다. 

 

까먹으셨을까봐 다시 가져옴

 

 

위에 이미지에 보시면 half _Glossiness; 등등으로 적혀있는 것이 보이시나요?

이는 프로퍼티스에 작성해두었던 스크립트를 CGPROGAM와 연결해주는 것과 같습니다.

원하는 기능을 만들기 위해선 void surf 에서 코드를 작성해주어야하지만

Subshader 안에 변수 선언을 해주지 않으면 프로퍼티스와 연결이 되지 않아 외부에서 조절이 불가능해집니다.

 

 

즉,

 

Subshader라는 집이 있다면, 밖(Properties)에 있는 물건을 거실에 놓아두어야 각각의 방에서 가져다 쓸 수 있습니다.

 

 

선풍기에 비교하자면 프로퍼티스는 버튼, void surf는 선풍기의 기계부품, 변수 선언은 전원 코드라고 볼 수 있습니다.

전원 코드를 연결하지 않으면 백날천날 강풍 버튼을 눌러도 선풍기는 작동하지 않죠. 

 

 

 

 

참고로 쉐이더의 첫 기본형은 '남는 공간'이 struct Inpt을 중심으로  나뉘어져 있는데

sampler2D와 그 외 것들을 구분해주기 위함 일 뿐 한 곳에 몰아서 작성한다 하여도 문제는 없습니다.

 

 

 

 

SubShader 안 남는 공간에서 변수 선언을 해주어야 프로퍼티스와 연결된다.

 

 

 

각각의 Range를 float으로 변수 선언한 뒤, 에미션의 각 채널에 해당하는 슬라이더의 변수 명을 넣어주었습니다.

 

바를 움직여 컬러를 설정할 수 있게 되었다!

 

 

 

 

 

여기서 AD가 이렇게 말합니다.

 

 

" 밝기 조절 슬라이더가 있었으면 좋겠는데요. 슬라이더를 왼쪽 끝까지 밀면 검정, 오른쪽 끝까지 밀면 흰색이 되게 해주세요. "

 

 

 

 

우선 밝기를 조절할 슬라이더부터 추가해 줍시다. 아까랑 똑같이 만들어주면 돼요.

프로퍼티스에서 인터페이스를 만들어주고~ Subshader에서 변수 선언을 해줍니다.

 

프로퍼티스 입력 완료!
변수 선언도 완료!

 

 

이제 void surf에서 연산만 해주면 되는데........

아마 여기서 많이 막히실 겁니다.

곱하자니....검정색은 되지만 흰색이 안되고.... 더하자니 흰색은 되지만 검정색은 안되고...

 

 

 

해결법은 생각보다 간단합니다.

 

프로퍼티스에서 Range의 범위를 -1에서 +1까지로 지정한 뒤 더해주면 됩니다.

0과 1사이의 수에 -1을 하면 HDR의 영역으로 넘어가지만 반드시 검은색이, +1을 하면 반드시 흰색이 되죠.

 

 

최종 화면
잘 작동한다!

 

 

 

 

 

 

 

 

 

Texture

 

워밍업은 여기서 마치고 본격적으로 텍스쳐를 출력해 봅시다.

역시나 가장 처음은 유저가 원하는 텍스쳐를 넣을 수 있도록 인터페이스를 만들어 줘야겠죠.

 

초기값은 "white" 혹은 "Black"으로 설정해줍시다. Red나 Gray가 불가능한 건 아닙니다!

텍스쳐의 변수명의 경우 예외명을 제외한 원하는 이름으로 지정하여도 상관 없지만

_MainTex라는 변수명을 이용할 경우 쉐이더를 갈아끼워도 호환이 가능합니다.  

 

 

초기값 옆 {}는 옵션을 입력하기 위한 곳 같지만... 저 곳을 이용하는 예제를 발견하신 분이 있다면 부디 연락주세요.

 

 

 

 

 

 

 

이제 Subshader안에서 변수 선언을 해야겠죠?

 

가장 처음 쉐이더를 생성하면 기본적으로 적혀있다. 지우지 않았다면 그대로 써도 됨.

 

텍스쳐는 float으로 가져오는 것이 아닌 ' sampler2D '로 가져옵니다.

 

 

처음으로 돌아가서, 어떤 이미지의 픽셀 하나하나의 색상을 결정해주는 것이 바로 쉐이더 코드입니다.

픽셀 하나마다 우리가 짠 쉐이더 코드 전체가 한 번씩 돌아가는 것이죠.

 

이처럼 텍스쳐를 입히는 것 역시

텍스쳐의 픽셀 하나하나씩 쏙쏙 찝어내어 오브젝트의 지정된 위치에 콕콕 박아주는 것입니다.

여기서 픽셀 하나하나 핀셋으로 쏙쏙 찝어내는 과정을 Sampilng(표본 추출)이라고 합니다.

 

 

한 땀 한 땀.....

 

이렇게 한 땀 한 땀 찝어낸 픽셀들을 오브젝트의 지정된 '위치'에 콕콕 박아야 한다고 했는데요.

여기서 필요한 위치가 바로 'UV의 죄표값'입니다.

 

 

한 마디로 정리하자면 텍스쳐를 오브젝트에 입히기 위해선

 

1. 입력할 텍스쳐

2. UV 좌표

 

이 두가지 정보가 필요합니다.

 

 

 

 

 

 

 

UV

 

UV좌표는 RGB, XYW와 마찬가지로 UVW란 세 개의 체널을 가지고 있지만 2차원 좌표만 필요로 하기 때문에

마지막 W를 제외한 UV 두 개의 채널을 이용합니다.

(물론 3차원 텍스쳐를 이용할 때 사용하지만 지금은 잊자)

 

 

UV를 컬러로 나타내면 다음과 같다.

 

숫자는 색상, 색상은 숫자.

 

RGB = XYZ = UVW인 이유를 여기서 다시 한 번 알 수 있습니다.

UV도 결국엔 float2(number,number)이기 때문에

좌표가 (0,0)인 곳은 검정색, (1,0)인 곳은 빨간색, (0,1)인 곳은 녹색 (1,1)인 곳은 노란색을 띄고 있으며,

또한 U는 가로, V는 세로로 그라데이션이 지고 있습니다. 

(언리얼은 좌측 상단이 (0.0)입니다.)

 

U = R = X / V = G = Y

 

 

 

3DMAX Edit UVWs

 

3DMAX에서 UV를 펼 때 버텍스를 잡고 이리저리 움직이면

하단의 UV 숫자가 내가 버텍스를 움직이는 만큼 변하는 것을 확인할 수 있습니다.

 

그렇습니다. UV를 편다는 것은 버택스에 좌표를 부여하는 과정입니다.

 

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

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

엔진에 불러왔을 때 이 데이터를 바탕으로 엔진에서 재구성이 이루어집니다.

 

 

때문에 UV값은 유저가 따로 설정해주는 것이 아닌, 모델링 데이터에 포함 되어있는 값을 '받아오는 것'인데,

이럴 때 사용하는 곳이 바로

 

struct Input 입니다.

 

지난 게시글에서 설명했던 것처럼 

struct Input은 Input이라는 이름의 구조체로 '엔진에서 받아와야하는 할 데이터'를 선언해주는 공간입니다.

이 안에서 선언할 수 있는 변수의 종류는 한정 되어있으며 이는 유니티 메뉴얼에서 확인하실 수 있습니다.

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

 

 

이 방은 비워둘 수 없는데 아마 다음과 같이 적혀있을 겁니다.

 

MainTex의 UV를 받아오는 변수 선언이다.

 

UV는 float2이기에 float2 상자에 넣어준 뒤 이전과 같이 UV를 필요로 하는 변수명을 입력해줍니다.

그 앞에 소문자로 uv를 적어주면 UV불러오기 완료입니다.

 

 

sampler2D _MainTex;'뭘 입힐 것인지'를 받아와주고,

sturct Input 내부의
float2 uv_MainTex;'어디에 입혀야하는지'를 받아와준다. 

 

이제 모든 준비를 마쳤으니 void surf에서 핵심 모터를 만들어주러 가봅시다.

 

 

 

 

 

 

 

 

 

tex2D 함수

 

이전 게시글에서 Input IN에 대해선 언급하지 않았었는데요,

기본적으로 쉐이더에선 다른 방에 들어가있는 꺼내오지 못하고 '거실'에 있는 변수만 방에 가지고 들어올 수 있습니다.

 

UV좌표가 필요한데 그건 sturct Input 방에 있잖아요!

void surf 뒤 괄호 안에 입력되어 있는 것는 해당 함수에 구조체를 불러오겠다는 것을 의미합니다.

 

'Input IN'은 sturct Input 방에 있는 걸 꺼내서 void surf 방에 집어 넣겠습니다. 라는 선언인 것이죠.

이제 sturct Input은 void surf 방 안에 있는 가방입니다. 

 

 

 

텍스쳐는 R,G,B,A까지 총 4개의 채널을 이용합니다.

어느 텍스쳐(sampler2D _MainTex;)를 어디에(float2 uv_MainTex;) 넣을 건지는 준비했으니....

 

텍스쳐를 UV에 맞춰 출력하기 위해 사용하는 함수가 바로 tex2D(Tex, TexUV) 입니다.

 

 

이렇게 입력을 해주면........
에러가 납니다.

 

 

 

아까 괄호 안은 함수에 불러올 구조체를 적어둔 것이라고 했죠?

현재 Input 이라는 구조체와 SurfaceOutputStandard라는 구조체가 불러져 왔으니 

void surf 방 안에 있는 가방은 2개가 됩니다.

 

어느 가방에서 꺼낸 건지 명시해주지 않아 에러가 난 것.

 

각각의 구조체 뒤에 붙어있는 IN과 o는 불러온 구조체를 해당하는 이름으로 부르겠다는 선언임으로

 

가방.꺼낼물건

 

이제 텍스쳐를 받고 붙이는 친구도 만들어졌으니

이 텍스쳐에 그림자 연산이 들어가야한다면 Albedo, 그림자가 연산되지 않아야 한다면 Emission에 넣어주면 끝.

 

여기서 텍스쳐는 float4를 이용하지만 Albedo나 Emission은 float3를 이용합니다.

그냥 넣어도 마지막 채널이 자동으로 짤려서 들어가지만 그것은 유니티가 힘내고 있는 것일 뿐,

원칙적으로 안되기 때문에

.rgb를 붙여 4개의 채널 중 앞의 3개의 채널만 꺼내서 사용하겠다고 덧붙여 주는 것이 좋습니다.

 

 

 

제 여자친구가 잘 출력되고 있습니다

 

 

 

따지고 보면 텍스쳐도 결국엔 float4 이기 때문에

컬러를 곱한다던가, 위에서 시도했던 밝기 조절을 넣는다던가의 응용역시 가능합니다.

 

 

 

 

근데 여기서 기획자가 말합니다.

 

 

 

" 석화 마법에 걸렸을 때 회색으로 변할 수 있게 만들어주세요. "

 

 

 

 

포토샵에서 말하자면 채도 조절 슬라이더를 만들어 달라는 것인데....

일단 이미지를 흑백으로 전환부터 해야합니다. 

 

포토샵은 커다란 쉐이더라고 했죠?

우리는 능이 할 수 있습니다.

RGB가 모두 일정한 값을 가질 때 회색이라는 색이 나옵니다. 

하지만 하나의 텍스쳐에 RGB가 갖고 있는 값은 모두 다르죠.

 

o.Emission = (R + G + B) /3;

 

위처럼 RGB를 모두 더해 최댓값을 구한 뒤 이를 3으로 나눠 각 채널에 분배 해주면 흑백 이미지를 얻을 수 있습니다.

이를 텍스쳐에 적용하려면

 

o.Emission = (MainTex.r + MainTex.g + MainTex.b) /3;

 

흑백이미지가 되었다!

 

하지만 석화 마법에 걸렸을 때 자연스럽게 흑백으로 변했으면 하는데....

 

여기서 이용할 수 있는 함수가 바로 Lerp함수 입니다.

 

 

 

 

 

 

 

 

 

 

Lerp 함수

 

https://ko.wikipedia.org/wiki/%EC%84%A0%ED%98%95_%EB%B3%B4%EA%B0%84%EB%B2%95

간단하게 설명하자면 A와 B라는 이미지가 있을 때 이 둘이 자연스럽게 전환되도록

'A와 B의 사이 값'을 구하는 공식이라고 이해하시면 좋습니다.

한 마디로 '그라데이션'입니다.

 

흔히 쓰이는 함수 중 하나로 Color나 Vector를 부드럽게 전환시켜 줄 때 사용됩니다.

 

 

러프의 공식은 다음과 같습니다.

 

lerp(x,y,s)

 

여기서 x와 y는 각각 전환 할 대상, s는 중간 값으로 한 자리인 float을 이용합니다.

주의할 점은 x와 y는 반드시 같은 단위 값을 가져야 합니다.

x가 float3라면 y도 float3, y가 float4라면 x도 float4여야하죠.

 

 

 

 

 

텍스쳐 2개를 받아 한 번 실험해 봅시다!

 

 

최애 2차 각성 하고싶어요

 

함수에서 s에 해당하는 값이 0일 경우 x에 할당된 텍스쳐가,

1일 경우 y에 할당된 텍스쳐가 선명하게 표시되는 것을 확인 할 수 있습니다.

 

 

A와 B에 각각 텍스쳐를 넣고, 마지막에 0과 1의 사이 값을 넣으면 어떻게 될까요?

 

0.5를 넣자 딱 반반씩 섞였다.

 

잠깐, 한 자릿 수 플롯....? 슬라이더....바.......? 

 

위에서 RGB슬라이더를 만들었던 것이 기억 나시나요?

맞습니다. lerp함수 마지막 자리는 float. 프로퍼티스에 Range를 만들어 여기다 넣어줄 수 있습니다.

 

 

반복 또 반복
각성~~~!!~!

 

여기서 lerp함수 y값에 (MainTex.r +MainTex.g+MainTex.b) /3 를 변수로 만들어 넣는다면

자연스럽게 택스쳐가 흑백으로 변하게 되겠네요!

 

 

 

하지만 lerp 함수의 진가는 바로 지금부터 시작입니다.

 

한자릿 수 float이 Range 하나만 있을까요?

당연히 아니죠. float4여도 원하는 채널만 쏙쏙 뽑아내 사용하는 것이 가능했죠. 

 

그렇다! 알파체널!!

 

s값에 MainTex의 알파값을 넣어주자 값이 1(흰색)인 영역은 MainTex2의 텍스쳐로 변화하였습니다.

0(검정)이었던 영역은 그대로 MainTex를 표시하고 있군요.

 

저번에 배웠던 '반전'공식 기억나시나요?

One Minus. 1에 특정 값을 빼면 그 값이 반전된 결과가 나왔었죠. 그걸 여기에 대입해 봅시다.

 

 

 

까꿍! 원본이 작아 화질이 나쁜 건 넘어가자!

 

여기서 One Minus한 알파 값에 위에서 만들었던 Range를 곱해주면

MainTex2위에 MainTex가 서서히 나타나는 것도 가능해집니다!

 

 

 

 

이 뿐만 아니라 각각 텍스쳐의 Tiling과 Offset을 개별적으로 조작할 수 있으며

버텍스 컬러를 곱하거나 위에서 만들었던 밝기 조절 슬라이더 역시 적용할 수 있습니다.