6.【cocos2d-x 源码分析】:渲染部分的详细分析(上)

对应源码位置: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的该方法为空,这里我们选取 DrawNodeSprite类来看

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的关系。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值