2013-12-11 49 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);