visit函数内会先sortAllChildren,然后调用自己的draw函数,然后遍历所有子节点递归调用字节的visit:
因为draw函数会创建渲染指令加到渲染队列中,所以先执行visit的节点会先进行绘制,
它的渲染指令在渲染队列的前面,sortAllChildren根据ZOrder,由小到大排序,ZOrder小的先绘制。
cocos渲染流程是从Director类的主循环mainLoop中开始:
1.调用drawScene():
//精简后的代码,方便理解
void Director::drawScene()
{
/*此处省略一些代码
.....
*/
_renderer->clear();
pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
if (_runningScene)
{
/*绘制当前场景,实际上不是真正绘制而是为场景内的每个节点创建渲染指令。
遍历访问场景内的节点树(visit和draw函数),初始化渲染指令加到渲染队列中。
为每个节点计算出将要传入着色器的数据:模型视图矩阵、shader对象(GLProgramState)等
这些数据封装在渲染指令中RenderCommand
*/
_runningScene->render(_renderer);
}
//这里是真正的渲染,visitRenderQueue函数:变量渲染队列中的所有指令
//processRenderCommand函数内处理单个指令,不同的渲染指令处理方式不同
//能合批的合批(QUAD_COMMAND和TRIANGLES_COMMAND可以合批)
//然后调用flush:1.发送顶点数据到opengl缓冲区 2.调用glDrawElements绘制
//CUSTOM_COMMAND则执行自定义的渲染回调函数,一般是onDraw()函数
_renderer->render();
popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
// swap buffers
if (_openGLView)
{
_openGLView->swapBuffers();
}
}
2._runningScene->render(_renderer):
void Scene::render(Renderer* renderer)
{
...
//计算节点相对于父节点坐标系的变换矩阵,也是当前节点相对于父节点的位移、旋转、缩放属性
//这些属性构成一个矩阵,这个矩阵需要乘以所有父节点的变换矩阵,就可以得到这个节点在世界坐标系的变换矩阵,可以理解成相对于父节点的变换和所有父节点变换要叠加到一起
const auto& transform = getNodeToParentTransform();
//此处调用基类visit函数
visit(renderer, transform, 0);
...
}
3.Node::visit()
//这是虚函数
void Node::visit(Renderer* renderer, const Mat4 &parentTransform, uint32_t parentFlags)
{
// quick return if not visible. children won't be drawn.
if (!_visible)
{
return;
}
.....
int i = 0;
if(!_children.empty())
{
sortAllChildren();
// draw children zOrder < 0
for( ; i < _children.size(); i++ )
{
auto node = _children.at(i);
if (node && node->_localZOrder < 0)
node->visit(renderer, _modelViewTransform, flags);
else
break;
}
// self draw
if (visibleByCamera)
this->draw(renderer, _modelViewTransform, flags);
for(auto it=_children.cbegin()+i; it != _children.cend(); ++it)
(*it)->visit(renderer, _modelViewTransform, flags);
}
else if (visibleByCamera)
{
this->draw(renderer, _modelViewTransform, flags);
}
}
void Sprite::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
#if CC_USE_CULLING
// Don't do calculate the culling if the transform was not updated
auto visitingCamera = Camera::getVisitingCamera();
auto defaultCamera = Camera::getDefaultCamera();
if (visitingCamera == defaultCamera) {
_insideBounds = ((flags & FLAGS_TRANSFORM_DIRTY)|| visitingCamera->isViewProjectionUpdated()) ? renderer->checkVisibility(transform, _contentSize) : _insideBounds;
}
else
{
_insideBounds = renderer->checkVisibility(transform, _contentSize);
}
if(_insideBounds)
#endif
{
_trianglesCommand.init(_globalZOrder, _texture->getName(), getGLProgramState(), _blendFunc, _polyInfo.triangles, transform, flags);
renderer->addCommand(&_trianglesCommand);
}
}