cocos-2dx 渲染(3)

上一章跟踪了 cocos2d-x调用CCScene::draw()的过程,直到访问子节点以及渲染,本章就具体看看几个类的渲染。

void Scene::render(Renderer* renderer)
{
    auto director = Director::getInstance();
    Camera* defaultCamera = nullptr;
    const auto& transform = getNodeToParentTransform();
    if (_cameraOrderDirty)
    {
        stable_sort(_cameras.begin(), _cameras.end(), camera_cmp);
        _cameraOrderDirty = false;
    }

    for (const auto& camera : _cameras)
    {
        if (!camera->isVisible())
            continue;

        Camera::_visitingCamera = camera;
        if (Camera::_visitingCamera->getCameraFlag() == CameraFlag::DEFAULT)
        {
            defaultCamera = Camera::_visitingCamera;
        }
        //矩阵操作
        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();</strong>
        //
        director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
    }

    Camera::_visitingCamera = nullptr;
}

visit递归访问该场景的子节点,传递transform(当前节点相对父节点的变换矩阵)和flags(该节点transform是否改变和大小变化)给子节点,子节点根据父节点的transform以及自身的参数来渲染,通过2种方式:直接执行opengl命令和将opengl传递给render对象
renderer->render()会执行渲染对象render的opengl渲染命令,对象render通过队列来存储opengl渲染命令。
通过一下类的draw(),可以很明显看出来。

0.CCSprite类

void Sprite::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
  //根据父节点flags(是否变化,位移、大小),自身的transform flag以及是否可见来判定是否渲染
#if CC_USE_CULLING
     _insideBounds = (flags & FLAGS_TRANSFORM_DIRTY) ? renderer->checkVisibility(transform, _contentSize) : _insideBounds;

    if(_insideBounds)
#endif
    {
        //初始化渲染参数
        _quadCommand.init(_globalZOrder, _texture->getName(), getGLProgramState(), _blendFunc, &_quad, 1, transform, flags);
        //将渲染命令添加到队列中
        renderer->addCommand(&_quadCommand);

#if CC_SPRITE_DEBUG_DRAW
        _debugDrawNode->clear();
        Vec2 vertices[4] = {
            Vec2( _quad.bl.vertices.x, _quad.bl.vertices.y ),
            Vec2( _quad.br.vertices.x, _quad.br.vertices.y ),
            Vec2( _quad.tr.vertices.x, _quad.tr.vertices.y ),
            Vec2( _quad.tl.vertices.x, _quad.tl.vertices.y ),
        };
        _debugDrawNode->drawPoly(vertices, 4, true, Color4F(1.0, 1.0, 1.0, 1.0));
#endif //CC_SPRITE_DEBUG_DRAW
    }
}

1.SpriteBatchNode类

void SpriteBatchNode::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
    // Optimization: Fast Dispatch
   //判断是否有quads(V3F_C4B_T2F_Quad结构,记录顶点,纹理和颜色3种数据)
    if( _textureAtlas->getTotalQuads() == 0 )
    {
        return;
    }


    for (const auto &child : _children)
    {
#if CC_USE_PHYSICS
        auto physicsBody = child->getPhysicsBody();
        if (physicsBody)
        {
            child->updateTransformFromPhysics(transform, flags);
        }
#endif
       //有子节点的情况下,先更新子节点的transform
        child->updateTransform();
    }
    //opengl渲染,初始化渲染命令
    _batchCommand.init(_globalZOrder, getGLProgram(), _blendFunc, _textureAtlas, transform, flags);
    //添加渲染函数到render对象
    renderer->addCommand(&_batchCommand);
}

为什么要先调用子类updateTransform(),以后有机会再谈。这里谈下Sprite和SpriteBatchNode的关系SpriteBatchNode一般用于多个Sprite使用相同的纹理,可以将这些Sprite加入SpriteBatchNode,这样渲染的时候就仅仅需要渲染一次.Node在visit的时候,针对其子节点,会先排序,排完后仅递归访问zorder<0的子节点。而排序函数会使用变量变量_reorderChildDirty标志位。当Sprite加入SpriteBatchNode的时候,会处理次标志位,这样加入SpriteBatchNode的Sprite就不会递归visit了。
可以看到SpriteBatchNode的渲染方式是通过添加Opengl的命令到render进程的。

2.Layer类渲染函数没有具体的实现,因为它仅仅作为个承载类,用来处理触摸事件以及对scene分层的作用。

3.LayerColor类

