정점 셰이더(Vertex Shader): 점들을 움직이는 마법.

232025년 08월 14일4

워킹 그룹의 논의는 첫 번째 셰이더, ‘정점 셰이더’의 구체적인 설계로 넘어갔다. 화이트보드에는 정점 셰이더의 역할이 간결하게 정의되었다.

입력(Input): 3D 모델을 구성하는 하나의 정점(Vertex)에 대한 정보.
출력(Output): 해당 정점이 2D 화면에 표시될 최종 위치(좌표).
역할: 모델의 모든 정점에 대해 각각 한 번씩 실행된다.

“핵심은, 이 셰이더가 ‘하나의 정점’에 대한 관점만 가진다는 겁니다.”
블라디미르가 강조했다. “정점 셰이더는 자신이 지금 처리하는 정점 외에 다른 정점들의 정보는 알 수 없습니다. 이 제약 덕분에 GPU는 수천 개의 코어를 이용해 모든 정점을 ‘동시에, 독립적으로’ 처리할 수 있는 겁니다. 이것이 바로 병렬 처리의 힘입니다.”

그렇다면 개발자는 정점 셰이더에 어떤 정보들을 전달할 수 있어야 할까? API 설계는 다시 한번 뜨거운 감자가 되었다.

가장 기본적인 입력 데이터는 ‘정점의 위치(Vertex Position)’였다. 이것은 모델의 원본 3D 좌표(x, y, z)로, 보통 attribute라는 키워드를 통해 셰이더에 전달하기로 합의되었다. attribute는 각 정점마다 다른 값을 가지는 데이터를 의미했다.

하지만 점의 위치만으로는 충분하지 않았다. 3D 공간에 놓인 객체를 2D 화면으로 옮기려면 복잡한 수학적 변환이 필요했다.

첫째, 모델 변환(Model Transform): 모델 자체를 이동(Translate), 회전(Rotate), 확대/축소(Scale)하는 변환.
둘째, 뷰 변환(View Transform): 3D 공간의 어느 위치, 어느 방향에서 카메라가 장면을 바라보고 있는지를 결정하는 변환.
셋째, 투영 변환(Projection Transform): 3D 공간을 2D 화면에 투영하는 방식(원근 투영 또는 직교 투영)을 결정하는 변환.

이 세 가지 변환은 모든 정점에 ‘동일하게’ 적용되어야 했다. 워킹 그룹은 이런 종류의 데이터를 uniform이라는 키워드를 통해 셰이더에 전달하기로 했다. uniform은 셰이더가 실행되는 동안 변하지 않는, 모든 정점에 공통으로 적용되는 데이터를 의미했다.

이 변환들은 일반적으로 4x4 크기의 ‘행렬(Matrix)’로 표현되었다. 개발자는 자바스크립트에서 이 세 가지 행렬(모델, 뷰, 투영 행렬)을 계산하여 uniform 변수로 셰이더에 넘겨주어야 했다.

이 모든 것을 종합하여, 아주 기본적인 정점 셰이더의 코드는 다음과 같은 형태를 띠게 될 것이었다.

// GLSL (OpenGL Shading Language) 코드

// 입력: 각 정점마다 다른 값들
attribute vec3 a_position; // 정점의 3차원 위치

// 입력: 모든 정점에 동일하게 적용되는 값들
uniform mat4 u_modelMatrix;
uniform mat4 u_viewMatrix;
uniform mat4 u_projectionMatrix;

void main() {
    // 최종 위치 = 투영 * 뷰 * 모델 * 원본 위치
    gl_Position = u_projectionMatrix
        * u_viewMatrix
        * u_modelMatrix
        * vec4(a_position, 1.0);
}

코드의 마지막 줄, gl_Position = ... 이 부분이 바로 정점 셰이더의 핵심이었다. gl_Position은 정점 셰이더가 반드시 값을 할당해야 하는 특별한 내장 출력 변수로, 이 계산의 결과값이 바로 해당 정점의 최종 화면 좌표가 되는 것이다.

“이 구조는 개발자에게 엄청난 자유를 줍니다.”
구글의 엔지니어가 흥분된 목소리로 말했다. “만약 우리가 u_modelMatrix 대신, 시간에 따라 변하는 행렬을 넘겨준다면? 모델은 저절로 회전하게 될 겁니다. a_position에 파도 함수(sine wave) 같은 것을 더해준다면? 평평한 메시가 출렁이는 물결처럼 보이게 할 수도 있죠.”

그의 말대로였다. 정점 셰이더는 단순히 점을 화면에 옮기는 것을 넘어, 3D 모델에 생명을 불어넣는 첫 번째 단계였다. 모든 동적인 움직임과 변형의 마법이 바로 이 작은 프로그램 안에서 시작되는 것이다.

이제 개발자들은 더 이상 미리 정해진 기능에 의존할 필요가 없었다. 그들은 행렬과 벡터, 그리고 약간의 수학만으로 자신만의 움직임을 창조할 수 있는 강력한 도구를 손에 넣게 될 터였다.

워킹 그룹은 이 구조를 WebGL 1.0의 정점 셰이더 표준으로 확정했다. 이제 남은 과제는 이 정점들이 모여 만들어진 면의 색을 칠하는 방법, 즉 프래그먼트 셰이더를 정의하는 것이었다. 형태를 만드는 마법은 완성되었고, 이제 색을 입히는 예술을 논할 차례였다.