我如何在 WebGL 中为逐帧(非常具体的)动画程序组织顶点数据?

How Might I organize vertex data in WebGL for a frame-by-frame (very specific) animated program?

我一直在做一个有非常具体要求的动画图形项目,经过大量搜索和测试编码,我认为我可以采用几种方法,但我一直在阅读 Khronos 和 MDN 文档加上我在这里看到的其他帖子,并没有回答我关于我的特定项目的所有问题。其间,我写了简短的测试程序(设置测试基础设施)。

首先,我应该描述一下这个项目:

绘制到屏幕上的主要对象是一个简单的四边形,周围环绕着黑色轮廓(LINE_LOOP 或 LINES 都可以,尽管我遇到了 z-fighting 问题......另一个问题)。当用户与程序交互时,会创建并立即绘制一个新的四边形,但在一定时间内其顶点会四处移动,直到四边形移动到其最终目的地。 (请注意,翻译不会这样做。)还会绘制随机黑线,有时这些线也会四处移动。

一旦其中一个四边形到达它的最终位置,它就再也不会移动了。 新的四边形总是在旧的四边形之上(靠近屏幕)。这意味着我需要将四边形和线条从最旧到最新分层。 *这也意味着最好为每个四边形和线条分配 z 值,即使图形采用像素坐标并使用正交矩阵。大家会同意吗?

鉴于这些参数,我有几个不同复杂程度的选项:

1> 采用面向对象的方法,只为每个四边形分配一个缓冲区,随机线也是如此。 --为正在移动的一个形状创建和销毁缓冲区的每一帧。我真的认为这是一个糟糕的想法,它可能只适用于在底层进行大量优化的更高级别的库。这种方法也没有利用几乎每个四边形都将保持不变的事实。

[vertices0] ... , [verticesN]
Draw x N (many draws for many small-size buffers)

2> 为每个四边形、轮廓和线条分配一个 z 值(如上所述)。分配一个巨大的顶点缓冲区和元素缓冲区来存储所有永久在其最终位置的四边形。仅在极不可能的情况下调整大小,即有人交互的时间足够长。创建第二个小缓冲区来存储一个临时移动四边形并使用 bufferSubData 每帧。当四边形到达目的地时,bufferSubData 将其放入大缓冲区并在创建下一个四边形时覆盖小缓冲区……所有这些都在同一帧上。我在这里的主要问题是:是否可以(安全?)使用 bufferSubData 并将其绘制在同一帧上?另外,我是否会在两个缓冲区上都使用 DYNAMIC_DRAW,即使较大的缓冲区会看到较少的更新?

[permanent vertices ... | uninitialized (keep a count)]
bufferSubData -> [tempVerticesForOneQuad]
Draw 2x

3> 仍然创建大缓冲区和小缓冲区,但不是每帧都使用 bufferSubData,而是创建第二个着色器程序并为 new/moving 四边形添加一个明确设置顶点位置的属性对于动画(我会传递顶点索引属性)。仅在四边形移动时使用小缓冲区绘制。对于四边形到达其目的地时的帧,绘制大缓冲区和小缓冲区,然后 bufferSubData 将最终坐标放入大永久缓冲区以用于 下一个 帧。

switchToShaderProgramA();
[permanent vertices...| uninitialized (keep a count)]
switchToShaderProgramB();
[temp vertices] <- shader B accepts indices for each vertex so we can do all animation in the vertex shader
---last frame of movement arrives : bufferSubData into the permanent vertices buffer for when the the next quad is created

我觉得第三个选项可能是最好的,但我想了解是否还有其他一些我没有考虑的因素。例如,我假设程序切换、附加属性和顶点着色器操作会比 2> 中仅替换缓冲区值更快。方法 3> 的优点(我认为)是我可以将缓冲区替换推迟到不需要绘制任何内容的时间。

不过,我仍然不确定如何处理随机出现的线条。我不能采用 "single quad vertex buffer" 方法,因为无法预测行数。我还可以为移动线分配一个大缓冲区吗?这些在四边形完成移动后也会保留,但我不认为我可以使用顶点着色器技巧,因为要设置的属性太多(与一个四边形的 4 个属性相反)。我想我可以先创建一个大的 "permanent line data" 缓冲区,但是在动画期间要做什么很棘手,因为线条会移动。也许 bufferSubData() + 在同一帧上绘制并不可怕?或者它可能是。这是我需要建议的地方。

我知道这个问题在代码方面可能不太具体,但我不相信我会被允许展示程序的核心。我所拥有的只是准备好典型的 WebGL 样板文件。

我期待听到人们对我可能如何进行的想法,以及在考虑上述三个选项时是否有任何我可能遗漏的权衡。 预先感谢您,如果需要澄清,请随时提出任何其他问题。

老实说,对于您所描述的内容,我觉得您选择哪个并不重要。在现代硬件上,每帧绘制几百个四边形和几千条线不会对硬件造成太大负担。

话虽如此,我同意方法 1 似乎效率很低。方法 2 听起来非常好。您可以在上传数据的同一帧上安全地绘制缓冲区。我认为使用 DYNAMIC_DRAW 还是 STATIC_DRAW 作为缓冲区并不重要。我倾向于将动态缓冲区视为每帧都在更新的东西。如果您仅每隔几秒或更短时间更新一次,那么静态就可以了。方法 3 也可以。在 2 和 3 之间,我会说做你更容易理解和编程的那个。

同样,对于线条,我会使用单独的缓冲区。听起来每帧都有一个变化,所以我会为此使用 DYNAMIC_DRAW。为它分配一个大缓冲区并每帧执行 glBufferSubData() 可能是一个很好的策略。一如既往,尝试并分析它会告诉你肯定的。