이전 포스팅 요약

이전 포스팅에서는 Unity Surface Shader를 만들고 텍스처만 출력하는 코드만 남기고 제거하고

해당 쉐이더를 분석했고, tex2D, lerp등 CG쉐이더에서 사용하는 함수를 알아보고, 

UV기본 개념에 대해서 포스팅했다.

 

Unity Surface Shader 기초 2강 - UV 응용

이전 포스팅에서 설명했지만 UV는 간단히 말해서 어떤 위치에 텍스처를 출력할것이냐 라는 좌표이다

다시 말해서 텍스처 좌표이다(Tex Coordinate).

 

UV의 활용은 크게 두 가지다. Tiling(UV곱하기), Offset(UV더하기)

 

이점을 활용하여 가장 손쉽게 할 수 있는 실시간으로 움직이는 이미지 쉐이더를 만들어 보겠다.

 

-빌트인 쉐이더 변수-

Unity Surface Shader에는 몇 가지 기능을 제공하는 성질의 예약된 변수가 존재한다.

예를 들어 런타임 시간가져오기, 변환 행렬, 광원 인자값 등이 있다.

이런 빌트인 쉐이더 변수들은 일반 변수하고 다르게 사용자가 직접 선언할 필요없고

이미 만들어져 있는 개념으로 UnityShaderVariables.cginc파일에 미리 선언되어 있다.

참고자료 : https://docs.unity3d.com/Manual/SL-UnityShaderVariables.html

 

본 주제로 실시간으로 움직이는 쉐이더를 만들기 위해 앞서 설명한 런타임 시간을 가져올 수 있는

float4 _Time이라는 빌트인 쉐이더 변수를 사용해 볼 것이다.

 

 void surf (Input IN, inout SurfaceOutputStandard o)
 {
 	fixed4 c = tex2D(_MainTex, IN.uv_MainTex + _Time.y);
 	o.Emission = c.rgb;
}

위 코드는 UV값에 _Time변수를 더하여 Offset을 계속 반복시키는 쉐이더 코드이다.

해당 코드에서 주목할점은 _Time.y이다, _Time은 float4 자료형으로된 변수이다.

즉 4개의 값이 _Time안에 존재한다는 것이다.

 

레퍼런스사이트에서 _Time에 대해서 보면 위와 같이 설명이 되어 있고, 좀더 쉽게 해석하자면

_Time.x  : t/20 (런타임 시간 / 20)

_Time.y  : t (런타임 시간)

_Time.z  : t * 2 (런타임 시간 * 2)

_Time.w : t * 3 (런타임 시간 * 3)

 

요약하면 float4에 xyzw각자 다른 값이 들어있지만 결국 _Time.y의 런타임 시간 값만 사용하면 된다.

 

 

-텍스처의 색상값으로 UV 조작-

     void surf (Input IN, inout SurfaceOutputStandard o)
     {
       fixed4 d = tex2D(_Tex_UV, IN.uv_Tex_UV) ;
       fixed4 c = tex2D(_MainTex, IN.uv_MainTex + d.r);

       o.Emission = c.rgb;
      }

이전에는 UV에 숫자값을 더하거나 곱하여 조작을 했는데 이번엔 텍스처를 한개 받아서 텍스처의 채널 1개로

UV값을 조작해 봤다.

채널의 색상값에 따라서 이전에 숫자로 UV조작한것과 동일한 결과나 나왔다.

 

여기서 만약 가운데 부분만 흰색값이 들어있는 텍스처를 넣으면 위 사진과 같이 찌그러진 느낌이 나게 된다.

이제 이 부분을 응용하여 몇몇의 쉐이더를 만들어 보자

 

-UV응용 : 물 쉐이더 만들기-

텍스처로 UV조작을 한 기능을 이용하여 간단한 물 쉐이더를 만들었다.

 

-1. 텍스처 2개를 받는 쉐이더 만들기

 

우선 상하좌우가 Tiling했을때 이어지는 물 텍스처와 전체적으로 UV를 조작할 수 있는 구름형태의

노이즈 텍스처를 준비한다.

 

