본문 바로가기
Opengles 3.0 with Android

Chapter 3.3 Primitive Restart < Skip >

by SimonLee 2025. 2. 24.

Primitive Restart는 OpenGL에서 인덱스 기반 드로우 호출에서 하나의 프리미티브를 종료하고 새로운 프리미티브를 시작할 수 있도록 해주는 기능입니다.

보통 **GL_TRIANGLE_STRIP**이나 **GL_LINE_STRIP**와 같은 프리미티브를 렌더링할 때, 특정 인덱스 값을 만나면 현재 프리미티브가 종료되고 새로운 프리미티브가 시작됩니다.

이렇게 하면 드로우 호출을 줄이고 복잡한 구조를 하나의 인덱스 배열로 관리할 수 있습니다.

 

Primitive Restart의 작동 원리

  1. Primitive Restart 활성화: glEnable(GL_PRIMITIVE_RESTART) 또는 glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX)를 사용하여 활성화합니다.
  2. 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