【问题标题】:OpenGLES 2.0 separate buffers for vertices, colors and texture coordinatesOpenGL ES 2.0 用于顶点、颜色和纹理坐标的单独缓冲区
【发布时间】:2012-02-04 03:29:49
【问题描述】:

我已经通过学习一些教程和编写自己的一些实验来学习 OpenGL 几天了。但是有一件事我真的不明白是什么阻止了我继续。我已经在谷歌上搜索了几个小时,但还没有找到我的问题的答案。

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

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 结构因数据过多而膨胀。

【问题讨论】:

    标签: opengl-es opengl-es-2.0 textures vbo vertex


    【解决方案1】:

    其实常用的方法是使用多个数组/缓冲区将顶点数据分成位置、颜色等。

    我上次接触 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);
    

    在这种情况下,位置和颜色是包含顶点数据的浮点数组和包含索引的索引作为无符号短裤。

    要渲染这些数据,您可以使用缓冲区和指向着色器的属性指针:

    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

    【讨论】:

      【解决方案2】:

      您当然可以将数据放在不同的缓冲区中。请记住,确定属性来源的是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);
      

      当不使用 VBO(我不确定在 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 使用不同的索引。

      【讨论】:

        【解决方案3】:

        您可以将它们分成子缓冲区,但是如果使用它们,则必须将它们用于所有顶点,如果使用索引缓冲区,则必须对所有顶点使用一个索引缓冲区(位置,颜色,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);
        }    
        

        【讨论】:

        • 如果您可以将不同的属性存储在完全不同的缓冲区中,并不能真正回答这个问题,您绝对可以。
        • 我的错。我只是不认为出于他的目的使用子缓冲区和完全独立的缓冲区之间没有显着差异,因为它们将一起分配,一起释放,并且如果它们为同一个对象保存不同的数据,则总是一起使用。
        • 确实如此,但他还是问了一个问题来回答。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-04-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-07-31
        • 1970-01-01
        相关资源
        最近更新 更多