【问题标题】:Instanced drawing of dynamic models in OpenGLOpenGL中动态模型的实例化绘图
【发布时间】:2012-08-15 16:03:40
【问题描述】:

我目前正在开发一个框架,可以方便地渲染大量动画模型。

一个模型被组织成一个简单的骨骼层次结构,根是躯干/骨盆,通常:

所以,作为伪代码,我目前正在渲染这样的模型:

RenderBone(Bone b, Mat4x4 currentTransform){
    Mat4x4 pos = currentTransform * b.boneTransform;
    SetUniform("transformation", pos);
    Draw(bone.mesh);
    for each Bone bc in b.children do{
         RenderBone(bc, pos);
    }
}

因此,对于使用具有 n 个骨骼的模型的单个演员,我需要 n 个 SetUniform(不包括设置纹理之类的东西)和 n 个绘制调用。

为了减少开销,并一次使用相同的模型渲染所有演员,我考虑切换到实例渲染。

但是,我能找到的所有信息和教程都是关于绘制立方体、球体或类似的简单对象的。我无法在任何地方看到一些关于如何使用实例绘图来渲染模型的简单易懂的信息,其中每个部分(骨骼)都需要为着色器提供不同的转换矩阵。

所以,问题: 使用glVertexAttribDivisorgl_InstanceID 我只能指定实例相关矩阵,而不是骨骼相关矩阵。那我该如何应用我的骨骼变换呢?

我能想到的唯一可行的解​​决方案是 - 我可以实例化每个骨骼,而不是实例化整个模型。从而绘制一种骨骼类型的所有实例,然后绘制另一种,等等。 但是我仍然需要相对频繁地使用转换矩阵更新缓冲区,而且它是更多的家务代码。

那么这是最好的选择吗?或者,更一般地说,是否有更好的不太复杂的渲染方式? 还是实例化渲染只有在与静态几何体一起使用时才真正发挥作用?

【问题讨论】:

  • "试图减少开销" 停在那里。你为什么要减少开销?您是否有来自感兴趣平台的实际分析数据表明这种开销特别是是一个问题?如果不是,那么您过早地进行了优化。
  • @NicolBolas 我没有。我知道你不能简单地在 OGL 调用上贴上价格标签,但人们似乎同意减少函数调用的数量是个好主意,是吗?目前我不需要优化任何东西,因为我可以通过我的棒和石头渲染实现达到 1000 FPS。但是,如果有一种渲染方式通常表现良好,那么为什么不从一开始就使用它呢?当我的问题集足够复杂以至于我实际上可以对其进行分析时,我仍然可以看到问题所在并在需要时更改实现。
  • "但是,如果有一种渲染方式通常表现良好,那么为什么不从它开始呢?" 因为它需要更长的时间来实现,而且可能不会最终的任何价值。它使代码更难调试并使开发时间更长,可能没有实际可衡量的收益。优化永远不应该仅仅因为你可以就去做。哦,不,减少函数调用的数量并不是先验更快;这是在获得性能数据之前不进行优化的另一个原因。
  • 感谢您写下您的答案,它为我提供了更多启示。我知道我基本上是为了优化而优化。但我并不是要构建一个高效的引擎以供实际使用,而是要有一个游乐场,让我可以在其中进行实验和学习,即使它们并不总是最好的想法。我认为这是学习 OpenGL 并从中获得乐趣的更好方法,而不是尝试制作一个游戏——在极少数情况下你真正完成它——让你失望,因为它比你在你的刚开始时的头:)
  • @Derek 该链接的重要部分是“对于某些 OpenGL 实现”,这意味着某些(也许是大多数)实现不会等待屏幕刷新率。这意味着您可以在每个显示的帧中多次交换。

标签: c++ opengl opengl-3


【解决方案1】:

当您需要绘制同一模型的 数千个 副本时,您可以使用实例化。一般来说,带有骨骼的网格不是您需要绘制数千个的东西。

实例化是一种优化,并不总能得到回报。除非您知道需要它(通过分析并查看您是否达到性能目标),否则您不应该费心尝试使用它。即便如此,它何时才是真正的性能提升也可能非常棘手。

有时,它只是无济于事。但这里有一些一般的经验法则:

  1. 除非您要渲染数千个实例,否则实例化是不值得的。
  2. 不应将实例化用于顶点过多或过少的网格。 100-1,000 左右。

请记住,这些是一般规则,而不是绝对规则。它们也依赖于硬件。

所以,问题是:使用 glVertexAttribDivisor 或 gl_InstanceID 我只能指定与实例相关的矩阵,而不是与骨骼相关的矩阵。那我该如何应用我的骨骼变换呢?

就你看到的例子或你看到其他人所做的事情而言,你想的太多了。像程序员一样思考。

gl_InstanceID 不是“实例相关矩阵”;它是一个索引。您如何使用该索引完全取决于您。您看到的大多数示例都使用此索引来查找矩阵数组,可能存储在uniform blockbuffer texture 中。该矩阵是您用于渲染的变换。每个索引代表单个实例的转换。

您的每个实例都有多个矩阵、多个转换。但是每个实例都有相同数量的骨骼(否则它不会被实例化渲染)。假设你有 5 块骨头。

同样,每个索引都是单个实例的转换。您的案例和标准之间的区别在于每个实例需要多少信息。常规情况需要一个矩阵;你需要五个。但无论哪种方式,想法都是一样的。

如果您当前实例需要骨骼索引 3,您只需使用以下表达式访问矩阵数组:(gl_InstanceID * 5) + 3,其中 5 是每个实例的骨骼数。

剩下的就是使用每个顶点的属性来传递用于变换每个顶点的骨骼索引。

【讨论】:

  • 但粒子系统不是最常见的实例化用途之一吗?它们通常使用 1-4 个顶点,而不是 100-1000 个。
  • @Jackalope:“粒子系统不是最常见的实例化用途之一吗?”不,它们不是。
猜你喜欢
  • 2011-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-31
  • 1970-01-01
  • 1970-01-01
  • 2011-08-24
相关资源
最近更新 更多