void LayerColor::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
   //这里的渲染有点不同,它有个回调
    _customCommand.init(_globalZOrder, transform, flags);
    _customCommand.func = CC_CALLBACK_0(LayerColor::onDraw, this, transform, flags);

    renderer->addCommand(&_customCommand);

    for(int i = 0; i < 4; ++i)
    {
        Vec4 pos;
        pos.x = _squareVertices[i].x; pos.y = _squareVertices[i].y; pos.z = _positionZ;
        pos.w = 1;
        _modelViewTransform.transformVector(&pos);
        _noMVPVertices[i] = Vec3(pos.x,pos.y,pos.z)/pos.w;
    }
}
void LayerColor::onDraw(const Mat4& transform, uint32_t flags)
{
    //glUseProgram(program)
    getGLProgram()->use();
    //设置uniform矩阵的
    getGLProgram()->setUniformsForBuiltins(transform);
    //打开OPENGL的状态
    GL::enableVertexAttribs( GL::VERTEX_ATTRIB_FLAG_POSITION | GL::VERTEX_ATTRIB_FLAG_COLOR );

    //
    // Attributes
    //
#ifdef EMSCRIPTEN
    //设置缓冲数据
    setGLBufferData(_noMVPVertices, 4 * sizeof(Vec3), 0);
    //位置渲染
    glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, 0, 0);
     //设置数据
    setGLBufferData(_squareColors, 4 * sizeof(Color4F), 1);
    //颜色渲染
    glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_FLOAT, GL_FALSE, 0, 0);
#else
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, 0, _noMVPVertices);
    glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_FLOAT, GL_FALSE, 0, _squareColors);
#endif // EMSCRIPTEN
    //颜色混合
    GL::blendFunc( _blendFunc.src, _blendFunc.dst );
   //从数组中读取顶点数据
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    // auto __renderer__ = Director::getInstance()->getRenderer();     
     //   __renderer__->addDrawnBatches(__drawcalls__);                   
     //   __renderer__->addDrawnVertices(__vertices__);
    CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1,4);
}

上面的回调函数是直接调用openglAPI 进行渲染了。
补充说明opengl相关的东西:
Node会存储一个GLProgramState指针_glProgramState,该指针持有其成员指针GLPromgram的“状态”(我理解成值,即uniforms和attributes)。
uniform变量是外部application程序传递给(vertex和fragment)shader的变量,一般用来表示:变换矩阵,材质,光照参数和颜色等信息。
attribute变量是只能在vertex shader中使用的变量,一般用来表示一些顶点的数据,如:顶点坐标,法线,纹理坐标,顶点颜色等。

4.Label
void Label::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
    // Don't do calculate the culling if the transform was not updated
    bool transformUpdated = flags & FLAGS_TRANSFORM_DIRTY;
#if CC_USE_CULLING
    _insideBounds = transformUpdated ? renderer->checkVisibility(transform, _contentSize) : _insideBounds;

    if(_insideBounds)
#endif
    {
        _customCommand.init(_globalZOrder, transform, flags);
        //渲染回调
        _customCommand.func = CC_CALLBACK_0(Label::onDraw, this, transform, transformUpdated);

        renderer->addCommand(&_customCommand);
    }
}
通过代码可以看出,Label的渲染方式与ColorLayer类似,重点在于起回调函数onDraw.
oid Label::onDraw(const Mat4& transform, bool transformUpdated)
{
    CC_PROFILER_START("Label - draw");

    // Optimization: Fast Dispatch
    if( _batchNodes.size() == 1 && _textureAtlas->getTotalQuads() == 0 )
    {
        return;
    }
    //获取Label的GLProgram
    auto glprogram = getGLProgram();
    //glUseProgram(program), installs the program object specified by program as part //of current rendering state.安装program作为当前渲染状态
    glprogram->use();
    GL::blendFunc( _blendFunc.src, _blendFunc.dst );

    if (_currentLabelType == LabelType::TTF)
    {
        glprogram->setUniformLocationWith4f(_uniformTextColor,
            _textColorF.r,_textColorF.g,_textColorF.b,_textColorF.a);
    }
//描边或者发光效果
    if (_currLabelEffect == LabelEffect::OUTLINE || _currLabelEffect == LabelEffect::GLOW)
    {
         glprogram->setUniformLocationWith4f(_uniformEffectColor,
             _effectColorF.r,_effectColorF.g,_effectColorF.b,_effectColorF.a);
    }
    //阴影效果
    if(_shadowEnabled && _shadowBlurRadius <= 0)
    {
        drawShadowWithoutBlur();
    }
    //设置uniforms矩阵
    glprogram->setUniformsForBuiltins(transform);
   //下面的2个for循环类似域Sprite和SpirteBatchNode的渲染
    for(const auto &child: _children)
    {
        if(child->getTag() >= 0)
            child->updateTransform();
    }

    for (const auto& batchNode:_batchNodes)
    {
        batchNode->getTextureAtlas()->drawQuads();
    }

    CC_PROFILER_STOP("Label - draw");
}

渲染暂时就写这么多,关于更多的类,大家可以自己进代码看下,以及更加详细的调用都可以直接看到。opengl的使用以及原理可以查看下面的网址:
http://blog.csdn.net/wl_soft50/article/details/7916720
http://blog.csdn.net/candycat1992/article/details/39676669
http://www.songho.ca/opengl/gl_transform.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值