Shader "Custom/Sha_Water"
{
    Properties
    {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
		_Tex_Noise("Distotion", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }

        CGPROGRAM

        #pragma surface surf Standard 

        sampler2D _MainTex;
		sampler2D _Tex_Noise;

        struct Input
        {
            float2 uv_MainTex;
			float2 uv_Tex_Noise;
        };

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
			fixed4 d = tex2D(_Tex_Noise, IN.uv_Tex_Noise);
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) ;
			
            o.Emission = c.rgb;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

2개의 텍스처를 받아서 출력하는 쉐이더를 만든다.

 

그리고 엔진내에서 위 사진과 같이 적용한다.

 

-2. Tiling 늘리기

 

 fixed4 c = tex2D (_MainTex, IN.uv_MainTex * 3.0f) ;

그리고 추가로 UV를 곱하여 Tiling을 늘려서 위 사진과 같이 더 넓어 보이는 물로 만든다.

 

 

-3. 꾸물꾸물 효과 만들기

fixed4 d = tex2D(_Tex_Noise, IN.uv_Tex_Noise + _Time.y);
fixed4 c = tex2D (_MainTex, IN.uv_MainTex * 3.0f + (d.r)) ;

다음 노이즈 텍스처의 UV에 시간값을 더하여 텍스처를 움직이게 만들고

물텍스처에다가 노이즈텍스처의 채널값 1개를 더하여 Offset효과를 일으켜서 텍스처가 꾸물꾸물 거리게 만든다.

이걸로 물쉐이더를 완성하고 마무리 작업으로 인터페이스 변수를 만들어 최대한 자연스러운 물을 만들어 본다.

 

-4. 마무리(인터페이스 변수를 활용)

Shader "Custom/Sha_Water"
{
	Properties
	{
		_MainTex("Albedo (RGB)", 2D) = "white" {}
		_Tex_Noise("Distotion", 2D) = "white" {}

		_Noise_Speed("Noise_Speed", Range(0, 1)) = 0.1
		_Noise_Power("Noise_Power", Range(0, 5)) = 1.5
	}
		SubShader
		{
			Tags { "RenderType" = "Opaque" }

			CGPROGRAM

			#pragma surface surf Standard 

			sampler2D _MainTex;
			sampler2D _Tex_Noise;

			float _Noise_Speed;
			float _Noise_Power;

			struct Input
			{
				float2 uv_MainTex;
				float2 uv_Tex_Noise;
			};

			void surf(Input IN, inout SurfaceOutputStandard o)
			{
				fixed4 d = tex2D(_Tex_Noise, IN.uv_Tex_Noise + (_Time.y * _Noise_Speed));
				fixed4 c = tex2D(_MainTex, IN.uv_MainTex * 3.0f + (d.r * _Noise_Power));


				o.Emission = c.rgb;

				o.Alpha = c.a;
			}
			ENDCG
		}
			FallBack "Diffuse"
}

 

-UV응용 : 불 쉐이더 만들기-

물쉐이더와 비슷한 원리로 두 개의 텍스처를 이용해 간단한 불 쉐이더를 만들어보자.

 

-1. 알파 블랜딩

불 쉐이더를 만들기 전에 첫 번째 난관이다.

이전에 정사각형 모양 텍스처가 아닌 정확히 불모양으로 출력되야하는데 뒷 배경까지 출력되기 때문이다.

이를 해결할 포인트는 쉐이더를 알파 블랜딩 쉐이더로 바꿔야한다.

 

알파 블랜딩이란?

우선 블랜딩이란 서로 다른 색이 혼합되서 새로운 색을 나타내는 것이다.

그럼 알파 블랜딩은 이미지 위에 또 다른 이미지를 겹쳤을때 마치 투명하게 비치는 효과를 내기 위해 컴퓨터의 색상표현 값에 RGB + A가 추가 되서 혼합되는 것이다.

 

-2. 알파 블랜딩 쉐이더 제작

우선 알파 블랜딩용 쉐이더로 하기 위해서는 좀더 복잡한 과정이 있지만 당장 알파채널을 이용해 블랜딩하기 위해

최소한의 과정을 이용했다.

위 사진에서 Tags에 Opaque를 Transperant로 변경하고 "Queue" = "Transparent"를 추가한다.

그 다음 #pragma에 alpha:fade를 추가한다.

 

Opaque를 Transperant로 바꾸는 의미는 반투명 종류의 쉐이더로 변경한다는 뜻이고

다음 "Queue"태그는 서브쉐이더 태그라고 하며 랜더링 순서를 결정할때 사용한다.

마지막으로 #pragma에 추가한 alpha:fade는 옵션 파라미터에 투명성 페이드를 활성화한다는 뜻이다.

참고 자료 : https://docs.unity3d.com/kr/current/Manual/SL-SubShaderTags.html

 

  void surf (Input IN, inout SurfaceOutputStandard o)
  {
      fixed4 d = tex2D(_Tex_UV, IN.uv_Tex_UV) ;
      fixed4 c = tex2D(_MainTex, IN.uv_MainTex);

    o.Emission = c.rgb;
    o.Alpha = c.a;
}

그리고 SurfaceOutputStandard의 Alpha 변수에 텍스처 변수의 .a값을 넣는다.

 

그 결과 Emission에 넣은 RGB맵이 Alpha에 넣은 알파채널에 의해 우측 결과물 처럼 깨끗하게 불모양이 나오게 된다.

 

-3. 두 개의 불 텍스처로 이글이글 효과내기

우선 위 두개의 텍스처로 한 개의 불을 만들어 내는 쉐이더를 만들것이다.

 

  void surf (Input IN, inout SurfaceOutputStandard o)
        {
			fixed4 d = tex2D(_Tex_Noise, IN.uv_Tex_Noise + float2(0.0f, _Time.y));
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex + d.r);

            o.Emission = c.rgb;
          
            o.Alpha = c.a * d.a;
        }

