상세 컨텐츠

본문 제목

Geometry Processing - Tesselation

Computer Graphics/Graphics Pipeline

by Banjosh 2024. 11. 29. 14:07

본문

Geometry Processing의 두 번째 단계: Tessellation

이번에는 Geometry Processing의 두 번째 단계인 Tessellation에 대해 알아보자. Tessellation은 기본 도형인 프리미티브(Primitive)를 더 세분화하는 과정이다. 이 과정은 Tessellation Shader를 통해 프로그래머가 직접 작업할 수 있으며, LOD(Level of Detail)을 활용해 더 효율적인 렌더링을 구현할 수 있다.


왜 Tessellation이 필요한가?

렌더링을 진행하다 보면 굴곡진 부분이나 화면에 가까운 물체의 해상도를 높이고 싶을 때가 있다. 그러나 모든 물체의 해상도를 높이는 것은 성능 비용이 너무 크기 때문에 적합하지 않다. 이런 경우, 원하는 부분에만 프리미티브를 세분화하여 정점을 늘릴 수 있고, 이를 통해 특정 부분의 표현을 더 세밀하게 만들 수 있다.


Tessellation의 쓰임새

  1. 곡면 표현
    • Tessellation을 사용하면 물체의 곡면을 더 자연스럽게 표현할 수 있다.
  2. 해상도 조절
    • 화면에 가까운 물체의 해상도를 높여 디테일을 향상시킬 수 있다.
  3. 디스플레이스먼트 맵
    • 물체 표면의 울퉁불퉁함을 더 디테일하게 표현할 때 사용한다.
      디스플레이스먼트 맵은 Tessellation과 연계되어 표면의 높이 정보를 기반으로 변형을 적용한다.

Tessellation의 세 단계

Tessellation은 크게 세 단계로 이루어진다:

1. Hull Shader

Hull Shader는 Tessellation 단계의 첫 번째 단계로, 프리미티브 단위로 처리하며 Tessellator에서 프리미티브를 얼마나 세분화할지 설정한다.

  • 외곽 테셀레이션 레벨
    • 프리미티브의 외곽을 얼마나 세분화할지 결정한다.
  • 내부 테셀레이션 레벨
    • 프리미티브 내부를 얼마나 세분화할지 결정한다.

Hull Shader는 정점의 추가나 삭제는 불가능하지만, 정점의 위치를 변경할 수 있다. 만약 정점의 위치를 변경하면, 변경된 정점을 기준으로 세분화가 이루어진다.


2. Tessellator

Tessellator는 GPU에서 자동으로 Tessellation 레벨에 따라 실제로 프리미티브를 세분화하는 단계이다. 이 단계에서 새롭게 생성되는 정점의 좌표는 월드 좌표계가 아닌 기존 프리미티브 정점들의 상대적 위치로 만들어진다.

예를 들어, 프리미티브가 삼각형이라면 Tessellator는 생성된 정점의 위치를 보간 좌표(Barycentric Coordinates)로 표현한다:

  • 보간 좌표는 삼각형의 세 정점 p1, p2, p3에 대한 가중치로 표현되며, (u, v, w) 형태로 나타난다.

3. Domain Shader

Domain Shader는 Tessellator에서 생성된 정점별로 연산을 수행하는 단계이다. Tessellator가 전달한 보간 좌표를 기반으로 새로운 정점의 실제 좌표를 계산한다.

주요 작업:

  • Tessellator가 생성한 보간 좌표를 사용해 실제 정점의 좌표를 계산한다.
  • 디스플레이스먼트 맵을 적용해 정점을 변형시킬 수도 있다.

Domain Shader 예제 코드

기본 구현

// Domain Shader에서 Tessellator가 전달한 보간 좌표 (gl_TessCoord)를 사용
layout(triangles, fractional_even_spacing) in;

void main() {
    // 보간 좌표 (u, v, w)
    vec3 tessCoord = gl_TessCoord;

    // 기존 제어점의 위치
    vec3 p0 = gl_in[0].gl_Position.xyz;
    vec3 p1 = gl_in[1].gl_Position.xyz;
    vec3 p2 = gl_in[2].gl_Position.xyz;

    // 보간 좌표를 사용하여 새로 생성된 정점의 위치 계산
    vec3 newPosition = tessCoord.x * p0 + tessCoord.y * p1 + tessCoord.z * p2;

    // 최종 위치를 출력
    gl_Position = vec4(newPosition, 1.0);
}

디스플레이스먼트 맵을 적용한 구현

layout(triangles, fractional_even_spacing) in;

void main() {
    // Tessellator가 생성한 정점의 보간 좌표 (u, v, w)
    vec3 tessCoord = gl_TessCoord;

    // 기존 제어점의 위치 (원래 프리미티브의 정점)
    vec3 p0 = gl_in[0].gl_Position.xyz;
    vec3 p1 = gl_in[1].gl_Position.xyz;
    vec3 p2 = gl_in[2].gl_Position.xyz;

    // 보간 좌표를 사용하여 새로 생긴 정점의 위치 계산
    vec3 newPosition = tessCoord.x * p0 + tessCoord.y * p1 + tessCoord.z * p2;

    // 디스플레이스먼트 맵을 사용해 정점 위치 변형
    float displacementValue = texture(displacementMap, texCoords).r;
    newPosition += normal * displacementValue;

    // 최종 위치를 출력
    gl_Position = vec4(newPosition, 1.0);
}

추가 설명

  • Domain Shader는 생략 가능한 단계이다. 디스플레이스먼트 맵과 같은 추가 효과가 필요 없는 경우에는 이 단계를 사용하지 않아도 된다.
  • 하지만 디테일한 표면 표현이 요구되는 경우, Domain Shader를 활용하면 더 자연스러운 결과를 얻을 수 있다.

Tessellation의 핵심

Tessellation은 프리미티브를 세분화하여 곡면, 해상도, 디테일 표현 등을 조절할 수 있는 강력한 도구이다. 이를 적절히 활용하면 렌더링 성능과 디테일을 모두 잡을 수 있다. Geometry Processing 단계의 두 번째로, 원하는 디테일 수준에 맞게 정점을 세분화하고 추가적인 효과를 적용할 수 있는 중요한 과정이다.

관련글 더보기