渲染批次合并之顶点
根据前面说过的render-flow流程接下来就是重头戏了render流程,其中包括了,检查两个渲染节点是否可以合并,同时把renderData的数据填充到modelBatch里的buffer中去。
所有需要渲染的节点都有一个rednerComponent(engine/cocos2d/core/cpmponents/CCRenderComponent.js),那就从这里入手。第一个参数其实就是modelBatcher。第二个参数cullingMask是跟camera相关的,后续再看。
先看modelBatcher(engine/cocos2d/core/renderer/webgl/model-batcher.js)的结构。
modelBatcher
我们可以看到它创建了4个类型的Buffer,并且持有了一个renderScene,renderScene以后再说。先看这个Buffer,对于2d渲染来说我们用到的也就是其中的_mesh-buffer,其他的基本也是相同的写法,只要搞懂一个,其他的也就都一样。先看看meshBuffer(engine/cocos2d/core/renderer/webgl/mesh-buffer.js)。
这个里面的东西很多。
这个gfx.VertexBuffer(engine/cocos2d/renderer/gfx/vertex-buffer.js)是用来绑定顶点数组的,当调用里面的update方法的时候就会调用gl.bindBuffer和gl.bufferData来绑定webGL数组。
同理gfx.IndexBuffer也是一样用来绑定顶点索引数组的。
然后我们再来看下vertexFormat这个参数的,它是在ModelBatcher的构造函数中传过来的
它的结构在engine/cocos2d/core/renderer/webgl/vertex-format.js里定义的。
可以看到它的定义跟我们在前段时间在Assembler2D里的renderData是一样的,只不过最后的颜色属性定义的不一样,renderData里定义了的是Uint32,而在这里用的是4个Uint8,虽然它们定义的不一样,但是数值和所占的字节数是一样的。
再来看mesh-buffer构建Buffer的地方。
这里的定义也和我们Assembler2D里的renderData是一样的。变量名都基本一样,这里都不细说了,不明白的可以看看前面的文章。
再来看看前面的renderScene,这个跟我们的CCScene不是一个东西,这里面只包含了渲染相关的数据,它的位置在engine/cocos2d/renderer/scene/scene.js。
对于2d渲染比较关注的是两个东西一个是models数组,一个model就是一个渲染批次。另一个是cameras数组,camera后面再说。先来看看model(engine/cocos2d/renderer/scene/model.js)里有些什么东西。
其中比较重要的是inputAssebler,effect。effect是material里的effect,这个自不必说,重点看inputAssebler(engine/cocos2d/renderer/core/input-assembler.js)。
this._vertexBuffer就是上面MessBuffer里的gfx.VertexBuffer的引用,this._indexBuffer就是上面Messbuffer里的gfx.IndexBuffer的引用,图元默认的参数给的是三角形,还有一个this._start表示顶点索引的开始位置,this._count表示要绘制的顶点索引的个数。
了解了上面这些我们就开始说它们是怎么将相邻的node节点渲染批次合并的。
先看Assembler2D里fillBuffers方法
其中
这个里面可以看到它就是用的ModelBatcher里的_meshBuffer。fillBuffers方法就是将Assembler2D持有的renderData里的数据填充到ModelBatcher里的_meshBuffer。
再看在此之前执行的CCRenderComponent里的_checkBacth方法,它是实现渲染批次合并的关键所在。
如果本节点的渲染的材质和前一个不一致就执行_flush方法。
在这里它就直接生成一个新的InputAssembler,根据messBuffer的顶点索引的start值来更新自己的start值,同时生成一个新的model,然后持有这个新的InputAssembler。然后将这个model添加到renderScene,以后再说这个renderScene,现在先忽略。最后更新messBuffer的顶点索引的start值。
到这里关于顶点属性的渲染批次就很明白了,假设场景有两个需要渲染的2d节点,节点A和节点B,其中节点A是本场景第一个渲染的节点,它们的材质都是一样,那么它们就不会执行_flush参数,也就是只会生成一个model,一个InputAssembler(start=0,count=12)(一个2d节点有6个索引)。那如果节点A和节点B材质不一样,就会生成两个model,节点A的InputAssember就为(start=0,count=6),节点B的InputAssember就为(start=6,count=6),这样就有两个model,尽管两个model中的InputAssember的_vertexBuffer,_indexBuffer依旧指向同一段数据,最后再渲染的时候也就会分两次来渲染。
当然还有其他的地方也会执行_flush函数。
比如当meshBuffer的数组过大的时候也会执行_flush函数。
顶点数组,索引数组合并只是渲染批次合并的一部分,想要达到真正的渲染批次合并,纹理的合并和纹理合并后的uv坐标更新也是很重要的一部分,这个下一篇文章再说。