用于顶点,颜色和纹理坐标的OpenGLES 2.0单独缓冲区

问题描述:

我已经学习了几天的OpenGL,现在通过一些教程并编写我自己的一些实验。但有一件事我真的不明白,阻止我继续下去。我一直在Google上搜索几个小时,但没有找到答案。用于顶点,颜色和纹理坐标的OpenGLES 2.0单独缓冲区

我应该在哪里为每个单独的顶点指定每个单独的颜色值和纹理坐标?如果这些属性总是被列在与顶点位置相同的数组(结构)中?像这样:

const Vertex Vertices[] = { 
    // Front 
    {{1, -1, 0}, {1, 0, 0, 1}, {TEX_COORD_MAX, 0}}, 
    {{1, 1, 0}, {0, 1, 0, 1}, {TEX_COORD_MAX, TEX_COORD_MAX}}, 
    {{-1, 1, 0}, {0, 0, 1, 1}, {0, TEX_COORD_MAX}}, 
    {{-1, -1, 0}, {0, 0, 0, 1}, {0, 0}}, 

    ... 

或者有没有办法将颜色值和纹理坐标放在不同的数组中?但是接下来会出现这样的问题:我如何使用单独的数组调用glDrawElements

如果你想知道为什么我想分开这些值:我目前在obj-c中创建自己的.obj解析器,我在想:如果加载没有纹理的模型并且只想显示物体上的颜色?或者:如果你想加载一个只有纹理贴图的模型,但是每个顶点没有单独的颜色?并且:没有将颜色值和纹理坐标放置在Vertex结构中的数据太多。

实际上,它是通常的方法来分离顶点数据到位置,颜色,等使用几个阵列/缓冲区。

我上次接触ES 2.0的时间是WebGL(它有一个稍微不同的规范,但最终基于ES 2.0)。

什么是基本上完成了使用

glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); 
glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(float), positions, GL_STATIC_DRAW); 
glBindBuffer(GL_ARRAY_BUFFER, 0); 

glBindBuffer(GL_ARRAY_BUFFER, colorBuffer); 
glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(float), colors, GL_STATIC_DRAW); 
glBindBuffer(GL_ARRAY_BUFFER, 0); 

... 

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); 
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(ushort), indices, GL_STATIC_DRAW); 
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 

与位置和颜色是包含顶点数据和包含的索引作为在这种情况下无符号短指数浮子阵列被写入数据到单独的缓冲器。

为了使这个数据,你会使用缓冲区和属性指向你的shader:

glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); 
glVertexAttribPointer(vertexPositionAttribute, 3, GL_FLOAT, false, 0, 0); 

glBindBuffer(GL_ARRAY_BUFFER, colorBuffer); 
glVertexAttribPointer(vertexColorAttribute, 4, GL_FLOAT, false, 0, 0); 

最后结合索引缓冲区:

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); 

和渲染:

glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, 0); 

获取属性:

glUseProgram(shaderProgram); 

vertexPositionAttribute= glGetAttribLocation(shaderProgram, "vertexPosition"); 
glEnableVertexAttribArray(vertexPositionAttribute); 

vertexColorAttribute = glGetAttribLocation(shaderProgram, "vertexColor"); 
glEnableVertexAttribArray(vertexColorAttribute); 

... 

如果没有定制着色器(使用固定功能),你可能能够使用

glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); 
glVertexPointer(3, GL_FLOAT, false, 0, 0); 

glBindBuffer(GL_ARRAY_BUFFER, colorBuffer); 
glColorPointer(4, GL_FLOAT, false, 0, 0); 

代替。不过,我建议不要这样做,因为它已经过时(如果在ES 2.0中可用的话)。 如果你仍然想使用它,你完全可以跳过整个缓冲区的业务和使用

glVertexPointer(3, GL_FLOAT, false, 0, positions); 
glColorPointer(4, GL_FLOAT, false, 0, colors); 

glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, indices); 

我希望这不是太混乱,并有助于一点。进一步阅读,虽然针对OpenGL,我会建议Nehe tutorials

+0

晶莹剔透! ;) – polyclick 2012-01-06 13:02:06

你可以将它们分成子缓冲区,但是如果你使用它们,那么你必须为它们提供所有的顶点,如果你使用了索引缓冲区,那么你必须使用一个索引缓冲区(位置,颜色, texcoord等)。下面是我的代码的一些摘录:

分配与

glBindBuffer(GL_ARRAY_BUFFER, mId); 
glBufferData(GL_ARRAY_BUFFER, 
       mMaxNumberOfVertices * (mVertexBlockSize + mNormalBlockSize + mColorBlockSize + mTexCoordBlockSize), 
       0, 
       mDrawMode); 

