如何为大型网格实现 VBO 并获得流畅的动画?

Posted

技术标签:

【中文标题】如何为大型网格实现 VBO 并获得流畅的动画?【英文标题】:How to implement VBOs for large meshes and get a smooth animation? 【发布时间】:2012-04-18 20:05:53 【问题描述】:

我正在 Maya 中创建一分钟长的动画,我打算在 OpenGL 中渲染它。我这样做是作为大学的一个项目。

场景中只有一张做出扭曲表情的人脸。网格有大约 2200 个四边形面。

我找到了一个将 Wavefront 对象文件转换为顶点和法线数组的 perl 脚本。我打算在 Maya 中以 24fps 的速度制作动画,然后将每一帧中的网格导出到 Wavefront 对象文件,然后通过 perl 脚本运行所有这些,这将为我提供顶点和法线数组。

因此,为了测试可行性,我将 Maya 中的两帧动画转换为目标文件,并通过脚本运行它(在调整后),这给了我数千个 glVertexglNormal 调用。我将所有这些复制到我的 C++ 程序并编译它。运行流畅。

考虑到当我将 60sec*24 帧的 glVertexglNormal 语句复制到程序中时程序会有多大,我想尝试使用 VBO。所以,我读过它,但我不太了解它的实现。

所以我的第一个问题是:在这种情况下实现 VBO 的最佳方法是什么?

我正在考虑顶点和法线的二维数组,每帧一个;和GL_STATIC_DRAW_ARB 用于glBufferDataARB() 的“使用”参数。

假设我的动画是 5 帧长,我的面部网格由 2200 个顶点和法线组成,那么我将创建:

GLfloat face_vertices[5][2200] = .....;
GLfloat face_normals[5][2200] = .....;

我的第二个问题:上面的代码是否会在 VBO 中工作或表现“良好”?

此外,我不确定这些信息是否重要,但我的计算机运行在 Intel 82945G 显卡上,而且我很确定我大学的 PC 也运行在 Intel 上。只是想我会提到这一点。

更新

性能也是一个问题。

【问题讨论】:

你的装备是怎么做的?顶点是否线性映射到关节?如果是,您可以轻松地将带有骨骼和动画的网格导出到 Wavefront 文件中,并由众多游戏引擎之一读取。其中一些能够在硬件着色器上播放动画,但我不确定 Intel 是否在支持列表中。 哦,我在那里做了一个相当大的精神跳跃。至于您的 VBO 优化 - 它们仅适用于具有旧(固定,例如无着色器)管道的静态网格。所以,如果英特尔的能力不是很强,你所能做的就是使用多个 VBO——每个用于一帧。 @Rekin “它们只适用于使用旧管道的静态网格物体” - 嗯?现在你是什么意思?顺便说一句,他没有使用任何绑定,而是一个简单的变形动画。 【参考方案1】:

我正在考虑顶点和法线的二维数组,每帧一个;和

我认为这是不正确的解决方案。有了这么多的数据,流式传输它是有意义的。例如,将 1 秒的动画保留在内存中,其余的按需加载。我相信这种情况有 GL_STREAM_**** 标志。

更好的解决方案是从 Maya 导出骨骼及其动画。 (在这种情况下,您需要一个永远不会改变的网格)

必须逐帧导出逐帧动画的唯一情况是导出计算量大得惊人的东西。我能想到的唯一现实场景是 3D 流体网格(拓扑改变非常框架等)——其他一切都可以在 CPU 或 GPU 上计算。但是,您的帖子并不表明这是您的情况。因此,根据墨菲定律,我必须假设您使用低效的解决方案来解决可以以不同方式解决的问题。

:上面的代码是否能在 VBO 中工作或表现“良好”?

无法回答,因为您没有说明您将如何绘制数据。通过滥用着色器、alpha 混合或多次渲染相同的东西,即使是简单的几何体,也有可能让任何机器陷入瘫痪。

【讨论】:

【参考方案2】:

如果您可以流畅地运行两帧动画,则可以流畅地运行 60*24 帧“相当于 glVertex 和 glNormal 语句”,前提是您可以从 Maya 动画中自动生成 GL 调用。这种方法的唯一问题是您的源代码和二进制可执行文件将非常庞大。这是使您的方法“不好”的问题。

因此,首先,您可以将数据放在一个数组(或 std::vector,就此而言)中,并使用一个简单的循环遍历它,而不是对每个顶点数据进行一次 glVertex 调用:

std::vector<int> idx;
std::vector<vertex> frame_data;
...
glBegin(GL_QUADS);
for(int i = 0; i < idx.size(); ++i) 
  glVertex3f(frame_data[idx].x, frame_data[idx].y, frame_data[idx].z);
  glNormal3f(frame_data[idx].nx, frame_data[idx].ny, frame_data[idx].nz);

glEnd();

idx 表示面,idx 中的每 4 个元素组成一个四边形。 Frame_data 是该帧的特定顶点数据,每一帧必须有一个这样的数组,并且该数据由 idx 索引。这样,您唯一需要解决的问题就是如何将这些数据读入您的应用程序。如果您可以从网格文件生成 glVertex 调用,则可以改为生成一个文件,您可以使用 C++ fstream 在应用程序中轻松加载该文件,并构建数据数组,然后遍历它们并进行绘图调用。

在这种情况下,您不会看到使用 glBegin()/glEnd() 和 VBO 之间的性能差异,因为 2200 个四边形的数量太少,无法产生任何影响,但这对您来说将是一个有趣的练习。组织数组中的数据(idx 和 vertex_data)是使用 VBO 的第一步。您必须将与一个顶点相关的所有数据打包到一个结构中:

struct vertex 
  float x, y, z; // Position
  float nx, ny, nz; // Normal
;
...
vertex frame_data[v_count];
uint16_t idx[2200 * 4]; // 4 vertices makes one face

请注意,v_count 不是 2200。这是您的四边形计数,因此该数字与 idx 大小相关,而不是 frame_data,即相同的顶点可以用于多个四边形面。 p>

您将需要两个缓冲区,一个静态缓冲区来保存 idx(一个 GL_ELEMENT_ARRAY_BUFFER 和 GL_STATIC_DRAW),因为这在执行期间不会改变,另一个“流”缓冲区来保存顶点数据(GL_ARRAY_BUFFER 和 GL_STREAM_DRAW)。最后一个使用“GL_STREAM_DRAW”,因为您需要为动画每帧重写其内容。见glBufferData。

【讨论】:

感谢您的所有回复,我会调查他们......现在有点忙不过来。见 UPDATE @SigTerm,我打算用 glDrawElements() 来绘制它。

以上是关于如何为大型网格实现 VBO 并获得流畅的动画?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 iOS 中获得最流畅的动画?

如何为 UITableViewCell 实现抖动动画

如何为 Grid RowDefinition 高度变化设置动画(当 Height="Auto" 时)

使用着色器为网格设置动画

如何通过许多子视图保持 iOS 动画流畅

如何为我的 GIF tilesheet 制作动画(OpenGL、DevIL、C++)