C++ 如何只向opengl发送新数据而不丢失以前的数据?

C++ 如何只向opengl发送新数据而不丢失以前的数据?,c++,opengl,C++,Opengl,我正在处理需要使用opengl渲染的点云数据。我每帧得到一个新的数据点向量。我希望能够缓存以前发送到opengl的数据,并且只向其发送最新的帧数据。我怎样才能做到 我做了一些搜索,发现了这个想法: 但看起来它最终在新的缓冲区中发送以前的和新的数据,而不是缓存和只发送最新的数据。所以我不确定这是不是最好的方法。如果我错了,请纠正我,或者建议其他方法。因此,您在CPU内存中存储了一些数据,并将更多数据附加到此存储中。然后您只想将附加的数据发送到GPU,而不是整个缓冲区 您的代码示例与此任务无关,因为

我正在处理需要使用opengl渲染的点云数据。我每帧得到一个新的数据点向量。我希望能够缓存以前发送到opengl的数据,并且只向其发送最新的帧数据。我怎样才能做到

我做了一些搜索,发现了这个想法:


但看起来它最终在新的缓冲区中发送以前的和新的数据,而不是缓存和只发送最新的数据。所以我不确定这是不是最好的方法。如果我错了,请纠正我,或者建议其他方法。

因此,您在CPU内存中存储了一些数据,并将更多数据附加到此存储中。然后您只想将附加的数据发送到GPU,而不是整个缓冲区

您的代码示例与此任务无关,因为
glCopyBufferSubData
再次将数据从GPU内存中的一个位置复制到GPU内存中的另一个位置

您需要组合使用
glBufferData
glBufferSubData
glBufferData
在GPU中分配内存,并可选择对其进行初始化
glBufferSubData
将一些数据写入已分配的GPU缓冲区。您可以将代码> > GLPUBER DATABOS/CODE>作为C的代码> Malc C <代码>或C++ >代码>新< /C> >,而<>代码> GLPUBER子数据< /C> >是一个特殊版本的C MeMCPY或C++ >代码> STD::更准确地说,
glBufferSubData
是从CPU到GPU的
memcpy
,而
glCopyBufferSubData
是从GPU到GPU的
memcpy

怎么把它们一起煮?与C中的方法相同。在初始化时(程序启动时)调用
glBufferData
一次,在需要追加数据时调用
glBufferSubData
。一定要分配足够的空间!由
glBufferData
分配的缓冲区以及
malloc
ed缓冲区不会增长。使用
glBufferSubData
溢出缓冲区会导致未定义的行为,并可能导致应用程序崩溃

尝试预测缓冲区的空间需求,仅当数据不适合缓冲区时才调用
glBufferData

请记住,使用已分配的缓冲区绑定调用
glBufferData
,将取消分配现有缓冲区并创建一个新的缓冲区

glBufferSubData
不会重新分配缓冲区,但会覆盖已经存在的数据

让我用C翻译来说明:

glGenBuffers(..., buf); // float* buf;
glBindBuffer(buf); // Tell opengl that we will use buf pointer, no analog in C.
glBufferData(/*non-null pointer*/); // buf = malloc(/*..*/); memcpy(to_gpu, from_cpu);
glBufferData(/*another non-null pointer*/); // free(buf); buf = malloc(/*..*/); memcpy(to_gpu, from_cpu);
glBufferSubData(...); // memcpy(to_gpu, from_cpu);
意识形态方法 您需要的是:

glGenBuffers(..., buf); // float* buf;
glBindBuffer(buf); // Tell opengl that we will use buf pointer, no analog in C.

// Initialization
glBufferData(/*non-null pointer*/); // buf = malloc(/*..*/); memcpy(to_gpu, from_cpu);

// Hot loop
while (needToRender) {
    if(needToAppend) {
        if (dataDoesNotFit) glBufferData(...); // Reallocate, same buffer name
        else glBufferSubData(...); // memcpy(to_gpu, from_cpu);
    }
}
在这里,我们只是偶尔重新分配内存,当我们需要附加一些东西并且缓冲区太小时

其他办法 我建议使用
glBufferData
重新分配,因为CPU上的单个缓冲区中已经有了所有数据。如果没有(即GPU上有一块数据,CPU上有另一块数据,但不是在一起),可以使用
glCopyBufferSubData
重新分配:

glBufferData(/*alloc new_gpu_buffer*/);
glCopyBufferSubData(/*from old_gpu_buffer to new_gpu_buffer*/);
glDeleteBuffers(/*old_gpu_buffer*/);
glBufferSubData(/*from_cpu_buffer to new_cpu_buffer*/)p; // Add some new data from CPU.

另一种更新GPU数据的方法是将其发送到CPU,所以您只需通过指针访问GPU内存。它可能很慢(阻塞缓冲区、暂停管道),并且仅在特殊情况下有用。如果您知道自己在做什么,请使用它。

由于OpenGL是一种专注于绘图的API(暂时忽略计算着色器),并且在绘制场景时,通常从空画布开始,您必须在整个时间跨度内保留完整的点云数据积压,您希望能够重新绘制

假设对于大量的点云数据,重新绘制整个集合可能需要一些时间,某种形式的cachine可能看起来是合理的。但让我们先做一些信封背面的计算:

如今,典型的GPU完全能够以超过10^9个顶点/秒的速度执行完整的顶点设置(20年前,GPU已经能够以20·10^6个顶点/秒的速度执行某些操作)。您典型的计算机显示少于10·10^6像素。因此,由于鸽子洞原理,如果你要画超过10·10^6点,你要么产生了严重的透支,要么填满了大部分像素;实际上,它将介于两者之间

但正如我们已经看到的,GPU能够以交互帧速率绘制那么多点。再画一张,很可能会填满你的屏幕或者阻塞数据

如果您希望整个过程保持可读性,则需要某种形式的数据退役。对于任何大小的可读点云,您的GPU都可以很好地重新绘制整个内容


考虑到数据退役的需要,我建议您分配一个大的缓冲区,在逐出之前能够在其生命周期内保存一整套点,并将其用作循环循环缓冲区:在新数据到达时有一个偏移量(使用
glBufferSubData
),在边缘处,您可能需要将其分为两个调用,将最新的书写索引作为一个统一的索引传递,以按年龄淡出点,然后只需提交一个
glpaurements
调用,即可一次性提取该缓冲区的全部内容。

@Sergey我不知道如何计算
dataDoesNotFit
@leehuang这取决于您的决定。例如,您知道您使用initial
glBufferData
分配了N个字节的缓冲区,并使用
std::vector data
对其进行了初始化,其中
data.size()
。现在您需要附加
std::vector moreData
。因此,将
dataDoesNotFit
计算为
const bool dataDoesNotFit=data.size()+moreData.size()>N我真的不知道如何在创建vbo时明确提到缓冲区的大小。您能提供一些代码来分配特定大小的vbo吗?@leehuang:要初始化缓冲区对象,您可以使用
glBufferData
(可调整大小的缓冲区)或
glBufferStorage
(固定大小的缓冲区–我建议这样做)。这两个函数都有一个
size
参数,这就是您要查找的参数。但是size变量不是s吗
glBufferData(/*alloc new_gpu_buffer*/);
glCopyBufferSubData(/*from old_gpu_buffer to new_gpu_buffer*/);
glDeleteBuffers(/*old_gpu_buffer*/);
glBufferSubData(/*from_cpu_buffer to new_cpu_buffer*/)p; // Add some new data from CPU.