'Ogre3D 삽질란/Intermediate Tutorial 7'에 해당되는 글 3건

  1. 2009.01.15 중급 튜토리얼 7-1
  2. 2009.01.15 중급 튜토리얼 7-2
  3. 2009.01.15 중급 튜토리얼 7-3 (마지막)

중급 튜토리얼 7-1

Ogre3D 삽질란/Intermediate Tutorial 7 2009. 1. 15. 23:09

중급 튜토리얼 7 (번역 : n_Sys)

중급 튜토리얼 7: 텍스쳐에 렌더링하기

튜토리얼 진행에 있어서 문제가 생긴다면 Help Forum 문의하세요.

목차

                               1 소개

                                       1.1 초기코드

                               2 텍스쳐에 렌더링하기

                                       2.1 render texture 하기

                                       2.2 텍스쳐를 파일로 저장하기

                               3 미니스크린 구현하기

                                       3.1 소개

                                       3.2 하기

                                       3.3 부터 하기

                               4 RenderTargetListener

                                       4.1 소개

                                       4.2 RenderTargetListener 하기

                               5 RTTs and shaders

                                       5.1 RTT 이더 내기

                               6

 

소개

이번 튜토리얼에서는 텍스쳐에 렌더링 하는 기본기를 가르쳐 드릴 계획입니다. 기법은 셰이더를 써서 구현되는 모션블러와 같은 다양한 특수효과와 함께 사용됩니다.

텍스쳐로 렌더링("RTT")하는 기법은 단순한 원리입니다. 출력되는 렌더링 데이터를 렌더윈도우가 아닌 텍스쳐로 바로 보냅니다. 텍스쳐는 하드디스크상의 일반 텍스쳐로도 사용될 있습니다.

 

사용자 삽입 이미지

 

튜토리얼에 대한 코드는 여기 찾을 있습니다.

 

초기코드

프로젝트에 cpp파일을 하나 생성하고 다음 코드를 추가하세요 :

#include "ExampleApplication.h"

 

class RTTListener : public ExampleFrameListener

{

public:

   RTTListener(RenderWindow *win, Camera *cam, SceneNode *sn)

        : ExampleFrameListener(win, cam), mPlaneNode(sn)

   {

   }

 

   bool frameStarted(const FrameEvent& evt)

   {

        mPlaneNode->yaw(Radian(evt.timeSinceLastFrame));

              

        return ExampleFrameListener::frameStarted(evt);

   }

 

protected:

   SceneNode      *mPlaneNode;

};

 

class RTTApplication : public ExampleApplication

{

protected:

   MovablePlane   *mPlane;

   Entity         *mPlaneEnt;

   SceneNode      *mPlaneNode;

 

   void createScene()

   {

       // Set ambient light

       mSceneMgr->setAmbientLight(ColourValue(0.2f, 0.2f, 0.2f));

 

       // Create a light

       Light* l = mSceneMgr->createLight("MainLight");

       l->setPosition(20, 80, 50);

 

       // Position the camera

       mCamera->setPosition(60, 200, 70);

       mCamera->lookAt(0, 0, 0);

 

        // Create a material for the plane (just a simple texture, here grass.jpg)

       MaterialPtr mat = MaterialManager::getSingleton().create("PlaneMat", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);

       TextureUnitState* t = mat->getTechnique(0)->getPass(0)->createTextureUnitState("grass_1024.jpg");

              

        // Create a simple plane

       mPlane = new MovablePlane("Plane");

       mPlane->d = 0;

       mPlane->normal = Vector3::UNIT_Y;

       MeshManager::getSingleton().createPlane("PlaneMesh", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,

           *mPlane, 120, 120, 1, 1, true, 1, 1, 1, Vector3::UNIT_Z);

       mPlaneEnt = mSceneMgr->createEntity("PlaneEntity", "PlaneMesh");

       mPlaneEnt->setMaterialName("PlaneMat");

 

       // Attach the plane to a scene node

       mPlaneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();

       mPlaneNode->attachObject(mPlaneEnt);

   }

 

   // Create a new frame listener

   void createFrameListener()

   {

       mFrameListener = new RTTListener(mWindow, mCamera, mPlaneNode);

       mRoot->addFrameListener(mFrameListener);

   }

};

 

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

