사전 정점 셰이더
개념

정점 셰이더

gabury1고친 사람 github-actions[bot]

정점 셰이더는 도형의 꼭짓점 하나를 화면 어디에 놓을지 정하는 프로그램입니다. 꼭짓점마다 따로 한 번씩 돌면서 그 점의 새 위치를 하나 내놓습니다. 그 위치를 어떻게 구할지는 그림을 그리는 쪽이 직접 적어 넣습니다.

쉽고 빠른 이해

정점 셰이더는 꼭짓점 하나의 새 위치를 계산해 내놓는 짧은 프로그램입니다. 삼각형 하나를 비스듬히 돌려서 그린다면 이 프로그램은 꼭짓점 셋에 대해 한 번씩 불려 나와 돌아간 위치를 하나씩 내놓습니다.

이게 없으면 물체를 옮기고 돌리는 방식이 그래픽 카드에 미리 박혀 있는 몇 가지로 묶입니다. 뼈대를 따라 휘는 사람 모형이나 물결치는 수면처럼 준비돼 있지 않은 움직임은 낼 방법이 없습니다. 위치 계산 칸을 비워 두면 만드는 쪽이 원하는 계산을 적어 넣을 수 있습니다.

어떻게 도나:

  1. 꼭짓점 목록이 메모리에서 하나씩 읽혀 들어옵니다
  2. 꼭짓점마다 이 프로그램이 따로 한 번씩 돕니다
  3. 각각이 화면 기준 위치 하나를 내놓고, 그 점들이 묶여 삼각형이 됩니다

대가는 이 칸에서 낸 값이 꼭짓점에만 있다는 것입니다. 꼭짓점과 꼭짓점 사이는 그 값을 섞어서 채웁니다. 삼각형 안쪽에서 급하게 변해야 하는 값을 이 칸에서 구하면 결과가 어긋나 보입니다. 그럴 때는 같은 계산을 뒤 칸으로 내려야 하고, 뒤 칸은 훨씬 여러 번 돕니다.

상세

이 절은 정점 셰이더가 무엇을 옮기는지부터 봅니다. 그다음 그림 그리는 흐름의 어디에 끼어 있는지, 무엇이 들어오고 무엇이 나가는지, 좌표를 옮기는 계산이 어떻게 생겼는지, 마지막으로 비용이 어디에 붙는지를 차례로 따라갑니다.

꼭짓점을 옮긴다는 말

정점은 도형을 이루는 점 하나입니다. 삼각형 하나는 정점 셋으로 이뤄지고, 사람 모형 하나는 정점 수만 개로 이뤄집니다. 정점에는 위치뿐 아니라 그 점의 색이나 표면이 향한 방향 같은 값도 함께 붙습니다.

모형을 만들 때 적어 둔 정점 위치는 그 모형 자신을 기준으로 잰 값입니다. 사람 모형의 손끝 좌표는 모형의 발밑을 원점으로 두고 적혀 있습니다. 화면에 그리려면 이 값이 화면을 기준으로 한 좌표로 바뀌어야 합니다.

바꿀 때 따져야 하는 것은 셋입니다. 물체가 세상 어디에 놓였는지, 카메라가 어디서 어느 쪽을 보는지, 그리고 먼 것이 작게 보이는 원근을 어떻게 줄 것인지입니다. 정점 셰이더가 하는 일이 이 셋을 꼭짓점 하나에 적용해 새 좌표를 내놓는 것입니다.

정점 셰이더가 꽂히는 칸

그림 그리는 흐름은 정해진 순서로 돕니다. 그 순서 안의 몇 칸이 비어 있어서 만드는 쪽이 프로그램을 적어 꽂을 수 있습니다. 그렇게 꽂는 프로그램을 통틀어 셰이더라고 부릅니다.

정점 셰이더는 그중 맨 앞 칸입니다. 메모리에서 읽어 온 꼭짓점이 다른 어떤 처리를 받기도 전에 이 칸을 지납니다.

