zoukankan      html  css  js  c++  java
  • cocos2dx 3.x 相机机制

    一,3.x相机使用方法:

      CCSize winSize=CCDirector::sharedDirector()->getWinSize();

        Camera* camera=Camera::create();

        camera->setCameraFlag(CameraFlag::USER1);

        this->addChild(camera);

        sprite->setCameraMask(2);    //因为2 & CameraFlag::USER1 !=0,所以cameraMask=2与CameraFlag::USER1匹配,sprite将使用上面创建的camera

        Vec3 eyePosOld=camera->getPosition3D();

        Vec3 eyePos=Vec3(x,y,eyePosOld.z);

        camera->setPosition3D(eyePos);

      assert(eyePos.z>0);

        camera->lookAt(Vec3(eyePos.x,eyePos.y,0), Vec3(0, 1, 0));

    注意,这里有个坑:camera->lookAt必须在camera->setPostion3D之后,因为lookAt中有一句

    Vec3::subtract(this->getPosition3D(), lookAtPos, &zaxis),即相减得出相机空间z轴,

    使用了getPosition3D。所以必须先设定好position3D再调lookAt才能得到正确结果。

    参考:

    http://www.cocos2d-x.org/news/344

    cocos2d_tests - Camera3DTest.cpp

    二,3.x与2.x相机的差别:

    cocos2dx 3.x中的相机机制与cocos2dx 2.x中差别很大。

    在2.x中每个节点都有camera,所以每个节点都有自己的局部view矩阵。矩阵堆栈将形如:(proj,view,model,view,model,...)

    而在3.x中相机不是隶属于节点的,而是全局的,所以节点没有自己的局部view矩阵,只有一个起始的view矩阵,即矩阵堆栈将形如:(proj,view,model,model,model,...)

    三,3.x相机实现原理:

    前面已经看到,使用3.x相机关键有三点:

    1,用户自己创建相机并指定cameraFlag。

    2,为节点指定与cameraFlg按位与不为0的cameraMask,则此节点即使用此相机。

    3,相机可addChild到任意一个节点(尽量使用根节点)。

    自定义相机的cameraFlag可取USER1~USER8,定义如下:

    enum class CameraFlag

    {

        DEFAULT = 1,

        USER1 = 1 << 1,

        USER2 = 1 << 2,

        USER3 = 1 << 3,

        USER4 = 1 << 4,

        USER5 = 1 << 5,

        USER6 = 1 << 6,

        USER7 = 1 << 7,

        USER8 = 1 << 8,

    };

    Node::setCameraMask(unsigned short mask, bool applyChildren)用来指定cameraMask,其第二个参数用来指明子节点是否递归地使用相同的mask,默认为true。要特别注意:node->setCameraMask(mask,true)只能使node的当前所有子节点的cameraMask设置为mask,但在此之后新添加的子节点则不会受影响(仍然是默认camera),需要记着手动进行设置,这里很容易被坑。又比如在Layer::init()里开头写了一句this->setCameraMask(mask,true)紧接着加了些子节点,那么要意识到这些子节点是不会被设置的,要么对每个子节点都手动调一次setCameraMask,要不就把this->setCameraMask写到init函数的末尾。

    不管camera被addChild到哪个节点,其都会被添加到Scene的_cameras成员中。如果Scene中任何一个节点都没有添加用户自定义相机,则scene的_cameras成员中只含有一个默认相机,就是Scene::_defaultCamera所引用的相机;如果Scene中某些节点添加了用户自定义相机,则_cameras[0]是默认相机,其余元素是用户相机。

    至于camera是如何被添加到_cameras中的,逻辑在Camera的onEnter函数中,如下:

    void Camera::onEnter()

    {

        if (_scene == nullptr)

        {

            auto scene = getScene();

            if (scene)

                setScene(scene);

        }

        Node::onEnter();

    }

    void Camera::setScene(Scene* scene)

    {

        if (_scene != scene)

        {

            //remove old scene

            if (_scene)

            {

                auto& cameras = _scene->_cameras;

                auto it = std::find(cameras.begin(), cameras.end(), this);

                if (it != cameras.end())

                    cameras.erase(it);

                _scene = nullptr;

            }

            //set new scene

            if (scene)

            {

                _scene = scene;

                auto& cameras = _scene->_cameras;

                auto it = std::find(cameras.begin(), cameras.end(), this);

                if (it == cameras.end())

                    _scene->_cameras.push_back(this);

            }

        }

    }

    下面解释3.x是如何实现相机与节点通过cameraFlag/cameraMask值进行配对儿的:

    只需看Scene::render(Renderer* renderer)函数:

    void Scene::render(Renderer* renderer)

    {

        auto director = Director::getInstance();

        Camera* defaultCamera = nullptr;

        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, Mat4::IDENTITY, 0);

            renderer->render();

            

            director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);

        }

        //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, Mat4::IDENTITY, 0);

            renderer->render();

            

            director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);

        }

        Camera::_visitingCamera = nullptr;

    }

    上面函数逻辑看起来写得很墨迹,其实意思是:

    对于这个Scene,用其_cameras中各相机分别render一遍。(不过它把默认相机的那遍render强制放到最后了,正是这个举动使代码变墨迹的)。

    针对上面逻辑,需澄清下面两个问题:

    问题1,关于“_cameras中各相机分别render一遍”:

    问题来了,假如_cameras里有十个相机,那么Scene就要render十遍,这不坑爹吗?其实也不算很坑爹,因为每遍render只render与当前相机匹配的节点,所以总起来仍然是每个节点render一次(除非用户人为地为某节点创建了两个cameraFlg与此节点cameraMask相匹配的相机)。

    从哪里可以看出每次render只render与当前相机匹配的节点呢?下面Node::visit(...)中的bool visibleByCamera = isVisitableByVisitingCamera()一句就是这个作用。

    void Node::visit(Renderer* renderer, const Mat4 &parentTransform, uint32_t parentFlags)

    {

        // quick return if not visible. children won't be drawn.

        if (!_visible)

        {

            return;

        }

        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)

                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;

    }

    bool Node::isVisitableByVisitingCamera() const

    {

        auto camera = Camera::getVisitingCamera();

        bool visibleByCamera = camera ? (unsigned short)camera->getCameraFlag() & _cameraMask : true;

        return visibleByCamera;

    }

    问题2,关于“把默认相机的那遍render强制放到最后”:

    不知道cocos团队为何要这样做,我还由于这个被坑了一次,经过如下:

    我在layer上加了一个background sprite (zOrder=0),然后又加了一个layer2 (zOrder=1),其中layer2使用了自定义相机camera2。
    本想让layer2显示在background sprite前面,但显示结果却是background会挡住layer2。
    开始不解,然后去看引擎代码,发现此结果是Scene::render(...)函数的逻辑造成:
    Scene::render(...)的逻辑是先用自定义相机去渲染使用它的相应节点,最后再用default相机渲染使用它的相应节点。
    所以导致无论zOrder怎么设,都会先渲染使用自定义相机的layer2,然后再渲染使用默认相机的background sprite。
    于是为了能够先渲染background sprite再渲染layer2,我只好又定义了一个camera1,让background sprite改为使用camera1(并且保证camera1是在camera2之前加到Scene:: _cameras中,即camera1先于camera2加到场景中),这样渲染效果就正常了。

     

  • 相关阅读:
    Win10+Ubuntu18.04 UEFI启动模式SSD+HDD
    Chap1:全景图[Computer Science Illuminated]
    [IDE] ECLIPSE取消自动更新
    [Unit Test] Unit Test Brief Introduction
    [ English ] 俚语 “Ping me=打我电话”
    Some Useful Resources for the Future Usage
    python错误记录
    django-用户认证模型
    Djiango-富文本编辑器
    Djiango-建立模型抽象基类
  • 原文地址:https://www.cnblogs.com/wantnon/p/4158135.html
Copyright © 2011-2022 走看看