2018년 3월 20일 화요일

cocos2dx에서 하나의 Scene에 두개의 카메라 동시에 표현하기

2017. 9. 27. 0:09

다음글은 cocos2dx 에서 하나의 화면에 두개의 카메라를 그릴때 어떻게 해야 할지를 구현하는 방법에 대해 적은 것이다. 
아래 화면을 보면 상단에는 확대된 3D화면이 하단에는 축소된 3D화면이 있는것을 알수있다( 쉽게 생각해서 레이싱 화면에서 앞으로 가는것이보이고, 백미러가 상단에 있는것 생각하면 된다)







위처럼 작업을 하려면 Open GL의 FrameBuffer에 대해서 알아야 하는데기본적인 코드에 대해서는 이전글인 다음의 URL을 참고하면 된다.

위 글을 읽었다면 코드는 간단하게 다음처럼 구현할수 있다. 
일단 3D 레이어를 하나 만들고 ( 위 URL 내용을 먼저 읽을것 )카메라를 2개를 생성을 한다.
// frame buffer object를 만들어서 그것을 카메라와 연결 void HelloWorld::createCameraWithFrameBuffer1() { float width = 600, height = 400; // 큰화면 _camera1 = Camera::createPerspective(95, width / height, 1, 10000); // 10000 : far plane distance , landscape _camera1->setCameraFlag(CameraFlag::USER1); _camera1->setPosition3D(Vec3(0, 0, 0)); _camera1->lookAt(Vec3(0, 0, 0), Vec3(0, 1, 0)); _camera1->retain(); m_layer3D->addChild(_camera1); experimental::Viewport vp; vp._left = 0; vp._bottom = 0; vp._width = 1; vp._height = 1; _camera1->setViewport(vp); auto fbo = experimental::FrameBuffer::create(1, width, height); //render target experimental::RenderTarget* renderTarget = experimental::RenderTarget::create(width, height); fbo->attachRenderTarget(renderTarget); renderTarget->getTexture()->setAliasTexParameters(); //stencil depth auto depth = experimental::RenderTargetDepthStencil::create(width, height); fbo->attachDepthStencilTarget(depth); //_camera1->setDepth(0); _camera1->setFrameBufferObject(fbo); // 그리기 평면 텍스쳐, 스프라이트를 만들어서 붙인다 Texture2D* texture = fbo->getRenderTarget()->getTexture(); auto pDraw = Sprite::createWithTexture(texture); pDraw->setFlippedY(true); // 화면이 뒤집혀 있기 때문에 pDraw->setAnchorPoint(Vec2::ZERO); pDraw->setPosition(Vec2(360, 800 )); addChild(pDraw); } // frame buffer object를 만들어서 그것을 카메라와 연결 void HelloWorld::createCameraWithFrameBuffer2() { float width = 400, height = 300; // 작은 화면 _camera2 = Camera::createPerspective(95, width / height, 1, 10000); // 10000 : far plane distance , landscape _camera2->setCameraFlag(CameraFlag::USER2); _camera2->setPosition3D(Vec3(0, 0, 0)); _camera2->lookAt(Vec3(0, 0, 0), Vec3(0, 1, 0)); _camera2->retain(); m_layer3D->addChild(_camera2); experimental::Viewport vp; vp._left = 0; vp._bottom = 0; vp._width = 1; vp._height = 1; _camera2->setViewport(vp); auto fbo = experimental::FrameBuffer::create(2, width, height); //render target experimental::RenderTarget* renderTarget = experimental::RenderTarget::create(width, height); fbo->attachRenderTarget(renderTarget); renderTarget->getTexture()->setAliasTexParameters(); //stencil depth auto depth = experimental::RenderTargetDepthStencil::create(width, height); fbo->attachDepthStencilTarget(depth); _camera2->setFrameBufferObject(fbo); // 그리기 평면 텍스쳐, 스프라이트를 만들어서 붙인다 Texture2D* texture = fbo->getRenderTarget()->getTexture(); auto pDraw = Sprite::createWithTexture(texture); pDraw->setFlippedY(true); // 화면이 뒤집혀 있기 때문에 pDraw->setAnchorPoint(Vec2::ZERO); pDraw->setPosition(Vec2(360, 100)); addChild(pDraw); }
생성된 두개의 카메라는 한 m_layer3D 에 addChild() 한다.두개의 Layer를 만드는 것이 아니고 , 하나의 레이어에 두개의 Camera를 만드는 것이다. 
그리고 최종적으로 카메라 보여지는 작업을 할때 아래처럼 두개가 보이게 한다.
m_layer3D->setCameraMask((int)CameraFlag::USER1 | (int)CameraFlag::USER2);

화면의 일부분만 3D 영역으로 사용하기 위해 Frame Buffer Object 사용하기

2017. 9. 26. 23:46
COCOS 2D-X에서 3D 작업을 할때 전체 화면이 아닌 화면의 일부 구역에 3D Layer를 만들고 싶을때가 있다. 
아래 그림을 보자
화면 상단에는 3D그리기 영역을 두고 하단에는 UI를 배치한 화면이다.

Open GL에서 제공하는 Frame Buffer를 이용해서 새롭게 그리기평면을 만들어서 작업을 한것이다.Frame Buffer가 무엇인지 알고 싶으면 아래 URL의 내용을 보면 된다.cocos2dx를 사용하면 확실히 알고 있을 필요는 없지만 내용을 읽어보고 대략적 이해는 필요할것 같다.
코딩은 아래처럼 하면 된다.아래 소스는 위에 화면처럼 화면을 만드는것을 전반에 대한 것이 아니고 FrameBufferObjet를 만드는 것에 대해서만 포스트 하는것이다. 

다음의 소스는 전부 cocos 3.14 버전에서 작업이 되었다. ( framebuffer나 viewport 관련 내용이 전부 experimental::인것을 보면 추후 바뀔것으로 예상된다.)
일단 3D레이어를 간단하게 만들고 추가한다
m_layer3D = Layer::create(); // 3D 개체를 올릴 레이어 생성 addChild(m_layer3D);

다음은 카메라를 생성하고 Frame Buffer Object를 만들어서 해당 카메라에 붙이는 부분이다.
void HelloWorld::createCameraWithFrameBuffer2() { float width = 400, height = 300; // 화면을 작게 설정했다. _camera2 = Camera::createPerspective(95, width / height, 1, 10000); _camera2->setCameraFlag(CameraFlag::USER2); _camera2->setPosition3D(Vec3(0, 0, 0)); _camera2->lookAt(Vec3(0, 0, 0), Vec3(0, 1, 0)); _camera2->retain(); m_layer3D->addChild(_camera2); experimental::Viewport vp; vp._left = 0; vp._bottom = 0; vp._width = 1; vp._height = 1; _camera2->setViewport(vp); auto fbo = experimental::FrameBuffer::create(2, width, height); //render target experimental::RenderTarget* renderTarget = experimental::RenderTarget::create(width, height); fbo->attachRenderTarget(renderTarget); renderTarget->getTexture()->setAliasTexParameters(); //stencil depth auto depth = experimental::RenderTargetDepthStencil::create(width, height); fbo->attachDepthStencilTarget(depth); _camera2->setFrameBufferObject(fbo); // 그리기 평면 텍스쳐, 스프라이트를 만들어서 붙인다 Texture2D* texture = fbo->getRenderTarget()->getTexture(); auto pDraw = Sprite::createWithTexture(texture); pDraw->setFlippedY(true); // 화면이 뒤집혀 있기 때문에 pDraw->setAnchorPoint(Vec2::ZERO); pDraw->setPosition(Vec2(360, 100)); addChild(pDraw); }
순서는 아래와 같다1) 카메라를 생성2) viewport를 생성3) framebuffer 생성4) render target 생성5) stencil depth 생성6) 텍스쳐 생성 ( 위에서 만든 render target 을 가져와서 사용 )7) cocos sprite 개체 생성 및 화면에 붙이기
viewport는 left=0, top=0, width=1, height=1 값으로 생성을 했는데그 이유는 cocos2dx의 cccamera.cpp소스의 applyViewport() 함수를 보면 아래처럼 되어 있기 때문이다
void Camera::applyViewport() { glGetIntegerv(GL_VIEWPORT, _oldViewport); if(nullptr == _fbo) { glViewport(getDefaultViewport()._left, getDefaultViewport()._bottom, getDefaultViewport()._width, getDefaultViewport()._height); } else { // 아래처럼 처리를 하기때문에 glViewport(_viewport._left * _fbo->getWidth(), _viewport._bottom * _fbo->getHeight(), _viewport._width * _fbo->getWidth(), _viewport._height * _fbo->getHeight()); } }
fbo에서 설정한 width와 height를 곱해주고 있는데 viewport의 성격과는 조금 맞지 않게 소스 설계가 된것으로 보인다 사실 fbo에서 설정한 width, height의 적용 부분에서 조금 더 고민이 필요했을것으로 보임, cocos2dx의 framebuffer관련 소스들이 전부 experimental  되어 있는데 이것을 보면 아직 작업이 완료된것같지 않다. 3.20버전 (?)정도에서는 이것이 전부 정식으로 추가되지 않을까 기대해 본다

그리고 작성된 카메라를 보기 위해서  아래처럼 하면 된다.
m_layer3D->setCameraMask((int)CameraFlag::USER2);
만약 cocos2dx 에서 기본적인 3D에 대해 공부를 하려면 코코스설치폴더/tests/cpp-tests 에 있는 프로젝트를 돌려보면 된다. 


위작업을 위해 구글링을 통해서 아래 두개사이트의 내용을 참고했다