如何将每个模型的模型矩阵仅发送一次到着色器

时间:2016-10-28 17:04:44

标签: opengl matrix glsl shader

作为参考,我跟随此tutorial。现在假设我有一个小型应用程序有多种类型的模型,如果我理解正确,我必须将每个模型从CPU发送到GPU(换句话说到我的顶点着色器)的MPV矩阵,因为每个模型可能从一个到另一个具有不同的模型矩阵。

现在查看教程和this post,我了解将矩阵发送到我的着色器(glUniformMatrix4fv(myMatrixID, 1, GL_FALSE, &myModelMVP[0][0])的调用应针对每个帧和每个模型进行因为每次它都会覆盖我MVP的前一个值(我最后一个模型的值)。但是,由于担心我的应用程序的性能,我不想通过总线发送无用的数据,如果我理解正确,我的模型矩阵常量为每个模型。

我正在考虑为每个模型的MVP矩阵制作一个制服,但我认为它不可扩展,如果我的视图或投影矩阵发生变化,我还必须更新所有这些...有没有办法避免多次发送我的模型矩阵,只在变化时发送我的视图和投影矩阵?

3 个答案:

答案 0 :(得分:3)

这些本质上是两个问题:如何仅在转换序列的一部分发生变化时避免发送数据,以及如何有效地提供自上一帧以来可能已更改的每个模型数据。

转换序列

首先,你有一个转换序列。你的位置在模特空间。然后,您在概念上将它们转换为世界空间,然后转换为相机/视图空间,最后转换为剪辑空间,您将位置写入gl_Position

这些变换中的大多数在整个帧中是恒定的,但可能在帧到帧的基础上发生变化。因此,您希望避免更改并非严格需要更改的数据。

如果你想这样做,那么显然你不能提供一个" MVP"矩阵。也就是说,您不应该有一个包含整个转换的矩阵。相反,您应该有一个表示转换特定部分的矩阵。

但是,出于性能以外的原因,您需要进行此分解。你不能在剪辑空间做很多照明操作;作为一个非线性空间,它会扰乱大量的照明操作。因此,如果您要进行照明,则需要在剪辑空间之前停止转换。

Camera/view space is the most common stopping point for lighting computations

现在,如果您使用模型到相机和相机到剪辑,那么即使模型本身没有移动,每个模型的模型到相机矩阵也会在相机更改时发生变化。因此,您可能需要上传一堆并非严格需要更改的矩阵。

为避免这种情况,您需要使用模型到世界和世界到剪辑(在这种情况下,您在世界空间中进行照明)。这里的问题是你接触到perils of world space数值精度可能会成为问题。

但这里有真正的性能问题吗?显然它在某种程度上取决于硬件。但是,请考虑许多应用程序具有数百个(如果不是数千个)对象,每个对象都具有每帧都会更改的矩阵。一个动画人物通常只有一百多个矩阵才能改变每一帧。

因此,上传一些本来可以保持不变的矩阵的性能成本似乎不太可能是现实问题。

每对象存储

您真正想要的是将每个对象数据的存储与程序对象本身分开。这可以通过UBO或SSBO完成;在这两种情况下,您都将统一数据存储在缓冲区对象中。

前者通常较小(64KB左右),而后者在其存储中基本上无限制(最小16MB)。显然,前者通常访问速度更快,但SSBO不应该被认为是缓慢的。

每个对象都有一个缓冲区的一部分,用于每个对象的数据。因此,你可以根据需要选择改变它。

即便如此,这样的系统并不能保证更快的性能。例如,如果当您尝试更改此帧时,实现仍在从最后一帧读取该缓冲区,则实现必须分配新内存或等到GPU完成。这不是假设的可能性;复杂场景的GPU渲染经常滞后于CPU背后的帧。

为避免这种情况,您需要对每个对象数据进行双重缓冲。但是当你这样做时,你必须总是上传他们的数据,即使它没有改变。为什么?因为它可能在两帧之前已经改变,并且你的双缓冲区中包含旧数据。

基本上,您尝试避免上传有时静态的每个模型数据的目标同样有可能损害性能以帮助它。

答案 1 :(得分:2)

首先,您的场景中至少某些可能会移动。如果是对象,那么模型矩阵将在帧与帧之间变化,如果是相机,则视图或投影矩阵将发生变化。 MVP包含三者的组成,所以它实际上会改变,你无法以某种方式远离它。

但是,您可能仍然可以从使用其中一些中受益:

  • 使用Uniform Buffer Objects。您只能将制服发送到GPU一次,然后重新绑定程序将从中读取制服的缓冲区。因此,不同的模型可能会使用不同的UBO作为参数(如模型矩阵)。

  • 使用Instancing。即使只渲染每个模型的一个实例,也可以将模型矩阵作为实例顶点属性传递。它将存储在VAO中,因此仅发送到GPU一次(或者必须更新时)。从好的方面来说,您现在可以通过实例化绘制调用轻松渲染同一模型的多个实例。

注意分离模型,视图和投影矩阵可能是有益的。视图和投影可以通过“相机描述”统一缓冲区对象传递,每个帧只更新一次,然后由所有程序引用。模型矩阵,如果没有改变,那么在VAO内将是恒定的。要做适当的照明,你必须将模型视图与投影分开。在GPU上使用三个矩阵可能看起来很吓人,但实际上你不必这样做,因为你可能会切换到基于四元数的管道,这反过来又简化了切线空间插值这样的事情。

答案 2 :(得分:1)

两个字:过早优化!

  

我不想通过总线发送无用的数据,如果我理解正确,我的模型矩阵对于每个模型都是不变的。

传输的数据量微不足道。单精度浮点数的4×4矩阵占用64个字节。无论如何,这几乎都没有。哎它需要更多数据来向GPU发出实际的绘图命令(通常统一的值更改被打包到与绘图命令相同的总线事务中)。

  

我正在考虑为每个模型的MVP矩阵制作一个制服

然后你将要用完制服。只有那么多统一的位置需要GPU来支持。你当然可以使用统一缓冲区对象,但这不是正确的应用程序。