flowchart TD
    A["정점 버퍼 · 꼭짓점 목록"] --> B["정점 셰이더 · 꼭짓점 하나마다"]
    B --> C["프리미티브 조립 · 점 셋을 삼각형으로"]
    C --> D["래스터화 · 덮인 칸을 고른다"]
    D --> E["프래그먼트 셰이더 · 덮인 칸 하나마다"]
    E --> F["프레임버퍼 · 화면 메모리"]

위 그림에서 프로그램을 꽂는 칸은 둘째와 다섯째입니다. 다섯째 칸에 꽂는 것이 프래그먼트 셰이더입니다. 삼각형이 덮은 화면 칸마다 한 번씩 돌면서 그 칸을 무슨 색으로 칠할지 정합니다.

두 칸으로 갈라 놓은 까닭은 도는 횟수가 다르기 때문입니다. 삼각형 하나의 꼭짓점은 셋뿐이지만 그 삼각형이 덮는 화면 칸은 수만 개가 되기도 합니다. 같은 계산이라도 앞 칸에 두면 세 번, 뒤 칸에 두면 수만 번 돕니다.

그래서 어느 계산을 어느 칸에 둘지가 그리는 쪽의 판단거리가 됩니다. 꼭짓점 사이를 부드럽게 이어도 되는 값은 앞 칸으로 올리고, 칸마다 달라야 하는 값만 뒤 칸에 남깁니다.

꼭짓점 하나마다 한 번씩 도는 프로그램

정점 셰이더는 꼭짓점 하나를 받아 위치 하나를 내놓습니다. 받는 것도 하나, 내놓는 것도 하나입니다. 몇 번 도는지는 그리려는 도형의 꼭짓점 수가 정합니다.

한 번 도는 동안 이 프로그램은 자기 몫의 꼭짓점만 봅니다. 옆 꼭짓점이 어디로 옮겨 갔는지 읽을 수 없고, 같은 삼각형에 속한 나머지 둘도 못 봅니다. 서로 쳐다보지 않기 때문에 GPU(Graphics Processing Unit, 그래픽 처리 장치)가 수천 개를 한꺼번에 돌릴 수 있습니다.

이 제약은 쓰는 쪽으로 그대로 넘어옵니다. 삼각형의 면이 어느 쪽을 향하는지처럼 꼭짓점 셋을 다 봐야 나오는 값은 이 칸에서 구할 수 없습니다. 그런 값은 모형을 만들 때 미리 계산해 정점에 붙여 두거나, 꼭짓점 셋을 한꺼번에 받는 뒤 칸에서 구합니다.

개수도 못 바꿉니다. 하나가 들어오면 하나가 나갑니다. 도형을 잘게 쪼개 꼭짓점을 불리는 일은 테셀레이션처럼 그 일을 맡은 다른 칸이 합니다.

들어오는 값 두 갈래

정점 셰이더가 읽을 수 있는 값은 크게 둘로 갈립니다. 그 값이 꼭짓점마다 다른지 아닌지가 가르는 기준입니다.

갈래 어디서 오나 예
꼭짓점마다 다른 값 메모리의 꼭짓점 목록에서 하나씩 읽혀 들어온다 모형 기준 위치 · 표면이 향한 방향
그리기 한 번 동안 고정된 값 바깥 프로그램이 미리 넣어 두고 모든 꼭짓점이 똑같이 읽는다 카메라 위치를 담은 숫자 덩이 · 흐른 시간

첫째 갈래를 정점 속성이라고 부릅니다. 꼭짓점 하나에 딸린 값이라는 뜻입니다. 위치는 거의 언제나 여기 들어가고, 색이나 표면 방향처럼 점마다 다른 값이 함께 붙습니다.

둘째 갈래는 유니폼이라고 부릅니다. 그리기 한 번이 도는 동안 모든 꼭짓점에게 같은 값이라 붙은 이름입니다. 카메라가 어디 있는지처럼 꼭짓점마다 달라질 이유가 없는 값이 여기 들어갑니다.

나가는 값과 뒤 칸이 받는 모양

정점 셰이더가 반드시 내놔야 하는 값은 하나, 그 꼭짓점의 새 위치입니다. 이 값을 안 내놓으면 뒤 칸이 삼각형을 어디에 그릴지 알 수 없습니다.

그 밖에 뒤 칸으로 넘기고 싶은 값을 함께 내보낼 수 있습니다. 색이나 표면 방향, 무늬 그림을 읽을 좌표가 그렇습니다. 앞 칸에서 한 번 계산해 넘기면 뒤 칸이 다시 계산하지 않아도 됩니다.

넘긴 값은 손대지 않은 모습으로 도착하지 않습니다. 꼭짓점 셋에서 나온 값이 삼각형 안쪽 칸마다 섞여서 도착합니다. 이 섞는 계산을 보간이라고 부릅니다.

꼭짓점 셋을 빨강·초록·파랑으로 두면 삼각형 안쪽이 부드럽게 물드는 것이 이 때문입니다. 안쪽 칸이 세 꼭짓점에서 각각 얼마나 떨어졌는지를 보고 그 비율대로 색을 섞습니다.

좌표를 옮기는 계산

앞서 말한 세 번의 옮김은 각각 변환 행렬 하나로 적습니다. 행렬은 옮기기·돌리기·키우기를 숫자 한 덩이로 적어 둔 것이고, 점에 곱하면 그 변환이 한 번에 적용됩니다.

셋을 미리 곱해 하나로 합쳐 둘 수 있습니다. 그러면 바깥 프로그램은 합친 덩이 하나만 넘기고, 정점 셰이더 안에서는 곱셈이 한 번으로 끝납니다. 꼭짓점 수만 번을 도는 프로그램이라 이 한 번이 곧 전체 비용입니다.

곱하기 전에 위치에 성분 하나를 더 붙입니다. 3차원 점인데 넷으로 만드는 것입니다. 이렇게 늘린 좌표를 동차 좌표라고 부릅니다.

넷째 성분이 원근을 나르는 몫입니다. 뒤 단계가 앞 셋을 넷째로 나누는데, 멀리 있는 점일수록 넷째가 커지도록 계산해 두면 나눈 뒤에 원점 쪽으로 더 끌려옵니다. 먼 것이 작게 보이는 것이 이 나눗셈의 결과입니다.

코드 한 벌

셰이더를 적는 언어는 그래픽 API(Application Programming Interface, 애플리케이션 프로그래밍 인터페이스)마다 다릅니다. 아래는 그중 GLSL(OpenGL Shading Language, OpenGL 셰이딩 언어)로 적은 가장 단순한 축의 정점 셰이더입니다. 꼭짓점을 화면 좌표로 옮기고 빨강 하나를 뒤 칸에 넘깁니다.

glsl
attribute vec3 aPos;  // (0.5, 0.5, 0.0)
uniform mat4 uMvp;
varying vec3 vColor;

void main() {
  vColor = vec3(1.0, 0.0, 0.0);
  vec4 h = vec4(aPos, 1.0); // (0.5,0.5,0,1)
  gl_Position = uMvp * h;   // (0.3,0.4,0.2,1)
}

오른쪽 주석은 꼭짓점 하나가 돌 때 그 줄이 쥔 값입니다. aPos 는 꼭짓점마다 읽혀 들어오는 모형 기준 위치이고, uMvp 는 세 번의 옮김을 미리 합쳐 둔 덩이입니다. 어느 꼭짓점이 돌든 uMvp 의 값은 같습니다.

본체는 세 줄입니다. 뒤 칸에 넘길 색을 하나 정하고, 위치에 성분 하나를 붙여 넷으로 늘리고, 거기에 덩이를 곱해 내놓습니다. gl_Position 이 이 프로그램이 반드시 채워야 하는 출력 이름입니다.

위치 말고 여기서 만드는 움직임

