【问题标题】:Using std::vector as vertex/element lists with OpenGL在 OpenGL 中使用 std::vector 作为顶点/元素列表
【发布时间】:2025-12-15 11:10:02
【问题描述】:

我想画一堆四边形。

现在我有一个问题;绘图工作正常而且速度很快,但我使用 std::vector 作为我的四边形的容器,它们真的非常非常慢。来自 XNA,我想我应该创建类似 spriteBatch 的东西,这样我就可以调用 DrawQuad() 将给定的四边形添加到列表中,然后最后调用 End() 来绘制每个四边形。

我当前的代码通常会在控制台上打印类似这样的内容:

DrawQuad(): 77
End(): 0

一遍又一遍。

Main.cpp(sf::Clock是SFML中的时钟类)

sf::Clock time;
for (int y = 0; y < 100; y++)
    for (int x = 0; x < 100; x++)
        renderer.DrawQuad("A", Vector2<GLfloat>(-1.0f + x * 0.02f, -1.0f + y * 0.02f));
std::cout << "DrawQuad(): " << time.getElapsedTime().asMilliseconds() << std::endl;

渲染器.cpp:

void TextRenderer::DrawQuad(string text, Vector2<GLfloat> position)
{
    //TOP LEFT
    vertexBufferVector.push_back(position.X);
    vertexBufferVector.push_back(position.Y);

    //TOP RIGHT
    vertexBufferVector.push_back(position.X + 0.02f);
    vertexBufferVector.push_back(position.Y);

    //BOTTOM RIGHT
    vertexBufferVector.push_back(position.X + 0.02f);
    vertexBufferVector.push_back(position.Y + 0.02f);

    //BOTTOM LEFT
    vertexBufferVector.push_back(position.X);
    vertexBufferVector.push_back(position.Y + 0.02f);

    int elementCount = elementBufferVector.size() / 6; 

    elementBufferVector.push_back(elementCount * 4);
    elementBufferVector.push_back(elementCount * 4 + 1);
    elementBufferVector.push_back(elementCount * 4 + 2);

    elementBufferVector.push_back(elementCount * 4 + 2);
    elementBufferVector.push_back(elementCount * 4 + 3);
    elementBufferVector.push_back(elementCount * 4);
}

void TextRenderer::End()
{
    sf::Clock time;

    GLfloat* vertexArray = &vertexBufferVector[0];
    glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * vertexBufferVector.size(), vertexArray, GL_STATIC_DRAW);

    GLint* elementArray = &elementBufferVector[0];
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLint) * elementBufferVector.size(), elementArray, GL_STATIC_DRAW);

    glDrawElements(GL_TRIANGLES, elementBufferVector.size(), GL_UNSIGNED_INT, 0);

    vertexBufferVector.clear();
    elementBufferVector.clear();

    std::cout << "End(): " << time.getElapsedTime().asMilliseconds() << std::endl;
}

知道自己在做什么的人如何解决这个问题? 10000个四边形真的不应该成为问题。

写完这一切后,我还将循环从 (100, 100) 增加到 (1000, 100),现在绘图需要 4-5 毫秒,这样算好吗?我想不...

【问题讨论】:

  • 4ms 大约是 250 fps,是不是太慢了?
  • 我应该评论一下给 DrawQuad 的“A”参数:起初我想做一些可以绘制文本的东西,但我从这个开始,所以它就在那里。罗杰:我不知道……这就是我问的原因。但是多次调用 DrawQuad 所需的 800 毫秒太多了
  • 考虑到您正在为每一帧重新创建顶点数组,100k 四边形的 4-5 毫秒是可以的。 OpenGL 应该有一种方法可以创建一个静态顶点缓冲区,只要您不更改其内容,该缓冲区的工作速度就会更快。
  • 这是有道理的,里夫。我没有看到使用静态缓冲区的方法,所以我只需要对我所拥有的感到满意
  • 您的 DrawQuads 调用并不像我的机器上的那么慢(使用 g++-4.7.2 和默认优化,它们需要 5-11 毫秒),但删除未使用的字符串一半,然后去 - O3 将其降低到不到 1 毫秒。我肯定会检查没有字符串的时间,因为目前你在每次循环迭代中创建和销毁一个相对昂贵的时间。

标签: c++ opengl


【解决方案1】:

既然这已经死了,我只会回答我的问题,也许它会对某人有所帮助。

