버텍스 처리는 GPU 렌더링 파이프라인에서 매우 중요한 단계로, 다음과 같은 작업들을 포함한다:

버텍스 셰이더

버텍스 셰이더(Vertex Shader)는 버텍스 데이터를 처리하는 첫 번째 단계이다. 이 셰이더는 각 버텍스에 대해 한 번 호출되며, 버텍스의 위치, 색상, 텍스처 좌표 등 다양한 속성을 계산하고 변환한다. 기본적인 작업은 모델 좌표를 뷰 좌표와 클립 좌표로 변환하는 것이다.

모델, 뷰, 프로젝션 변환

모델 변환

모델 변환(Model Transformation)은 각 버텍스를 로컬 모델 좌표계에서 월드 좌표계로 변환하는 단계이다. 이 변환을 위해 모델 행렬 \mathbf{M}을 사용한다. 주어진 버텍스 \mathbf{v_{local}}가 있을 때, 모델 좌표계에서 월드 좌표계로 변환된 버텍스 \mathbf{v_{world}}는 다음과 같이 계산된다:

\mathbf{v_{world}} = \mathbf{M} \mathbf{v_{local}}

뷰 변환

뷰 변환(View Transformation)은 월드 좌표계를 카메라의 뷰포인트(viewpoint)에 맞춘 뷰 좌표계로 변환하는 단계이다. 이 변환을 위해 뷰 행렬 \mathbf{V}를 사용한다. 모델 좌표계에서 월드 좌표계로 변환된 버텍스 \mathbf{v_{world}}가 있을 때, 뷰 좌표계로 변환된 버텍스 \mathbf{v_{view}}는 다음과 같이 계산된다:

\mathbf{v_{view}} = \mathbf{V} \mathbf{v_{world}}

프로젝션 변환

프로젝션 변환(Projection Transformation)은 3D 뷰 좌표계를 2D 클립 좌표계로 변환하는 단계이다. 주로 원근 투영(perspective projection)이 사용되며, 이를 위해 프로젝션 행렬 \mathbf{P}를 사용한다. 뷰 좌표계에서 변환된 버텍스 \mathbf{v_{view}}가 있을 때, 클립 좌표계로 변환된 버텍스 \mathbf{v_{clip}}는 다음과 같이 계산된다:

\mathbf{v_{clip}} = \mathbf{P} \mathbf{v_{view}}

클립 스페이스와 정규화 장치 좌표

클립 스페이스(Clip Space)는 원근 투영 변환이 완료된 후의 좌표 공간이다. 클립 좌표계에서의 버텍스는 일반적으로 4차원 동차 좌표(homogeneous coordinates)로 표현된다. 마지막으로, 클립 스페이스의 좌표는 정규화 장치 좌표(Normalized Device Coordinates, NDC)로 변환된다. 이 변환을 위해 클립 스페이스 좌표 \mathbf{v_{clip}} = (x_{clip}, y_{clip}, z_{clip}, w_{clip})를 다음과 같이 변환한다:

\mathbf{v_{ndc}} = \left( \frac{x_{clip}}{w_{clip}}, \frac{y_{clip}}{w_{clip}}, \frac{z_{clip}}{w_{clip}} \right)

버텍스 속성 보간

버텍스 셰이더는 버텍스의 속성 값을 픽셀 셰이더(프래그먼트 셰이더)에 전달한다. 주로 텍스처 좌표, 법선 벡터, 색상 등이 포함된다. 이 속성 값들은 중간 단계인 래스터화 단계에서 삼각형의 각 픽셀(fragment)에 대해 적절히 보간(interpolation)된다.

GLSL(오픈GL 셰이딩 언어)

버텍스 셰이더를 작성할 때 주로 GLSL(오픈GL 셰이딩 언어, OpenGL Shading Language)을 사용한다. 셰이더는 일반적으로 다음과 같은 주요 단계로 구성된다:

부가 코드

GLSL 셰이더는 C 언어와 유사한 구문을 사용한다. 각 셰이더는 버텍스 셰이더와 프래그먼트 셰이더 코드를 포함할 수 있으며, 각 셰이더는 main 함수를 가져야 한다. 버텍스 셰이더에서는 주로 입력 버텍스 데이터를 처리하고 변환하는 작업을 수행한다.

버텍스 셰이더의 예

다음은 간단한 버텍스 셰이더 코드 예제이다.

#version 330 core

// 입력 버텍스 속성
layout(location = 0) in vec3 aPos; // 버텍스 좌표
layout(location = 1) in vec2 aTexCoord; // 텍스처 좌표

// 각 버텍스에 변경되는 변환 행렬들
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

// 프래그먼트 셰이더로 전달할 텍스처 좌표
out vec2 TexCoord;

void main()
{
    // 월드 좌표계 변환
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    TexCoord = aTexCoord;
}

이 예제 코드는 각 버텍스의 위치를 변환하고, 해당 버텍스의 텍스처 좌표를 프래그먼트 셰이더로 전달한다.

보간 및 프래그먼트 셰이더

버텍스 셰이더에서 전달된 값들은 래스터화 단계에서 각 프래그먼트에 적절히 보간된다. 그런 다음, 프래그먼트 셰이더(Fragment Shader)에서 최종 색상을 계산한다.

프래그먼트 셰이더의 예

다음은 간단한 프래그먼트 셰이더 코드 예제이다.

#version 330 core

// 버텍스 셰이더에서 전달된 텍스처 좌표
in vec2 TexCoord;

// 출력 최종 색상
out vec4 FragColor;

// 샘플러 유닛에 매핑된 텍스처
uniform sampler2D texture1;

void main()
{
    // 텍스처에서 색상을 샘플링하고 최종 색상으로 설정
    FragColor = texture(texture1, TexCoord);
}

이 예제 코드에서는 텍스처를 사용하여 프래그먼트의 최종 색상을 결정한다.


지금까지 설명 드린 바와 같이, 버텍스 처리는 GPU 렌더링 파이프라인의 핵심 단계 중 하나이다. 이 과정에서 버텍스 셰이더는 입력 버텍스 데이터를 처리하고, 다양한 변환을 적용하여 최종적으로 프래그먼트 셰이더에 필요한 데이터를 준비한다.

최적화 및 고급 주제

버텍스 셰이더의 성능을 최적화하려면 다음과 같은 고급 주제를 고려할 수 있다:

  1. 버텍스 버퍼 객체(Vertex Buffer Objects, VBO): CPU와 GPU 간의 데이터 전송을 최소화하기 위해 VBO를 사용하여 버텍스 데이터를 GPU 메모리에 저장한다.
  2. 인스턴싱(Instancing): 동일한 객체를 여러 번 렌더링해야 할 경우, 인스턴싱 기법을 사용하여 버텍스 셰이더의 호출 횟수를 줄일 수 있다.
  3. 텍스처 매핑(Texture Mapping): 복잡한 지오메트리 데이터를 렌더링하기보다 텍스처 매핑을 사용하여 복잡한 디테일을 표현할 수 있다.

이 외에도 다양한 테크닉과 알고리즘이 GPU 렌더링 파이프라인에서 사용된다.