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

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

중급 튜토리얼 6-1

Ogre3D 삽질란/Intermediate Tutorial 6 2009. 1. 7. 13:58

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

중급 튜토리얼 6: 데칼 투영하기(Projective Decals)

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

목차

                               1 소개

                               2 시작하기

                                       2.1 스쳐

                                       2.2 코드

                               3 데칼 투영하기

                                       3.1 절두체

                                       3.2 재질 변경

                                       3.3 함수호출

                               4 뒷면투영현상 거하기

                                       4.1 소개

                                       4.2 하기

                                       4.3 재질 하기

                               5 하게 하기

                                       5.1

                                       5.2 Field of View 하기

                               6 마디

 

소개

이번 튜토리얼에서는 객체에 투영된 데칼을 장면속에 추가하는 방법을 다룹니다. 지면상의 표시점이나 조준점 또는 뭔가에 투영되어 표시되는 데칼을 표시해야 텍스쳐를 투영시키는 방법이 유용하게 쓰입니다(스플래팅처럼 목표객체가 영구히 바뀌지는 않습니다). 조준점이 투영된 오우거머리의 스크린 입니다 :



튜토리얼에 대한 코드는 여기 찾을 있습니다. 내용을 천천히 진행하면서 완성되어지는 결과물을 직접 확인하세요.

 

 

시작하기

새로운 텍스쳐

프로젝트를 시작하기전에 앞으로 사용하게될 2개의 이미지를 추가해야 합니다. 다음 2개의 그림파일 링크를 오른버튼 클릭으로 오우거가 참조할 있는 위치에 저장하세요 : decal.png decal_filter.png

제일 무난한 위치는 media/materials/textures 폴더입니다(대부분 사람들의 OgreSDK 포함되어 있을 입니다).

초기 코드

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

#include "ExampleApplication.h"

 

// A FrameListener that gets passed our projector node and decal frustum so they can be animated

class ProjectiveDecalListener : public ExampleFrameListener

{

public:

    ProjectiveDecalListener(RenderWindow* win, Camera* cam, SceneNode *proj, Frustum *decal)

        : ExampleFrameListener(win, cam), mProjectorNode(proj), mDecalFrustum(decal), mAnim(0)

    {

    }

 

    bool frameStarted(const FrameEvent& evt)

    {

        return ExampleFrameListener::frameStarted(evt);

    }

 

protected:

    SceneNode *mProjectorNode;

    Frustum *mDecalFrustum;

    float mAnim;

};

 

class ProjectiveDecalApplication : public ExampleApplication

{

protected:

    SceneNode *mProjectorNode;

    Frustum *mDecalFrustum;

    Frustum *mFilterFrustum;

 

    void createScene()

    {

        // Set ambient light

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

 

        // 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);

 

        // Make 6 ogre heads (named head0, head1, etc.) arranged in a circle

        Entity *ent;

        for (int i = 0; i < 6; i++)

        {

            SceneNode* headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();

            ent = mSceneMgr->createEntity("head" + StringConverter::toString(i), "ogrehead.mesh");

            headNode->attachObject(ent);

            Radian angle(i * Math::TWO_PI / 6);

            headNode->setPosition(75 * Math::Cos(angle), 0, 75 * Math::Sin(angle));

        }

    }

 

    // The function to create our decal projector

    void createProjector()

    {

    }

 

    // A function to take an existing material and make it receive the projected decal

    void makeMaterialReceiveDecal(const String &matName)

    {

    }

 

    // Create new frame listener

    void createFrameListener(void)

