OpenGL 정점 캐시 객체

13670 단어 OpenGL캐시 대상vbo
정점 캐시 개체(Vertex Buffer Object, 약칭 VBO)는 개발자가 상황에 따라 정점 데이터를 메모리에 저장할 수 있도록 한다.
VBO를 사용하지 않으면glVertexPointer/glNormalPointer로 정점 데이터를 지정합니다. 이때 정점 데이터는 시스템 메모리에 저장되며, 매번 렌더링할 때마다 데이터를 시스템 메모리에서 메모리로 복사하여 적지 않은 시간을 소모합니다.
실제로 많은 복사는 불필요하다. 예를 들어 정적 대상의 정점 데이터는 변하지 않는다. 만약에 그것들을 메모리에 넣을 수 있다면 매번 렌더링할 때마다 복사 조작을 하지 않아도 적지 않은 시간을 절약할 수 있다.

1. 확장 확인


GL_ARB_vertex_buffer_object, OpenGL의 확장, 즉 VBO입니다.
이 확장을 사용하려면 먼저 현재 OpenGL 버전에서 이 확장이 지원되는지 확인해야 합니다.함수는 다음과 같습니다. 임의의 확장을 검사할 수 있으며, 파라미터는 확장자의 문자열입니다.
int CheckExtension(char *extName)
{
    char *p = (char *)glGetString(GL_EXTENSIONS);
    char *end;
    int extNameLen = (int)strlen(extName);

    end = p + strlen(p);
    while (p < end) {
        int n = (int)strcspn(p, " ");
        if ((extNameLen == n) && (strncmp(extName, p, n) == 0)) {
            return TRUE;
        }
        p += (n + 1);
    }
    return FALSE;
}

그런 다음 CheckExtension을 호출하여 확장이 있는지 확인합니다. 확장자는 GL 입니다.ARB_vertex_buffer_object.확장이 존재하면, wglGetProcAddress를 호출해서 각각 필요한 확장 함수의 바늘을 가져옵니다.예를 들면 glGenBuffers, glBindBuffer입니다.
이 함수가 확장 라이브러리에 있다는 것을 나타내기 위해glGenBuffersaRB라는 이름을 사용하는 경우도 있습니다.구 버전의 OpenGL에서 흔히 볼 수 있듯이 새 버전의 OpenGL은glGenBuffers를 사용하여 이렇게 ARB를 제거하면 된다.
if(CheckExtension("GL_ARB_vertex_buffer_object"))
{
    glGenBuffers = (PFNGLGENBUFFERSARBPROC) wglGetProcAddress("glGenBuffersARB");
    glBindBuffer = (PFNGLBINDBUFFERARBPROC) wglGetProcAddress("glBindBufferARB");
    glBufferData = (PFNGLBUFFERDATAARBPROC) wglGetProcAddress("glBufferDataARB");
    glBufferSubData = (PFNGLBUFFERSUBDATAARBPROC) wglGetProcAddress("glBufferSubDataARB");
    glDeleteBuffers = (PFNGLDELETEBUFFERSARBPROC) wglGetProcAddress("glDeleteBuffersARB");
}
else
{
    cout<<"ERROR: GL_ARB_vertex_buffer_object extension was not found"<<endl;
    return FALSE;
}

2. 정점 캐시 객체 작성


