OpenGL 쉐이더 프로그래밍 - Buffer Objects

타입·2025년 9월 10일

컴퓨터 그래픽스

목록 보기
18/24

Vertex Buffer Objects (VBO)

일반적인 프로그램의 데이터는 메인 메모리(RAM)에 저장
RAM -> CPU -> GPU로 매 프레임 같은 데이터를 전달하는 건 비효율적

그래픽스 데이터는 RAM에서 VRAM으로 1번만 복사하여 저장
이후 VRAM -> GPU로 데이터를 매 프레임 전달

  • 메모리 주소
    • VBO 사용 전: 메모리의 절대 주소로 접근
    • VBO 사용 후: 미리 예약된 버퍼의 오프셋으로 접근

VBO 사용 시나리오

  • draw에 사용하는 array를 buffer 영역으로 전환
    1. glGenBuffers: 버퍼 생성
    2. glBindBuffers: GPU와 바인딩
  • RAM에 있던 data를 VRAM으로 전송
    1. glBufferData: 버퍼의 size 결정, 버퍼 전체 복사
    2. glBufferSubData: 버퍼 일부 복사
  • buffer 영역의 삭제
    1. glDeleteBuffers: 버퍼 전체 삭제

VBO 예제 프로그램

피라미드 회전 예제를 베이스로 VBO를 사용하도록 수정한 프로그램

  • VBO 세팅 함수
glm::vec4 vertPos[] = {	... };
glm::vec4 vertColor[] = { ... };
GLuint vbo[1];

void prepareVBO(void) {
	// 버퍼 생성하여 vbo 배열에 버퍼ID 저장
	glGenBuffers(1, vbo);
    
    // 생성한 버퍼를 GPU의 attribute 레지스터에서 사용할 배열로 바인딩
	glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
    
    // 버퍼의 사이즈를 결정 (18개의 xyzw 벡터가 2개), 크기만큼 VRAM에 할당
    // NULL을 넣어 아직 데이터 복사를 하지 않음
	glBufferData(GL_ARRAY_BUFFER, 2 * 18 * 4 * sizeof(GLfloat), NULL, GL_STATIC_DRAW);
    
    // offset을 0으로, vertPos 배열을 복사
	glBufferSubData(GL_ARRAY_BUFFER, 0, 18 * 4 * sizeof(GLfloat), vertPos);
    // 이후 위치에 vertColor 배열을 복사
	glBufferSubData(GL_ARRAY_BUFFER, 18 * 4 * sizeof(GLfloat), 18 * 4 * sizeof(GLfloat), vertColor);
}
  • drawFunc 함수
void drawFunc(void) {
	...
    
    // 절대주소가 아닌 offset을 넘겨줌
	GLuint locPos = glGetAttribLocation(prog, "aPos");
	glEnableVertexAttribArray(locPos);
	glVertexAttribPointer(locPos, 4, GL_FLOAT, GL_FALSE, 0, (GLvoid*)(0));
    
	GLuint locColor = glGetAttribLocation(prog, "aColor");
	glEnableVertexAttribArray(locColor);
	glVertexAttribPointer(locColor, 4, GL_FLOAT, GL_FALSE, 0, (GLvoid*)(18 * 4 * sizeof(GLfloat)));
    
    ...
}

이전 프로그램의 결과대로 정상적으로 회전하는 것을 확인

Structure의 VBO 저장

vertex = position + color

struct vertex {
	glm::vec4 pos;
    glm::vec4 color;
};

0번째 vertex의
pos의 offset은 0 byte,
color의 offset은 4 x 4 bytes

상대 거리 stride = 8 x 4 bytes 만큼 vertex 위치 계산 가능

Index Buffer Objects (IBO)

vertex 데이터 뿐만 아니라 index 배열 정보도 VRAM에 복사 가능
(glDrawElements() 함수가 index 배열을 활용하여 vertex 데이터를 참고함)

버퍼 바인딩 시 target을 GL_ELEMENT_ARRAY_BUFFER로만 세팅하면 VBO와 세팅방법은 똑같음

예제 프로그램

VBO와 IBO를 한 번에 사용하는 예제 프로그램

  • 버퍼 세팅 함수
glm::vec4 vertPos[] = { // 5 vertices
	{ 0.0F, 0.5F, 0.0F, 1.0F }, // v0
	{ 0.5F, -0.3F, 0.0F, 1.0F }, // v1
	{ 0.0F, -0.3F, -0.5F, 1.0F }, // v2
	{ -0.5F, -0.3F, 0.0F, 1.0F }, // v3
	{ 0.0F, -0.3F, 0.5F, 1.0F }, // v4
};

glm::vec4 vertColor[] = { // 5 colors
	{ 1.0F, 1.0F, 1.0F, 1.0F, }, // v0: white
	{ 1.0F, 0.3F, 0.3F, 1.0F, }, // v1: red
	{ 0.3F, 1.0F, 0.3F, 1.0F, }, // v2: green
	{ 0.3F, 0.3F, 1.0F, 1.0F, }, // v3: blue
	{ 1.0F, 1.0F, 0.3F, 1.0F, }, // v4: yellow
};

GLuint indices[] = { // 6 * 3 = 18 indices
	0, 1, 2, // face 0: v0-v1-v2
	0, 2, 3, // face 1: v0-v2-v3
	0, 3, 4, // face 2: v0-v3-v4
	0, 4, 1, // face 3: v0-v4-v1
	1, 4, 3, // face 4: v1-v4-v3
	1, 3, 2, // face 5: v1-v3-v2
};

GLuint vbo[1];
GLuint ibo[1];

void prepareVBO(void) {
    // vertex 수가 변경되어 버퍼 사이즈도 맞춰줌
	glGenBuffers(1, vbo);
	glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
	glBufferData(GL_ARRAY_BUFFER, 2 * 5 * 4 * sizeof(GLfloat), NULL, GL_STATIC_DRAW);
	glBufferSubData(GL_ARRAY_BUFFER, 0, 5 * 4 * sizeof(GLfloat), vertPos);
	glBufferSubData(GL_ARRAY_BUFFER, 5 * 4 * sizeof(GLfloat), 5 * 4 * sizeof(GLfloat), vertColor);
    
	// IBO 세팅
	glGenBuffers(1, ibo);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo[0]);
    // 18개의 인덱스 데이터를 IBO에 복사 
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, 18 * sizeof(GLuint), indices, GL_STATIC_DRAW);
}
  • drawFunc 함수
void drawFunc(void) {
	...
    
	GLuint locPos = glGetAttribLocation(prog, "aPos");
	glEnableVertexAttribArray(locPos);
	glVertexAttribPointer(locPos, 4, GL_FLOAT, GL_FALSE, 0, (GLvoid*)(0));
    
    // 5개의 vertex를 사용하도록 offset 값 변경
	GLuint locColor = glGetAttribLocation(prog, "aColor");
	glEnableVertexAttribArray(locColor);
	glVertexAttribPointer(locColor, 4, GL_FLOAT, GL_FALSE, 0, (GLvoid*)(5 * 4 * sizeof(GLfloat)));
    
    ...
    
    // IBO와 결합되어 있는 상황
	glDrawElements(GL_TRIANGLES, 18, GL_UNSIGNED_INT, (GLvoid*)(0));
    
    ...
}
profile
주니어 언리얼 프로그래머

0개의 댓글