我没有使用向量,而是使用了具有设定大小的数组。这将 100000 个四边形(以及纹理)的总渲染时间降低到平均约 3.260 毫秒。

我的 .h 现在看起来像这样:

const int MAX_BUFFER_SIZE = 1000;
const int MAX_VERTEX_BUFFER_SIZE = MAX_BUFFER_SIZE * 16;
const int MAX_ELEMENT_BUFFER_SIZE = MAX_BUFFER_SIZE * 6;

...

GLint vertexBufferArrayInserts;
GLfloat vertexBufferArray[MAX_VERTEX_BUFFER_SIZE];
GLint elementBufferArray[MAX_ELEMENT_BUFFER_SIZE];

以及.cpp文件的相关部分:

void TextRenderer::DrawQuad(Vector2<GLfloat> position)
{
    if(vertexBufferArrayInserts == MAX_BUFFER_SIZE)
        End();

    //TOP LEFT
    vertexBufferArray[vertexBufferArrayInserts * 16] = position.X;
    vertexBufferArray[vertexBufferArrayInserts * 16 + 1] = position.Y;
    vertexBufferArray[vertexBufferArrayInserts * 16 + 2] = 0.0f;
    vertexBufferArray[vertexBufferArrayInserts * 16 + 3] = 0.0f;

    //TOP RIGHT
    vertexBufferArray[vertexBufferArrayInserts * 16 + 4] = position.X + 16.0f;
    vertexBufferArray[vertexBufferArrayInserts * 16 + 5] = position.Y;
    vertexBufferArray[vertexBufferArrayInserts * 16 + 6] = 24.0f / 512.0f;
    vertexBufferArray[vertexBufferArrayInserts * 16 + 7] = 0.0f;

    //BOTTOM RIGHT
    vertexBufferArray[vertexBufferArrayInserts * 16 + 8] = position.X + 16.0f;
    vertexBufferArray[vertexBufferArrayInserts * 16 + 9] = position.Y + 16.0f;
    vertexBufferArray[vertexBufferArrayInserts * 16 + 10] = 24.0f / 512.0f;
    vertexBufferArray[vertexBufferArrayInserts * 16 + 11] = 32.0f / 512.0f;

    //BOTTOM LEFT
    vertexBufferArray[vertexBufferArrayInserts * 16 + 12] = position.X;
    vertexBufferArray[vertexBufferArrayInserts * 16 + 13] = position.Y + 16.0f;
    vertexBufferArray[vertexBufferArrayInserts * 16 + 14] = 0.0f;
    vertexBufferArray[vertexBufferArrayInserts * 16 + 15] = 32.0f / 512.0f;


    //ELEMENT BUFFER
    elementBufferArray[vertexBufferArrayInserts * 6] = vertexBufferArrayInserts * 4;
    elementBufferArray[vertexBufferArrayInserts * 6 + 1] = vertexBufferArrayInserts * 4 + 1;
    elementBufferArray[vertexBufferArrayInserts * 6 + 2] = vertexBufferArrayInserts * 4 + 2;
    elementBufferArray[vertexBufferArrayInserts * 6 + 3] = vertexBufferArrayInserts * 4 + 2;
    elementBufferArray[vertexBufferArrayInserts * 6 + 4] = vertexBufferArrayInserts * 4 + 3;
    elementBufferArray[vertexBufferArrayInserts * 6 + 5] = vertexBufferArrayInserts * 4;

    vertexBufferArrayInserts++;
}

void TextRenderer::End()
{
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * vertexBufferArrayInserts * 16, vertexBufferArray);

    glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(GLint) *  vertexBufferArrayInserts * 6, elementBufferArray);

    glDrawElements(GL_TRIANGLES, vertexBufferArrayInserts * 6, GL_UNSIGNED_INT, 0);

    vertexBufferArrayInserts = 0;
}

这就是我的计时方式:

sf::Clock timer;
renderer.Begin(projMatrix);

for (int y = 0; y < 100; y++)
    for(int x = 0; x < 1000; x++)
        renderer.DrawQuad(Vector2<GLfloat>(16.0f * x, 16.0f * y));

renderer.End();

avgDrawTime += timer.getElapsedTime().asMicroseconds();
drawCalls++;

if(drawCalls % 100 == 0)
    std::cout << avgDrawTime / drawCalls << std::endl;

【讨论】: