cocos2dx深度檢測與Zorder


cocos2dx里面有兩個渲染隊列,RenderQueue和TransparentRenderQueue。我們可以從Renderer::render()的代碼看到:

void Renderer::render()
{
    //Uncomment this once everything is rendered by new renderer
    //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //TODO: setup camera or MVP
    _isRendering = true;
    
    if (_glViewAssigned)
    {
        //Process render commands
        //1. Sort render commands based on ID
        for (auto &renderqueue : _renderGroups)
        {
            renderqueue.sort();
        }
        visitRenderQueue(_renderGroups[0]);
        flush();
        
        //Process render commands
        //draw transparent objects here, do not batch for transparent objects
        if (0 < _transparentRenderGroups.size())
        {
            _transparentRenderGroups.sort();
            glEnable(GL_DEPTH_TEST);
            visitTransparentRenderQueue(_transparentRenderGroups);
            glDisable(GL_DEPTH_TEST);
        }
    }
    clean();
    _isRendering = false;
}

先繪制的是RenderQueue,沒有啟用深度檢測,之后繪制的TransparentRenderQueue才會啟用深度檢測。

 

一般情況下我們使用的是RenderQueue,它有如下性質:

1. RenderQueue里面的東西完全是按Zorder來決定渲染的先后順序的,Zorder越小越靠前。

2. 若Zorder相等,則按chlid隊列中的順序決定,先加入到隊列的靠前。

3. 可以使用glBlendFunc做顏色混合以及使用2D shader(因為顏色混合需要禁用深度緩存或者把深度緩存設為只讀)

 

若要啟用深度檢測,我們則需要把繪制物體放入TransparentRenderQueue中,方法就是設置onDraw回調的時候,設置command的tansparent為ture,如:

_customCommand.init(1);
_customCommand.func = CC_CALLBACK_0(CCGSpell::onDraw, this, transform, flags);
_customCommand.setTransparent(true);
renderer->addCommand(&_customCommand);

若要將Sprite放入TransparentRenderQueue,則要像上面那樣修改Sprite::draw(),或者在子類中override它。

 

由於TransparentRenderQueue無法使用顏色混合(雖然修改Render模塊可以做到,但一般情況下最好是不要修改cocos2dx的代碼),我們主要使用的還是RenderQueue。

只是使用RenderQueue繪制3D場景的時候經常會遇到如下問題,先貼代碼:

auto marisa = CCGSprite::create("textures/marisa.png");
marisa->setAnchorPoint(Vec2(0.5, 0));
marisa->setPosition3D(Vec3(origin.x, origin.y - 200, 0));
addChild(marisa, 0);

auto mare = CCGSprite::create("textures/mare.png");
mare->setAnchorPoint(Vec2(0.5, 0));
mare->setPosition3D(Vec3(origin.x, origin.y - 200, -1000));
addChild(mare, 0);

auto spr3D = Sprite3D::create("Sprite3DTest/boss1.obj");
spr3D->setScale(20.f);
spr3D->setTexture("Sprite3DTest/boss.png");
spr3D->setPosition3D(Vec3(origin.x, origin.y, -500));
spr3D->runAction(RepeatForever::create(RotateBy::create(3, 360)));
addChild(spr3D);

auto ground = CCGSprite::create("textures/grassHR.jpg");
ground->setPosition3D(Vec3(origin.x, origin.y - 200, 0));
ground->setRotation3D(Vec3(-90, 0, 0));
addChild(ground, 0);

場景中除動畫人物外有4個精靈,其中有一個3D精靈spr3D,以及一個繞x軸旋轉90度的ground。若按上面的代碼會得到如下效果:

與xy平面平行部分都OK,但是與xy平面垂直的ground卻壓在所有精靈上面,這就是沒有深度檢測造成的。

說到這里就不得不說下Zorder與positionZ的關系。

在調用setPosition3D,setPositionZ的時候,其實是做了兩件事:

1. 根據positionZ設置transform,也就是實際渲染在場景中的位置。

2. 用positionZ設置GlobalZOrder的值,而GlobalZOrder則決定了渲染順序。

那么上面問題的原因就是ground的Zorder最大(依次是0, -1000, -500, 0),而且addchild是在Zorder同樣大的marisa之后。

因此是最后渲染出來的。

那么,如果我們不想移動ground的位置,又想把ground移到最后,那么單獨設置一下ground的Zorder便可:

auto ground = CCGSprite::create("textures/grassHR.jpg");
ground->setPosition3D(Vec3(origin.x, origin.y - 200, 0));
ground->setRotation3D(Vec3(-90, 0, 0));
ground->setGlobalZOrder(-2000);
addChild(ground, 0);

修改后的效果如下:

 

而對於GlobalZOrder和LocalZOrder的區別是,GlobalZOrder改變的是物體在整個Scene中的渲染順序,而LocalZOrder改變的只是物體在其父節點下的渲染順序。

由於本例的父節點就是Scene,因此GlobalZOrder和LocalZOrder的效果是相同的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM