사전 지오메트리 셰이더
개념

지오메트리 셰이더

gabury1고친 사람 github-actions[bot]

지오메트리 셰이더는 그림을 그리는 도중에 도형 자체를 불리거나 없애는 프로그램입니다. 삼각형이나 선분 같은 도형을 한 개씩 받아, 여러 개로 불려 내보내거나 한 개도 안 내보내고 버립니다. 도형에 딸린 꼭짓점을 한꺼번에 받아 봅니다. 그래서 꼭짓점 하나만 봐서는 못 하는 계산도 맡습니다.

쉽고 빠른 이해

지오메트리 셰이더는 도형 한 개를 받아 도형 여러 개를 내놓는 짧은 프로그램입니다. 점 하나를 받아 작은 네모 판 한 장을 만들어 내보내면, 점 수천 개로 적어 둔 눈송이가 화면에서는 판 수천 장이 됩니다.

이게 없으면 그릴 도형의 개수가 그래픽 카드에 넘기기 전에 다 정해져 있어야 합니다. 바로 앞 단계는 꼭짓점을 하나씩 따로 봅니다. 그래서 개수를 못 바꾸고 옆 꼭짓점도 못 봅니다. 도형을 한 덩이로 받아 개수까지 바꾸는 단계를 하나 열어 둔 것입니다.

어떻게 도나:

  1. 앞 단계를 지난 꼭짓점들이 도형 한 개로 묶여 들어옵니다
  2. 도형 한 개마다 이 프로그램이 한 번씩 돕니다
  3. 꼭짓점을 원하는 만큼 내보내고 도형을 끊는다고 알리면 그만큼 도형이 만들어집니다

대가는 몇 개가 나올지 미리 알 수 없다는 것입니다. 그래서 최대 몇 개까지 내보낼지를 프로그램에 적어 두어야 합니다. 그래픽 카드는 그만큼 내보낼 공간을 미리 잡아 둡니다. 나온 도형을 들어온 순서대로 다시 줄 세워야 해서 앞뒤 단계만큼 여러 개를 나란히 돌리기 어려워집니다.

상세

이 절은 지오메트리 셰이더가 무엇을 한 덩이로 받는지부터 봅니다.

도형 한 개를 한 덩이로 받는다는 말

정점(꼭짓점)은 도형을 이루는 점 하나입니다. 이 글에서는 꼭짓점이라고 부릅니다. 점 셋이 모이면 삼각형 하나가 됩니다. 화면에 보이는 모형은 그 삼각형을 수만 개 이어 붙인 것입니다.

그래픽 카드(GPU, Graphics Processing Unit, 그래픽 처리 장치)가 한 번에 그릴 수 있는 가장 작은 도형을 프리미티브라고 부릅니다. 점 하나, 선분 하나, 삼각형 하나가 프리미티브입니다.

꼭짓점 목록을 넘기면 그래픽 카드가 정해진 규칙대로 그것을 묶습니다. 이 묶는 단계가 프리미티브 조립입니다.

지오메트리 셰이더는 이 프리미티브 한 개를 한 덩이로 받습니다. 삼각형이 들어오면 꼭짓점 셋의 값을 모두 읽을 수 있습니다.

읽을 수 있는 범위가 넓어지면 할 수 있는 계산도 달라집니다. 바로 앞 단계(뒤에 나올 정점 셰이더)는 꼭짓점마다 따로 돌기 때문에 옆 꼭짓점을 못 봅니다. 이 단계는 셋을 함께 보므로 꼭짓점 사이의 관계에서만 나오는 값을 구할 수 있습니다.

지오메트리 셰이더가 꽂히는 단계

그림 그리는 흐름은 정해진 순서로 돕니다. 그 순서 안의 몇 단계는 비어 있어서 만드는 쪽이 프로그램을 적어 꽂습니다. 그렇게 꽂는 프로그램을 통틀어 셰이더라고 부릅니다. 흐름 전체가 그래픽스 파이프라인입니다.

지오메트리 셰이더는 꼭짓점을 하나씩 다루는 단계를 지난 다음에 옵니다. 앞 단계인 정점 셰이더가 꼭짓점 하나의 새 위치를 정하고 나면, 그 꼭짓점들이 도형으로 묶여 이 단계에 들어옵니다.

flowchart TD
    A["정점 셰이더 · 꼭짓점 하나마다"] --> B["프리미티브 조립 · 점 셋을 삼각형으로"]
    B --> C["지오메트리 셰이더 · 도형 하나마다"]
    C --> D["클리핑 · 화면 밖을 잘라 낸다"]
    D --> E["래스터화 · 덮인 픽셀을 고른다"]
    E --> F["프래그먼트 셰이더 · 덮인 픽셀 하나마다"]

클리핑은 화면 밖으로 나간 부분을 잘라 내는 단계입니다. 화면에 안 보일 부분을 미리 덜어 내면 뒤에서 할 일이 줄어듭니다.

그다음 도형은 화면의 픽셀로 쪼개집니다. 픽셀은 화면을 이루는 점 하나입니다. 래스터화가 삼각형이 덮은 픽셀을 골라냅니다.

그 픽셀마다 프래그먼트 셰이더가 한 번씩 돌면서 무슨 색으로 칠할지 정합니다. 도형을 늘리거나 없애는 일이 래스터화보다 앞에서 끝나야 하는 까닭이 이것입니다.

이 단계는 비워 둘 수 있습니다. 꽂아 넣은 프로그램이 없으면 앞 단계에서 나온 도형이 손대지 않은 모습으로 뒤 단계에 넘어갑니다.

개수를 바꾼다는 말

앞 단계는 꼭짓점 하나가 들어오면 꼭짓점 하나를 내놓습니다. 개수가 고정입니다. 지오메트리 셰이더는 이 규칙에서 풀려 있습니다.

도형 한 개를 받고 아무것도 안 내보내면 그 도형은 사라집니다. 조건에 안 맞는 삼각형을 걸러 내는 일이 이렇게 됩니다. 반대로 꼭짓점을 계속 내보내면 도형이 여러 개 만들어집니다.

flowchart TD
    subgraph S1["버림 · 내보낸 꼭짓점이 없다"]
        A1["들어온 삼각형 1개"] --> A2["나간 도형 0개"]
    end
    subgraph S2["불림 · 꼭짓점 넷을 내보낸다"]
        B1["들어온 점 1개"] --> B2["나간 삼각형 2개 · 네모 판 한 장"]
    end
    subgraph S3["앞뒤 단계는 늘 이 꼴이다"]
        C1["들어온 꼭짓점 1개"] --> C2["나간 꼭짓점 1개"]
    end

내보내는 도형의 종류가 받은 종류와 달라도 됩니다. 점 한 개를 받아 삼각형 둘을 내보내면 점이 네모난 판으로 바뀝니다.

점 하나를 화면을 향한 판으로 부풀린 것이 빌보드입니다. 연기나 불티를 그리는 파티클 시스템이 이 방법을 씁니다.

다만 끝없이 내보낼 수는 없습니다. 최대 몇 개의 꼭짓점을 내보낼지 프로그램 첫머리에 적어 두어야 합니다.

들어오는 것과 나가는 것

이 프로그램은 자기가 무엇을 받고 무엇을 내보낼지를 첫머리에 적어 두고 시작합니다. 셋입니다.

적어 두는 것 무엇을 정하나
입력 도형 종류 점·선분·삼각형 중 무엇을 한 덩이로 받나
출력 도형 종류 무엇을 만들어 내보내나
최대 꼭짓점 수 한 번 도는 동안 몇 개까지 내보내나

셋 다 돌기 전에 정해져 있어야 하는 값입니다. 돌려 보고 나서 바꿀 수 없습니다.

들어오는 꼭짓점 값은 배열로 도착합니다. 앞 단계는 꼭짓점 값을 하나씩 받지만 이 단계는 도형 한 개분을 함께 받기 때문입니다. 삼각형을 받으면 값이 셋 들어옵니다. 인덱스를 바꿔 가며 셋을 모두 읽습니다.

이웃한 도형의 꼭짓점까지 함께 받는 입력 종류도 있습니다. 삼각형 하나와 그 세 변에 맞닿은 이웃 꼭짓점 셋을 합쳐 여섯 개를 받는 꼴입니다. 이렇게 이웃이 딸려 오는 입력이 인접 프리미티브입니다.

flowchart TD
    subgraph S1["받는 삼각형 · 꼭짓점 셋"]
        A["꼭짓점 A"] --- B["꼭짓점 B"]
        B --- C["꼭짓점 C"]
        C --- A
    end
    subgraph S2["세 변에 맞닿은 이웃 · 꼭짓점 셋"]
        NAB["변 AB 쪽 이웃"]
        NBC["변 BC 쪽 이웃"]
        NCA["변 CA 쪽 이웃"]
    end
    A --- NAB
    B --- NAB
    B --- NBC
    C --- NBC
    C --- NCA
    A --- NCA

내보낼 때는 명령 둘을 씁니다. 하나는 꼭짓점을 하나 내보내는 명령이고, 다른 하나는 도형을 끊는 명령입니다. 꼭짓점을 몇 개 내보내다가 끊으면 거기까지가 도형 한 개가 됩니다. 끊은 다음에 내보내는 꼭짓점들은 그다음 도형이 됩니다.

코드 한 벌

셰이더를 적는 언어는 그래픽 API(Application Programming Interface, 애플리케이션 프로그래밍 인터페이스)마다 다릅니다. 아래는 GLSL(OpenGL Shading Language, OpenGL 셰이딩 언어)로 적은 짧은 예입니다. 점 한 개를 받아 그 점 둘레에 작은 네모 판 한 장을 만들어 내보냅니다.

glsl
layout(points) in;
layout(triangle_strip, max_vertices = 4) out;
const float d = 0.1;

void main() {
  vec4 p = gl_in[0].gl_Position;  // (0.2,0.3)
  gl_Position = p + vec4(-d,-d,0,0);
  EmitVertex();    // 좌하 (0.1,0.2)
  gl_Position = p + vec4( d,-d,0,0);
  EmitVertex();    // 우하 (0.3,0.2)
  gl_Position = p + vec4(-d, d,0,0);
  EmitVertex();    // 좌상 (0.1,0.4)
  gl_Position = p + vec4( d, d,0,0);
  EmitVertex();    // 우상 (0.3,0.4)
  EndPrimitive();  // 판 한 장 완성
}

첫 세 줄이 이 프로그램의 약속입니다. 받는 것은 점 하나입니다. 내보내는 것은 삼각형 스트립 꼴(바로 아래에서 설명합니다)로 꼭짓점 넷까지입니다.

gl_in 이 들어온 도형의 꼭짓점 배열입니다. 점 하나를 받았으니 칸이 하나뿐입니다.

vec4 는 값 넷을 담는 꼴입니다. 여기서 쓰는 것은 앞의 둘(가로·세로)이고, 뒤 둘은 이 예에서 안 바뀝니다.

본체는 같은 두 줄의 되풀이입니다. 내보낼 꼭짓점의 위치를 gl_Position 에 넣고 EmitVertex 를 부르면 꼭짓점이 하나 나갑니다. 넷을 내보낸 뒤 EndPrimitive 를 부르면 거기까지가 판 한 장이 됩니다.

오른쪽 주석은 받은 점이 화면 가운데서 조금 오른쪽 위에 있을 때 그 줄이 내놓는 앞의 두 값입니다. 받은 점 하나에서 네 꼭짓점이 상하좌우로 조금씩 벌어져 나가는 것이 보입니다.

triangle_strip 은 꼭짓점 넷으로 삼각형 둘을 만드는 꼴입니다. 앞의 셋이 첫 삼각형이 되고 뒤의 셋이 둘째 삼각형이 됩니다. 가운데 둘을 두 삼각형이 나눠 씁니다.

flowchart TD
    subgraph S1["꼭짓점 넷으로 삼각형 둘 · 따로 적으면 여섯이 든다"]
        T1["첫 삼각형"] --> V0["꼭짓점 0 · 좌하"]
        T1 --> V1["꼭짓점 1 · 우하"]
        T1 --> V2["꼭짓점 2 · 좌상"]
        T2["둘째 삼각형"] --> V1
        T2 --> V2
        T2 --> V3["꼭짓점 3 · 우상"]
    end

삼각형 둘을 따로 적으면 꼭짓점 여섯이 듭니다. 이 꼴은 넷으로 끝납니다. 내보낼 개수를 아껴야 하는 이 단계와 맞습니다.

이 단계가 아니면 못 하는 일

앞 단계가 꼭짓점 하나만 본다는 것, 개수를 못 바꾼다는 것은 앞에서 봤습니다. 그래서 이 단계에서만 나오는 그림이 몇 가지 있습니다.

첫째는 면 하나의 방향입니다. 꼭짓점에는 위치 말고 향한 방향도 값으로 붙어 다닙니다. 빛을 얼마나 받는지는 이 방향으로 정합니다.

삼각형이 어느 쪽을 향하는지는 꼭짓점 셋을 다 봐야 나옵니다. 꼭짓점마다 붙은 방향 대신 면 하나의 방향으로 빛을 계산하면, 표면이 부드럽게 이어지지 않고 면마다 각이 집니다. 이렇게 면 하나에 붙이는 방향이 면 법선입니다.

