【发布时间】:2016-02-04 17:39:53
【问题描述】:
渲染复杂网格的最佳方式是什么?我在下面写了不同的解决方案,想知道您对它们有何看法。
举个例子:如何渲染“Crytek-Sponza”网格?
PS:我不使用 Ubershader,只使用单独的着色器
如果您通过以下链接下载网格:
http://graphics.cs.williams.edu/data/meshes.xml
将其加载到 Blender 中,您会看到整个网格由大约 400 个子网格组成,分别具有自己的材质/纹理。
虚拟渲染器(版本 1)将分别渲染 400 个子网格中的每一个!这意味着(为了简化情况)400 个绘制调用,每个绘制调用都绑定到材质/纹理。对性能非常不利。很慢!
pseudo-code version_1:
foreach mesh in meshList //400 iterations :(!
mesh->BindVBO();
Material material = mesh->GetMaterial();
Shader bsdf = ShaderManager::GetBSDFByMaterial(material);
bsdf->Bind();
bsdf->SetMaterial(material);
bsdf->SetTexture(material->GetTexture()); //Bind texture
mesh->Render();
现在,如果我们注意加载的材料,我们会注意到 Sponza 实际上仅由 25 种不同的材料组成(如果我记性好的话 :))!
因此,更智能的解决方案(第 2 版)应该是批量收集所有顶点/索引数据(在我们的示例中为 25 个),而不是将 VBO/IBO 存储到子网格类中,而是存储到一个名为 Batch 的新类中。
pseudo-code version_2:
foreach batch in batchList //25 iterations :)!
batch->BindVBO();
Material material = batch->GetMaterial();
Shader bsdf = ShaderManager::GetBSDFByMaterial(material);
bsdf->Bind();
bsdf->SetMaterial(material);
bsdf->SetTexture(material->GetTexture()); //Bind texture
batch->Render();
在这种情况下,每个 VBO 都包含共享完全相同的纹理/材质设置的数据!
好多了!现在我认为 25 VBO 用于渲染 sponza 太多了!问题是渲染 sponza 的缓冲区绑定的数量!我认为一个好的解决方案应该是分配一个新的 VBO,如果第一个“满”(例如让我们假设 VBO 的最大大小(在 VBO 类中作为属性定义的值)是 4MB 或 8MB)。
pseudo-code version_3:
foreach vbo in vboList //for example 5 VBOs (depends on the maxVBOSize)
vbo->Bind();
BatchList batchList = vbo->GetBatchList();
foreach batch in batchList
Material material = batch->GetMaterial();
Shader bsdf = ShaderManager::GetBSDFByMaterial(material);
bsdf->Bind();
bsdf->SetMaterial(material);
bsdf->SetTexture(material->GetTexture()); //Bind texture
batch->Render();
在这种情况下,每个 VBO 不包含共享完全相同的纹理/材质设置的必要数据!这取决于子网格加载顺序!
好吧,VBO/IBO 绑定减少了,但绘制调用没必要减少! (你对这个肯定没问题吗?)。但总的来说,我认为这个版本 3 比以前的版本更好!您对此有何看法?
另一个优化应该是将 sponza 模型的所有纹理(或纹理组)存储在纹理数组中!但是,如果您下载 sponza 包,您会看到所有纹理都有不同的大小!所以我认为它们不能绑定在一起,因为它们的格式不同。
但如果可能的话,版本 4 的渲染器应该只使用更少的纹理绑定,而不是整个网格的 25 个绑定!你觉得有可能吗?
那么,根据您的说法,渲染 sponza 网格的最佳方式是什么?你还有什么建议吗?
【问题讨论】:
-
"例如,假设最大大小为 4MB 或 8MB -> 这取决于您的硬件" 我们不是在您的其他问题中解决了这个问题吗?我在哪里明确指出,除了您的 GPU 有多少内存之外,没有最大大小?你最近见过只有 8MB 的嵌入式 GPU 吗?
-
你在这些测试中的帧率是多少?如果您要达到 60 fps,那么每帧 400 个绘制调用(带有一些状态更改)确实不成问题。当然,更少总是可取的,但它不应该成为瓶颈。每帧 25 个绘制调用是花生。
-
对不起,我想说如果我认为最大 VBO 大小(作为我的 VBO 类中的属性)定义为 4MB 或 8MB。 (我告诉它取决于硬件,因为在某些硬件上,VBO 分配可能会因给定大小而失败)。 4MB 对我来说是在执行我的 opengl 应用程序时定义的配置大小。