渲染复杂网格的最佳方法是什么?我在下面写了不同的解决方案,想知道你对它们有什么看法。
让我们举个例子:如何渲染‘’网格?
PS:我不使用Ubershader,只使用单独的着色器。
如果您在以下链接上下载网格:
http://graphics.cs.williams.edu/data/meshes.xml然后在搅拌机中加载,你会看到整个网格是由大约400个子网格组成的,它们各自有自己的材料/纹理。
一个虚拟渲染器(版本1)将分别呈现每个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();现在,如果我们照顾材料被加载,我们可以注意到,海绵状是在现实中,只有(如果我有一个良好的记忆力:) 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,如果第一个是'full‘(例如,假设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个绑定!你觉得有可能吗?
那么,根据你的说法,什么是最好的方式来渲染海绵状网格?你还有别的建议吗?
发布于 2016-02-04 18:05:52
你把注意力集中在错误的事情上。有两种方式。
首先,您没有理由不能将网格的所有顶点数据放入单个缓冲区对象中。请注意,这与批处理无关。记住:批处理是关于抽签调用的数量,而不是您使用的缓冲区的数量。您可以在同一个缓冲区中呈现400次抽签调用。
这“最大的尺寸”,你似乎想要的是一个虚构的,从没有从现实世界。如果你愿意的话你可以拿着它。只是不要期望它会使您的代码更快。
因此,当呈现这个网格时,根本没有理由切换缓冲区。
其次,批处理实际上并不是关于抽签调用的数量(在OpenGL中)。它实际上是关于抽签调用之间的状态变化的成本。
这段视频清晰地说明了(大约31分钟),不同状态的相对成本发生变化。在它们之间发出两个没有状态变化的平手调用是很便宜的(相对来说)。但是,不同的州变化有不同的代价。
更改缓冲区绑定的成本非常小(假设您使用的是独立顶点格式,因此更改缓冲区并不意味着更改顶点格式)。改变程序甚至纹理绑定的成本要大得多。因此,即使您必须创建多个缓冲区对象(同样,您不必这样做),这也不会成为主要的瓶颈。
因此,如果性能是您的目标,您最好集中精力于昂贵的状态更改,而不是廉价的状态更改。制作一个单一的着色器,可以处理所有的材料设置为整个网格,所以你只需要改变他们之间的制服。使用数组纹理,使您只有一个纹理绑定调用。这将把纹理绑定变成一个统一的设置,这是一个更便宜的状态变化。
您甚至可以做一些更有意义的事情,包括基本实例计数等。但对于这样一个微不足道的例子来说,这太过分了。
https://stackoverflow.com/questions/35207922
复制相似问题