    {

        mFrameListener= new ProjectiveDecalListener(mWindow, mCamera, mProjectorNode, mDecalFrustum);

        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

        ProjectiveDecalApplication 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

컴파일 실행시켜보세요. 6개의 오우거머리가 보일것입니다.

 


:

중급 튜토리얼 6-2

Ogre3D 삽질란/Intermediate Tutorial 6 2009. 1. 7. 13:56

데칼 투영하기

절두체

절두체는 near far 끝점으로 실제로 보여지는 공간을 피라미드형태로 표시합니다. 오우거에서는 카메라와 함께 사용되어 출력하는데 이용됩니다(카메라 클래스는 Frustum 클래스로부터 상속받습니다). 튜토리얼에서는 데칼을 화면상의 메쉬로 투영시키는데 절두체가 사용될 입니다.

프로젝터를 생성하기위해 첫번째로 해야 일은 사용될 절두체를 생성하고 SceneNode attach하는 입니다. createProjector함수에 다음코드를 추가하세요 :

       mDecalFrustum = new Frustum();

       mProjectorNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("DecalProjectorNode");

       mProjectorNode->attachObject(mDecalFrustum);

       mProjectorNode->setPosition(0,5,0);

이렇게 만들어진 프로젝터는 필름영사기와 유사하게 멀리갈수록 데칼 크기가 커지는 형태입니다. 거리에 상관없이 일정한 데칼의 크기, 모양을 유지하도록 생성시키고 싶다면 다음 코드를 추가하면 됩니다(하지만 지금은 그렇게 하지마세요) :

       // Do not add this to the project

       mDecalFrustum->setProjectionType(PT_ORTHOGRAPHIC);

       mDecalFrustum->setNearClipDistance(25);

직교성 절두체의 field of view, 화면비율, near clip 거리를 설정하여 프로젝터가 떨어진 거리와는 상관없이 일정한 크기의 데칼이 표시되므로 적절한 크기와 형태를 사전에 예측해야 합니다.

진행하기전에 여기서 절두체가 어디로 데칼을 투영하며 비추고 있는지를 알아두세요. 프로그램에서는 형태로 오우거머리가 나열되어 있고 중앙에 절두체가 위치하며(5 unit거리만큼 살짝 떨어져 있긴 합니다) -Z방향을 바라보고 있습니다(방향을 따로 바꾸지 않은 기본값입니다). 최종적으로 데칼이 오우거머리의 뒷면을 투영하게 됩니다.

 

재질 변경

객체에 데칼이 보여지기 위해서는 객체가 데칼을 받을 있도록 되어야 합니다. 일반적인 텍스쳐위에 데칼이 그려지는 과정을 새로 하나 추가함으로써 구현합니다. 절두체는 투영될 데칼의 위치, 크기, 모양을 결정합니다. 데모에서는 데칼을 받을 있도록 객체의 재질을 직접적으로 수정을 가할 이지만 대부분의 실제 프로그램에서는 원본재질의 사본을 따로 보관해둠 으로써 원래재질로 되돌릴 있도록 해야 합니다.

가장먼저 해야 일은 재질 포인터를 얻고 재질처리단계를 하나 추가합니다. makeMaterialReceiveDecal함수를 찾아서 다음코드를 추가하세요 :

       MaterialPtr mat = (MaterialPtr)MaterialManager::getSingleton().getByName(matName);

       Pass *pass = mat->getTechnique(0)->createPass();

처리단계가 추가되었습니다. 이제 블렌딩과 조명에 대한 설정을 해야 합니다. 객체에 입혀진 기존의 텍스쳐와 혼합된 형태의 새로운 텍스쳐가 추가되어야 합니다. Scene blending Transparent alpha 설정시켜주고 depth bias 1 설정합니다( 데칼은 투명처리가 되지 않습니다). 마지막으로 조명을 disable 시켜줌으로써 장면상의 조명값에의해 재질이 변함없이 보여질 있도록 합니다. 만약 데칼이 조명의 영향을 받도록 설정하고 싶다면 마지막줄 함수호출을 실행하지 않으면 됩니다 :

       pass->setSceneBlending(SBT_TRANSPARENT_ALPHA);

       pass->setDepthBias(1);

       pass->setLightingEnabled(false);

decal.png 이미지를 이용하여 새로운 텍스쳐상태를 생성합니다. 그리고 두번째 함수는 텍스쳐투영을 활성화시키고 생성된 절두체를 입력받습니다. 마지막 2 함수들은 필터링과 어드레싱모드를 설정합니다 :

       TextureUnitState *texState = pass->createTextureUnitState("decal.png");

       texState->setProjectiveTexturing(true, mDecalFrustum);

       texState->setTextureAddressingMode(TextureUnitState::TAM_CLAMP);

       texState->setTextureFiltering(FO_POINT, FO_LINEAR, FO_NONE);

어드레싱모드를 clamp 바꾸면 데칼이 객체위에 반복되어 나타나는걸 방지할 있습니다. 필터링옵션으로는 확대시 기본선형(standard linear)으로, 축소시에는(FO_POINT), 밉매핑시에는 아무런 옵션도 사용되지 않습니다. 이러한 옵션은 데칼이 최소화되었을때 데칼의 외곽경계선부분(투명색) 흐릿해지는것을 방지해 줍니다. 이러한 옵션을 사용하지 않는다면 데칼이 투영된 바깥쪽이 무척 지저분해 보일 입니다.

이것으로 재질에 필요한 설정은 모두 끝났습니다.

 

함수호출

재질과 프로젝터를 설정하는 함수가 완성되었습니다. 설정을 실제로 적용시키기 위해서는 호출되어야 합니다. 다음 코드를 createScene멤버함수 마지막부분 추가하세요 :

       createProjector();

       for (unsigned int i = 0; i < ent->getNumSubEntities(); i++)

           makeMaterialReceiveDecal(ent->getSubEntity(i)->getMaterialName());

이전루프에서 ent변수에 오우거머리 엔티티를 이미 저장해뒀습니다. 사용되는 모든 오우거머리들은 동일한 재질이 사용되므로 랜덤하게 하나의 객체를 잡아서 그에 해당하는 재질이름을 얻어내기만 하면 됩니다.

컴파일 실행시켜보세요. 데칼이 투영된 오우거머리 몇개가 보일겁니다.

 

뒷면투영현상 제거하기

소개

프로그램을 실행시켰을때 눈치채셨을지도 모르겠습니다. 실제로는 2개의 데칼이 투영되었습니다. 하나는 절두체가 바라보는 -Z방향으로, 다른하나는 절두체의 뒷면인 +Z방향으로 오우거 머리를 투영하고있습니다. 이렇게 보이는 이유는 절두체의 정면으로 투영될때 절두체의 뒷면으로도 (반전된)데칼이 반응하기 때문입니다.

절대로 이런 결과를 원하지는 않습니다. 잠시후 뒷면투영현상을 제거하기위해 필터에 대해 소개드릴 입니다.

 

프로젝터 수정하기

뒷면투영현상을 걸러내기위해서 보이는방향만을 보여지게끔 필터기능을 가진 새로운 절두체가 필요합니다. createProjector멤버함수에 다음코드를 추가하세요 :

       mFilterFrustum = new Frustum();

       mFilterFrustum->setProjectionType(PT_ORTHOGRAPHIC);

       SceneNode *filterNode = mProjectorNode->createChildSceneNode("DecalFilterNode");

       filterNode->attachObject(mFilterFrustum);

       filterNode->setOrientation(Quaternion(Degree(90),Vector3::UNIT_Y));

특별하게 새로운 코드는 보이지 않습니다. 이전코드와 다른점이 있다면 노드가 뒤로 바라보게끔 90 회전한 뿐입니다.

 

재질 수정하기

재질에 추가시킨 텍스쳐 처리단계에 또다른 텍스쳐상태를 추가시켜야 합니다. MakeMaterialReceiveDecal 다음코드를 추가하세요 :

       texState = pass->createTextureUnitState("decal_filter.png");

       texState->setProjectiveTexturing(true, mFilterFrustum);

       texState->setTextureAddressingMode(TextureUnitState::TAM_CLAMP);

       texState->setTextureFiltering(TFO_NONE);

이전코드와 차이점이 없습니다. 절두체필터, 텍스쳐필터를 사용하고 필터링기능을 껐습니다. 이제 실행시켜보세요. 이제 정면으로 투영된 데칼만 보일 입니다.

 

화려하게 투영하기

간단한 회전

투영을 화려하게 보이게 하기 위하여 프로젝터를 회전하고 Field of View 갱신할 입니다. 다음코드를 frameStarted멤버함수에 추가하여 프로젝터를 회전시킵니다 :

       mProjectorNode->rotate(Vector3::UNIT_Y, Degree(evt.timeSinceLastFrame * 10));

실행시켜 보세요. 원으로 배치된 오우거머리를 따라돌며 데칼이 투영됨을 있을 입니다.

 

Field of View 수정하기

프로젝터의 field of view 수정해 봅시다. Orthographic(직교성) 프로젝터를 사용하지 않았으므로 field of view값을 조절하여 투영되는 객체의 사이즈를 늘리거나 줄일 있습니다. 보여지기 위해서 FOVy (field of view Y)값이 15~25 사이의 각도값이 되어야 합니다. 다음의 코드는 데칼의 사이즈를 늘리거나 줄이게 됩니다(frameStarted멤버함수에 추가하세요) :

       mAnim += evt.timeSinceLastFrame / 2;

       if (mAnim >= 1)

           mAnim -= 1;

 

       mDecalFrustum->setFOVy(Degree(15 + Math::Sin(mAnim * Math::TWO_PI) * 10));

이제 실행시켜 보세요.

:

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

Ogre3D 삽질란/Intermediate Tutorial 6 2009. 1. 7. 13:52

마지막 한마디

데칼에 대해서 마지막으로 한가지 드리고 싶은 말씀은 만약 프로그램상에서 데칼을 사용할 계획이라면 데칼의 외곽 경계선 부분의 픽셀들은 반드시 완전한 투명값(zero alpha) 되어야 합니다. 그렇지 않으면 텍스쳐 클램핑작업으로 인해 데칼이 지저분해 보일 입니다.

: