2013-12-11 95 views
4

我不明白OpenGL的緩衝區是如何工作的。我通過OpenGL紅皮書第8版學習了OpenGL。 例如,我有位置的陣列,彩色陣列和索引數組:OpenGL的緩衝區如何工作?

static const GLfloat strip_position[] = 
    { 
     -4.0f, 0.0f, -1.0f, 1.0f, //0 
     -3.5f, -1.0f, -1.0f, 1.0f, //1 
     -3.0f, 0.0f, -1.0f, 1.0f, //2 
     -2.5f, -1.0f, -1.0f, 1.0f, //3 
     -2.0f, 0.0f, -1.0f, 1.0f, //4 
     -1.5f, -1.0f, -1.0f, 1.0f, //5 
     -1.0f, 0.0f, -1.0f, 1.0f, //6 
     -0.5f, -1.0f, -1.0f, 1.0f, //7 
     0.0f, 0.0f, -1.0f, 1.0f //8 
    }; 
static const GLfloat strip_colors[] = 
    { 
     1.0f, 1.0f, 1.0f, 1.0f, 
     1.0f, 1.0f, 0.0f, 1.0f, 
     1.0f, 0.0f, 1.0f, 1.0f, 
     1.0f, 0.0f, 0.0f, 1.0f, 
     0.0f, 1.0f, 1.0f, 1.0f, 
     0.0f, 1.0f, 0.0f, 1.0f, 
     0.0f, 0.0f, 1.0f, 1.0f, 
     1.0f, 0.0f, 1.0f, 1.0f, 
     1.0f, 1.0f, 0.0f, 1.0f, 
    }; 

static const GLushort strip_indices[] = 
{ 
    0, 1, 2, 3, 4, 5, 6, 7, 8 
}; 

Good.Then創建頂點數組對象是如下:

GLuint vao[1]; // vertex array object 
    glGenVertexArrays(1, vao); 
    glBindVertexArray(vao[0]); 

在我的理解中,第一個參數(GLsizei n)是位置數組(或我的對象之一的頂點座標)的數量。 然後,我創建元素數組緩衝區是如下:

GLuint ebo[1]; // element buffer object 
glGenBuffers(1, ebo); 
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo[0]); 
glBufferData(
      GL_ELEMENT_ARRAY_BUFFER, 
      sizeof(strip_indices), 
      strip_indices, 
      GL_STATIC_DRAW 
); 

然後,我創建頂點緩衝區對象是如下:

GLuint vbo[1]; // vertex buffer object 
glGenBuffers(1, vbo); 
    glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); 
    glBufferData(
       GL_ARRAY_BUFFER, 
       sizeof(strip_position) + sizeof(strip_colors), 
       NULL, 
       GL_STATIC_DRAW 
    ); 
    glBufferSubData(
        GL_ARRAY_BUFFER, 
        0,      //offset 
        sizeof(strip_position), //size date 
        strip_position   //data 
    ); 
    glBufferSubData(
        GL_ARRAY_BUFFER, 
        sizeof(strip_position), //offset 
        sizeof(strip_colors), //size data 
        strip_colors    //data 
    ); 

下一頁我叫glVertexAttribPointer()是如下:

glVertexAttribPointer(
         0,   //index 
         4,   //size 
         GL_FLOAT, //type 
         GL_FALSE, //normalized 
         0,   //stride 
         NULL  //pointer 
); 
glVertexAttribPointer(
         1, 
         4, 
         GL_FLOAT, 
         GL_FALSE, 
         0, 
         (const GLvoid*)sizeof(strip_position) 
); 
glEnableVertexAttribArray(0); 
glEnableVertexAttribArray(1); 

那是什麼功能?(glVertexAttribPointer()glEnableVertexAttribArray()
好的。我完成了初始化我的數據。現在我可以得出如下結果:

glBindVertexArray(vao[0]); 
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo[0]); 
glDrawElements(GL_TRIANGLE_STRIP, 8, GL_UNSIGNED_SHORT, NULL); 

OpenGL如何理解,哪個緩衝區需要使用,它在哪裏?單詞「綁定」是指一個關係?即與某物綁定的東西?如果我想顯示兩個對象,我該怎麼做? 例如,我有兩個位置數組,兩個位置數組和兩個索引數組?

static const GLfloat TWOstrip_colors[] = 
{ 
    1.0f, 1.0f, 1.0f, 1.0f, 
    1.0f, 1.0f, 0.0f, 1.0f, 
    1.0f, 0.0f, 1.0f, 1.0f, 
    1.0f, 0.0f, 0.0f, 1.0f, 
    0.0f, 1.0f, 1.0f, 1.0f, 
    0.0f, 1.0f, 0.0f, 1.0f, 
    0.0f, 0.0f, 1.0f, 1.0f, 
    1.0f, 0.0f, 1.0f, 1.0f, 
    1.0f, 1.0f, 0.0f, 1.0f 
}; 
static const GLfloat TWOstrip_colors[] = 
    { 
     1.0f, 1.0f, 1.0f, 1.0f, 
     1.0f, 1.0f, 0.0f, 1.0f, 
     1.0f, 0.0f, 1.0f, 1.0f, 
     1.0f, 0.0f, 0.0f, 1.0f, 
     0.0f, 1.0f, 1.0f, 1.0f, 
     0.0f, 1.0f, 0.0f, 1.0f, 
     0.0f, 0.0f, 1.0f, 1.0f, 
     1.0f, 0.0f, 1.0f, 1.0f, 
     1.0f, 1.0f, 0.0f, 1.0f, 
    }; 

static const GLushort TWOstrip_indices[] = 
{ 
    0, 1, 2, 3, 4, 5, 6, 7, 8 
}; 

這是怎麼回事?

回答

2

的OpenGL有所謂的對象的概念。那些不是模型或幾何對象,而是內部狀態的封裝。如果您熟悉面向對象的對象,並且C++ STL OpenGL對象可以被認爲是一種類實例。

呼叫glGenBuffers(count, out_names)可以roughtly解釋成類似

std::map<GLuint, openglobject*> bufferobjects; 

glGenBuffers(GLuint count, std::vector<GLuint> *out_names) 
{ 
    out_names->resize(count); 
    for(int i=0; i < count; i++) { 
     GLuint name = get_next_free_handle_ID(); 
     bufferobjects[name] = NULL; 
     out_names.set(i, name); 
    } 
} 

那麼它是什麼,它保留了一個句柄ID(OpenGL調用他們的名字)和手柄之間的內部映射分配插槽它和bufferobject實例指針。

呼叫glBindBuffer實際創建緩衝區對象,就像

glBindBuffer(GLenum target, GLuint name) 
{ 

    openglobject *objinstance = NULL; 

    if(name != 0) { 
     if(!bufferobjects.has_key(name)) { 
      push_openglerror(INVALID_NAME); 
      return; 
     } 

     objinstance = bufferobjects[name]; 

     if(NULL == bufferobjects[name]) { 
      switch(target) { 
      case GL_ARRAY_BUFFER: 
       objinstance = new OpenGLArrayBuffer; break; 

      case GL_ELEMENT_ARRAY_BUFFER: 
       objinstance = new OpenGLElementArrayBuffer; break; 

      /* ... and so on */  

      default: 
       push_openglerror(INVALID_TARGET); return; 
      } 

      bufferobjects[name] = objinstance; 

      } 
     } 
    } 

    if(objinstance != NULL && target_of(objinstance) != target) { 
     opengl_pusherror(INVALID_TARGET); 
    } 

    switch(target) { 
    case GL_ARRAY_BUFFER: 
     /* this would be a static function of the subclass setting 
      * global singleton instance pointer 
      */ 
     OpenGLArrayBuffer::make_current(objinstance); 
     break; 

     /* ... and so on */ 
    } 
} 

我想你可以看到這是怎麼回事東西:緩衝區target指定子類的實例,你使用和工作類型它的靜態成員。

glBufferData然後實際分配特定對象的內存,並可以使用傳遞給它的緩衝區的內容對它進行初始化。 glBufferSubData只是將數據複製到內部存儲。

這麼多Buffer對象(其中有幾種)。


另一部分是頂點數組對象。這些都是之間頂點屬性該創建關聯特殊的OpenGL對象,這是每頂點數據傳遞給基於其屬性索引着色器和陣列緩衝器對象從該數據是需要。

當你調用glGenVertexArray這樣的事情發生了:

std::map<GLuint, openglobject*> vertexarrayobjects; 

glGenVertexArrays(GLuint count, std::vector<GLuint> *out_names) 
{ 
    out_names->resize(count); 
    for(int i=0; i < count; i++) { 
     GLuint name = get_next_free_handle_ID(); 
     vertexarrayrobjects[name] = NULL; 
     out_names.set(i, name); 
    } 
} 

看起來很熟悉,不是嗎?唯一的區別是,使用了不同的映射結構。 glBindVertexArray做一個實例等的分配。

現在電話glEnableVertexAttributeglVertexAttribPointer可以被認爲是以下幾點:

glEnableVertexAttribute(GLuint idx) 
{ 
    ((OpenGLVertexArrayObject*)currentvertexarray)->add_attribute(idx); 
} 

glVertexAttribPointer(GLuint idx, ..., void *ptr) 
{ 
    ((OpenGLVertexArrayObject*)currentvertexarray)->bind_attribute(
      idx, 
      OpenGLArrayBuffer::get_current(), 
      (off_t)ptr); 
} 

好吧,最後一點需要一些解釋。你傳遞一個指針glVertexAttribPointer是OpenGL的1.1遺留那裏沒有OpenGL的緩衝對象,而是直接指向你的程序內存。然後引入緩衝區對象,並且這些對象在綁定時不需要指針,只需要一個字節大小的偏移量。所以OpenGL的開發者去髒的路線,只是騙了關於它的編譯器。 I did explain the details in my answer to the question "What is the result of NULL + int?"

