Vertex-Fragment Shader

Vertex Shader + Fragment Shader. 절차상 2개이지만 보통 함께 쓰이기 때문에 Vertex-Fragment Shader라고 한다. 참고로 HSLS(DirectX)에서는 Fragment 대신 Pixel이라는 표현을 쓴다.

유니티에서는 Surface Shader를 주로 사용하지만, 이전의 DirectX 기반으로 게임을 만들 때는 Vertex-Pixel Shader를 주로 사용하였다.

파이프라인

https://i2.wp.com/www.khronos.org/opengl/wiki_opengl/images/RenderingPipeline.png?resize=271%2C602&ssl=1

https://www.khronos.org/opengl/wiki/Rendering_Pipeline_Overview

위 이미지는 OpenGL 의 파이프라인인데, 전체를 다 보면 복잡하니 간략화 하면 아래와 같다.

  1. CPU로부터 넘겨 받은 3D 정점 데이터를 Vertex Shader 단계에서 받아서 처리하고
  2. 그 처리된 데이터가 다시 여러 단계를 거치며 2D 픽셀화 되면
  3. 그 픽셀을 Fragment Shader 단계에서 받아서 처리하고
  4. 그 처리된 데이터가 다시 몇 단계를 거친 후에 화면에 뿌려진다.

렌더링 파이프라인에서 프로그래머가 개입 가능한 지점은 한정되어 있고 그 외의 단계는 하드웨어가 알아서 처리한다. 이렇게 쓰면 마치 제약적인 것처럼 생각되는데, 하드웨어가 알아서 처리하는 단계는 사실 자동화된 단계라고 이해하는 편이 낫다. 사람이 직접하면 대단히 번거로운 작업을 하드웨어가 알아서 해주는 것.

위 파이프라인에는 프로그래머의 개입이 가능한 지점으로 Tessellation이나 Geometry Shader 단계가 있지만 여기서는 생략.

Unity에서의 작성법

SubShader
{
	Pass
	{
		CGPROGRAM
		#pragma vertex vert // vert는 vertex shader를 처리하는 함수 이름이 된다.
		#pragma fragment frag // frag는 fragment shader를 처리하는 함수 이름이 된다.

		struct appdata
		{
			// vert에서 받을 파라미터들
		};

		struct v2f
		{
			// frag에서 받을 파라미터들
		};

		v2f vert (appdata v) // 위에서 appdata라고 선언한 형식의 구조체를 인자로 받는다.
		{
			v2f o;
			// Vertex 처리
			return o; // 위에서 v2f라고 선언한 형식의 구조체를 return 한다.
		}

		fixed4 frag (v2f i) : SV_Target // vert에서 처리하고 return한 구조체를 인자로 받는다.
		{
			fixed4 col = tex2D(_MainTex, i.uv);
			// Fragment 처리
			return col; // fixed4나 float4, half4 등의 형식으로 return 한다.
		}
		ENDCG
	}
}

Vertex-Fragment Shader는 SubShader 안의 Pass 안의 CGPROGRAM – ENDCG 사이에 작성한다. Pass가 여러개 필요하다면 SubShader 안에 Pass 블록을 여러개 만든다.

#pragma 부분이 중요한데, 여기서 vertex, fragment라고 선언함으로써 Vertex-Fragment Shader를 처리할 수 있게 된다. vertex, fragment 다음에 나오는 vert와 frag는 임의로 지정할 수 있는데, 그 이름은 Vertex와 Fragment를 처리하는 함수의 이름이 된다. –일반적으로는 vert, frag를 쓴다.

파이프라인상 Vertex Shader가 Fragment Shader 보다 먼저 실행되기 때문에 vert함수에서 처리한 값을 frag 함수에서 받아서 처리하는 식으로 코드가 작성된다.

vert와 frag에서 받는 파라미터는 구조체 형식인데, 어떤 데이터를 받을지는 프로그래머가 정할 수 있다. 받고자 하는 데이터를 구조체 안에 선언해 주면 된다.

위의 작성 예에서는 생략되었지만, Shader 작성에 필요한 변수는 별도로 선언해서 사용한다. 유니티에서는 Properties를 이용해서 변수를 외부에서 제어할 수 있게 해주므로 Shader 함수에서 사용할 변수는 Properties에 등록해서 사용하는게 일반적이다.