VBO를 설정하려면 다음 세 단계가 필요합니다.
  • 캐시 대상을 만들고glGenBuffers()
  • 를 사용합니다
  • 캐시 대상을 귀속시키고glBindBuffer()
  • 캐시 대상에 정점 데이터를 복사하고 glBufferData()
  • 를 사용합니다.
    세 함수의 소개는 다음과 같다.
    void glGenBuffers(GLsizei n, GLuint* ids)
    캐시 대상을 만들고 캐시 대상의 식별자를 되돌려줍니다.
  • n - 캐시를 작성한 개체의 수입니다.
  • ids - 캐시 객체의 단일 ID 또는 여러 ID를 저장하는 GLuint형 변수 또는 배열입니다.

  • void glBindBuffer(GLenum target, GLuint id)
    캐시 대상을 만들면 사용할 수 있도록 캐시 대상을 연결해야 합니다.귀속, 즉 현재 어떤 캐시 대상을 사용할 것인지 지정합니다.
    target - 캐시 대상이 저장할 데이터 형식입니다. 두 가지 값만 있습니다: GLARRAY_BUFFER, GLELEMENT_ARRAY_BUFFER.교점 좌표, 텍스쳐 좌표, 수직 벡터, 색상 배열 등과 같은 교점 관련 속성의 경우 GLARRAY_BUFFER;GL 을 사용하는 색인 배열ELEMENT_ARRAY_glDrawElements()에서 사용할 수 있도록 BUFFER
    id - 캐시된 객체의 ID입니다.
    void glBufferData(GLenum target, GLsizei size, const void* data, GLenum usage)
    캐시 개체에 데이터를 복제합니다.
  • target - 캐시 대상의 유형, 두 가지 값만 있음: GLARRAY_BUFFER 및 GLELEMENT_ARRAY_BUFFER.
  • size-수 그룹 데이터의 크기, 단위는 바이트(bytes)입니다.
  • 데이터 - 배열 데이터의 포인터입니다. NULL로 지정하면 VBO는 해당하는 크기의 캐시 대상만 만듭니다.
  • usage - 캐시 대상이 어떻게 사용되는지 세 가지가 있는데 정적 (static), 동적 (dynamic)과 흐름 (stream)이다.총 9개의 값:
  • GL_STATIC_DRAW
    GL_STATIC_READ
    GL_STATIC_COPY
    GL_DYNAMIC_DRAW
    GL_DYNAMIC_READ
    GL_DYNAMIC_COPY
    GL_STREAM_DRAW
    GL_STREAM_READ
    GL_STREAM_COPY

    이전 버전의 OpenGL은 ARB를 추가하여 GL처럼 변합니다.STATIC_DRAW_ARB의 형식입니다.
  • Static은 캐시 대상에 있는 데이터를 변경할 수 없음을 가리킨다(한 번 설정하고 여러 번 사용).
  • 다이나믹은 데이터가 빈번하게 변경될 것을 가리킨다(반복적으로 설정하고 사용).
  • Stream은 프레임마다 데이터가 변경되는 것을 말합니다(한 번 설정하고 한 번 사용).
  • Draw는 GPU로 전송되어 그리기(application to GL)에 사용됨을 나타냅니다.
  • Read는 데이터가 클라이언트 애플리케이션(GL to application)으로 읽히는 것을 의미합니다.
  • Copy 는 데이터를 그리거나 읽는 데 사용합니다(GL to GL).

  • Draw는 VBO에만 유용합니다.Copy와 Read는 PBO(픽셀 캐시 객체) 및 FBO(프레임 캐시 객체)에만 의미가 있습니다.
    VBO 메모리 관리자는 태그에 따라 적절한 저장 위치를 선택합니다.
    void glBufferSubData(GLenum target, GLint offset, GLsizei size, void* data)
    glBufferData () 와 마찬가지로 캐시 대상에 데이터를 복사하는 데 사용됩니다.이것은 오프셋으로 존재하는 캐시에 데이터를 복사할 수 있습니다.
    void glDeleteBuffers(GLsizei n, const GLuint* ids)
    하나 이상의 캐시 객체를 제거합니다.

    3. 정점 캐시와 인덱스 캐시 사용


    다음과 같은 데이터가 있습니다.
    GLfloat vertexs[] = { 0.0f, 0.0f, 0.0f, 0.2f, 0.0f, 0.0f,
                         -0.2f, 0.0f, 0.0f, 0.0f, 0.2f, 0.0f,
                         0.0f, -0.2f, 0.0f, 0.0f, 0.0f, 0.2f,
                         0.0f, 0.0f, -0.2f};
    
    GLubyte indexs[] = {0,1,2,3,4,5,6};

    캐시 대상을 생성하고 데이터를 복사합니다.
    GLuint vboVertexId;
    GLuint vboIndexId;
    
    glGenBuffers(1, &vboVertexId);
    glBindBuffer(GL_ARRAY_BUFFER, vboVertexId);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertexs), vertexs, GL_STATIC_DRAW);
    
    glGenBuffers(1, &vboIndexId);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboIndexId);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexs), indexs, GL_STATIC_DRAW);

    그리고 사용할 때 아래 코드를 넣으세요.
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_INDEX_ARRAY);
    
    glBindBuffer(GL_ARRAY_BUFFER, vboVertexId);
    glVertexPointer(3, GL_FLOAT, 0, 0);
    
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboIndexId);
    glIndexPointer(GL_UNSIGNED_BYTE, 0, 0);
    
    ... // 
    
    glDisableClientState(GL_VERTEX_ARRAY); 
    glDisableClientState(GL_INDEX_ARRAY);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    캐시된 객체를 사용하는 방법은 다음과 같습니다.
    //1.  
    glBegin(GL_POINTS);
        glArrayElement(0);
        glArrayElement(1);
        glArrayElement(2);
        glArrayElement(5);
    glEnd();
    
    //2.  
    glDrawElements(GL_POINTS, 7, GL_UNSIGNED_BYTE, 0);
    
    //3.  
    glDrawArrays(GL_POINTS,0,7);

    4. 서로 다른 유형의 데이터를 캐시 개체로 복사


    glBufferSubData () 를 사용하면 몇 개의 데이터를 캐시 대상에 복사할 수 있습니다.예를 들어 다음과 같은 데이터가 있습니다.
    GLfloat vertexs[] = {0.0f, 0.0f, 0.0f, 0.2f, 0.0f, 0.0f,
                        -0.2f, 0.0f, 0.0f, 0.0f, 0.2f, 0.0f,
                        0.0f, -0.2f, 0.0f, 0.0f, 0.0f, 0.2f,
                        0.0f, 0.0f, -0.2f};
    
    GLfloat colors[] = { 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
                        0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f,
                        0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
                        0.0f, 0.0f, 0.0f};

    현재, 두 개의 그룹을 같은 캐시 대상에 저장하려면, 정점 그룹은 앞에 있고, 색 그룹은 뒤에 있습니다.코드는 다음과 같습니다.
    glGenBuffers(1, &vboVertexId);
    glBindBuffer(GL_ARRAY_BUFFER, vboVertexId);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertexs)+sizeof(colors), 0, GL_STATIC_DRAW);
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertexs) , vertexs);     // , 
    glBufferSubData(GL_ARRAY_BUFFER, sizeof(vertexs), sizeof(colors), colors);

    캐시 대상을 만든 후 glVertexPointer와 glColorPointer로 해당하는 포인터 위치를 지정해야 합니다.그러나 glColorPointer의 마지막 매개변수 때문에 포인터 유형이어야 합니다.다음 코드를 보십시오.glColorPointer의 마지막 매개 변수는 편향량으로 색 그룹의 위치를 표시합니다.
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);
    glEnableClientState(GL_INDEX_ARRAY);
    
    glBindBuffer(GL_ARRAY_BUFFER, vboVertexId);
    glVertexPointer(3, GL_FLOAT, 0, 0);
    glColorPointer(3,GL_FLOAT,0,(void*)sizeof(vertexs));    // 
    
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboIndexId);
    glIndexPointer(GL_UNSIGNED_BYTE, 0, 0);
    
    glDrawArrays(GL_POINTS,0,7);
    
    glDisableClientState(GL_VERTEX_ARRAY); 
    glDisableClientState(GL_COLOR_ARRAY); 
    glDisableClientState(GL_INDEX_ARRAY);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    5. 캐시된 객체 수정


    VBO는 목록을 표시하는 것보다 캐시 객체의 데이터를 읽고 수정할 수 있다는 장점이 큽니다.가장 간단한 방법은 VBO에 데이터가 있지만 glBuffer Data () 와glBuffer SubData () 를 다시 복사하는 것이다. 이 경우 프로그램은 클라이언트 (CPU) 와 장치 (GPU) 두 개의 데이터를 저장해야 한다.
    다른 방법은 캐시 대상을 클라이언트에 비추고 바늘을 통해 데이터를 수정하는 것이다.
    void* glMapBuffer(GLenum target, GLenum access)
    현재 연결된 캐시 대상을 클라이언트에 비추고glMapBuffer는 캐시 대상을 가리키는 바늘을 되돌려줍니다.OpenGL이 지원되지 않으면 NULL로 돌아갑니다.
  • target - GL_ARRAY_BUFFER 또는 GLELEMENT_ARRAY_BUFFER.
  • access-의 값은 3개의 GLREAD_ONLY、 GL_WRITE_ONLY、 GL_READ_WRITE, 읽기 전용, 쓰기 전용, 읽기 및 쓰기 가능

  • 만약 OpenGL이 캐시 대상을 조작하고 있다면, 이 함수는 OpenGL이 처리될 때까지 성공하지 못할 것입니다.기다림을 피하기 위해 glBindBuffer(GL ARRAY BUFFER, 0)를 사용하여 캐시 객체의 적용을 중지하고 glMapBuffer를 호출할 수 있습니다.
    GLboolean glUnmapBuffer(GLenum target)
    데이터를 수정한 후에 데이터를 장치 측에 반영합니다.
    사용 방법은 다음과 같은 코드를 보십시오.
    glBindBuffer(GL_ARRAY_BUFFER, vboVertexId);
    GLfloat* ptr = (float*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
    
    if(ptr)
    {
        ptr[0] = 0.2f;  ptr[1] = 0.2f;  ptr[2] = 0.2f;
        glUnmapBuffer(GL_ARRAY_BUFFER);
    }
    
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    참고 자료


    [1] GL 정보ARB_vertex_buffer_object 확장
    [2] OpenGL Vertex Buffer Object (VBO)

    좋은 웹페이지 즐겨찾기