对应源码位置:cocos2d-x-3.3\cocos\render\*
主循环中的渲染部分
下面代码选自 void Director::drawScene()
if (_runningScene)
{
//clear draw stats
_renderer->clearDrawStats();
//render the scene
//从场景中 获取各种渲染命令
_runningScene->render(_renderer);
_eventDispatcher->dispatchEvent(_eventAfterVisit);
}
// draw the notifications node
if (_notificationNode)
{
_notificationNode->visit(_renderer, Mat4::IDENTITY, 0);
}
if (_displayStats)
{
showStats();
}
//真正的渲染
_renderer->render();
void Scene::render(Renderer* renderer)
的介绍
void Scene::render(Renderer* renderer)
{
auto director = Director::getInstance();
Camera* defaultCamera = nullptr;
//这里 getNodeToParentTransform 就是 根据 节点的各种属性 位置 缩放 旋转 凑出这个矩阵
//计算 节点的 模型矩阵 这里叫做 transform
//当子类的 模型矩阵 左乘父类的 模型矩阵 就是相当于在局部空间上 在合并父级节点的变换
//另外 node类的 实现上 为了效率 都设置了标志位 只有节点位置等属性变化了 才重新计算
//这个 矩阵。
const auto& transform = getNodeToParentTransform();
//对每个 camera 遍历操作
for (const auto& camera : _cameras)
{
Camera::_visitingCamera = camera;
if (Camera::_visitingCamera->getCameraFlag() == CameraFlag::DEFAULT)
{
//
defaultCamera = Camera::_visitingCamera;
continue;
}
//设置 投影矩阵
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, Camera::_visitingCamera->getViewProjectionMatrix());
//visit the scene
//下面详细展开
visit(renderer, transform, 0);
renderer->render();
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
}
//默认 camera
//draw with default camera
if (defaultCamera)
{
Camera::_visitingCamera = defaultCamera;
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, Camera::_visitingCamera->getViewProjectionMatrix());
//visit the scene
visit(renderer, transform, 0);
renderer->render();
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
}
Camera::_visitingCamera = nullptr;
}
对 visit(renderer, transform, 0);
现在具体看看:
void Node::visit(Renderer* renderer, const Mat4 &parentTransform, uint32_t parentFlags)
{
// quick return if not visible. children won't be drawn.
//如果无法看不见 后续子节点 也不应该 被看到
if (!_visible)
{
return;
}
//主要 根据 parentFlags 判断 当父节点 的位置 等属性变化 即父节点 模型矩阵 变化
//则更新自己的 模型矩阵
uint32_t flags = processParentFlags(parentTransform, parentFlags);
// IMPORTANT:
// To ease the migration to v3.0, we still support the Mat4 stack,
// but it is deprecated and your code should not rely on it
Director* director = Director::getInstance();
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewTransform);
bool visibleByCamera = isVisitableByVisitingCamera();
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)
//递归的重点都是 按序调用node的 draw方法 继续深入
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);
}
//恢复现场
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
// FIX ME: Why need to set _orderOfArrival to 0??
// Please refer to https://github.com/cocos2d/cocos2d-x/pull/6920
// reset for next frame
// _orderOfArrival = 0;
}
对 this->draw(renderer, _modelViewTransform, flags);
具体分析 node的该方法为空,这里我们选取 DrawNode
和Sprite
类来看
void DrawNode::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
if(_bufferCount)
{
_customCommand.init(_globalZOrder);
_customCommand.func = CC_CALLBACK_0(DrawNode::onDraw, this, transform, flags);
//可以看出来 这部分 都是 使用了 addCommand 将绘制命令加到 render中 此处未曾看到
//与opengl相关内容 显然 都是将渲染命令的具体 封装到 RenderCommand
renderer->addCommand(&_customCommand);
}
if(_bufferCountGLPoint)
{
_customCommandGLPoint.init(_globalZOrder);
_customCommandGLPoint.func = CC_CALLBACK_0(DrawNode::onDrawGLPoint, this, transform, flags);
renderer->addCommand(&_customCommandGLPoint);
}
if(_bufferCountGLLine)
{
_customCommandGLLine.init(_globalZOrder);
_customCommandGLLine.func = CC_CALLBACK_0(DrawNode::onDrawGLLine, this, transform, flags);
renderer->addCommand(&_customCommandGLLine);
}
}
void Sprite::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
// Don't do calculate the culling if the transform was not updated
//这里先判断 是否可见 再来选择加入 渲染命令
_insideBounds = (flags & FLAGS_TRANSFORM_DIRTY) ? renderer->checkVisibility(transform, _contentSize) : _insideBounds;
if(_insideBounds)
{
_quadCommand.init(_globalZOrder, _texture->getName(), getGLProgramState(), _blendFunc, &_quad, 1, transform);
renderer->addCommand(&_quadCommand);
}
}
具体看看 checkVisibility
的实现,他负责判断 当前对象矩形 是否在最后投影到屏幕空间后 是否还包含在屏幕之中(相交的情况,留给opengl处理)。这样可以在应用程序阶段阶段,直接不添加渲染命令,减轻opengl负担。
//关于代码的 理解 可参考 https://www.jianshu.com/p/aacd6872ca8c 这里偷来一张图
//主要意思为 把代表当前对象的 矩形变化到 屏幕空间
//然后 判断当前矩形是否完全不在屏幕上
//采用的方式 可以实现为将 屏幕空间代表的矩形 四周增加半个 对象矩形的 半长宽
//再判断 对象矩形中心是否在 新屏幕空间矩形的内部即可。
//下面代码 意思和这个差不多 只是反着写的 无所谓
bool Renderer::checkVisibility(const Mat4 &transform, const Size &size)
{
auto scene = Director::getInstance()->getRunningScene();
// only cull the default camera. The culling algorithm is valid for default camera.
if (scene && scene->_defaultCamera != Camera::getVisitingCamera())
return true;
// half size of the screen
Size screen_half = Director::getInstance()->getWinSize();
screen_half.width /= 2;
screen_half.height /= 2;
float hSizeX = size.width/2;
float hSizeY = size.height/2;
Vec4 v4world, v4local;
v4local.set(hSizeX, hSizeY, 0, 1);
transform.transformVector(v4local, &v4world);
// center of screen is (0,0)
v4world.x -= screen_half.width;
v4world.y -= screen_half.height;
// convert content size to world coordinates
float wshw = std::max(fabsf(hSizeX * transform.m[0] + hSizeY * transform.m[4]), fabsf(hSizeX * transform.m[0] - hSizeY * transform.m[4]));
float wshh = std::max(fabsf(hSizeX * transform.m[1] + hSizeY * transform.m[5]), fabsf(hSizeX * transform.m[1] - hSizeY * transform.m[5]));
// compare if it in the positive quadrant of the screen
float tmpx = (fabsf(v4world.x)-wshw);
float tmpy = (fabsf(v4world.y)-wshh);
bool ret = (tmpx < screen_half.width && tmpy < screen_half.height);
return ret;
}
最后
这里可以看到 渲染部分首先按序遍历整个场景,要绘制的节点将绘制命令,添加到render
中,由 render
进行具体的渲染工作。
下一篇 具体分析 RenderCommand
以及 其与 OpenGL
的关系。