Cocos2d-x 3.x中关于自动材质合并的应用

2015年03月19日 12:00 0 点赞 0 评论 更新于 2025-11-21 17:43

今天我们主要来深入学习一下Cocos2d-x 3.x中有关自动材质合并的应用问题,对于这方面还不太清楚的开发者可以重点关注。

1. OpenGL的渲染

要理解自动材质合并,就需要先了解它的实现原理。关于OpenGL的管线渲染,网络上有大量的资料,这里就不详细展开了。我们主要关注与自动材质合并概念相关的两个关键点:顶点着色器与片段着色器。这两者的具体作用在很多资料中都能查到,这里不再赘述。

实际上,自动材质合并的核心在于省略上传材质的步骤,仅上传顶点坐标。通过这种方式,可以减少大量材质上传到寄存器时的IO消耗,从而达到渲染优化的目的。

2. Cocos2d-x的自动材质合并实现方式

其大体实现理论是通过相关材质合并(通常使用TexturePacker进行图片合并操作)以及显示对象的图层管理来实现的。

在CCRenderer文件的Renderer::drawBatchedTriangles()方法中有如下代码:

for(const auto& cmd : _batchedCommands)
{
auto newMaterialID = cmd->getMaterialID();
if(_lastMaterialID != newMaterialID || newMaterialID == MATERIAL_ID_DO_NOT_BATCH)
{
// Draw quads
if(indexToDraw > 0)
{
glDrawElements(GL_TRIANGLES, (GLsizei) indexToDraw, GL_UNSIGNED_SHORT, (GLvoid*) (startIndex*sizeof(_indices[0])) );
_drawnBatches++;
_drawnVertices += indexToDraw;
startIndex += indexToDraw;
indexToDraw = 0;
}
// Use new material
cmd->useMaterial();
_lastMaterialID = newMaterialID;
}
indexToDraw += cmd->getIndexCount();
}

这段代码的逻辑较为清晰。它会判断当前命令的材质ID与上一次提交的材质ID是否相同。如果相同,则不会重复进行材质提交;如果不同,则会将新材质ID对应的材质提交到GPU的材质寄存器,以供后续的渲染操作使用。

由此我们可以总结出,这种优化方式的原理很简单:如果相邻的两个渲染对象使用的是相同的材质,那么在渲染时,只有第一个使用该材质的对象会向GPU提交材质到寄存器,后续相邻的使用相同材质的对象将不再进行材质提交过程。

3. 示例

下面通过一个具体例子来说明自动材质合并的优化效果。如下图所示,我们对角色对象进行优化设计。

在程序中,每个角色对象所使用的动画材质可能不同。因此,我们将角色对象单独放入一个显示容器中,这个容器只存放角色对象。同时,我们把角色名字统一放入另一个专门的容器中,这样所有的角色名字就形成了一个材质批次关系。另外,角色脚下的阴影可以放入第三个容器中,这样所有角色的脚下阴影就可以合并成一个批次进行处理。

假设我们有10个不同形象的角色,如果采用原始方式,将每个角色的所有元素都放入一个容器中,那么每个角色会产生3个批次,10个角色总共会有30个批次。

而按照优化后的层级进行填充,每个角色独特的角色动画会产生10个批次,角色名称形成1个批次,角色阴影形成1个批次,总计只有12个批次。

由此可见,这种优化方式非常简单,只需要对容器关系进行简单调整即可。

4. 更复杂的优化

实际上,我们还可以实现更加个性化的优化,这就需要我们自行编写一个新的Shader。

例如,对于角色对象,我们为其赋予一个专门的角色对象Shader,并且让容器中只存在使用这种新型Shader的角色对象。

在这个Shader中,我们可以不再每次只使用一个材质寄存器,而是使用最小提供的8个材质寄存器。每次只更新必须更新的材质寄存器,这样就能实现一个更复杂且更符合我们需求的优化方案。

作者信息

boke

boke

共发布了 3994 篇文章