【问题标题】:OpenGL: How does Vertex Buffer manage its memory?OpenGL:顶点缓冲区如何管理其内存?
【发布时间】:2021-05-28 16:58:30
【问题描述】:

我正在学习 OpenGL,并试图正确理解事物。如果我的理解在任何点不正确,请纠正我。

简介

假设我们有一个三角形。这个三角形有它的顶点。假设这些顶点只有位置集 - 没有颜色,没有其他任何东西。这些顶点使用缓冲区传递给着色器 - 我们称之为 VB(教程中为 VBO)。

着色器如下:

顶点着色器:

#version 330 core
layout (location = 0) in vec3 aPos;

void main()
{
    gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}

片段着色器:

#version 330 core
out vec4 FragColor;

void main()
{
    FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}

VB 是一个未格式化的数据数组。例如,如果我们想将 3 个单字节值传递给这个缓冲区(0、255、16),数据将如下所示:

00FFF0

但是,着色器不知道如何读取数据,因此我们需要通过告诉它们什么是什么来“指导”它们。为此,我们使用顶点数组对象。让我们称我们的顶点数组对象为 VA。

要将数据传递到缓冲区,请使用glBufferData。像这样调用函数时:

glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

我们通知 OpenGL,我们要将数组 vertices 中的 sizeof(vertices) 元素缓冲到当前绑定到 GL_ARRAY_BUFFER 的缓冲区中以进行静态绘图。

然后,我们告诉 VA 如何使用这些数据:

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

这样我们告诉 VA 从偏移量 0 处的活动缓冲区获取 3 个浮点值,而不是对坐标进行归一化,并将此数据设置为位置 0 处的顶点属性。

这样,着色器最终获得了工作所需的所有数据并绘制了我们的三角形。

问题

但是,如果我们想要在我们已经将数据传递到缓冲区之后更改一个顶点怎么办?

据我了解,我们需要像以前一样调用glBufferData。但是它如何影响最初在缓冲区中的数据呢?它会覆盖它吗?

如果它确实覆盖了它,我们如何传递另一个数据,比如颜色,而不覆盖位置?

如果不是,VA 怎么知道它“指向”的数据不再是最新的?

【问题讨论】:

标签: c++ opengl vertex-buffer


【解决方案1】:

可以使用glBufferSubData(或Mapping)更新缓冲区数据。 glBufferData 创建一个大小不可变的新数据存储。因此,您不能将其他数据添加到缓冲区。您必须首先创建一个足够大的缓冲区。
但是,您可以为其他属性创建单独的缓冲区。

【讨论】:

    【解决方案2】:

    据我了解,我们需要像以前一样调用 glBufferData。但是它如何影响最初在缓冲区中的数据呢?它会覆盖它吗?

    glBufferSubData() 允许您覆盖缓冲区的子范围,因此您可以使用它来选择性地更新部分缓冲区。

    如果它确实覆盖了它,我们如何传递另一个数据,比如颜色,而不覆盖位置?

    最简单的方法是构建缓冲区,使其由单独的顺序缓冲区组成,而不是一个大的交错缓冲区。

    看起来大概是这样(注意stride参数设置为0,它告诉驱动程序数据没有交错):

    size_t coord_start = 0;
    size_t normal_start = coord_start + coord_data_len;
    size_t color_start = normal_start + normal_data_len;
    
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)coord_start );
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)normal_start );
    glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (void*)color_start );
    

    这样,当您调用glBufferSubData() 来更新包含颜色的内存范围时,您将不理会坐标和普通数据。

    或者,您也可以为不同的属性创建单独的缓冲区。

    如果不是,VA 怎么知道它“指向”的数据不再是最新的?

    这就是事情变得有点棘手的地方。在纸面上,这是驱动程序的问题,您不必担心。

    实际上,由于 GPU 与 CPU 上发生的事情并行运行,因此您最终可能会遇到这样一种情况,即更新当前正在使用的缓冲区可能会在同步自行解决时导致一些速度变慢。

    因此,有时最好只创建一个全新的缓冲区并“从头开始”填充它,而不是更新现有的缓冲区。这样,您可以确信缓冲区更新过程不会影响任何使用缓冲区的渲染。不过,何时子更新与新缓冲区更好取决于很多因素。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多