OpenGL VAO VBO EBO(IBO)的绑定、解绑问题

文章目录

  • 前言
    • VBO
    • 链接顶点属性
    • EBO(IBO)
    • VAO
  • 绑定顺序
  • 解绑顺序

前言

首先感谢知乎的一篇文章,解答了我的疑惑,在此记录一下心得。

那么我们先简单介绍一下这三个对象,以及一些必要操作。

VBO

VBO,全称Vertex Buffer Object,中文名称:顶点缓冲对象。我们利用这个对象来管理显存,如果你想往显卡上发送数据,就需要用到这个对象,大体流程如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

链接顶点属性

对应的函数名称:glVertexAttribPointer。这个函数用来干嘛呢?我们利用VBO向显卡传送数据,但是没有告诉显卡怎么处理这些数据,这个函数就是用来干这件事的。通过调用glVertexAttribPointer显卡知道如何处理数据了,我们还需要调用glEnableVertexAttribArray(Index)告诉显卡索引为Index的顶点属性现在已经启用了。那么现在流程变为:
OpenGL VAO VBO EBO(IBO)的绑定、解绑问题_第1张图片

EBO(IBO)

EBO或者IBO,全称Element(Index) Buffer Object,中文名称:索引缓冲对象。虽然利用之前的对象和方法就可绘制了,但是如果使用指定每个三角面片的坐标这种方法构造数据时,显然会有很多重复出现的点,造成空间、时间浪费。EBO就是用来解决这个问题的,我们可以一次性提供模型的所有顶点信息(无重复项),然后指定一个索引数组,其内存储要绘制的物体坐标在顶点数组内的索引。这样就可以减少重复数据所占据的空间了。
OpenGL VAO VBO EBO(IBO)的绑定、解绑问题_第2张图片
在这里插入图片描述

VAO

VAO,全称Vertex Array Object,中文名称:顶点数组对象。上面提到的绘制方法复用性太差了,每次绘制我们都要绑定VBO、EBO并链接顶点属性,有没有办法简化这些操作呢?利用VAO就可以做到。简而言之,它是用来存储其它对象的状态的,只要我们在初始化时设置好了VAO,在绘制时就可以直接绑定对应的VAO而无需上述的繁琐操作,极大的增加了复用性。
OpenGL VAO VBO EBO(IBO)的绑定、解绑问题_第3张图片
注意:上图没有给出绑定EBO的操作,我们可以在绑定VBO之后绑定EBO。
OpenGL VAO VBO EBO(IBO)的绑定、解绑问题_第4张图片

绑定顺序

如前言中所说,先绑定VAO,然后绑定VBO和EBO,再链接顶点属性指针即可。

解绑顺序

这个是比较迷惑的一点,我们在绑定VBO并链接顶点属性指针之后,即可解绑该VBO,但是如果解绑EBO的操作一定要放到解绑VAO的操作之后。也就是说解绑顺序是:VBO(注意要先链接顶点属性指针)、VAO、EBO。这是为什么呢?和VAO的具体实现有很大的关系,我在知乎上找到了一个回答的很好的文章,可以看一下VAO的实现(伪代码,原理应该相似)。

作者:「已注销」
链接:https://www.zhihu.com/question/39082624/answer/79638826
来源:知乎

struct VertexAttributeState  
{  
    bool                bIsEnabled = false;  
    int                 iSize = 4; //This is the number of elements in each attrib, 1-4.  
    unsigned int        iStride = 0;  
    VertexAttribType    eType = GL_FLOAT;  
    bool                bIsNormalized = false;  
    bool                bIsIntegral = false;  
    void *              pPtrOrBufferObjectOffset = 0;  
    BufferObject *      pBufferObj = 0;  
};  
   
struct VertexArrayObjectState
{  
    BufferObject *pElementArrayBufferObject = NULL;  
    VertexAttributeState attributes[MAX_VERTEX_ATTRIB];  
}  
   
static VertexArrayObjectState *pContextVAOState = new VertexArrayObjectState();  
static BufferObject *pCurrentArrayBuffer = NULL;
//绑定缓冲对象 如VBO EBO等
void glBindBuffer(enum target, uint buffer)  
{  
    BufferObject *pBuffer = ConvNameToBufferObj(buffer);  
   
    switch(target)  
    {  
    //绑定VBO 先用一个临时变量存储
    case GL_ARRAY_BUFFER:  
        pCurrentArrayBuffer = pBuffer;  
        break;  
    //绑定EBO 直接更新
    case GL_ELEMENT_ARRAY_BUFFER:  
        pContextVAOState->pElementArrayBufferObject = pBuffer;  
        break;  
    ...  
    }  
}  
//禁用属性
void glEnableVertexAttribArray(uint index)  
{  
    pContextVAOState->attributes[index].bIsEnabled = true;  
}  
//启用属性
void glDisableVertexAttribArray(uint index)  
{  
    pContextVAOState->attributes[index].bIsEnabled = false;  
}  

//链接顶点属性指针
void glVertexAttribPointer(uint index, int size, enum type, boolean normalized, sizei stride, const void *pointer)  
{  
    VertexAttributeState &currAttrib = pContextVAOState->attributes[index];  
   
    currAttrib.iSize = size;  
    currAttrib.eType = type;  
    currAttrib.iStride = stride;  
    currAttrib.bIsNormalized = normalized;  
    currAttrib.bIsIntegral = true;  
    currAttrib.pPtrOrBufferObjectOffset = pointer;  
    //此时才会保存VBO!!
    currAttrib.pBufferObj = pCurrentArrayBuffer;  
}

代码不长,阅读后我们可以发现VAO的奥妙。其实它存储的是一组VertexAttributeState和一个ElementArrayBufferObject。也就是说根据需要,我们可以为一个VAO绑定多个不同的VBO并设置对应的顶点属性,但是一个VAO只能有一个EBO。所以如果在解绑VAO之前解绑了EBO,那么该VAO存储的EBO就无了,自然绘制不出东西。而且这段代码也体现了VBO和顶点属性指针的关系,在绑定VBO后,一定要设置好顶点属性指针,不然就等于白给呀。

最后贴几张回复贴吧,感觉有点意思。
OpenGL VAO VBO EBO(IBO)的绑定、解绑问题_第5张图片
OpenGL VAO VBO EBO(IBO)的绑定、解绑问题_第6张图片
OpenGL VAO VBO EBO(IBO)的绑定、解绑问题_第7张图片

你可能感兴趣的:(OpenGL)