#define WIN32_LEAN_AND_MEAN

#include "windows.h"

#endif

 

#ifdef __cplusplus

extern "C" {

#endif

 

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

   INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT)

#else

   int main(int argc, char **argv)

#endif

   {

       // Create application object

       RTTApplication app;

 

       try {

           app.go();

       } catch(Exception& e) {

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

           MessageBoxA(NULL, e.getFullDescription().c_str(),

               "An exception has occurred!", MB_OK | MB_ICONERROR | MB_TASKMODAL);

#else

           std::cerr << "An exception has occurred: " << e.getFullDescription();

#endif

       }

 

       return 0;

   }

 

#ifdef __cplusplus

}

#endif

컴파일 실행시켜보세요. Y축을 따라서 회전하는 단순한 평판이 보일 입니다.

 

사용자 삽입 이미지

 

:

중급 튜토리얼 7-2

Ogre3D 삽질란/Intermediate Tutorial 7 2009. 1. 15. 23:06

텍스쳐에 렌더링하기

render texture 생성하기

우선 텍스쳐생성부터 해야 합니다.

Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().createManual("RttTex", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_2D, mWindow->getWidth(), mWindow->getHeight(), 0, PF_R8G8B8, TU_RENDERTARGET);

첫번째 매개변수는 텍스쳐 이름인데 보통 RttTex 이름지어 집니다. 두번째는 리소스그룹, 세번째는 텍스쳐종류(지금은 2D 텍스쳐), 네번째와 다섯번째는 텍스쳐의 폭과 높이입니다. 그리고 밉맵의 수와 사용하게될 텍스쳐 포멧, 사용목적에 따른 flag값들을 설정해 줍니다. 텍스쳐포멧에는 매우 다양한 타입들이 존재합니다. 24비트 RGB 텍스쳐를 구성하는 가장 단순한 형태는 PF_R8G8B8 입니다. 만약 투명값을 가지는 알파채널이 필요하다면 PF_R8G8B8A8 값이 적당할 입니다.

몇가지 매개변수에 쓰일 정보값과 나중에 RenderTargetListener 넣을 현재 텍스쳐에 대한 render target 구해야 합니다.

Ogre::RenderTexture *renderTexture = texture->getBuffer()->getRenderTarget();

 

renderTexture->addViewport(mCamera);

renderTexture->getViewport(0)->setClearEveryFrame(true);

renderTexture->getViewport(0)->setBackgroundColour(ColourValue::Black);

renderTexture->getViewport(0)->setOverlaysEnabled(false);

render texture 구했다면 뷰포트를 추가시킬 차례입니다. RenderTexture 표현될 컨텐츠로 채워질 뷰포트 입니다. 뷰포트가 프레임마다 검은색으로 지워지도록 설정하고 뷰포트위에 아무것도 올라오지 않도록 하기위해서 오버레이속성을 비활성화 시킵니다.

 

텍스쳐를 파일로 저장하기

이제 여기서 한가지 검사를 하고 넘어가 봅시다. 방금전 앞에서 생성시켰던 RenderTexture 파일로 저장하는 입니다. RenderTexture클래스는 RenderTarget으로부터 파생되었기 때문에 내부컨텐츠를 파일로 저장시키는 함수사용이 가능합니다(RenderWindow에서도 똑같이 적용됩니다). 파일로 저장시키기전에 RenderTexture 업데이트 시켜줘야 함을 유의하세요. update() 함수를 사용하거나 setAutoUpdate() 함수를 호출하여 자동으로 업데이트가 수행되도록 있습니다.

// Either this way

renderTexture->setAutoUpdated(true);

// or this way

renderTexture->update();

renderTexture->writeContentsToFile("start.png");

프로그램을 실행하고나면 exe파일이 위치한 디렉토리에 png확장자를 가진 파일이 보일 입니다. 파일은 화면상에 보여졌던 컨텐츠를 담고 있습니다(이번에 사용되어졌던 평면텍스쳐입니다).

 

미니스크린 구현하기

간략한 소개

프로그램상에서 화면 오른쪽 하단에 작은 미니스크린을 구현할 차례입니다. Rectangle2D 장면에 추가하여 텍스쳐로 생성했던 RenderTexture 출력하도록 만듭니다. 결과적으로 같은장면을 2 보게 됩니다: 한번은 평소처럼 RenderWindow 출력하고 두번째는 Rectangle2D위에 텍스쳐로 출력합니다.

방법을 통해서 게임속에서의 TV화면(또는 비슷한) 구현이 가능합니다. 레벨의 어느 부분에 카메라를 배치하고 앞에서 소개드린 방법처럼 RenderTexture 생성한 다음 그것을 TV화면에 출력시키면 됩니다.

 

미니스크린용 사각영역 설정하기

Ogre::Rectangle2D 생성은 무척 간단합니다:

Ogre::Rectangle2D *miniScreen = new Ogre::Rectangle2D(true);

miniScreen->setCorners(0.5, -0.5, 1.0, -1.0);

miniScreen->setBoundingBox(AxisAlignedBox(-100000.0*Vector3::UNIT_SCALE, 100000.0*Vector3::UNIT_SCALE));

첫번째 라인에서 텍스쳐좌표 속성값을 true 설정합니다. 그리하여 나중에 사각형에 텍스쳐를 맵핑할 있게 됩니다. 두번째 라인에서는 사각형의 꼭지점 좌표를 설정합니다. 왼쪽은 -1.0, 오른쪽은 +1.0, 상단은 +1.0, 하단은 -1.0입니다. 그래서 사각형은 결과적으로 프로그램용 윈도우의 오른쪽 하단에 위치하게 됩니다. 그리고 사각형에 엄청나게 바운딩박스를 적용시켜 사각형의 장면노드를 바라보지 않더라도 항상 그려질 있도록 만듭니다. 이렇게 수동으로 크기를 설정하는 대신에 AxisAlignedBox::setInfinite() 사용해도 됩니다. 하지만 과거에 버그가 있던던 사례가 있었습니다. 이렇게 수동으로 박스를 설정하는것이 가장 안정된 방법입니다.

이제 미니스크린용 사각형이 생성되었습니다. 이제 SceneNode attach 차례입니다. 별로 생소한 코드는 없습니다.

Ogre::SceneNode *miniScreenNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("MiniScreenNode");

miniScreenNode->attachObject(miniScreen);

시점에서 실행시키면 프로그램 윈도우의 오른쪽 하단에 흰색 사각형이 자리잡고 있음을 있을 입니다.

사용자 삽입 이미지


재료들로부터 재질 생성하기

다음단계는 앞에서 생성했던 RenderTexture 출력하는 입니다. 이제 재질을 생성해야 합니다. 2가지 방법이 있는데 재질스크립트를 사용하거나 실행도중에 코드로 직접적으로 생성시키는 방법이 있습니다. 마지막 함수는 필요한 모든것을 하나의 파일에서 완료시키기 위해 호출되어질 입니다.

MaterialPtr material = MaterialManager::getSingleton().create("RttMat", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);

Ogre::Technique *technique = material->createTechnique();

technique->createPass();

material->getTechnique(0)->getPass(0)->setLightingEnabled(false);

material->getTechnique(0)->getPass(0)->createTextureUnitState("RttTex");

첫번째 라인은 재질을 생성하고 technique pass 2줄에 걸쳐서 추가합니다. 세번째 라인은 미니스크린에서 실제 텍스쳐가 색상이 어둡게 보이는 것을 방지하기 위해서 조명효과를 비활성화 시킵니다. 마지막 라인은 미리 생성해 두었던 RenderTexture 이용해 새로운 TextureUnitState 생성합니다. 이제 재질을 미니스크린으로 적용시킬 준비가 되었습니다.

miniScreen->setMaterial("RttMat");

실행시켜보면 예측하지 못한 결과가 보일 입니다: 미니스크린안에 미니스크린이 들어있습니다! 이를 해결하기위해 RenderTargetListener 소개합니다.

 

RenderTargetListener 클래스의 사용

간략한 소개

