OpenGLES3.0_Example/Chapter_2/app/src/main/cpp/Scene/ManyCubes.cpp at main · dlgmlals3/OpenGLES3.0_Example
Contribute to dlgmlals3/OpenGLES3.0_Example development by creating an account on GitHub.
github.com
InitBufferObject(), RenderCube()
정의
VBO (Vertex Buffer Object) |
GPU 메모리에 정점 데이터를 저장하는 버퍼 객체 | 정점 데이터(좌표, 색상, 노멀 등)를 CPU에서 GPU로 효율적으로 전송하여 렌더링 성능을 향상시킴 |
EBO (Element Buffer Object) |
GPU 메모리에 인덱스 데이터를 저장하는 버퍼 객체 | 인덱스를 사용하여 정점 데이터를 재사용함으로써 메모리 사용을 최적화하고, 드로우 콜 효율을 높임 |
VAO (Vertex Array Object) |
정점 관련 상태(바인딩된 VBO, EBO, 정점 속성 설정 등)를 캡슐화한 객체 | 복잡한 정점 속성 설정을 한 번에 저장 및 복원하여, 상태 관리와 렌더링 설정을 단순화시킴 |
> glVertexAttribPointer 사용
VBO 사용 |
바인딩된 버퍼 내 오프셋 (바이트 단위) | glBindBuffer(GL_ARRAY_BUFFER, vbo); glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); |
최적화됨: 정점 데이터는 glBufferData/glBufferSubData로 한 번 GPU에 업로드되어, 이후 드로우콜마다 GPU 메모리에서 직접 읽으므로 CPU→GPU 복사가 반복적으로 일어나지 않음. |
클라이언트 사이드 배열 | CPU 메모리 상의 실제 데이터 주소 | glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 0, cubeVerts); |
비최적화: 매번 드로잉 시 CPU 메모리에서 데이터를 읽어와 GPU로 복사해야 하므로, 오버헤드가 증가하고 성능이 저하됨. |
클라이언트 사이드 배열
// 예시 데이터 (필요한 데이터로 채워주세요)
float cubeVerts[] = {
// 4개의 정점, 각 정점 3개 좌표 (x, y, z)
0.5f, 0.5f, 0.0f, // 정점 0: 오른쪽 위
0.5f, -0.5f, 0.0f, // 정점 1: 오른쪽 아래
-0.5f, -0.5f, 0.0f, // 정점 2: 왼쪽 아래
-0.5f, 0.5f, 0.0f // 정점 3: 왼쪽 위
};
float cubeColors[] = {
// 각 정점에 대한 컬러 (R, G, B)
1.0f, 0.0f, 0.0f, // 정점 0: 빨강
0.0f, 1.0f, 0.0f, // 정점 1: 초록
0.0f, 0.0f, 1.0f, // 정점 2: 파랑
1.0f, 1.0f, 0.0f // 정점 3: 노랑
};
unsigned short cubeIndices[] = {
// 사각형을 두 개의 삼각형으로 구성하는 인덱스
0, 1, 3, // 첫 번째 삼각형
1, 2, 3 // 두 번째 삼각형
};
// 렌더링 함수 (VBO/VAO/EBO 사용 없이, 클라이언트 메모리 배열 이용)
// glEnableClientState 대신 glEnableVertexAttribArray를 사용합니다.
void renderCube_Original_NoBuffers() {
// 만약 혹시 바인딩된 버퍼 객체가 있다면 해제합니다.
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
// 정점 속성을 활성화합니다.
// 여기서는 셰이더에서 정점 위치를 location 0, 컬러를 location 1로 사용한다고 가정합니다.
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
// 클라이언트 메모리에 있는 데이터를 직접 지정합니다.
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, cubeVerts);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, cubeColors);
// 인덱스 데이터를 이용하여 도형(여기서는 두 개의 삼각형으로 구성된 사각형)을 그립니다.
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, cubeIndices);
// 사용 후 정점 속성 비활성화 (상태 정리를 위해)
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
}
VBO + EBO
// 전역 데이터 (예시: cubeVerts, cubeColors, cubeIndices)
// cubeVerts: 정점 좌표 배열 (예: 24개의 float)
// cubeColors: 정점 컬러 배열 (예: 24개의 float)
// cubeIndices: 인덱스 배열 (예: 36개의 unsigned short)
float cubeVerts[] = { /* 정점 좌표 데이터 */ };
float cubeColors[] = { /* 정점 컬러 데이터 */ };
unsigned short cubeIndices[] = { /* 인덱스 데이터 */ };
GLuint vbo, ebo;
GLsizei size = 24 * sizeof(float); // 정점 데이터 또는 컬러 데이터 크기
GLsizei indexSize = 36 * sizeof(unsigned short); // 인덱스 데이터 크기
// VBO와 EBO를 생성하고 데이터를 GPU에 업로드하는 초기화 함수 (VAO 미사용)
void initBuffers_NoVAO() {
// VBO 생성 및 설정
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
// 전체 버퍼 크기는 정점 데이터 + 컬러 데이터
glBufferData(GL_ARRAY_BUFFER, size + size, NULL, GL_STATIC_DRAW);
// 버퍼의 첫 부분에 정점 데이터 업로드
glBufferSubData(GL_ARRAY_BUFFER, 0, size, cubeVerts);
// 버퍼의 두 번째 부분(오프셋 size)에 컬러 데이터 업로드
glBufferSubData(GL_ARRAY_BUFFER, size, size, cubeColors);
// EBO 생성 및 설정
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexSize, NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, indexSize, cubeIndices);
// 바인딩 해제 (옵션)
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
// 렌더링 함수: VBO와 EBO를 직접 바인딩하여 정점 속성 설정 후 그리기
void renderCube_NoVAO() {
// 1. VBO 바인딩 및 정점 속성 설정
glBindBuffer(GL_ARRAY_BUFFER, vbo);
// 셰이더에서 정점 위치를 attribute location 0, 컬러를 location 1로 사용한다고 가정합니다.
glEnableVertexAttribArray(0);
// 정점 데이터: VBO의 오프셋 0부터 읽음 (각 정점 3개 float)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(1);
// 컬러 데이터: VBO의 오프셋 'size'부터 읽음 (각 정점 3개 float)
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)size);
// 2. EBO 바인딩 (인덱스 데이터를 사용하기 위해)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
// 3. 그리기: 인덱스 데이터를 사용하여 삼각형 36개로 구성된 도형(큐브) 렌더링
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, (void*)0);
// 4. 사용한 정점 속성 비활성화 및 버퍼 바인딩 해제
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
VBO + EBO + VAO
// 전역 데이터 (예시 데이터)
// cubeVerts: 정점 좌표 (예: 24개의 float = 8 vertices * 3)
// cubeColors: 정점 컬러 (예: 24개의 float)
// cubeIndices: 인덱스 데이터 (예: 36개의 unsigned short)
float cubeVerts[] = { /* ... 정점 좌표 데이터 ... */ };
float cubeColors[] = { /* ... 정점 컬러 데이터 ... */ };
unsigned short cubeIndices[] = { /* ... 인덱스 데이터 ... */ };
GLuint VAO, VBO, EBO;
GLsizei size = 24 * sizeof(float); // 정점 데이터 또는 컬러 데이터 크기
GLsizei indexSize = 36 * sizeof(unsigned short); // 인덱스 데이터 크기
// 초기화: VAO, VBO, EBO 생성 및 데이터 업로드
void initBuffers() {
// VAO, VBO, EBO 객체 생성
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
// VAO 바인딩: 이후의 설정이 VAO에 저장됨
glBindVertexArray(VAO);
// VBO 설정: 정점 데이터와 컬러 데이터를 하나의 버퍼에 연속적으로 저장
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// 전체 크기: 정점 데이터 + 컬러 데이터
glBufferData(GL_ARRAY_BUFFER, size + size, NULL, GL_STATIC_DRAW);
// 첫 번째 부분에 정점 데이터 업로드
glBufferSubData(GL_ARRAY_BUFFER, 0, size, cubeVerts);
// 두 번째 부분(오프셋 size)에 컬러 데이터 업로드
glBufferSubData(GL_ARRAY_BUFFER, size, size, cubeColors);
// EBO 설정: 인덱스 데이터를 업로드
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexSize, NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, indexSize, cubeIndices);
// 정점 속성 설정 (예제에서는 attribute 0: 위치, attribute 1: 컬러)
glEnableVertexAttribArray(0);
// 위치 데이터: VBO의 오프셋 0부터 읽어들이며 3개의 float씩
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(1);
// 컬러 데이터: VBO의 오프셋 'size'부터 읽어들이며 3개의 float씩
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)size);
// VAO 설정 완료 후 해제 (EBO 바인딩 정보는 VAO에 포함됨)
glBindVertexArray(0);
}
// 렌더링 함수 (VBO/EBO 방식)
// 필요시 glBufferSubData를 이용하여 GPU 버퍼의 데이터를 업데이트할 수 있음.
void renderCube_VBO() {
// 예: 만약 애니메이션 등으로 정점 데이터를 업데이트하고 싶다면...
// float newCubeVerts[] = { ... 업데이트된 데이터 ... };
// glBindBuffer(GL_ARRAY_BUFFER, VBO);
// glBufferSubData(GL_ARRAY_BUFFER, 0, size, newCubeVerts);
// glBindBuffer(GL_ARRAY_BUFFER, 0);
// 셰이더 프로그램 활성화, MVP 행렬 uniform 업데이트 등은 생략하고 VAO 바인딩 후 드로우 콜
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, (void*)0);
glBindVertexArray(0);
}
Function >
0. glBufferSubData
할당된 버퍼 객체 내의 특정 부분을 업데이트하는 함수입니다.
즉, 한 번 glBufferData로 전체 버퍼의 저장소를 할당한 후, 그 저장소 중 일부를 새 데이터로 덮어쓰고 싶을 때 사용합니다.
void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void *data);
- 매개변수 설명:
- target:
업데이트할 버퍼의 용도를 지정합니다. 예를 들어, 정점 데이터의 경우 GL_ARRAY_BUFFER, 인덱스 데이터의 경우 GL_ELEMENT_ARRAY_BUFFER를 사용합니다. - offset:
버퍼 저장소 내에서 데이터를 업데이트할 시작 오프셋(바이트 단위)입니다. 즉, 버퍼의 어느 위치부터 새 데이터를 덮어쓸지를 지정합니다. - size:
업데이트할 데이터의 크기를 바이트 단위로 지정합니다. - data:
새로 업데이트할 데이터를 가리키는 포인터입니다. 이 데이터는 버퍼 내의 해당 오프셋부터 size 바이트 만큼 복사됩니다.
- target:
1. glBufferData
OpenGL에서 버퍼 객체(예: VBO, EBO 등)의 데이터 저장소를 생성하고 초기화하는 함수입니다. CPU 메모리의 데이터를 GPU 메모리에 복사하거나, 단순히 저장소를 할당하는 역할을 합니다.
void glBufferData(GLenum target, GLsizeiptr size, const void *data, GLenum usage);
- 매개변수 설명:
- target:
버퍼의 용도를 지정합니다. 예를 들어, 정점 데이터의 경우 GL_ARRAY_BUFFER, 인덱스 데이터의 경우 GL_ELEMENT_ARRAY_BUFFER를 사용합니다. - size:
할당할 버퍼 저장소의 크기를 바이트 단위로 지정합니다. - data:
초기화할 데이터를 가리키는 포인터입니다. 만약 NULL을 지정하면, 지정된 크기만큼의 저장소만 할당되고 초기 데이터는 채워지지 않습니다. - usage:
버퍼 데이터의 사용 패턴에 대한 힌트입니다. 예를 들어,- GL_STATIC_DRAW: 데이터가 거의 변경되지 않을 때
- GL_DYNAMIC_DRAW: 데이터가 자주 변경될 때
등의 값을 사용하여 드라이버에게 최적화를 위한 정보를 제공합니다.
- target:
2. glGetAttribLocation()
glGetAttribLocation은 셰이더 프로그램에서 속성(attribute) 변수의 위치(인덱스)를 얻는 함수입니다. 버텍스 데이터를 셰이더의 특정 속성에 연결하기 위해 해당 속성의 위치를 알아야 하며, 그 위치는 컴파일된 셰이더 프로그램에서만 결정됩니다.
GLint glGetAttribLocation(GLuint program, const GLchar *name);
- program: 셰이더 프로그램의 ID입니다. (glCreateProgram으로 생성한 프로그램)
- name: 셰이더 속성(attribute) 변수의 이름입니다. 이 이름은 셰이더 소스 코드에서 정의한 in 변수를 가리킵니다.
- 셰이더 프로그램 내에서 속성의 인덱스 위치를 반환합니다. 이 값은 glVertexAttribPointer나 glEnableVertexAttribArray에서 속성의 인덱스로 사용됩니다.
- 반환값이 -1이면 해당 이름을 가진 속성이 셰이더에서 존재하지 않거나 최적화로 인해 제거된 것입니다.
3. glVertexAttribPointer()
glVertexAttribPointer는 버텍스 데이터를 셰이더 속성에 연결하는 함수입니다.
이 함수는 OpenGL에 버텍스 버퍼에 저장된 데이터의 형식과 구조를 알려줍니다.
즉, 메모리에서 데이터를 어떻게 읽어야 할지 정의합니다.
void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
- index: 셰이더에서 속성의 인덱스. (glGetAttribLocation으로 얻은 값)
- size: 속성의 요소 개수. 예를 들어, vec3는 3, vec4는 4가 됩니다.
- type: 버텍스 데이터의 타입. 보통 GL_FLOAT를 사용합니다.
- normalized: 정수형 데이터를 정규화할지 여부를 결정합니다. (GL_TRUE 또는 GL_FALSE)
- stride: 버텍스 사이의 간격(바이트 단위). 같은 속성의 데이터가 연속되어 있으면 0으로 설정합니다.
- pointer: 버퍼 내에서 속성 데이터의 시작 위치입니다. 보통 NULL 또는 0으로 설정해 첫 번째 데이터를 가리키게 합니다.
이 함수는 중요한 특징이 있다.
glVertexAttribPointer는 GPU에서 버퍼를 읽어 올 수도 있고, CPU에서 읽어올 수 도 있다.
> VBO를 사용하는 경우:
glBindBuffer(GL_ARRAY_BUFFER, vbo);
VBO를 바인딩한 상태에서 pointer에 들어가는 값은 버퍼에서 해당 속성의 데이터가 시작하는 위치를 가리킵니다.
이 값은 버퍼에서의 오프셋이 됩니다.
일반적으로는 NULL 또는 0으로 첫 번째 데이터 요소부터 시작하게 합니다.
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(positionAttribLocation, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
> VBO를 사용하지 않는 경우:
만약 버퍼가 아닌 CPU 메모리에 있는 데이터를 직접 참조할 경우(예: 배열),
pointer는 그 배열의 메모리 주소가 됩니다.
이때는 GPU에 데이터가 전송되지 않고, CPU 메모리에서 직접 버텍스 데이터를 읽습니다.
일반적으로는 이 방법보다 VBO를 사용하는 것이 더 효율적입니다.
아래 예제 에서도 VBO를 사용하지 않는다.
float vertices[] = { ... }; // CPU 메모리에 있는 버텍스 데이터
glBindBuffer(GL_ARRAY_BUFFER, 0); // VBO 사용 안 함
glVertexAttribPointer(positionAttribLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices);
4. glEnableVertexAttribArray()
glEnableVertexAttribArray는 **셰이더에서 특정 속성(attribute)**을 활성화하는 함수입니다.
이 함수가 호출되면 해당 속성의 데이터를 읽을 수 있게 됩니다.
이 함수가 호출되지 않으면, OpenGL은 해당 속성 데이터를 읽어들이지 않습니다.
void glEnableVertexAttribArray(GLuint index);
index: 활성화하려는 속성의 인덱스. (glGetAttribLocation으로 얻은 값)
5. glGetUniformLocation
glGetUniformLocation은 특정 셰이더 프로그램에서 uniform 변수의 위치(인덱스)를 가져오는 함수입니다.
이 위치를 알면 나중에 해당 변수에 값을 설정할 수 있습니다.
GLint glGetUniformLocation(GLuint program, const GLchar *name);
- program: uniform 변수를 가져올 셰이더 프로그램의 ID입니다. (glCreateProgram으로 생성한 프로그램)
- name: uniform 변수의 이름입니다. 이 이름은 셰이더 소스 코드에서 정의된 uniform 변수의 이름과 일치해야 합니다.
- 지정한 uniform 변수의 인덱스 위치를 반환합니다. 이 값은 나중에 glUniform* 함수를 호출할 때 사용됩니다.
- 반환값이 -1이면 해당 이름을 가진 uniform 변수가 셰이더에서 존재하지 않거나 최적화로 인해 제거된 것입니다.
6. glUniform1f
glUniform1f는 float 타입의 uniform 변수를 설정하는 함수입니다.
이 함수를 사용하여 셰이더에서 정의된 uniform 변수의 값을 업데이트할 수 있습니다.
void glUniform1f(GLint location, GLfloat v0);
- location: 설정할 uniform 변수의 위치. (glGetUniformLocation으로 얻은 값)
- v0: uniform 변수에 설정할 float 값입니다.
정리하면, 유니폼 위치를 가져와서 유니폼 값을 설정합니다.
'Opengles 3.0 with Android' 카테고리의 다른 글
Chapter 2.3 Transform and Projection (0) | 2025.02.21 |
---|---|
Chapter 2.6 Touch Event ( Skip ) (1) | 2025.02.21 |
Chapter 1.0 EGL Context ( Skip ) (0) | 2025.02.21 |
Chapter 1.0 시작하면서... (3) | 2025.02.21 |
10.2 Object Picking ( with Raytracing ) (0) | 2025.01.30 |