OpenGL 的缓冲区如何工作? [英] How do OpenGL's buffers work?

查看:62
本文介绍了OpenGL 的缓冲区如何工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不明白 OpenGL 的缓冲区是如何工作的.我通过 OpenGL 红皮书第 8 版学习 OpenGL.例如,我有一个位置数组、一个颜色数组和一个索引数组:

I don't understood how do OpenGL's buffers work. I learn OpenGL, by means of OpenGL Redbook 8th edition. For example, I have an array of position, an array of color and an array of indices:

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 I create Vertex Array Object is follows:

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

在我的理解中,第一个参数 (GLsizei n) 是位置数组的数量(或一个我的对象的顶点坐标).然后我创建Element Array Buffer如下:

In my understanding, first parameter (GLsizei n) is number of an arrays of position(or coordinate of vertices of ONE my object). Then I create Element Array Buffer is follows:

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
);

然后我创建顶点缓冲对象如下:

Then I create Vertex Buffer Object is follows:

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())
好的.我完成了初始化我的数据.现在我可以绘制如下:

What does that function?(glVertexAttribPointer() and glEnableVertexAttribArray())
Okay. I finished initialize a my data. Now I can draw it's follows:

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

OpenGL 是如何理解的,需要使用哪个缓冲区以及它在哪里?绑定"一词是指关系吗?即某物与某物绑定?如果我想显示两个对象,我该怎么办?例如,我有两个位置数组,两个位置数组和两个索引数组?

How OpenGL understood, which buffer need to use and where it is? Word "bind" is mean a relation? i.e. something bind with something? And If I want to display a two object, what do I do? For example, I have a two arrays of position, a two arrays of position and a two arrays of indices?

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
};

这是怎么做的?

推荐答案

OpenGL 有所谓的对象的概念.这些不是模型或几何对象,而是内部状态的封装.如果您熟悉面向对象的编程,并且可以将 C++ STL OpenGL 对象视为类实例.

OpenGL has the notion of so called objects. Those are not models or geometrical objects, but encapsulations of internal state. If you're familiar with object oriented programming, and the C++ STL OpenGL objects can be thought of kind of class instances.

调用 glGenBuffers(count, out_names) 可以粗略地解释为类似

The call glGenBuffers(count, out_names) could be roughtly interpreted into something like

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 称它们为名称)并在句柄和缓冲区对象实例指针之间的内部映射中为其分配一个插槽.

So what it does is, it reserves a handle ID (OpenGL calls them names) and allocates a slot for it in the internal mapping between handles and bufferobject instance pointers.

调用 glBindBuffer 实际上创建了缓冲区对象,类似这样

The call glBindBuffer actually creates the buffer object, something like that

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 指定了你正在使用的实例的子类类型及其静态成员.

I think you can see there this is going: The buffer target specifies the type of subclass the instance is you're working with and its static members.

glBufferData 然后实际分配特定对象的内存,并可以使用传递给它的缓冲区的内容对其进行初始化.glBufferSubData 只是将数据复制到内部存储.

glBufferData then actually allocates memory of the particular object, and can initialize it with the contents of a buffer you pass to it. glBufferSubData just copies data to the internal storage.

缓冲区对象(有几种)到此为止.

So much for the Buffer Objects (of which there are several kinds).

另一部分是顶点数组对象.这些是特殊的 OpenGL 对象,它们在顶点属性之间创建关联,这些属性是基于属性索引和数组缓冲区对象传递给着色器的每个顶点数据数据是需要的.

The other part are the Vertex Array Objects. Those are special OpenGL objects that create an association between vertex attributes, which are per-vertex data passed to the shaders based on their attribute index and the array buffer objects from which this data is takes.

当你调用 glGenVertexArray 时会发生这样的事情:

When you call glGenVertexArray something like this happens:

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 进行实例的分配等.

Looks familiar, doesn't it? The only difference is, that a different mapping structure is used. glBindVertexArray does the allocation of an instance and so on.

现在调用 glEnableVertexAttributeglVertexAttribPointer 可以认为如下:

Now the calls glEnableVertexAttribute and glVertexAttribPointer can be thought as the following:

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 开发人员走上了肮脏的道路,只是向编译器撒了谎.我在回答NULL + int 的结果是什么?"这个问题时确实解释了详细信息

Okay, that last bit requires some explanation. That you pass a pointer to glVertexAttribPointer is a legacy from OpenGL-1.1 where there were no OpenGL buffer objects and instead you pointed directly to memory of your program. Then buffer objects got introduced and those don't require a pointer but a byte sized offset when binding. So the OpenGL devs went the dirty route and just lied to the compilers about it. I did explain the details in my answer to the question "What is the result of NULL + int?"

请注意,OpenGL-4 引入了一个新的、更强大、更灵活的 API 来创建 VAO 属性 ←→ VBO 绑定.

Note that OpenGL-4 introduced a new, much more powerfull and flexible API to create VAO attribute ←→ VBO bindings.

这篇关于OpenGL 的缓冲区如何工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