이 칸이 열려 있으니 위치를 아무렇게나 계산해도 됩니다. 시간 값을 받아 꼭짓점의 높이를 물결 모양으로 흔들면 수면이 출렁이고, 뼈대 위치를 받아 꼭짓점을 따라 휘게 하면 사람 모형이 걷습니다. 뒤엣것을 스키닝이라고 부릅니다.

같은 모형을 여러 벌 그릴 때도 이 칸이 쓰입니다. 나무 한 그루의 꼭짓점 목록을 한 벌만 올려 두고, 그릴 때마다 다른 위치 값을 받아 꼭짓점을 옮기면 숲이 됩니다. 이렇게 한 목록으로 여러 벌을 그리는 방식이 인스턴싱입니다.

이런 움직임을 바깥 프로그램에서 계산하면 매 장면마다 꼭짓점 수만 개를 고쳐 다시 올려야 합니다. 정점 셰이더에 맡기면 목록은 한 번만 올리고 숫자 몇 개만 바꿔 넘깁니다.

비용이 붙는 방식

이 프로그램이 도는 횟수는 그리는 꼭짓점 수를 따라갑니다. 화면이 커지든 작아지든 꼭짓점 수는 그대로이므로 창 크기와는 상관이 없습니다. 같은 장면을 작은 창에서 그리나 큰 화면에서 그리나 이 칸의 일은 같습니다.

늘어나는 쪽은 모형이 정교해질 때입니다. 사람 모형 하나를 부드럽게 보이려고 꼭짓점을 두 배로 늘리면 이 칸도 두 배로 돕니다. 멀리 있어 몇 칸밖에 안 차지하는 물체도 꼭짓점 수가 같으면 같은 만큼 돕니다.

줄이는 방법은 대개 둘입니다. 멀리 있는 물체는 꼭짓점이 적은 모형으로 바꿔 그리거나(레벨 오브 디테일), 화면 밖으로 나간 물체는 아예 그리지 않습니다(컬링).

관련 항목

정점 셰이더 앞뒤에 놓이는 처리 단계

그래픽스 파이프라인 · 입력 어셈블러 · 프리미티브 · 프리미티브 조립 · 클리핑 · 원근 나누기 · 뷰포트 변환 · 후면 제거 · 래스터화

정점 셰이더로 들어오는 입력

정점 속성 · 정점 버퍼 · 인덱스 버퍼 · 정점 배열 객체 · 유니폼 · 법선 · UV 좌표 · 정점 색

정점 셰이더가 거치는 좌표 공간

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

좌표를 옮길 때 쓰는 계산

변환 행렬 · 모델 행렬 · 뷰 행렬 · 투영 행렬 · 원근 투영 · 직교 투영 · 행렬 곱

정점 셰이더가 내보낸 값을 이어받는 처리

보간 · 배리잉 · 원근 보정 보간 · 프래그먼트 · 프래그먼트 셰이더

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

셰이더 · 테셀레이션 · 테셀레이션 제어 셰이더 · 테셀레이션 평가 셰이더 · 지오메트리 셰이더 · 메시 셰이더 · 컴퓨트 셰이더

정점 셰이더를 적는 언어와 그 처리

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

정점 셰이더를 돌리는 하드웨어와 그 성질

GPU · 셰이더 코어 · 통합 셰이더 구조 · SIMD · 워프 · 비디오 메모리

정점 단계에서 만드는 표현 기법

스키닝 · 모프 타깃 · 인스턴싱 · 변위 매핑 · 빌보드 · 정점 조명

정점 셰이더 비용을 키우는 요인

정점 수 · 드로 콜 · 정점 캐시 · 레벨 오브 디테일 · 컬링 · 절두체 컬링

정점 셰이더를 제공하는 그래픽 API

OpenGL · WebGL · Vulkan · Direct3D · Metal · WebGPU

정점 셰이더가 속하는 상위 분류

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

다른 이름: vertex shader · 버텍스 셰이더