본문 바로가기
Opengles 3.0 with Android

Chapter 2.1 VBO(Vertex Buffer Object) 사용하여 정사각형 렌더링. 해본다.

by SimonLee 2024. 10. 17.
이 코드는 OpenGL을 사용해 정사각형(Square)을 그리기 위한 VBO(Vertex Buffer Object)와 EBO(Element Buffer Object)를 설정하고, 이를 기반으로 렌더링하는 과정입니다.

CreateVBO 함수에서 VBO와 EBO를 생성하고 데이터들을 전송한 뒤,

Render 함수에서 셰이더에 데이터를 연결하고 삼각형 두 개를 그려 정사각형을 렌더링합니다.

 

void Square::CreateVBO() {
    // VBO 생성 -> 바인딩 -> 버퍼에 데이터 복사
    glGenBuffers(1, &bufferId);
    glBindBuffer(GL_ARRAY_BUFFER, bufferId);
    // 버텍스 배열 먼저 넣고 뒤에 컬러 배열 있음.
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices) + sizeof(colors), nullptr, GL_STATIC_DRAW);
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
    glBufferSubData(GL_ARRAY_BUFFER, sizeof(vertices), sizeof(colors), colors);

    // EBO 생성 -> 바인딩 -> 버퍼 복사
    glGenBuffers(1, &indexId);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexId);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexes), indexes, GL_STATIC_DRAW);
}


void Square::Render() {
    glUseProgram( program->ProgramID );

    // 버퍼 정보를 셰이더에 알려줌
    char attrib = ProgramManagerObj->ProgramGetVertexAttribLocation(program, (char*)"VertexPosition");
    glVertexAttribPointer(attrib, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(attrib);
    char attribColor = ProgramManagerObj->ProgramGetVertexAttribLocation(program, (char*)"VertexColor");
    // 버텍스 배열 이후에 컬러 배열 있음.
    glVertexAttribPointer(attribColor, 3, GL_FLOAT, GL_FALSE, 0, (void*)sizeof(vertices));
    glEnableVertexAttribArray(attribColor);

    // 렌더링 삼각형 2개는 count : 6, 마지막 인자는 0이면 바인딩된 인덱스 버퍼 처음부터 사용.
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
}

 

CreateVBO 함수 설명

  • VBO 생성 및 데이터 전송:
    • **glGenBuffers**로 VBO를 생성하고, **glBindBuffer**로 VBO를 바인딩합니다.
    • **glBufferData**는 VBO에 필요한 메모리를 할당합니다. 이때 정점 데이터색상 데이터를 한 번에 보낼 공간을 확보한 후, **glBufferSubData**를 사용해 먼저 정점 데이터를 전송하고, 그 다음에 색상 데이터를 전송합니다.
    • 정점 데이터는 vertices 배열, 색상 데이터는 colors 배열에서 가져와 메모리에 복사됩니다.
  • EBO 생성 및 데이터 전송:
    • **glGenBuffers**로 EBO를 생성하고, **glBindBuffer**로 EBO를 바인딩한 뒤, **glBufferData**로 인덱스 데이터를 GPU로 전송합니다.
    • 인덱스 데이터는 indexes 배열로부터 가져와, 정점들을 어떻게 연결해 삼각형을 그릴지를 결정합니다.

 

Render 함수 설명

  • 셰이더 프로그램 활성화:
    • **glUseProgram**으로 현재 사용할 셰이더 프로그램을 활성화합니다. 이 셰이더는 정점과 색상 데이터를 처리하는 역할을 합니다.
  • 정점 및 색상 속성 설정:
    • **glVertexAttribPointer**는 VBO에서 가져온 데이터를 셰이더 속성과 연결하는 함수입니다. 정점 데이터는 VBO의 처음부터 시작되며, 색상 데이터는 정점 데이터 뒤에 위치해 있습니다.
    • **glVertexAttribPointer**에서 오프셋을 주어, sizeof(vertices)만큼 이동한 후 색상 데이터를 참조하게 설정합니다.
  • 정점 속성 활성화:
    • **glEnableVertexAttribArray**는 지정된 속성을 활성화하여 GPU가 해당 데이터를 참조할 수 있게 해줍니다. 정점 위치와 색상 두 가지 속성을 모두 활성화합니다.
  • 렌더링 호출:
    • glDrawElements 함수는 인덱스 배열을 사용하여 도형을 그리는 함수입니다. 이 코드에서는 **2개의 삼각형(총 6개의 인덱스)**을 사용하여 정사각형을 그립니다.
    • **GL_TRIANGLES**는 삼각형을 그리는 모드이며, 인덱스 배열을 사용하여 정점 데이터를 재사용할 수 있습니다.

 

요약

CreateVBO 함수는 정점 데이터와 색상 데이터를 GPU 메모리에 전송하고, 인덱스 버퍼를 설정하여 삼각형 두 개를 그리기 위한 데이터를 준비합니다.

Render 함수는 준비된 데이터를 셰이더 프로그램과 연결하고, **glDrawElements**를 사용해 인덱스 배열을 통해 도형을 렌더링합니다.

이 방식은 정점 데이터를 효율적으로 관리하며, 중복된 정점 데이터의 재사용을 통해 성능을 최적화할 수 있습니다.

 

 

 

 

=========================================================================================

API 설명

1. glBufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage)

  • 설명: 버퍼를 할당하고 데이터를 채워넣는 함수.
    • target: 버퍼의 유형을 지정 (GL_ARRAY_BUFFER는 정점 데이터를 위한 버퍼).
    • size: 할당할 버퍼의 크기(바이트 단위).
    • data: 버퍼에 채울 초기 데이터.
    • usage: 데이터의 사용 패턴을 지정 (GL_STATIC_DRAW, GL_DYNAMIC_DRAW 등).매개변수:

2. glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void* data)

  • 설명: 이미 할당된 버퍼에서 일부 데이터를 업데이트하는 함수.
    • target: 업데이트할 버퍼의 유형 (GL_ARRAY_BUFFER).
    • offset: 업데이트할 데이터가 시작될 오프셋(바이트 단위). 버퍼에서 어느 위치부터 데이터를 변경할지 결정.
    • size: 업데이트할 데이터의 크기(바이트 단위).
    • data: 버퍼의 오프셋에 덮어쓸 새로운 데이터.매개변수:

3. glGenBuffers(GLsizei n, GLuint* buffers)

  • 설명: 버퍼 객체를 생성하는 함수.
  • 매개변수:
    • n: 생성할 버퍼의 개수.
    • buffers: 생성된 버퍼 ID를 저장할 배열.
     

4. glBindBuffer(GLenum target, GLuint buffer)

  • 설명: 지정된 버퍼 객체를 바인딩하여 이후의 작업들이 이 버퍼에 적용되도록 설정.
  • 매개변수:
    • target: 바인딩할 버퍼의 유형 (GL_ARRAY_BUFFER).
    • buffer: 바인딩할 버퍼 객체의 ID.
     

5. glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer)

  • 설명: 정점 데이터를 셰이더 속성과 연결해주는 함수.
    • index: 셰이더에서 참조할 정점 속성 위치 (예: 위치, 색상 등).
    • size: 정점 속성의 요소 수 (예: 3이면 x, y, z).
    • type: 데이터 타입 (GL_FLOAT).
    • normalized: 정규화 여부.
    • stride: 정점 사이의 바이트 간격.
    • pointer: 버퍼에서 데이터가 시작될 오프셋 위치.매개변수:

pointer가 nullptr 이면 바인딩 된 버퍼에서 데이터를 가져옴

6. glEnableVertexAttribArray(GLuint index)

  • index: 활성화할 정점 속성의 인덱스. 이 인덱스는 **glVertexAttribPointer()**에서 지정한 정점 속성의 인덱스와 일치해야 해.
    • 예를 들어, 위치 데이터가 glVertexAttribPointer(0, ...)로 설정되었다면, 위치 속성 배열을 활성화하기 위해 glEnableVertexAttribArray(0)를 사용해

7. glDrawArrays(GLenum mode, GLint first, GLsizei count)

  • 설명: 현재 바인딩된 버퍼를 기반으로 정점 데이터를 그리는 함수.

매개변수:

  • mode: 그릴 도형의 유형 (GL_TRIANGLES 등).
  • first: 그리기 시작할 첫 번째 정점의 인덱스.
  • count: 그릴 정점의 개수.

8. glDrawElements(GLenum mode, GLsizei count, GLenum type, const void *indices);

매개변수 설명

  1. mode (GLenum):
    • 그릴 도형의 **프리미티브 타입(Primitive Type)**을 지정합니다.
    • 대표적인 값들은 다음과 같습니다:
      • GL_TRIANGLES: 정점 3개씩 모아서 삼각형을 그립니다.
      • GL_TRIANGLE_STRIP: 정점들이 줄을 지어 삼각형을 형성하는 방식입니다.
      • GL_LINES: 두 개의 정점으로 선을 그립니다.
      • GL_POINTS: 점을 그립니다.
  2. count (GLsizei):
    • 인덱스 배열의 요소 개수를 나타냅니다. 즉, 그릴 도형에 필요한 인덱스의 수입니다.
    • 예를 들어, 삼각형 두 개를 그리기 위해서는 6개의 인덱스가 필요합니다.
  3. type (GLenum):
    • 인덱스 배열에 저장된 데이터의 타입을 지정합니다.
    • 사용 가능한 값은 GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, 또는 **GL_UNSIGNED_INT**입니다.
    • 인덱스 배열이 1바이트, 2바이트, 또는 4바이트 크기의 정수 값으로 저장될 수 있기 때문에 이에 따라 타입을 설정해 줍니다.
  4. indices (const void*):
    • 인덱스 배열의 시작 위치를 나타냅니다. 보통 0을 넣어 사용하며, 이는 GPU에 바인딩된 인덱스 버퍼의 처음부터 인덱스 데이터를 읽으라는 의미입니다.
    • **EBO(Element Buffer Object)**에 바인딩된 버퍼에서 인덱스 데이터의 시작 주소를 참조할 때 이 값이 사용됩니다.

 

 

질문

glVertexAttribPointer와 vec4

glVertexAttribPointer 함수에서 size는 정점당 몇 개의 요소를 셰이더로 보낼 것인지를 결정하는 값입니다. 이 값이 2로 설정되었으므로, 각 정점에 대해 x, y 좌표만 전달되고, z와 w 좌표는 전달되지 않습니다.

셰이더에서 vec4와 glVertexAttribPointer의 상호작용

셰이더에서 VertexPosition이 **vec4**라면, OpenGL은 자동으로 부족한 값기본값으로 채워줍니다. 만약 **2개의 값(x, y)**만 전달되면, 셰이더에서 z는 0.0, w는 1.0으로 자동 설정됩니다.

즉, 다음과 같이 동작합니다:

  • glVertexAttribPointer에서 2개의 값을 전달: (x, y).
  • 셰이더의 vec4는 자동으로 다음과 같이 채워집니다: (x, y, 0.0, 1.0).

이 방식은 2D 데이터를 3D 공간에 표시하려고 할 때 자주 사용됩니다. 즉, z축이 0인 평면 위에서 도형을 그리는 것이며, w는 1.0으로 유지되어 동일한 좌표계 변환이 적용됩니다.