이전 물 쉐이더와 비슷하게 노이즈 텍스처가 흐르는 코드를 만들었다.

 

첫 번째 특이점은 

fixed4 d = tex2D(_Tex_Noise, IN.uv_Tex_Noise + float2(0.0f, _Time.y));

노이즈 부분에서 UV값을 더해주는 코드를 대각선 방향으로 Offset이동하는 것이 아닌 한쪽 방향으로 이동해야 하기

때문에 위 코드 처럼 float2를 따로 만들어서 원하는 방향의 변수만 더 해준다.

 

위 코드대로 하면 노이즈가 아래로 흐르는 것을 확인 할 수 있는데.

fixed4 d = tex2D(_Tex_Noise, IN.uv_Tex_Noise + float2(0.0f, 1 - _Time.y));

간단하게 1- 를 하여 값을 반전시켜서 노이즈를 위로 흐르게 할 수 있다.

 

두 번쨰 특이점은 

o.Alpha = c.a * d.a;

o.Alpha에 두 개의 텍스처를 곱하여 넣었다.

 

그 이유는 잘라 버릴 알파쪽을 두 텍스처의 알파 채널을 곱하여 유동적일 수 있도록 보이게 하기 위해서 이다.

 

	fixed4 d = tex2D(_Tex_Noise, IN.uv_Tex_Noise + float2(0.0f, _Time.y * 0.5f));
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex + (d.r * 0.1f));

나머지 _Time.y와 d.r에 소수점을 곱하여 강도 조절을 하여 불이 흐르는 쉐이더를 만들었다.

 

-4. 마무리(인터페이스 변수를 활용)

Shader "Custom/Sha_4st3"
{
	Properties
	{
		_MainTex("Albedo (RGB)", 2D) = "white" {}
		_Tex_Noise("Albedo2 (RGB)", 2D) = "white" {}

		_Noise_Power("_Noise_Power", Range(0, 1)) = 0.1
		_Noise_Speed("_Noise_Speed", Range(0, 10)) = 1.0

		_Emission_Power("_Emission_Power", Range(0, 10)) = 2.0
	}
		SubShader
		{
			Tags { "RenderType" = "Transparent"  "Queue" = "Transparent"}
			LOD 200

			CGPROGRAM
			// Physically based Standard lighting model, and enable shadows on all light types
			#pragma surface surf Standard alpha:fade

			// Use shader model 3.0 target, to get nicer looking lighting
			#pragma target 3.0

			sampler2D _MainTex;
			sampler2D _Tex_Noise;

			float _Noise_Power;
			float _Noise_Speed;
			float _Emission_Power;

			struct Input
			{
				float2 uv_MainTex;
				float2 uv_Tex_Noise;
			};


			void surf(Input IN, inout SurfaceOutputStandard o)
			{
				fixed4 d = tex2D(_Tex_Noise, IN.uv_Tex_Noise + float2(0.0f, 1 - (_Time.y * _Noise_Speed)));
				fixed4 c = tex2D(_MainTex, IN.uv_MainTex + d.r * _Noise_Power);

				o.Emission = c.rgb * _Emission_Power;

				o.Alpha = c.a * d.a;
			}
			ENDCG
		}
			FallBack "Diffuse"
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


WRITTEN BY
CatDarkGame
Technical Artist dhwlgn12@gmail.com

,