Primitive Restart는 OpenGL에서 인덱스 기반 드로우 호출에서 하나의 프리미티브를 종료하고 새로운 프리미티브를 시작할 수 있도록 해주는 기능입니다.
보통 **GL_TRIANGLE_STRIP**이나 **GL_LINE_STRIP**와 같은 프리미티브를 렌더링할 때, 특정 인덱스 값을 만나면 현재 프리미티브가 종료되고 새로운 프리미티브가 시작됩니다.
이렇게 하면 드로우 호출을 줄이고 복잡한 구조를 하나의 인덱스 배열로 관리할 수 있습니다.
Primitive Restart의 작동 원리
- Primitive Restart 활성화: glEnable(GL_PRIMITIVE_RESTART) 또는 glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX)를 사용하여 활성화합니다.
- Restart 인덱스 설정: 특정 인덱스를 Restart 인덱스로 지정합니다. 인덱스 배열에서 이 값을 만나면, 현재의 프리미티브가 종료되고 다음 프리미티브가 시작됩니다.
기본 Restart 인덱스 값
GL_PRIMITIVE_RESTART_FIXED_INDEX를 사용할 때는 가장 큰 인덱스 값이 기본 Restart 인덱스로 지정됩니다.
- GL_UNSIGNED_BYTE → 255
- GL_UNSIGNED_SHORT → 65535
- GL_UNSIGNED_INT → 4294967295
Primitive Restart를 사용하는 이유
Primitive Restart를 사용하면 여러 프리미티브를 하나의 인덱스 배열로 관리할 수 있어서 드로우 호출을 줄일 수 있으며, 코드가 간결해집니다. 특히, 복잡한 지오메트리를 렌더링할 때 유용하며, 예를 들어 큐브, 복잡한 폴리곤 등 서로 연결되지 않은 면을 한 번에 렌더링할 수 있습니다.
Primitive Restart 예제
예제: Primitive Restart를 사용한 GL_TRIANGLE_STRIP 렌더링
아래 예제에서는 두 개의 사각형을 GL_TRIANGLE_STRIP으로 그리며, Primitive Restart를 사용하여 두 스트립을 하나의 인덱스 배열로 관리합니다.
GLushort indices[] = {
0, 1, 2, 3, // 첫 번째 사각형
65535, // Primitive Restart 인덱스
4, 5, 6, 7 // 두 번째 사각형
};
// Primitive Restart 활성화 및 설정
glEnable(GL_PRIMITIVE_RESTART);
glPrimitiveRestartIndex(65535); // Restart 인덱스 설정
// 드로우 호출
glDrawElements(GL_TRIANGLE_STRIP, 9, GL_UNSIGNED_SHORT, indices);
이 코드에서 65535를 만나면 OpenGL은 현재 스트립을 종료하고, 그 다음 인덱스(4번 인덱스)부터 새로운 스트립을 시작합니다.
Primitive Restart vs. Triangle Degeneration
- Primitive Restart: 설정한 Restart 인덱스를 사용하여 프리미티브를 분리하며, 각 프리미티브 간 연결이 없어도 됩니다. 설정이 직관적이고, 코드가 간결해집니다.
- Triangle Degeneration: 퇴화된 삼각형(같은 정점이 반복된 삼각형)을 사용하여 프리미티브를 분리하며, 연속된 점을 사용해야만 새로운 프리미티브가 시작됩니다.
요약
Primitive Restart는 하나의 드로우 호출에서 여러 개의 독립된 프리미티브를 렌더링할 수 있게 해주며, 복잡한 지오메트리를 효율적으로 표현하고 렌더링할 수 있도록 돕습니다. 프리미티브 리스타트를 사용하면 드로우 호출을 줄이고 코드가 간단해지며, 성능 최적화에도 유리합니다.
전체 코드
GLfloat cubeVerts[][3] = {
-1, -1, 1 , // V0
-1, 1, 1 , // V1
1, 1, 1 , // V2
1, -1, 1 ,// V3
-1, -1, -1 ,// V4
-1, 1, -1 ,// V5
1, 1, -1 , // V6
1, -1, -1 // V7
};
GLfloat cubeColors[][3] = {
{ 1.0, 1.0, 1.0 }, //0
{ 1.0, 1.0, 0.0 }, //1
{ 1.0, 1.0, 0.0 }, //2
{ 1.0, 1.0, 0.0 }, //3
{ 0.0, 1.0, 1.0 }, //4
{ 0.0, 1.0, 1.0 }, //5
{ 0.0, 1.0, 1.0 }, //6
{ 0.0, 1.0, 1.0 }, //7
};
// 36[ indices
GLushort cubeIndices[] = {
0,3,1, 3,2,1,
7,4,6, 4,5,6,
4,0,5, 0,1,5,
0xFFFF,
3,7,2, 7,6,2,
1,2,5, 2,6,5,
3,0,7, 0,4,7
};
GLushort firstHalfIndices[] = {
0,3,1, 3,2,1,
7,4,6, 4,5,6,
4,0,5, 0,1,5,
};
GLushort secondHalfIndices[] = {
3,7,2, 7,6,2,
1,2,5, 2,6,5,
3,0,7, 0,4,7
};
Cube::Cube( Renderer* parent )
{
...
// Create name buffer object ID
size = 24*sizeof(float);
glGenBuffers(1, &vId);
// Create VBO
glBindBuffer( GL_ARRAY_BUFFER, vId );
glBufferData( GL_ARRAY_BUFFER, size + size, 0, GL_STATIC_DRAW );
glBufferSubData( GL_ARRAY_BUFFER, 0, size, cubeVerts );
glBufferSubData( GL_ARRAY_BUFFER, size, size, cubeColors );
// Create IBO
unsigned short indexSize = sizeof( unsigned short )*37;
glGenBuffers(1, &iId);
glBindBuffer( GL_ARRAY_BUFFER, iId );
glBufferData( GL_ARRAY_BUFFER, indexSize, 0, GL_STATIC_DRAW );
glBufferSubData( GL_ARRAY_BUFFER, 0, indexSize, cubeIndices );
// Create IBO for Non Primitive Restart vertex indices
indexSize = sizeof( unsigned short )*18;
glGenBuffers(1, &iIdNoPrmtvRstrt1);
glBindBuffer( GL_ARRAY_BUFFER, iIdNoPrmtvRstrt1 );
glBufferData( GL_ARRAY_BUFFER, indexSize, 0, GL_STATIC_DRAW );
glBufferSubData( GL_ARRAY_BUFFER, 0, indexSize, firstHalfIndices );
// Create IBO for Non Primitive Restart vertex indices
indexSize = sizeof( unsigned short )*18;
glGenBuffers(1, &iIdNoPrmtvRstrt2);
glBindBuffer( GL_ARRAY_BUFFER, iIdNoPrmtvRstrt2 );
glBufferData( GL_ARRAY_BUFFER, indexSize, 0, GL_STATIC_DRAW );
glBufferSubData( GL_ARRAY_BUFFER, 0, indexSize, secondHalfIndices );
// Enable the attribute locations
glEnableVertexAttribArray(VERTEX_LOCATION);
glEnableVertexAttribArray(COLOR_LOCATION);
// Unbind buffer
glBindBuffer( GL_ARRAY_BUFFER, 0 );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
}
void Cube::RenderCube()
{
glDisable(GL_CULL_FACE);
char uniform = ProgramManagerObj->ProgramGetUniformLocation( program, ( char* )"MODELVIEWPROJECTIONMATRIX" );
if ( uniform >= 0 ){
glUniformMatrix4fv( uniform, 1, GL_FALSE,(float*)TransformObj->TransformGetModelViewProjectionMatrix() );
}
//Enable the Primitive Restart Index
//Bind the VBO
glBindBuffer( GL_ARRAY_BUFFER, vId );
glVertexAttribPointer(VERTEX_LOCATION, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glVertexAttribPointer(COLOR_LOCATION, 3, GL_FLOAT, GL_FALSE, 0, (void*)size);
if (usePrimitiveRestart){
LOGI("Using Primitive Restart - One Draw Call");
glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, iId );
glDrawElements(GL_TRIANGLES, 36+1, GL_UNSIGNED_SHORT, (void*)0); // Plus 1 because it has Primitive Restart Index.
}
else{
LOGI("Using Primitive Restart - Two Draw Calls");
glDisable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, iIdNoPrmtvRstrt1 );
glDrawElements(GL_TRIANGLES, 18, GL_UNSIGNED_SHORT, (void*)0);
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, iIdNoPrmtvRstrt2 );
glDrawElements(GL_TRIANGLES, 18, GL_UNSIGNED_SHORT, (void*)0);
}
glBindBuffer( GL_ARRAY_BUFFER, 0 );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
}
'Opengles 3.0 with Android' 카테고리의 다른 글
Chapter 4. OBJ Loader (0) | 2025.03.08 |
---|---|
Chapter 3.2 Instancing 렌더링 (0) | 2025.02.24 |
Chatper 3.1 Layout Qualifier, Grouping Uniforms(UBO) (0) | 2025.02.24 |
Chapter 2.5 VAO ? VBO ? ( 참고 ) (1) | 2025.02.22 |
Chapter 2.1 Shader Build (0) | 2025.02.22 |