glBufferSubData(GL_ARRAY_BUFFER, mVertexOffset, numberOfVertsToStore * mVertexBlockSize, vertices); 
glBufferSubData(GL_ARRAY_BUFFER, mNormalOffset, numberOfVertsToStore * mNormalBlockSize, normals); 
glBufferSubData(GL_ARRAY_BUFFER, mColorOffset, numberOfVertsToStore * mColorBlockSize, colors); 
glBufferSubData(GL_ARRAY_BUFFER, mTexCoordOffset, numberOfVertsToStore * mTexCoordBlockSize, texCoords); 

和使用这种填(我不认为不断切换clientStates是最佳做法虽然)

void Vbo::draw(GLenum primMode) 
{ 
    glEnableClientState(GL_VERTEX_ARRAY); 
    glVertexPointer(mVertexComponents, GL_FLOAT, 0, (void*)mVertexOffset); 

    if(mNormalBlockSize){ 
    glEnableClientState(GL_NORMAL_ARRAY); 
    glNormalPointer(GL_FLOAT, 0, (void*)mNormalOffset); 
    } 
    if(mColorBlockSize){ 
    glEnableClientState(GL_COLOR_ARRAY); 
    glColorPointer(mColorComponents, GL_FLOAT, 0, (void*)mColorOffset); 
    } 
    if(mTexCoordBlockSize){ 
    glEnableClientState(GL_TEXTURE_COORD_ARRAY); 
    glTexCoordPointer(mTexCoordComponents, GL_FLOAT, 0, (void*)mTexCoordOffset); 
    } 

    if (mAttachedIndexBuffer) 
    { 
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mAttachedIndexBuffer); 
    glDrawElements(primMode, 
        mAttachedIndexBuffer->getNumberOfStoredIndices(), 
        mAttachedIndexBuffer->getDataType(), 
        0); 
    } 
    else 
    { 
    glDrawArrays(primMode, 0, mNumberOfStoredVertices); 
    } 

    if(mTexCoordBlockSize) 
    { 
    glDisableClientState(GL_TEXTURE_COORD_ARRAY); 
    } 
    if(mColorBlockSize) 
    { 
    glDisableClientState(GL_COLOR_ARRAY); 
    } 
    if(mNormalBlockSize) 
    { 
    glDisableClientState(GL_NORMAL_ARRAY); 
    } 
    glDisableClientState(GL_VERTEX_ARRAY); 
}  
+1

如果您可以在完全不同的缓冲区中存储不同的属性,那么您确实无法回答这个问题。 – 2012-01-06 12:34:24

+0

我的不好。我只是不认为使用子缓冲区和完全独立的缓冲区有其显着的区别,因为它们将被一起分配,一起释放,并且如果它们为同一对象保存不同的数据,则一起使用。 – PeterT 2012-01-06 12:38:34

+0

确实如此,但他仍然问了一个问题要回答。 – 2012-01-06 12:47:31

你当然可以有你的数据在不同的缓冲区。请记住,确定属性来源的是glVertexAttribPointer调用。因此,要为属性使用不同的缓冲区,请在致电glVertexAttribPointer之前绑定不同的GL_ARRAY_BUFFERglDrawElements没有什么关系吧:

glBindBuffer(GL_ARRAY_BUFFER, positionBuffer); 
glVertexAttribPointer(0, ...); 
glEnableVertexAttribArray(0); 
glBindBuffer(GL_ARRAY_BUFFER, colorBuffer); 
glVertexAttribPointer(1, ...); 
glEnableVertexAttribArray(1); 
glBindBuffer(GL_ARRAY_BUFFER, 0); 

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); 
glDrawElements(...); 

glDisableVertexAttribArray(0); 
glDisableVertexAttribArray(1); 

当不使用维也纳组织(这我不知道是可能的ES 2.0),只需拨打glVertexAttribPointer用不同的阵列为每个属性:

glVertexAttribPointer(0, ..., positionArray); 
glEnableVertexAttribArray(0); 
glVertexAttribPointer(1, ..., colorArray); 
glEnableVertexAttribArray(1); 

glDrawElements(..., indexArray); 

glDisableVertexAttribArray(0); 
glDisableVertexAttribArray(1); 

但是,在您的示例中保持单个顶点的属性通常更高效,因为这样更容易缓存。如果几乎所有的对象都使用了所有的数组,那么将它们放在一起,而不是为几个不使用它的对象启用属性可能是一个更好的主意。但是如果使用的属性真的不同于对象之间,单独的缓冲解决方案可能是一个更好的主意。您也可以将所有单独的属性数组依次存储在一个VBO中,并在glVertexAttribPointer调用中使用相应的缓冲区偏移量,因此每个对象只需要一个VBO。

但是,当然,您只能为每个对象使用一个索引数组,而不能使用位置和颜色的不同索引数组。这可能需要您稍后处理从OBJ文件读取的数据,因为OBJ文件确实可以使用位置,法线和texCoords的不同索引。