渲染复杂网格的最佳方法是什么?我在下面写了不同的解决方案,想知道您对它们有何看法。
让我们举个例子:如何渲染“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 太多了!问题是渲染海绵的缓冲区绑定数量!我认为一个好的解决方案应该是,如果第一个 VBO 已“满”,则分配一个新的 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 个绑定!你认为这可能吗?
那么,根据您的说法,渲染斯庞扎网格的最佳方法是什么?您还有其他建议吗?