본문 바로가기
유니티/Shader Study

URP Shader Study #11 - Vertex shader를 활용한 mesh animation 구현

by witn331ss 2023. 12. 6.

Vertex shader를 활용한 mesh animation 구현

 

Shader "URPTraining/Vertex_Test_shader"
{


   Properties
             {   
                _Texture2D("Texture2D", 2D) = "white" {}
           	}  

	SubShader
	{  

	Tags
            {
	   "RenderPipeline"="UniversalPipeline"
                "RenderType"="Opaque"          
                "Queue"="Geometry"
            }
    	Pass
    	{  		
     	 Name "Universal Forward"
              Tags { "LightMode" = "UniversalForward" }

       	HLSLPROGRAM

        	#pragma prefer_hlslcc gles
        	#pragma exclude_renderers d3d11_9x
        	#pragma vertex vert
        	#pragma fragment frag

       	#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"        	
  
         	struct VertexInput
         	{
            	float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
          	};


        	struct VertexOutput
          	{
           	float4 vertex  	: SV_POSITION;
            float2 uv : TEXCOORD0;

      	};

            float4 _Texture2D_ST;
            Texture2D _Texture2D;

            SamplerState sampler_Texture2D;


      	VertexOutput vert(VertexInput v)
        	{

          	VertexOutput o;      
          	o.vertex = TransformObjectToHClip(v.vertex.xyz);
            /*
            인덱스 버퍼에서 가져온 버텍스 포지션을
            TransformObjectToHClip 함수를 이용해 로컬공간에서 *클립공간으로 변환하는 뜻이다.

            ##

            *클립공간은 3D공간을 2D로 투영하기 전 중단 단계이며
            카메라의 *뷰 프리스텀에 맞게 조정된 공간이다.

            클립공간은 레스터라이제이션 과정에 앞서 위치하고 있는데 다음과 같은 역할을 해준다
            
            1. 3D 객체의 좌표를 카메라 관점의 좌표로 조정해준다. (이 때 뷰변환과 투영변환을 통해 이루어진다)
            2. 이 과정에서 뷰 프리스텀내에 있는 객체만 렌더링하기 위해서 클리핑이 수행된다.
            
            *뷰 프리스텀이란 카메라가 볼 수 있는 공간을 나타내는 피라미드 형태의 구조이며
            Fov(Field of View)에 따라 넓어지는 모양을 가르킨다. 즉 렌더링해야 할 3D공간을 정의한다.
            
            */

            //o.vertex.y += v.vertex.x;
            //ㄴ 이녀석은 그냥 무지성으로 Y에 X를 더할랭 ㅋㅋ 하는거라 로컬 기준으로 대각선의 모습을 보일 것이다

            half3 positionWS = TransformObjectToWorld(v.vertex.xyz);
             //인덱스 버퍼에서 버텍스 포지션을 가져와 월드공간으로 변환하겠다는 소리.
             // 좌표를 3개 쓰니까 당연히 float 혹은 half를 이용.

            o.vertex.y += positionWS.x;
            // 인덱스 버퍼에 있는 로컬공간 Y에 월드로 변환한 X를 더하며 대입하겠다는 소리다.
            // 유니티에서는 Y는 위, X는 옆이니까 위에 옆값을 더한다는 소리니 렌더링된 오브젝트는 대각선의 모습을 보일 것이다.
            

            o.uv = v.uv.xy * _Texture2D_ST.xy + _Texture2D_ST.zw;

            

         	return o;
        	}

        half4 frag(VertexOutput i) : SV_Target
        {

            float4 TEX01 =  _Texture2D.Sample(sampler_Texture2D, i.uv);

            float4 color = TEX01;
             return color;
            
       	
        	}

        	ENDHLSL  
    	}
     }
}

 

 

여기서 더 심도있게 넘어가보자. 사인을 써보자!

아까   o.vertex.y += positionWS.x; 이 부분을 sin으로 감싸줘보자

o.vertex.y += sin(positionWS.x);
/*

해석해보자면 인덱스 버퍼에 있는 로컬공간 Y에 X축으로 움직이는 sin을 더해서 대입한다는 얘기다.
얘는 옆으로 파도치는 모습이고 월드를 대입했기 때문에 월드 포지션을 움직이면 따라서 같이 움직일 것이다.

*/

 

오 근데 자동으로 움직이고 싶은데.. 그러먼 어떡하지?

당연히 _Time을 사용하면 된다!

 

일단은 월드로 움직이긴 싫으니까 월드에서 다시 로컬로 사용하겠다고 바꾼 뒤에 적용해보자.

 

o.vertex.y += sin(v.vertex.x + v.vertex.y + v.vertex.z + _Time.y);
            // 인덱스 버퍼에서 가져온 Y(위) 포지션에 사인에 타임으로 움직이는 로컬 XYZ을 고대로 넣어버린다는 뜻

참고로 y는 x보다 빠르게 움직인다는 뜻이다 (1/20 , 1/8 , 1/4 ,1) 이였나 가물가물한데 전에 타임에 적어둔게 있을것임

하여튼 큰 의미는 아님 속도를 더 빠르게하려면 프로퍼티스로 빼서 float으로 지정하고 임의대로 곱하고 움직이게끔 하면 되는 문제.

 

 

자 sin같이 파형을 수식으로 구할 수도 있지만 우리가 전에 사용했던 방법, 그러니까 FlowMap 같은 것으로도 제어할 수 있다.

물론 이 경우엔 Tex2Dlod 함수를 사용해야해서 샘플러를 따로 정의해주어야만 한다.

 

Tex2Dlod 함수는 컴퓨터 그래픽스에서 텍스처(이미지)를 다룰 때 사용하는 특별한 방법인데

이 함수의 목적은 텍스처의 다양한 '해상도' 버전 중에서 원하는 하나를 선택해서 사용하는 것이다.

 

LOD: 'Level of Detail'의 약자로, 간단히 말해 '해상도 수준'을 의미합니다.

하나의 텍스처에는 여러 해상도의 버전이 있을 수 있습니다.

가까이 있는 객체에는 고해상도 텍스처를, 멀리 있는 객체에는 저해상도 텍스처를 사용하는 식입니다.