그래픽/TA STUDIO (동아리 활동)

[TA STUDIO] #3. MainTex와 SubTex. (깜빡이는 원 만들기)

케이이이이 2019. 10. 13. 18:40

본 포스팅은 청강문화산업대학교의 게임 제작 동아리 L.A.B.S

TA STUDIO에서 공부한 내용을 바탕으로 작성되었습니다.

 


 

오늘은 #1강에서 다뤘던 반짝이는 원 만들기(:bloom 효과) 에서 조금 더 나아가봅시다.

2강에서 배웠던 _Time 함수를 응용해, 깜빡이는 원을 만들어 보려고 합니다.

 

#1강에서 했던 부분에서 이어 진행하니, 위쪽의 링크를 참고해주세요!

 


#들어가기 전, _Time에 대해 조금 더.

 

_Time은 씬이 열린 다음부터의 시간입니다. (이 값을 반환합니다.)

_Time은 각각 (x, y, z, w)로 나눌 수 있으며, 각각 (t/20, t, t*2, t*3)에 대응합니다. 즉,

_Time.x = t / 20 ; // 1 / 20의 속도
_Time.y = t ;  // 본래의 속도(시간)
_Time.z = t * 2 ;  // 2배의 속도
_Time.w = t * 3 ;  // 3배의 속도

정도로 볼 수 있으며,

저번에 언급했듯이 쉐이더 애니메이션을 위해, 원하는 UV방향으로 텍스처를 흐르게 할 수 있습니다.

 

 

 

#그럼 이제, 깜빡이게 해보자!

1강까지의 상태. 포스트 프로세싱: bloom을 넣어주었다.

이 상태까진 다들 되어있으시겠죠?

여기서, 동그라미를 깜빡이게 하려면 어떻게 해야 할까요?

 

 

1) 컬러 피커 값을 조절해 키 애니메이션을 만든다.

2) 투명도에 관여하는 프로퍼티 값을 만들어 키 애니메이션을 만든다.

프로퍼티 값을 생성하고, 활용하는 방법은 추후 다시 자세하게...

사실 이런 식으로 키 애니메이션을 만드는게 가장 간단한 방법이죠.

(특히나 이런 간단한 애니메이션을 만든다면 더더욱.)

 

하지만, 일정 수준 이상 넘어간다면 이런 키 애니메이션으로는 한계가 존재합니다.

(노가다를 통해 할 수는 있겠지만, 능률이 떨어지겠죠.)

따라서 우리는 쉐이더를 통해 이러한 애니메이션을 만드는 방법을 알아 볼 겁니다.

 

 

#_Time을 사용한다면..?

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex + _Time.y) * _Color;
			o.Emission = float3(1, 1.5, 1.7);
			//o.Albedo = c.rgb;
			o.Alpha = c.a;
        }
        ENDCG

이전 코드에서, IN.uv_MainTex에 _Time.y를 더해봅시다.

무슨 일이 일어날까요?

 

대각선으로 원이 움직인다.

#2강 내용을 읽으셨던 분들은 어느정도 유추하실 수 있었겠죠?

 

IN.uv_MainTex는 IN.uv_MainTex.x와 IN.uv_MainTex.y로 분리할 수 있는데.

우리는 분리를 안 하고 그냥 _Time.y를 더해줬으니까요. x와 y방향이 동시에 더해지는 것과 같죠.

(정확히는 텍스쳐의 u, v)

 

자세한 내용은 #2강을 참고하세요!

 

그럼 우리는 이 방법을 어떻게 이용해서 깜빡이게 만들 수 있을까요?

 

포토샵에서의 예시 (TA STUDIO장님 자료)

여기서는 흔히 마스킹 이라고 부르는 기법을 통해 깜빡임을 구현해보려고 합니다.

이를 위해 서브 텍스트를 만들어 볼게요!

 


 