대부분의 경우 RTT위에는 장면상의 몇몇 객체들만 표시될 입니다. 지금 하려고 하는 경우에는 화면상에서 미니스크린이 아직 표시되지 않은상태의 화면, 미니스크린에 표시될 하나의 텍스쳐만 필요합니다. 그렇기 때문에 화면이 RenderTexture 저장되기 전에 미니스크린을 숨겨줘야 합니다. 바로 이러한 역할을 RenderTargetListener 담당합니다. RenderTargetListener클래스는 2개의 중요한 함수를 가지고 있습니다: preRenderTargetUnpdate() postRenderTargetUpdate()입니다. 함수 이름에서 추측할 있듯 첫번째 함수는 RenderTexture 채워지기 전에(여기서 미니스크린을 숨길 있습니다) 자동으로 호출되는 반면 두번째 함수는 RenderTexture 채워진 이후에 호출됩니다(여기서 미니스크린을 다시 보이게 합니다).

 

RenderTargetListener 구현하기

RenderTargetListener 구현하는건 간단합니다. 우선은 application class RenderTargetListener클래스로부터 상속받게끔 하여 자체적으로 listener 포함하게끔 만듭니다. 다음에 pre- postRenderTargetUpdate() 함수를 이용해서 미니스크린을 켜고 끕니다. 그러고 나면 어플리케이션 클래스는 기본적으로 다음의 형태를 띄게 됩니다 :

class RTTApplication : public ExampleApplication, public RenderTargetListener

{

protected:

   SceneNode           *mPlaneNode;

   Rectangle2D         *mMiniScreen;

 

   void createScene()

   {

       [...]

   }

 

   void createFrameListener()

   {

       [...]

   }

 

   void preRenderTargetUpdate(const RenderTargetEvent &evt)

   {

        mMiniScreen->setVisible(false);

   }

 

   void postRenderTargetUpdate(const RenderTargetEvent &evt)

   {

        mMiniScreen->setVisible(true);

   }

};

이제 마지막단계인 RenderTexture listener 추가하는 일이 남았습니다. 어플리케이션 클래스가 RenderTargetListener에서 상속되었으므로 매개변수로 넘기는 것이 가능합니다.

renderTexture->addListener(this);

되었습니다. 이제 프로그램에 미니스크린이 추가되었습니다. 간단한 형태입니다.

 

사용자 삽입 이미지

:

중급 튜토리얼 7-3 (마지막)

Ogre3D 삽질란/Intermediate Tutorial 7 2009. 1. 15. 22:36

RTTs and shaders

RTT 셰이더 보내기

RTT 종종 셰이더와 함께 사용됩니다. 그렇기에 RenderTexture 전달방법에 대해서 알아둘 필요가 있습니다. 걱정마세요. 무척이나 간단합니다.

가장 단순한 예를 들자면 셰이더를 위한 텍스쳐가 실행시간동안 한번도 바뀌지 않는경우 입니다. 텍스쳐를 바꿀 필요가 없을경우 재질스크립트의 이름만 알려주면 됩니다. 이번 예제에서 쓰인 경우는 RttTex입니다. 재질 속성파일에서의 texture_unit 속성은 다음처럼 되어야 합니다 :

texture_unit

{

    texture RttTex

}

만약 셰이더에 사용되는 텍스쳐가 바뀌게 된다면 다음의 2줄을 추가해 주세요 :

Ogre::MaterialPtr material = Ogre::MaterialManager::getSingleton().getByName("Sepia");

material->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("OtherRttTex");

첫번째 라인에서 새로 사용하고싶은 재질에 대한 포인터를 구하고(위에서 사용된 경우는 Sepia 셰이더 재질입니다) 두번째라인에서 텍스쳐이름을 적당한 이름으로 바꿔줍니다.

앞에서 설명드린 2가지 방법중 하나로 재질 스크립트내부에서 올바른 텍스쳐이름을 설정했다면 다음라인을 통해서 cg셰이더텍스쳐에 접근이 가능해 집니다:

uniform sampler2D SceneSampler : register(s0)

마무리 지어졌습니다. 미니스크린속의 텍스쳐는 셰이더효과를 통해서 다음과 같은 형태로 보일 입니다(sepia 셰이더 효과):

사용자 삽입 이미지

 

결론

RTT 시작하기위한 기초적인 내용이었습니다. 코드로 부터 차근차근 단계를 쌓아보고 그래픽효과에 대한 새로운 세상으로 도전해 보세요.

: