Quick Syntax
// GL 3.3 Core: vao에는 메시 속성, instanceVbo에는 vec3 위치 배열이 있습니다.
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, instanceVbo);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr);
glEnableVertexAttribArray(2);
glVertexAttribDivisor(2, 1);
glDrawArraysInstanced(GL_TRIANGLES, 0, vertexCount, instanceCount);divisor 0은 정점마다, 1은 인스턴스마다 입력을 바꿉니다. 메시의 모든 속성에 divisor 1을 주면 메시 자체도 인스턴스 데이터처럼 읽히므로 공유 정점과 인스턴스 입력을 구분합니다.
데이터 분리
셰이더는 layout(location = 2) in vec3 offset;을 받아 위치에 더할 수 있습니다. gl_InstanceID는 draw 내부의 인스턴스 번호로 0부터 증가합니다. 이 번호를 배열 인덱스로 쓰는 경우 GPU에 준비한 데이터 수가 instanceCount를 감당해야 합니다.
| 값 | 기본·범위 | 동작과 실수 |
|---|---|---|
| divisor | 초기 0, 부호 없는 정수 | d가 양수면 인스턴스 d개마다 입력 증가 |
| instanceCount | 0 이상 | 0이면 출력 없음, 입력 저장소 크기와 일치 |
| vertexCount/index count | 해당 draw의 정점·인덱스 개수 | 인스턴스 수와 곱한 값을 넣지 않음 |
| 인스턴스 mat4 | 4개 연속 속성 슬롯 | 각 열의 offset·enable·divisor 모두 설정 |
두 위치를 실제로 전달하기
instanceVbo의 준비가 필요한 경우, GL 3.3 초기화에서 다음과 같이 두 개의 위치를 올립니다. 기존 메시 VAO는 위치 속성을 location 0에서 읽는다고 가정합니다.
const GLfloat offsets[] = {-0.4f, 0, 0, 0.4f, 0, 0};
GLuint instanceVbo = 0;
glGenBuffers(1, &instanceVbo);
glBindBuffer(GL_ARRAY_BUFFER, instanceVbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(offsets), offsets, GL_STATIC_DRAW);
const GLsizei instanceCount = 2;상단의 속성 2·divisor 1 연결 뒤 다음 정점 셰이더를 사용하는 프로그램으로 그립니다. 메시 좌표는 화면 안에 두 인스턴스가 들어올 만큼 작은 범위로 준비합니다. 프래그먼트 셰이더는 고정 색 출력으로 시작할 수 있습니다.
#version 330 core
layout(location = 0) in vec3 position;
layout(location = 2) in vec3 offset;
void main() {
gl_Position = vec4(position + offset, 1.0);
}instanceCount 2이면 같은 메시가 x=-0.4와 x=0.4 위치에 하나씩 배치됩니다. divisor가 0으로 남으면 offset 배열이 정점마다 증가하여 이 두 위치를 인스턴스별로 읽지 않습니다. 마지막 사용 뒤 instanceVbo를 삭제합니다.
행렬과 선택 기준
물체마다 이동만 필요하면 vec3로 충분합니다. 회전·크기를 포함하면 mat4를 쓸 수 있지만 슬롯 네 개와 더 많은 전송량을 사용합니다. 속성 슬롯을 아껴야 하거나 데이터가 커지면 uniform 배열·UBO·지원 환경의 SSBO를 비교합니다. 하나의 mat4 첫 슬롯만 divisor를 설정하면 나머지 열이 다른 속도로 읽혀 변환이 깨집니다.
같은 메시·호환되는 프로그램과 상태를 묶을 때 인스턴싱이 유리합니다. 텍스처·블렌드·프로그램이 물체마다 달라져 draw를 다시 나눠야 한다면 단순히 instanced 함수를 호출한 것으로 제출 비용 문제가 사라지지 않습니다. 투명도 정렬도 별도로 남습니다.
mat4 인스턴스의 네 슬롯
layout(location = 2) in mat4 instanceModel;은 location 2~5를 사용합니다. 버퍼에 열 우선 float 16개씩 저장했고 이 네 슬롯이 다른 속성과 겹치지 않는 예입니다.
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, matrixVbo);
for (GLuint column = 0; column < 4; ++column) {
GLuint slot = 2 + column;
glVertexAttribPointer(slot, 4, GL_FLOAT, GL_FALSE, 16 * sizeof(GLfloat),
reinterpret_cast<void*>(column * 4 * sizeof(GLfloat)));
glEnableVertexAttribArray(slot);
glVertexAttribDivisor(slot, 1);
}vec3 offset 예제와 대체 관계입니다. 같은 VAO에서 바꿔 쓴다면 이전 속성 구성까지 의도대로 갱신하고, 셰이더에서도 위치를 instanceModel * vec4(position, 1)로 계산합니다.
버전 경계
instanced draw는 3.1, glVertexAttribDivisor는 3.3에 코어로 들어왔습니다. 두 기능이 항상 같은 버전에서 시작했다고 설명하지 않습니다. 4.2 base-instance 계열은 인스턴스 입력의 시작점을 조정하지만 gl_InstanceID 자체에 base instance가 더해지는 것은 아닙니다. 이를 셰이더에서 직접 읽는 기능은 더 높은 버전 또는 대응 확장을 확인합니다.
구형 방식의 draw 반복은 CPU가 물체별 값을 갱신한 뒤 호출하는 대체 경로입니다. 출력 개념은 대응해도 제출 비용·데이터 배치가 다릅니다. 배치와 간접 그리기에서 조합 조건을 이어서 봅니다.
참고 링크
3 sources