사이즈는 16*16으로 해 주었다.
딱 절반을 나눠서, 검은색과 하얀색으로 나누고, 알파채널도 똑같이 적용해줌.
저장할 땐 언제나 Targa로!

 

32bit로 저장해야 알파 채널이 저장된다.


 

검은색과 하얀색을 절반 나눠서 텍스쳐를 만든 이유가 뭘까요?

이유는 0과 1로 표현되는 값을 가져오기 위해서입니다. (쉐이더에서 검은색은 0, 하얀색은 1로 표현.)

 

자세한 내용은 하면서 이어가 볼게요!

일단, 엔진에 방금 만든 subTex를 불러와줍시다.

 

텍스쳐 설정도 잊지 말자.

저번에 원 텍스쳐에 했던 것 처럼 설정을 해줍시다.

깜빡이게 만들기 전에, 한번 이 텍스쳐를 그냥 MainTex로 적용해볼까요?

 

Material 텍스쳐에 끌어서 적용해보자.

자, 어떻게 보일까요?

실제 텍스쳐는 대각선으로 움직이지만, 텍스쳐의 모양 때문에 x축으로만 움직이는 것으로 보인다.

뭔가 색다른 맛이 있는 애니메이션이네요. (ㅋㅋ)

 

다시 돌아와서, 우리는 텍스쳐를 하나만 적용하는게 아니죠!

두 개의 텍스쳐를 가져와서, 이 텍스쳐의 값을 이용해 깜빡이게 만들어야 합니다.

 

텍스쳐를 하나 더 받게 하기 위해서, 쉐이더를 열어 줍시다.

	Properties
	{
		_Color("Color", Color) = (1,1,1,1)
		_MainTex("Albedo (RGB)", 2D) = "white" {}
		_SubTex("SubTex", 2D) = "white" {} //Properties에 이 부분을 추가해주자!
    	}

먼저, Unity에서 텍스쳐를 받아올 수 있도록 Properties에 값을 추가해줍시다.

이 경우에는, 변수명은 _SubTex로, 유니티에 표시될 이름은 SubTex로 했습니다.

 

 


#잠깐 옆으로 빠져서... Properties 구조 살펴보기.

앞으로도 Properties를 자유자재로 사용하려면, 구조를 살펴봐야겠죠.

_SubTex("SubTex", 2D) = "white" {}

변수명 인스펙터 GUI 명 타입 기본 값

변수명: 쉐이더 코딩 내에서 사용할 이름입니다. 값을 가져오거나, 호출 할 때 이 이름을 사용합니다.

인스펙터 GUI명: Unity내의 Inspector 인터페이스에서 표시할 이름입니다.

타입: 인터페이스의 형식입니다. 위의 2D의 경우, 텍스쳐를 받는 인터페이스라는 의미를 가집니다.

기본 값: 인터페이스 생성 시 갖는 기본값 입니다. 위의 경우, 텍스처가 아무것도 없을 때 흰색으로 설정하겠다는 의미입니다.

 

Properties Type에 들어갈 수 있는 속성들 중 일부입니다. 

타입 내용
Range(min, max) 최소값(min)부터 최대값(max)까지의 범위를 갖는 float 프로퍼티의 슬라이더를 만든다.
Color Inspector탭에 Color={float, float, float, float)를 선택할 수 있는 컬러 피커를 만든다.
2D 쉐이더로 드래그할 수 있는 텍스쳐 견본을 만든다.
Rect 2의 승수가 아닌 텍스처 견본을 만든다. 2D GUI 요소와 같은 기능 한다 
Cube Inspector탭에서 쉐이더로 드래그 할 수 있는 큐브 맵(cube map) 견본을 만든다.
Float Inspector 탭에 입력이 가능한 float 값을 만든다.
Vector 방향, 색상을 만들 수 있는 4개의 float 프로퍼티를 만든다.

자세한 내용은: https://docs.unity3d.com/kr/530/Manual/SL-Properties.html 이쪽으로!

 


 

