버퍼(Buffer), GPU에게 데이터를 전달하는 컨테이너.

262025년 08월 15일4

셰이더라는 강력한 프로그램을 만들었지만, 그 프로그램이 처리할 데이터가 없다면 아무 소용이 없었다. 3D 모델을 구성하는 수만 개의 정점 위치, 각 정점의 색상, 텍스처 좌표 등. 이 방대한 양의 데이터를 어떻게 CPU의 메모리에서 GPU의 메모리로 효율적이고 안전하게 전달할 것인가?

이 중대한 과제를 해결하기 위해, 워킹 그룹은 OpenGL의 핵심 개념 중 하나인 ‘버퍼(Buffer)’를 WebGL로 가져오기로 했다.

버퍼는 GPU가 직접 접근할 수 있는 특별한 메모리 영역, 즉 GPU 메모리에 생성되는 일종의 ‘데이터 컨테이너’였다. 웹 개발자는 CPU가 있는 자바스크립트 세상에서 이 컨테이너에 데이터를 채워 넣고, GPU는 셰이더를 실행할 때 이 컨테이너에서 데이터를 꺼내 쓴다.

WebGL API에서 버퍼를 사용하는 과정은 명확한 단계로 설계되었다.

  1. 버퍼 생성 (Create):
    가장 먼저, 개발자는 gl.createBuffer() 함수를 호출하여 비어있는 버퍼 객체를 생성한다. 이것은 아직 GPU 메모리에 아무것도 할당하지 않은, 이름표만 붙은 빈 상자와 같다.

  2. 버퍼 바인딩 (Bind):
    다음으로, gl.bindBuffer(target, buffer) 함수를 호출한다. 이 ‘바인딩’ 과정은 매우 중요한 개념이다. WebGL은 한 번에 하나의 버퍼에만 집중하여 작업한다. bindBuffer는 "이제부터 내가 내리는 모든 버퍼 관련 명령은 바로 이 버퍼에 대한 것이다"라고 WebGL에 알려주는 행위다. target 인자는 이 버퍼를 어떤 용도로 사용할지를 지정하는데, 가장 대표적인 것이 정점 데이터를 담기 위한 gl.ARRAY_BUFFER였다.

  3. 데이터 채우기 (Fill):
    버퍼가 바인딩된 상태에서, 개발자는 gl.bufferData(target, data, usage) 함수를 호출하여 실제 데이터를 버퍼에 채워 넣는다.
    data 인자에는 자바스크립트의 TypedArray(예: Float32Array)가 사용되었다. 이는 C++과 같은 저수준 언어와 데이터를 주고받기에 훨씬 효율적이기 때문이었다.
    usage 인자는 이 데이터가 어떻게 사용될지에 대한 힌트를 GPU에게 주는 역할을 했다. 예를 들어, 거의 변하지 않을 정적인 데이터는 gl.STATIC_DRAW로, 매 프레임 내용이 바뀔 동적인 데이터는 gl.DYNAMIC_DRAW로 지정하여 GPU가 메모리 관리를 최적화할 수 있도록 도왔다.

bufferData 함수가 호출되는 순간, 자바스크립트 세상에 있던 데이터가 버스(Bus)를 타고 마침내 GPU 메모리로 복사되는, 실질적인 데이터 전송이 일어난다.

  1. 셰이더와 연결 (Connect):
    데이터가 GPU 메모리에 도착했지만, 아직 셰이더는 이 데이터가 어디에 있는지, 어떻게 해석해야 하는지 알지 못한다. 이 연결고리를 만드는 것이 gl.vertexAttribPointer() 함수였다.
    이 함수는 현재 바인딩된 버퍼의 데이터가, 정점 셰이더의 어떤 attribute 변수와 연결되는지를 알려준다. 그리고 데이터의 형식(예: 32비트 실수), 한 덩어리의 크기(예: 좌표는 3개의 실수), 데이터 간의 간격 등 세부적인 레이아웃 정보까지 함께 지정한다.
    마지막으로 gl.enableVertexAttribArray()를 호출하여 이 연결을 활성화하면, 비로소 준비가 끝난다.

이 모든 과정이 끝나고 gl.drawArrays() 함수가 호출되면, GPU는 마침내 버퍼로부터 약속된 데이터를 읽어와 정점 셰이더를 실행하고 화면에 그림을 그리기 시작하는 것이다.

이 버퍼 메커니즘은 처음 배우는 개발자에게는 다소 복잡하고 장황하게 느껴질 수 있었다. 왜 그냥 자바스크립트 배열을 셰이더에 바로 넘겨줄 수 없는가?

그 이유는 ‘성능’ 때문이었다.
매 프레임 수만 개의 정점 데이터를 자바스크립트에서 GPU로 넘기는 것은 엄청난 비용이 드는 작업이다. 버퍼는 데이터를 미리 GPU 메모리에 올려두고, 필요할 때마다 CPU의 개입을 최소화하며 재사용할 수 있게 해주는 핵심적인 최적화 기법이었다.

워킹 그룹은 이 엄격하지만 효율적인 버퍼 모델을 WebGL의 표준으로 확정했다. 이것은 웹 개발자들에게 C++이나 C#으로 네이티브 게임을 개발하는 것과 유사한, 저수준의 데이터 제어 경험을 제공하는 것이었다.

이제 3D 모델의 뼈대를 이루는 ‘형상 데이터’를 GPU에 전달하는 길이 열렸다. 다음 과제는 그 뼈대 위에 입힐 옷, 즉 ‘이미지 데이터’를 전달하는 방법을 정의하는 것이었다.