請注意,OpenGL-4引入了一個新的功能強大且靈活的API來創建VAO屬性←→VBO綁定。

+0

有點兒失望地看到你沒有這方面的更多upvotes,固體超強的答案。 –

3

總是有一個由glBindBuffer(target, id)設置的每個目標的「當前緩衝區」,大多數緩衝區操作知道其操作。

openGL使用glEnableVertexAttribArray來知道它應該查找哪些屬性,如果沒有調用,那麼openGL將不會使用這些數據。

glVertexAttribPointer告訴openGL在當前綁定GL_ARRAY_BUFFER的哪裏必須找到當前vertexArrays的屬性。在您的示例:(假設vbo[0]仍然結合至GL_ARRAY_BUFFER

  1. 屬性索引0vbo[0]找到具有4floats每個頂點tightly packed和開始從0
  2. 屬性索引1vbo[0]找到具有4floats每個頂點tightly packed並從sizeof(strip_position)

這些綁定在堅持要求glBindBuffer所以如果你想重新綁定,那麼你就需要其他的緩衝呼叫glVertexAttribPointer綁定,然後你可以再拆散

我建議你隨時撥打glBindBuffer與0緩衝這樣的openGL知道你不想再與當前的緩衝工作,避免怪異的行爲

創建第二個對象,你可以在每次切換對象按時補充各個緩衝區

,或者你可以創建2套緩衝區:

GLuint vao[2]; // vertex array object 
glGenVertexArrays(2, vao); 
glBindVertexArray(vao[0]); 

GLuint ebo[2]; // element buffer object 
glGenBuffers(2, ebo); 
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo[0]); 
glBufferData(
      GL_ELEMENT_ARRAY_BUFFER, 
      sizeof(strip_indices), 
      strip_indices, 
      GL_STATIC_DRAW 
); 
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo[1]); 
glBufferData(
      GL_ELEMENT_ARRAY_BUFFER, 
      sizeof(strip_indices), 
      TWO_strip_indices, 
      GL_STATIC_DRAW 
); 
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 

GLuint vbo[2]; // vertex buffer object 
glGenBuffers(2, vbo); 
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); 
glBufferData(
      GL_ARRAY_BUFFER, 
      sizeof(strip_position) + sizeof(strip_colors), 
      NULL, 
      GL_STATIC_DRAW 
); 
glBufferSubData(
       GL_ARRAY_BUFFER, 
       0,      //offset 
       sizeof(strip_position), //size date 
       strip_position   //data 
); 
glBufferSubData(
       GL_ARRAY_BUFFER, 
       sizeof(strip_position), //offset 
       sizeof(strip_colors), //size data 
       strip_colors    //data 
); 
//fill other buffer (assuming the first TWOstrip_colors was actually TWOstrip_position 
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]); 
glBufferData(
      GL_ARRAY_BUFFER, 
      sizeof(TWOstrip_position) + sizeof(TWOstrip_colors), 
      NULL, 
      GL_STATIC_DRAW 
); 
glBufferSubData(
       GL_ARRAY_BUFFER, 
       0,      //offset 
       sizeof(TWOstrip_position), //size date 
       strip_position   //data 
); 
glBufferSubData(
       GL_ARRAY_BUFFER, 
       sizeof(TWOstrip_position), //offset 
       sizeof(TWOstrip_colors), //size data 
       strip_colors    //data 
); 
glBindBuffer(GL_ARRAY_BUFFER, 0); 


glBindVertexArray(vao[0]); 
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]) 
glVertexAttribPointer(
         0,   //index 
         4,   //size 
         GL_FLOAT, //type 
         GL_FALSE, //normalized 
         0,   //stride 
         NULL  //pointer 
); 
glVertexAttribPointer(
         1, 
         4, 
         GL_FLOAT, 
         GL_FALSE, 
         0, 
         (const GLvoid*)sizeof(strip_position) 
); 

glBindVertexArray(vao[1]); 
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]); 
glVertexAttribPointer(
         0,   //index 
         4,   //size 
         GL_FLOAT, //type 
         GL_FALSE, //normalized 
         0,   //stride 
         NULL  //pointer 
); 
glVertexAttribPointer(
         1, 
         4, 
         GL_FLOAT, 
         GL_FALSE, 
         0, 
         (const GLvoid*)sizeof(TWOstrip_position) 
); 
glBindBuffer(GL_ARRAY_BUFFER, 0); 
glBindVertexArray(0); 

然後得出:

glEnableVertexAttribArray(0); 
glEnableVertexAttribArray(1); 

glBindVertexArray(vao[0]); 
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo[0]); 
glDrawElements(GL_TRIANGLE_STRIP, 8, GL_UNSIGNED_SHORT, NULL); 

glBindVertexArray(vao[1]); 
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo[1]); 
glDrawElements(GL_TRIANGLE_STRIP, 8, GL_UNSIGNED_SHORT, NULL);