그 다음, _SubTex를 쉐이더에서 사용하기 위한 코드를 입력해줍시다.

        sampler2D _MainTex;
	sampler2D _SubTex; //이곳과

        struct Input
        {
            float2 uv_MainTex;
	    float2 uv_SubTex; //이곳을 추가!
        };

간단하게

sampler는 텍스처가 UV로 넘어가기 전 단계... 라고 보면 될 것 같고.

Input 구조체 안에 규칙에 맞게 코드를 입력해, 엔진에서 텍스처를 받아온다고 보면 될 것 같습니다.

이 경우에는 U와 V로 이루어졌기 때문에 float2, _SubTex의 uv라는 뜻으로 앞에 uv를 붙입니다.

그렇다고 들었어요... 그렇대요... 아마...?

 

마지막으로, surf 함수에 텍스처 연산 함수인 tex2D를 적어줍니다.

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex + _Time.y) * _Color;
            float4 sub = tex2D(_SubTex, IN.uv_SubTex); //이곳을 추가해준다!

            o.Emission = float3(1, 1.5, 1.7);
            //o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
        ENDCG

 

위의 fixed c = tex2D와 같은데, _MainTex가 _SubTex로 바뀌었다 뿐입니다.

 

그리고 출력을 위해 Emission값과 Alpha값에 각각의 속성을 곱해줍시다.

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            float4 sub = tex2D(_SubTex, IN.uv_SubTex + _Time.y);

            o.Emission = c.rgb * sub.rgb;
            o.Alpha = c.a * sub.a;
        }
        ENDCG

저장하고 유니티로 돌아온 후,

MainTex에는 원 텍스쳐,

SubTex에는 아까 만든 반절 네모(?) 텍스쳐를 넣어주면?

 

MainTex와 SubTex를 설정해주면..?
어.. 뭔가 움직이긴 하는데요..?

깜빡이는 것과는 좀 멀죠.

SubTex 타일링의 x축 방향을 0으로 만들어 줍시다.

SubTex의 Tiling X축 값을 0으로 만들었다.
이제 된다 !

저번(1강)처럼 밝게 하고 싶으면 Emission 부분에 수치를 곱해주면 됩니다.

저는 푸른빛이 돌게 하고 싶어서 float3(0, 1.5, 1.7) 값을 곱해주겠습니다.

o.Emission = c.rgb * sub.rgb * float3(1, 1.5, 1.7);

살짝 푸른 빛이 돌게 반짝인다!

 

이렇게 되는 이유가 뭘까요?

 

MainTex는 Time 함수와 연관이 없으니 그렇다 하더라도...

SubTex에서 무엇을 하고 있는 걸까요?

 

간단하게 말하자면, 지속적으로 0~1 사이의 값을 MainTex에 곱해주고 있기 때문입니다.

쉐이더에서는 0과 1로 색을 표현합니다. (검은색은 0, 하얀색은 1 처럼.)

 

우리가 만든 SubTex 쉐이더는 0(검은색)과 1(하얀색)사이의 값을 X축 방향으로 가지도록 만들어 줬고,

이 텍스쳐를 X축의 방향으로 움직이면서 (실제로는 대각선이지만, 타일링의 모양 때문에 X축이라고 말함.) 지속적으로 MainTex에 이 값을 곱해줬기 때문입니다.

 

1이 곱해지면 원본 그대로, 0이 곱해지면 0(검은색, 혹은 투명도 100%)이 출력되겠죠?

 


아래의 뻘짓을 통해 이런 것도 할 수 있다! 이것저것 건드려보자!

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex + _Time.z) * _Color;
            float4 sub = tex2D(_SubTex, IN.uv_SubTex + _Time.y * 0.5);

            o.Emission = c.rgb * sub.rgb * float3(1, 1.5, 1.7);
            o.Alpha = c.a * sub.a;
        }

깜빡깜빡.