둘째는 모형의 테두리입니다. 이웃 도형까지 받아 보면 앞을 향한 면과 뒤를 향한 면이 맞닿는 변을 골라낼 수 있습니다. 그 변만 굵은 선으로 다시 내보내면 만화처럼 윤곽선이 그려집니다. 이 변을 골라내는 일이 실루엣 추출입니다.

셋째는 그물 그림입니다. 삼각형 하나를 받아 그 세 변을 선분으로 내보내면 모형이 뼈대만 남은 그물처럼 보입니다. 모형이 어떤 삼각형으로 짜였는지 확인할 때 쓰는 와이어프레임 렌더링이 이것입니다.

비용이 붙는 방식

이 단계는 몇 개가 나올지 돌려 보기 전에는 모릅니다. 앞뒤 단계는 하나가 들어오면 하나가 나가지만 이 단계만 그렇지 않습니다. 그래서 그래픽 카드는 프로그램에 적힌 최대 꼭짓점 수만큼 공간을 미리 잡아 둡니다.

적어 둔 최대치가 클수록 한 번에 나란히 돌릴 수 있는 수가 줄어듭니다. 넷만 내보내는 도형이 섞여 있어도 잡아 두는 공간은 적어 둔 최대치를 따릅니다.

순서도 지켜야 합니다. 먼저 그린 것 위에 나중 것을 덧칠하는 순서가 정해져 있어서, 순서가 바뀌면 겹친 그림의 색이 달라집니다. 그래서 도형은 들어온 순서대로 나가야 합니다. 먼저 끝난 결과라도 앞 도형이 끝날 때까지 기다렸다 나갑니다.

그래픽 카드는 같은 프로그램 수천 개를 한꺼번에 돌려 값을 버는 장치입니다. 이 단계는 그 동시성을 깎습니다.

그래서 같은 그림을 다른 단계에서 내는 길을 먼저 봅니다. 같은 모형을 여러 벌 그리는 일은 인스턴싱이 받습니다. 도형을 잘게 쪼개는 일은 테셀레이션이 받습니다.

그릴 도형을 미리 만들어 두는 계산은 컴퓨트 셰이더가 받습니다. 도형을 만들어 내는 단계 자체를 다시 설계한 메시 셰이더도 있습니다.

관련 항목

지오메트리 셰이더 앞뒤에 놓이는 처리 단계

그래픽스 파이프라인 · 입력 어셈블러 · 정점 셰이더 · 테셀레이션 · 프리미티브 조립 · 클리핑 · 컬링 · 래스터화 · 프래그먼트 셰이더 · 출력 병합기

이 단계가 받고 내놓는 도형 종류

프리미티브 · 정점 · 삼각형 스트립 · 선분 스트립 · 점 스프라이트 · 인접 프리미티브 · 메시

같은 흐름의 다른 단계에 꽂히는 셰이더

셰이더 · 테셀레이션 제어 셰이더 · 테셀레이션 평가 셰이더 · 컴퓨트 셰이더 · 메시 셰이더 · 태스크 셰이더

지오메트리 단계에서 만드는 표현 기법

빌보드 · 파티클 시스템 · 실루엣 추출 · 와이어프레임 렌더링 · 그림자 볼륨 · 면 법선 · 법선

이 단계가 내보낸 정점을 메모리로 받아 내는 경로

스트림 출력 · 트랜스폼 피드백 · 정점 버퍼 · 비디오 메모리 · 인다이렉트 드로

지오메트리 셰이더를 적는 언어와 그 처리

GLSL · HLSL · SPIR-V · 셰이더 컴파일 · 셰이더 프로그램 · 셰이더 단계

지오메트리 셰이더를 규정하는 그래픽 API

OpenGL · OpenGL ES · Direct3D · Vulkan

이 단계를 돌리는 그래픽 카드와 그 성질

GPU · 셰이더 코어 · 통합 셰이더 구조 · SIMD · 워프 · 병렬 처리

지오메트리 셰이더 비용을 키우는 요인

프리미티브 수 · 드로 콜 · 병목 · 오버드로 · 레벨 오브 디테일

정점이 거쳐 가는 좌표 공간

클립 공간 · 동차 좌표 · 월드 좌표계 · 뷰 좌표계 · 정규화 장치 좌표 · 화면 좌표

지오메트리 셰이더가 속하는 상위 분류

그래픽스 · 렌더링 · 실시간 렌더링 · 지오메트리 처리 · 3D 그래픽스

다른 이름: geometry shader · 기하 셰이더