Notice
Recent Entries
« 2025/05 »
일
월
화
수
목
금
토
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Ogre3D 삽질란 2008. 11. 14. 14:09
세계 최대의 오픈소스 3D렌더링엔진 Ogre3D 의 번역자료가 별로 없는듯 하여 안타깝습니다. 이미 다른 사이트에 번역본이 있긴 하지만 오래된 문서에다 업데이트도 거의 중지된듯하여 개인적으로 자료정리도 해 놓을겸 다시 번역을 하게되었습니다. 번역된 문서는 1.6.0 Final 버젼 기준입니다. 멋진 3D 게임제작을 꿈꾸는 많은 분들에게 조금이나마 도움이 되었으면 합니다. 그리고 직역을 하다보니 어색한 부분이 많아서 나름 의역을 한 문구도 간간히 있습니다. 하지만 최대한 원문의 문장을 살리려 노력했습니다. 제가 시간이 나는대로 지속적으로 업데이트 할 예정입니다. 번역에 잘못된 부분이 있다면 가차없이 제보 바랍니다. ^^;; 그럼~ 많은 도움이 되시길 바랍니다.
공유하기
URL 복사 카카오톡 공유 페이스북 공유 엑스 공유
Ogre3D 삽질란/Basic Tutorial 3 2008. 11. 14. 13:29
입문자 튜토리얼 3: Terrain, Sky, Fog, and the Root object
이 튜토리얼 진행중 문제가 발생한다면 Help 포럼 에 문의하세요 .
이 튜토리얼은 독자가 C++ 프로그래밍이 가능하고 오우거 어플리케이션 설정 및 컴파일이 가능하다는 가정하에 진행됩니다 . 이 튜토리얼은 이전 튜토리얼을 기초로 작성되었으며 독자는 이전 튜토리얼들을 거쳐왔다고 가정합니다 .
이 튜토리얼에서는 어떻게 지형 , 하늘 그리고 안개를 조작하는지 안내 할 것입니다 . 이 튜토리얼 이후에 여러분은 Skyboxes, Skyplanes 그리고 Skydomes 의 차이점을 알고 쓸 수 있게 될 것입니다 . 종류에 따른 안개들의 차이점도 알게 될 것이며 어떻게 쓰는지도 알게 될 것입니다 .
튜토리얼을 진행함에 있어서 여러분은 천천히 코드를 따라가며 입력하고 결과를 지켜볼 필요성이 있습니다 . 튜토리얼의 최종 코드는 여기 (http://www.ogre3d.org/wiki/index.php/BasicTutorial3Source) 서 얻을 수 있습니다 . 만약 작성한 코드에 문제가 발생한다면 이 코드와 비교해 보세요 .
지난번 튜토리얼처럼 미리 작성된 코드를 기초로 시작할 것 입니다 . 프로젝트를 생성하고 아래의 코드를 입력하세요 :
#include "ExampleApplication.h"
class TutorialApplication : public ExampleApplication
{
protected:
public:
TutorialApplication()
{
}
~TutorialApplication()
{
}
protected:
void chooseSceneManager(void)
{
}
void createScene(void)
{
}
};
#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT)
#else
int main(int argc, char **argv)
#endif
{
// Create application object
TutorialApplication app;
try {
app.go();
} catch(Exception& e) {
#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
MessageBox(NULL, e.getFullDescription().c_str(), "An exception has occurred!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
fprintf(stderr, "An exception has occurred: %s\n",
e.getFullDescription().c_str());
#endif
}
return 0;
}
만약 오우거 SDK 를 Windows 에서 사용한다면 "[OgreSDK_DIRECTORY]\samples\include" 디렉토리 (ExampleApplication.h 파일이 있는곳 ) 를 include 가 가능하도록 프로젝트에 추가해 주세요 . 만약 오우거엔진 소스를 직접 사용하신다면 [OgreSource_DIRECTORY]\Samples\Common\include" 을 추가해 주세요 . 다음 진행을 위해서 컴파일이 에러없이 되도록 해두세요 . 그러나 실행은 하지마세요 ! 나중에 이 코드에 더 추가할 계획입니다 . 만약 컴파일에 문제가 생긴다면 Wiki 페이지에서 컴파일러별 설정 정보를 찾아보시고 문제가 지속된다면 Help 포럼에 문의하세요 .
프로그램이 동작되면 WASD 키로 움직이고 마우스로 주변을 둘러보는 기능을 합니다 . ESC 키는 종료키 입니다 .
Ogre3D 삽질란/Basic Tutorial 3 2008. 11. 14. 13:28
Root
이 데모에서는 오우거에서 지형을 출력할 것입니다 . 이걸 하기위해서 ExampleApplication 에서 기본으로 수행되는 SceneManager 설정을 TerrainSceneManager 클래스로 설정해야 합니다 . chooseSceneManager 함수를 찾아서 다음 코드를 추가합니다 :
mSceneMgr = mRoot->createSceneManager(ST_EXTERIOR_CLOSE);
Root 객체 (mRoot 는 Root 의 인스턴스입니다 ) 는 오우거의 " 핵심 " 객체 입니다 . 여기 (http://www.ogre3d.org/docs/manual/manual_4.html) 에서 오우거 객체들간의 관계를 나타내는 UML 다이어그램을 볼 수 있습니다 . 이것들이 RenderSystem 을 제외한 실제로 쓰이는 거의 모든 객체들 입니다 . 이 코드는 Root 노드에게 ST_EXTERIOR_CLOSE 타입의 SceneManager 를 요구하는 코드입니다 . Root 객체가 요청된 SceneManager 를 찾기위해 SceneManagerEnumerator 에 쿼리를 실행하고 해당되는 객체의 포인터를 리턴합니다 .
여러분의 어플리케이션이 생성된 후 오우거의 Root 객체를 건드릴 일이 거의 없었을 것이며 SceneManagerEnumerator 과 직접적인 상호작업을 한 적도 없었을 겁니다 .
SceneManager 생성
필자는 여러분의 추후에 있을 혼란을 방지하고자 SceneManager 생성과 저장에 대해서 설명해 드리려 합니다 . SceneManager 는 Singleton 이 아닙니다 . 원하는 갯수만큼 생성할 수 있으며 SceneNodes/Lights/ 기타 와는 다르게 "new SceneManager()" 방식으로 바로 생성할 수 있습니다 ( 이 방식을 정말 원하신다면요 ). 여러개의 개별적인 도형들과 Entity 들을 가진 여러개의 SceneManager 를 동시에 다룰 수 있습니다 . Viewport 를 다시 생성하거나 ( 중급 튜토리얼 4 에서 다룹니다 ) 여러개의 SceneManager 를 다수의 Viewport 를 이용해서 동시에 출력하는것으로 시간에 구애받지 않고 SceneManager 를 서로 교체하는게 가능합니다 .
왜 SceneManager 를 수동으로 생성하는 것 대신 createSceneManager 함수를 사용할까요 ? 오우거의 플러그인 시스템은 1 개의 SceneManager 로 동작될때 가장좋은 유연성을 발휘합니다 . SceneType 열거형 변수에는 몇몇 종류의 장면타입만이 설정되어 있습니다 . 지금까지는 ExampleApplication 클래스에서 SceneManager 를 ST_GENERIC 으로 설정했습니다 . plugins.cfg 파일을 다뤄보지 않은 이상 어쩌면 이게 SceneManager 의 기본타입이라고 생각할지도 모르겠습니다만 실제로 알고보면 전혀다른 이름의 객체가 대신 쓰이고 있습니다 ! OctreeSceneManager 플러그인을 사용하면 기본값으로 OctreeSceneManager 를 ST_GENERIC 로 등록하고 있습니다 . OctreeSceneManager 는 보이지 않는 객체는 아예 그리지 않음으로써 평균적으로 일반 SceneManager 에 비해서 빠릅니다 . 만약 OctreeSceneManager 를 plugins.cfg 에서 제거시킨다면 ST_GENERIC 를 요구하면 기본 SceneManager 가 선택되거나 플러그인들에 기준하여 뭔가 더 나은뭔가가 대신에 선택될 것 입니다 . 정말 멋진 시스템입니다 .
이렇게 많은 분량을 진행 했음에도 SceneManager 를 생성할때 사용되는 Root 객체의 최소한의 기본내용들만 다뤘습니다 . 사실 SceneManager 를 요청할때 SceneType 열거형 변수대신 문자열로 대체 가능합니다 . 오우거는 유연한 SceneManager 생성시스템을 사용하며 오우거에서 몇개의 SceneManager 를 설정하던지 , 몇개를 생성하고 파괴하던지 제한이 없습니다 . 예를 들어보자면 , "FooSceneManager" 라는 이름으로 생성할 수 있는 SceneManager 를 설치했다고 가정합시다 . 한개의 SceneManager 를 다음과 같이 생성할 수 있을 것 입니다 :
// do not add this to the project
mSceneMgr = mRoot->createSceneManager("FooSceneManager");
이 방식은 기본 이름으로 한개를 생성할 것 입니다 . 튜토리얼에서는 이렇게 기본 이름으로 생성시키지만 실제로 사용하실때에는 2 번째 매개변수로 SceneManager 의 이름을 항상 지정해 줘야 합니다 . 예를 들면 2 개의 SceneManager 를 2 개의 이름들로 만들고 싶다면 다음과 같이 할 수 있습니다 :
// do not add this to the project
mSceneMgr1 = mRoot->createSceneManager("FooSceneManager", "foo");
mSceneMgr2 = mRoot->createSceneManager("FooSceneManager", "bar");
SceneManagers 에 이름을 짓는것으로 그것들의 포인터를 관리할 필요가 없어집니다 . Root 객체가 대신 해주기 때문이죠 . 나중에 "foo", "bar" 이름의 SceneManager 들이 필요하다면 다음과 같이 하면 됩니다 :
// do not add this to the project
SceneManager *foo = mRoot->getSceneManager("foo");
SceneManager *bar = mRoot->getSceneManager("bar");
SceneManager 를 다 쓰고나면 Root 의 destroySceneManager 함수를 써서 제거하고 메모리를 절약하세요 .
여기서는 다루지 않지만 , SceneManagerFactory 클래스를 상속시켜서 유저만의 SceneManager 생성기를 정의 할 수 있습니다 . 상속시켜서 만든 유저만의 SceneManager 나 기본적인 SceneManager 에서 몇몇 기능들을 수정된 방식으로 쓰고 싶을 경우 ( 카메라 및 조명 생성 , 기하학 정보 로딩 , 기타등등 ..) 유용하게 사용됩니다 .
장면에 지형 추가하기
이제 지형을 생성하기위해 준비가 끝났습니다 . 기본 SceneManager 는 범용적인 장면 생성을 위해서 세분화된 setWorldGeometry 함수를 정의합니다 . 이 함수는 TerrainSceneManager 클래스와 함께 읽혀질 지형설정 파일이름을 필요로 합니다 . TutorialApplication::createScene 함수를 찾아서 다음 코드를 추가합니다 :
mSceneMgr->setWorldGeometry("terrain.cfg");
컴파일 후 실행시켜 봅시다 . 쉽군요 . 지형을 바라보는데 있어서 부적절한 카메라 위치때문에 카메라 시작위치를 수정해야 할지도 모르겠군요 .
terrain.cfg 파일
terrain.cfg 파일에는 수많은 선택사항들이 있으며 지형생성을 위해서 이미지를 설정하는 최소한의 기본사항을 설명드릴 것 입니다 . 지형설정파일에 대한 더 자세한 내용을 원하신다면 여기 (http://www.ogre3d.org/wiki/index.php/Terrain_Scene_Manager ) 를 참조하세요 . TerrainSceneManager 에 대한 중요한 한가지는 페이징 기능을 염두하고 디자인되었으나 아직 구현되지 않았습니다 . 지형에서의 페이징 기법은 큰 지형덩어리를 여러개의 조각으로 나눈 다음에 보이는 부분만을 뽑아내어 출력하는 시스템 입니다 . 이러한 기능은 대규모의 지형을 프레임 하락없이 보여줄 수 있게끔 해 줍니다 . Paging Scene Manager 오우거 플러그인이 이러한 기능을 담당합니다 .
TerrainSceneManager 는 지형생성시 Heightmap 방식을 사용합니다 . "Heightmap.image" 속성을 조절하여 어떤 방식의 Heightmap 을 사용할지를 설정할 수 있습니다 . WorldTexture 속성에서 어떤 덱스쳐가 지형에 쓰일지를 설정합니다 . 지형 장면 매니저에서는 "DetailTexture" 속성으로 WorldTexture 가 얼마나 실제같이 보이게 할 것인지도 설정가능합니다 . terrain.cfg 에서 설정된 각각의 이미지들을 찾아보시면 도움이 될 것입니다 (Media 폴더안에 있을것 입니다 ).
어떻게 heightmap 을 생성하는지는 포럼에서 토론되어졌던 주제입니다 . heightmap 과 관련된 궁금한 내용을 직접 찾아보세요 .
지형조명
이전 튜토리얼에서는 조명과 그림자에 대해 전체지면을 할애하였지만 불행히도 TerrainSceneManager 에 적용하기는 쉽지 않습니다 . 지금은 이것만 알아두세요 . 차라리 텍스쳐의 세부적인 부분에 조명효과를 추가하는것이 일반적인 조명을 적용하는 것 보다 더 쉽습니다 . 추후 안개 섹션에서 " 가짜 어둠 " 을 구현하는 방법을 다룰 것 입니다 . 만약 지형에 조명을 적용하고 싶다면 Paging Scene Manager 사용법 (http://www.ogre3d.org/wiki/index.php/Paging_Scene_Manager ) 을 참조하세요 . 그러한 구현에는 그게 좀 더 향상된지원을 해줍니다 .
오우거에서는 3 가지 종류의 하늘을 제공합니다 : SkyBoxes, SkyDomes, SkyPlanes. 이제부터 하나하나 살펴보도록 하겠습니다 . Example/* 텍스쳐를 쓰기위해 chooseSceneManager 함수에 다음 라인을 추가하세요 :
ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
SkyBoxes
SkyBox 는 장면속의 모든 객체들을 둘러싼 거대한 큐브라 보시면 됩니다 .
백문이 불여일견 , 이것에 대해 설명하는 것 보다는 한번 보시는게 이해가 빠를 것 입니다 . 다음 라인을 createScene 에 추가하세요 :
mSceneMgr->setSkyBox(true, "Examples/SpaceSkyBox");
컴파일 후 실행시켜 봅시다 . 깔끔한가요 ? (SkyBox 는 매우 크기때문에 텍스쳐의 실제해상도는 매우 낮아집니다 ; 고해상도의 SkyBox 는 좀 더 나아보입니다 .) SkyBox 설정을 위해서 setSkyBox 호출시 몇몇 유용한 기능들을 제어할 수 있는 매개변수들이 있습니다 . 첫번째 옵션은 SkyBox 를 활성화할지의 여부입니다 . 단순히 'mSceneMgr->setSkyBox(false, "");' 이런 호출만으로 비활성화가 됩니다 . 두번째 매개변수는 SkyBox 재질설정 스크립트입니다 .
세번째와 네번째 매개변수는 setSkyBox 에 있어서 이해하는데 중요합니다 . 세번재는 SkyBox 가 카메라로부터 얼마나 멀리 떨어지느냐며 , 네번째는 SkyBox 가 장면을 그린 이후에 그려질 것인지 아닌지를 설정합니다 . 이런 이유로 어떤일이 벌어질지 관찰하기위해 세번째 매개변수값을 기본값인 5000 에서 아주 가까운 값으로 바꿉니다 :
mSceneMgr->setSkyBox(true, "Examples/SpaceSkyBox", 10);
컴파일하고 실행시켜봐도 아무것도 바뀐게 없군요 ! 그 이유는 네번째 매개변수로 SkyBox 가 가장 먼저 그려질지 아닐지인 여부가 디폴트로 true 값으로 설정되어 있습니다 . 만약 SkyBox 가 먼저 그려진다면 그 위에 다른 모든것 ( 지형같이 ..) 은 그 이후에 겹쳐서 그려지기때문에 SkyBox 는 항상 배경화면처럼 보입니다 . ( 거리값을 근접클리핑 거리보다 높게 설정하지 않으면 SkyBox 는 보이지 않을겁니다 !) 먼저 그리는 것은 그다지 좋아보이진 않습니다 . 왜냐하면 모든게 그려지기 때문이죠 . 마지막에 그린다면 보이는 부분만 그려지는데 약간의 속도 향상을 가져다 줍니다 . 그렇기 때문에 SkyBox 는 마지막에 그려지도록 설정합시다 :
mSceneMgr->setSkyBox(true, "Examples/SpaceSkyBox", 5000, false);
다시보면 전에 했던것과 비슷해 보이지만 SkyBox 의 보이지 않는 부분은 그려지지 않을것 입니다 . 이 기법을 쓸때 조심해야 할 점이 한가지 있습니다 . 만약 SkyBox 가 너무 가깝게 설정된다면 장면의 일부분을 잘라버릴지도 모릅니다 . 예제로 다음을 시도해 보세요 :
mSceneMgr->setSkyBox(true, "Examples/SpaceSkyBox", 100, false);
보시는것과 같이 SkyBox 가 지형을 절단시켜 버렸군요 . 절대로 우리가 원하는게 아닙니다 . 만약 SkyBox 를 어플리케이션에서 활용할 계획이라면 어떻게 써야할지 결정해야 합니다 . 빠른 렌더링 속도를 바란다면 지형 이후에 그리는게 바람직 할 것이며 지형지물에 방해되지 않도록 신중하게 해야 할 것입니다 . 일반적인 경우 2 번째 매개변수 이후로는 기본값을 사용하는게 안전합니다 .
SkyDomes
SkyDome 은 SkyBox 와 매우 비슷하며 setSkyDome 함수를 호출하여 렌더링 공간과 카메라가 있는 주면에 커닿란 큐브를 만듬으로 사용하게 됩니다 . 그러나 큰 차이점은 SkyBox 내부의 원형표면상의 " 미리 설계된 " 텍스쳐가 사용된다는 것입니다 . 여전히 큐브를 바라보는 상태이지만 구면체를 둘러싼 텍스쳐 처럼 보입니다 . 큰 단점으로는 큐브의 바닥면은 텍스쳐가 없으므로 바닥을 메꿀만한 뭔가의 지형이 항상 필요하다는 것 입니다 .
오우거에서 기본으로 제공하는 텍스쳐로 이 예제를 깔끔하게 보여줄 것 입니다 . createScene 함수에서 setSkyBox 를 지우고 그 대신에 이 코드를 추가하세요 :
mSceneMgr->setSkyDome(true, "Examples/CloudySky", 5, 8);
실행시켰을 때 카메라를 지형의 정 중앙에 위치시키고 지형과 가까운 상태에서 카메라를 움직이세요 ( 이게 가장 나아보입니다 ). 보시고 나면 R 키를 눌러서 메쉬 뷰 모드로 바꿔보세요 . 보시는것과 같이 여전히 큐브를 바라보고 있지만 ( 바닥없이 ), 구름이 꼭대기 부근에서 원형으로 둘러 쌓인것 처럼 보입니다 . ( 구름의 움직임은 "Examples/CloudySky" 재질속성이며 SkyDome 의 기본 제공기능이 아닙니다 )
처음 2 개의 setSkyDome 매개변수는 setSkyBox 와 같으며 'mSceneMgr->setSkyDome(false, "");' 호출로 SkyDome 을 끌 수 있습니다 . 세번째 매개변수는 SkyDome 의 곡률입니다 . API 레퍼런스에서는 이 값으로 2~65 사이값을 추천합니다 ; 낮은값은 거리감이 좋아지며 , 높은값은 화질손실이 낮아지며 좀 더 부드러운 효과를 볼 수 있습니다 . 세번째 매개변수값을 2~65 값을 적용시켜보고 차이점을 관찰하세요 . API 레퍼런스가 가르키는 거리효과는 아래 스크린 샷에서 확인할 수 있습니다 . 이 셋팅은 곡률값 2 셋팅입니다 :
곡률값 64 셋팅입니다 :
4 번째 매개변수는 텍스쳐의 쪼개지는 횟수 입니다 . 이 값은 텍스쳐 크기에 따라 조절할 필요가 있습니다 . 그리고 이 값은 정수가 아닌 실수값을 가집니다 . 어플리케이션에서 필요하다면 텍스쳐를 1.234 번 쪼갤수 도 있습니다 . 4 번째와 6 번째 값은 거리값과 drawFirst 값 입니다 . 이 값들은 SkyBox 섹션에서 이미 설명한 내용들입니다 .
SkyPlanes
SkyPlane 은 SkyBox 와 SkyDome 과는 매우 다릅니다 . 거대한 큐브를 써서 하늘을 그리는 대신 단순한 평면 1 개를 이용합니다 . ( 앞으로 소개될 SkyPlane 설정에 있어서 시점이 지면의 중심을 향해야 하고 지면과 가까워야 합니다 .) createScene 의 모든 내용을 지우세요 . 가장 먼저 해야할 일은 평면을 생성하고 아랫방향으로 바라보게끔 하는 것 입니다 . setSkyPlane 함수는 SkyBox 나 SkyDome 과 같이 거리에 관련된 매개변수를 가지고 있지 않습니다 . 대신 평면의 d 값으로 설정됩니다 :
Plane plane;
plane.d = 1000;
plane.normal = Vector3::NEGATIVE_UNIT_Y;
이제 평면이 정의되었고 SkyPlane 을 생성할 수 있습니다 . 4 번째 매개변수는 SkyPlane 의 크기 ( 여기서는 1500x1500 단위크기 ) 이며 5 번째 매개변수는 얼마나 많은 수의 텍스쳐가 타일형식으로 나열될 것인지 입니다 :
mSceneMgr->setSkyPlane(true, plane, "Examples/SpaceSkyPlane", 1500, 75);
컴파일 후 실행시켜 보세요 . SkyPlane 에 2 개의 문제점이 있군요 .
그 중 첫번재는 , 사용된 텍스쳐의 해상도가 너무 낮다는것과 예쁘게 보이지도 않습니다 . 이것은 텍스쳐를 고화질에 고해상도로 생성하면 간단히 해결되는 문제입니다 . 그러나 가장 중요한 문제는 수평으로 바라볼때 SkyPlane 의 끝이 보인다는 점 입니다 . 이것은 아무리 좋은 텍스쳐를 쓴다고 해도 수평선을 바라보는 상황에서는 해결되지 않습니다 . SkyPlane 이 기본적으로 유용하게 쓰일 경우는 높은 벽 ( 또는 언덕 ) 이 시선주위에 존재 할때 입니다 . SkyPlane 을 사용할 때에는 full SkyBox/SkyDome 을 생성하는 것 보다 낮은 그래픽 처리량을 기대할 수 있습니다 .
다행히도 이것들이 SkyPlane 으로 할 수 있는 전부는 아닙니다 . 6 번째 매개변수는 SkyBox 와 SkyDome 섹션에서 다뤘던 "renderFirst" 값 입니다 . 7 번째 매개변수는 SkyPlane 의 곡률을 설정할 수 있으므로 평면대신 볼록한 표면을 쓸 수 있습니다 . 이제부터 SkyPlane 을 생성할때 x, y 조각들로 설정해야 합니다 ( 초기 SkyPlane 은 나누어 지지 않은 하나의 사각형이지만 곡면이 필요할때 작은 사각형들로 구성되어야 합니다 ). 8, 9 번째 매개변수는 x, y 조각 갯수입니다 :
mSceneMgr->setSkyPlane(true, plane, "Examples/SpaceSkyPlane", 1500, 50, true, 1.5f, 150, 150);
실행시켜 보세요 . 약간 부자연 스럽긴 하지만 훨씬 나은 SkyPlane 이 보입니다 . 여기서 사용된 구름재질 대신에 이것을 사용해도 됩니다 :
mSceneMgr->setSkyPlane(true, plane, "Examples/CloudySky", 1500, 40, true, 1.5f, 150, 150);
컴파일후 실행시켜 보세요 . 구름이 흘러가는 모습이 SkyDome 보다는 약간 부자연스럽게 보일 것 입니다 . 특히 땅 끝에 서서 수평선을 바라볼때 말이죠 .
한가지 덧붙인다면 'mSceneMgr->setSkyPlane(false, Plane(), "");' 호출로 SkyPlane 을 지울 수 있습니다 .
어느것을 써야 하나 ?
어떤 하늘을 사용할 것인지는 전적으로 유저에게 달렸습니다 . -y 방향까지 포함하는 사방을 봐야 한다면 SkyBox 밖에 없을 것입니다 . 만약 지면 또는 -y 방향을 가로막는 뭔가가 있다면 SkyDome 이 좀 더 리얼한 결과를 보여줄 것입니다 . 수평선을 볼 수 없는 상황 ( 산으로 둘러쌓인 계곡이나 성벽으로 둘러쌓인 성 내부 ) 에서는 SkyPlane 이 GPU 자원을 적게 소비하여 아주 좋은 결과값을 보여줄 것입니다 . SkyPlane 을 쓰는 중요한 이유는 다음 섹션 , 멋진 안개효과에서 알게 될 것입니다 .
이것들은 단지 제안에 불과합니다 . 유저의 어플리케이션에서 어떤게 가장 나아 보이는지 직접 실험해 보세요 .
Ogre3D 삽질란/Basic Tutorial 3 2008. 11. 14. 13:25
안개 소개
오우거에서의 안개는 사용하는데 어렵지 않습니다 . 안개를 프로그램에 적용시키는데에는 한가지 절차만 알면 됩니다 . TerrainSceneManager 를 쓸때 setFog 함수는 setWorldGeometry 함수 전에 호출되어야 합니다 . ( 다른 SceneManager 에서는 상관없습니다 .) 어느게 먼저 호출되느냐에따라 다른 vertex 프로그램이 안개와 지형을 만드는데 선택됩니다 . ( 오우거 1.0.0 에서는 잘못된 호출 순서로 인한 급격한 안개량 증가로 아무것도 안보이게 만드는 큰 버그거리였습니다 ; 오우거 1.0.1. 에서 고쳐졌습니다 .)
시작하기전에 createScene 함수에서 setWorldGeometry 부분만 제외하고 다 지우세요 .
안개를 설정함에 있어서 가장 중요한 것은 빈 공간에 안개 Entity 를 흔히들 상상하는것 처럼 만들지 않는다는 점 입니다 . 그 대신에 안개는 단지 현재 바라보고 있는 객체에 적용되는 필터일 뿐입니다 . 이것은 흥미로운 점을 내포합니다 . 아무것도 없는 공간을 쳐다볼 때 ( 그 어떤 객체도 보고 있지 않을때 ), 안개는 보이지 않습니다 . 사실은 뷰포트의 배경색만 보이는 것 입니다 . 그래서 안개가 제대로 보이게 하기 위해서는 배경색을 안개와 동일하게 맞춰줘야 합니다 .
2 가지 종류의 기본 안개타입이 있습니다 : linear 와 exponential 방식입니다 . Linear 안개는 선형적으로 안개가 짙어지고 exponential 안개는 급격하게 짙어집니다 ( 매 거리마다 바로 전에 짙어진 만큼보다 더 짙어 집니다 ). 설명하는 것 보다 보여주는게 이해가 쉬울것입니다 . 다음 예제를 봅시다 .
안개 종류
가장 이해하기 쉬운 linear 방식 안개를 먼저 보도록 합시다 . 첫번째로 해야 할 일은 setWorldGeometry 호출 이후에 뷰포트의 배경색을 설정해야 합니다 . createViewport 함수를 재정의 하여 구현할 수 있지만 ( 지난번 튜토리얼에서 했던 것 처럼 ) 가끔씩은 매번 뷰포트를 다시 생성하지 않고도 설정할 수 있어야 합니다 . 이렇게 하면 됩니다 :
ColourValue fadeColour(0.9, 0.9, 0.9);
mWindow->getViewport(0)->setBackgroundColour(fadeColour);
getNumViewports 멤버함수를 사용하여 뷰포트 갯수를 절정할 수 있고 1 개 이상의 뷰포트를 다룰때 그 함수를 통해서 하나하나 선택할 수 있지만 그런경우는 잘 없습니다 ( 그리고 여기서는 1 개의 뷰포트만 씁니다 ). 뷰포트는 한번에 획득될 수 있습니다 . 배경색을 설정한 지금 안개를 만들 수 있습니다 . 이 코드는 setWorldGeometry 이전에 호출되어야 함을 명심하세요 :
mSceneMgr->setFog(FOG_LINEAR, fadeColour, 0.0, 50, 500);
첫번째 setFog 매개변수는 안개 종류 ( 이 경우는 linear) 입니다 . 두번째 매개변수는 안개에 쓰일 색상 ( 이 경우는 매우 옅은 회색 또는 C# 의 "WhiteSmoke") 입니다 . 세번째 매개변수는 linear 안개방식에서는 쓰이지 않습니다 . 네번째와 다섯번째 매개변수는 안개가 두터워질 범주를 설정합니다 . 이 경우는 안개가 50 에서 시작하여 500 에서 끝납니다 . 이 말은 카메라로부터 0 과 50 사이에는 안개가 없으며 50 부터 500 까지는 선형적으로 두꺼워 집니다 . 500 이상 떨어진 경우 안개때문에 아무것도 보이지 않습니다 . 컴파일 하고 실행시켜보세요 .
다른 타입은 exponential 안개입니다 . 안개의 시작과 끝 경계를 설정하는것 대신에 안개의 농도를 설정합니다 (4, 5 번째 매개변수는 안씁니다 ). 아까 setFog 함수내용을 이 내용으로 바꾸세요 :
mSceneMgr->setFog(FOG_EXP, fadeColour, 0.005);
컴파일 후 실행시켜 보세요 . DirectX Renderer 를 사용하신다면 안개때문에 아무것도 안보일 수 있습니다 . 이때는 setFog 함수를 setWorldGeometry 다음에 호출하시면 해결됩니다 . OpenGL Renderer 는 이 설명드렸던 것 처럼 동작합니다 . 이건 안개가 생성되는것 마다 다르게 보입니다 . 좀더 세밀한 exponential 안개함수도 있습니다 ( 앞서 소개드린 함수보다 더 진하게 생성됩니다 ). FOG_EXP2 를 쓸때 좀 더 진한 안개가 연출됩니다 . 예전의 setFog 코드를 이 코드로 바꿔보세요 :
mSceneMgr->setFog(FOG_EXP2, fadeColour, 0.003);
다시 컴파일하고 실행시켜 보세요 . 오우거에서 제공하는 세가지 안개들은 손쉽게 교체가 가능합니다 . 당신의 어플리케이션에서 세가지 안개함수들을 시험해 보시고 뭐가 가장 나아 보이는지 확인하세요 .
안개와 하늘
SkyBox 와 SkyDome 을 안개와 같이 출력할때 재미있는 문제점들에 봉착할 수 있습니다 . SkyDome 과 SkyBox 가 큐브형태이기 때문에 안개가 나타날때 원형표면에 대해 문제가 발생합니다 . createScene 함수 내용을 삭제하세요 . 만약 그럴싸하게 SkyDome 과 안개 매개변수를 선택한다면 문제점을 바로 확인할 수 있습니다 .
ColourValue fadeColour(0.9, 0.9, 0.9);
mSceneMgr->setFog(FOG_LINEAR, fadeColour, 0.0, 50, 515);
mWindow->getViewport(0)->setBackgroundColour(fadeColour);
mSceneMgr->setWorldGeometry("terrain.cfg");
mSceneMgr->setSkyDome(true, "Examples/CloudySky", 5, 8, 500);
컴파일하고 실행시켜보세요 . 만약 카메라를 움직이면 바라보고 있는 SkyDome 의 한 부분이 안개에 의해 잘려나간 것을 볼 수 있을것 입니다 ( 중앙이 아닌 측면의 blue 색상으로 나타납니다 ) :
우리가 원하던 결과값이 아닙니다 . 다른 방법은 SkyPlane 을 대신 사용하는 것 입니다 . createScene 함수내용을 다음으로 바꿔주세요 :
ColourValue fadeColour(0.9, 0.9, 0.9);
mSceneMgr->setFog(FOG_LINEAR, fadeColour, 0.0, 0, 130);
mWindow->getViewport(0)->setBackgroundColour(fadeColour);
Plane plane;
plane.d = 100;
plane.normal = Vector3::NEGATIVE_UNIT_Y;
mSceneMgr->setWorldGeometry("terrain.cfg");
mSceneMgr->setSkyPlane(true, plane, "Examples/CloudySky", 500, 20, true, 0.5, 150, 150);
제대로 보이는군요 . 위로 보면 정상적인 하늘이 보입니다 ( 안개가 제대로 표시된다면 실제세계와 비슷한 화면입니다 ). 곡률을 적용해도 괜찮습니다 . 이 방법은 수평선을 바라볼때 문제되는 SkyPlane 의 단점을 잘 해결해 줍니다 .
하늘의 일부분에만 안개를 적용하는 방법도 있습니다 . 그러나 그렇게 하기 위해서는 하늘 텍스쳐를 위해 재질 스크립트 수정을 필요로 합니다 .
이런 내용은 이 튜토리얼의 범주를 벗어납니다 . 추후 특정재질을 위해 안개를 비활성화 시키시려면 여기 (http://www.ogre3d.org/docs/manual/manual_16.html#SEC64) 를 참조하세요 .
안개를 어둠처럼 사용하기
안개를 설정하고 항상 하늘을 사용하지 않아도 됩니다 . 왜냐하면 안개가 너무 짙으면 어쨋든 하늘은 안보이기 때문입니다 . 지금까지 설명한 안개의 설명에 의하면 한정된 경우 멋진 그래픽트릭을 쓸 수 있게끔 해줍니다 . 안개를 밝은 색상 대신에 매우 어두운 색상으로 만들면 어떻게 되는지 봅시다 ( 지금 작성한 코드의 SkyPlane 은 카메라와 겨우 10 단위거리 밖에 떨어져 있지 않습니다 .) :
ColourValue fadeColour(0.1, 0.1, 0.1);
mWindow->getViewport(0)->setBackgroundColour(fadeColour);
mSceneMgr->setFog(FOG_LINEAR, fadeColour, 0.0, 10, 150);
mSceneMgr->setWorldGeometry("terrain.cfg");
Plane plane;
plane.d = 10;
plane.normal = Vector3::NEGATIVE_UNIT_Y;
mSceneMgr->setSkyPlane(true, plane, "Examples/SpaceSkyPlane", 100, 45, true, 0.5, 150, 150);
컴파일하고 실행시켜 보세요 . 이게 그 결과입니다 :
나쁘진 않군요 . 물론 가능하다면 적절한 조명으로도 이런 효과를 낼 수 있습니다 . 그러나 이 방법은 안개의 유연한 기능을 보여주고 오우거엔진으로 할 수 있는 재미있는 기능이기도 합니다 . 검은 안개는 FPS 게임에서 " 시력약화 " 나 " 암흑 " 과 같은 마법 효과를 낼 수 있는 흥미로운 기법이 될 수도 있습니다 .
Ogre3D 삽질란/Basic Tutorial 2 2008. 11. 8. 17:14
입문자 튜토리얼 2: Cameras, Lights, and Shadows
이 튜토리얼 진행중 문제가 발생한다면 Help 포럼 에 문의하세요 .
이 튜토리얼은 독자가 C++ 프로그래밍이 가능하고 오우거 어플리케이션 설정 및 컴파일이 가능하다는 가정하에 진행됩니다 . 이 튜토리얼은 첫번째 튜토리얼을 기초로 작성되었으며 독자는 첫번째 튜토리얼을 거쳐왔다고 가정합니다 .
이 튜토리얼은 지금까지 배운것에 이어서 처럼 오우거 구성의 몇가지를 소개할 것 입니다 . 빛을 중심으로 어떻게 그것들이 오우거에서 그림자를 생성하는지를 다룰 것 입니다 . 카메라의 아주 기초적인 부분 역시도 다룰것 입니다 .
튜토리얼을 진행하면서 독자는 진행단계에 맞추어서 천천히 코드를 스스로 입력하고 결과물을 지켜 볼 필요성이 있습니다 . 오우거엔진의 개념을 따라잡는대에는 이것만한 방법이 없습니다 ! 대충 눈으로 훑고 넘어가지 마세요 .
지난번 튜토리얼처럼 미리 작성된 코드를 기초로 시작할 것 입니다 . TutorialApplication 클래스의 createViewport 와 createCamera 함수를 더 추가할 것 입니다 . 이 두 함수들은 기본 ExampleApplication 클래스 에 정의되어 있지만 이 튜토리얼에서는 어떻게 카메라와 뷰포트가 실제로 생성되고 쓰이는지를 살펴볼 것 입니다 .
프로젝트를 생성하고 아래 소스코드를 입력하세요 :
#include "ExampleApplication.h"
class TutorialApplication : public ExampleApplication
{
protected:
public:
TutorialApplication()
{
}
~TutorialApplication()
{
}
protected:
virtual void createCamera(void)
{
}
virtual void createViewports(void)
{
}
void createScene(void)
{
Entity *ent;
Light *light;
}
};
#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else
int main(int argc, char **argv)
#endif
{
// Create application object
TutorialApplication app;
try {
app.go();
} catch( Exception& e ) {
#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occurred!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
fprintf(stderr, "An exception has occurred: %s\n",
e.getFullDescription().c_str());
#endif
}
return 0;
}
만약 오우거 SDK 를 Windows 에서 사용한다면 "[OgreSDK_DIRECTORY]\samples\include" 디렉토리 (ExampleApplication.h 파일이 있는곳 ) 를 include 가 가능하도록 프로젝트에 추가해 주세요 . 만약 오우거엔진 소스를 직접 사용하신다면 [OgreSource_DIRECTORY]\Samples\Common\include" 을 추가해 주세요 . 다음 진행을 위해서 컴파일이 에러없이 되도록 해두세요 . 그러나 실행은 하지마세요 ! 나중에 이 코드에 더 추가할 계획입니다 . 만약 컴파일에 문제가 생긴다면 Wiki 페이지에서 컴파일러별 설정 정보를 찾아보시고 문제가 지속된다면 Help 포럼에 문의하세요 .
오우거에서의 카메라
카메라는 생성된 장면을 보는데 사용합니다 . 카메라는 SceneNode 처럼 특수 객체로 취급됩니다 . 카메라는 setPosition, yaw, roll, pitch 함수들을 가지고 있고 SceneNode 에 attach 할 수도 있습니다 . SceneNode 처럼 카메라의 위치는 그 부모객체에 상대적인 위치를 가집니다 . ( 누가 생각 해 냈는지 기막힌 아이디어 입니다. ) 모든 움직임과 회전은 기본적으로 카메라 - SceneNode 를 생각하면 됩니다 . 오우거에서의 카메라는 기대하던것과는 달리 한번에 하나의 카메라만 사용되어야 합니다(일단 지금은 그렇습니다). 한 장면의 일부분을 위해서 1개의 카메라를 생성하고 또 다른 장면의 일부분을 위해 하나의 카메라를 따로 생성한 다음 보고싶은 부분을 보기 위해 카메라를 활성화/비활성화를 하지 않는다는 뜻 입니다. 그 대신에 SceneNode 를 생성해서 " 카메라 거치대 " 처럼 사용하는 방법으로 구현될 수 있습니다 . 장면의 일부분을 표시할때 카메라는 알아서 해당되는 SceneNode 에 위치되어 유저가 보고 싶은 방향을 바라보게 됩니다 . 이 기법은 FrameListener 튜토리얼에서 다시 보게 될 겁니다 .
카메라 생성
ExampleApplication 에서 사용되는 카메라 생성 기본함수를 교체해 봅시다 .
TutorialApplication::createCamera 멤버 함수로 갑시다 . 가장 먼저 해야 할 일은 카메라 생성입니다 . 카메라가 SceneManager 에 포함되어 있어서 SceneManager 객체를 통해서 생성해야 합니다 . 다음 라인을 넣어서 카메라를 생성합니다 :
// create the camera
mCamera = mSceneMgr->createCamera("PlayerCam");
이 명령은 "PlayerCam" 이라는 이름으로 카메라를 생성합니다 . 참고로 카메라의 포인터를 따로 관리하지 않아도 SceneManager 의 getCamera 함수를 이용해서 이름을 기준으로 카메라포인터를 리턴받을 수 있습니다 .
다음으로 해야 할 일은 카메라의 위치와 어디를 바라 볼 지를 설정 하는 것 입니다 .
원점 주위에 객체를 두게 될 것이기 때문에 카메라는 적당한 +z 방향에 두고 원점을 바라보게 될 것 입니다 . 방금 전 코드 다음에 다음 라인을 추가하세요 :
// set its position, direction
mCamera->setPosition(Vector3(0,10,500));
mCamera->lookAt(Vector3(0,0,0));
lookAt 함수는 센스쟁이입니다 . 카메라가 어디에 있던지 yaw, rotate, pitch 과정을 거치지 않고도 유저가 원하는 방향으로 바라보게 해줍니다 . SceneNode 역시 이 함수를 가지고 있어서 Entity 가 바라보는 방향을 다양한 상황에서 손쉽게 원하는 방향으로 향하도록 할 수 있습니다 .
마지막으로 가까운 클리핑 거리를 5 유닛 으로 설정합니다 . 카메라에서 클리핑 거리는 얼마나 가깝거나 멀어야 보이지 않는지를 의미합니다 . 근접 클리핑 거리는 Entity 가 너무 가깝게 있을때 가로막는 객체를 꿰뚫어서 화면을 볼 수 있도록 해 줍니다 . 어떠한 상황에서 객체가 너무 가까이 있으면 화면을 꽉차게 가로막아서 화면의 아주 작은부분을 제외하고는 아무것도 볼 수 없게 됩니다 . 이것 말고도 원거리 클리핑거리도 설정 가능합니다 . 이 수치는 일정한 거리를 넘어선 물체를 렌더링엔진이 그리는것을 중단시킵니다 . 만약 화면상에서 매우 먼 거리에 걸쳐서 많은양의 객체를 그려야 할 경우라면 원거리 클리핑 수치조절은 프레임성능을 올리는데 중요하게 쓰여질 것 입니다 . 근접 클리핑 거리를 설정하기위해 다음 라인을 추가하세요 :
mCamera->setNearClipDistance(5);
원거리 클리핑 영역 설정은 간단하게 setFarClipDistance 를 호출하면 됩니다 ( 보통 스텐실 쉐도우와 같이 쓰면 안되지만 여기 튜토리얼에서는 쓰입니다 ).
Ogre3D 삽질란/Basic Tutorial 2 2008. 11. 8. 17:12
Ogre Viewports
여러개의 카메라를 다룰경우 뷰포트 클래스 개념은 무척이나 유용하게 쓰일 것 입니다 . 이 이야기를 지금 꺼내는 이유는 오우거가 장면을 그릴때 사용할 카메라를 어떻게 선택하는지를 이해하는것이 중요하기 때문이죠 . 한번에 여러개의 SceneManager 가 실행하는것이 가능합니다 . 하나의 스크린을 여러개의 영역으로 분할해서 각각의 카메라들이 각각의 영역을 그리는것 ( 예를 들면 콘솔게임에서 하나의 화면에서 2 인플레이를 하는것 ) 역시 가능합니다 . 이러한 작업들이 가능하지만 세부적인 방법은 Advanced 튜토리얼에서 다루게 될 것 입니다 .
오우거가 어떻게 장면을 그리는지를 이해하려면 오우거의 3 가지 구성요소를 알아야 합니다 : Camera, SceneManager 그리고 RenderWindow 입니다 . RenderWindow 는 다루지 않았습니다만 간단하게 말하면 모든것이 출력되는 윈도우라 생각하시면 됩니다 . RenderWindow 에게 어떤 카메라가 화면에 출력할지를 알려줘야 하며 출력될 화면의 영역도 알려줘야 합니다 . RenderWindow 에게 알려주는 카메라 출력 영역은 유저의 뷰포트 입니다 . 일반적인 오우거 사용에 있어서는 보통 하나의 카메라를 생성하고 화면 전체를 사용하는 RenderWindow 를 등록하므로 단 하나의 뷰포트 객체를 가지게 됩니다 .
이제 뷰포트를 생성하기위해 카메라를 어떻게 등록하는지를 다뤄 봅시다 . 그 다음 그려질 장면의 배경색을 설정하기위한 뷰포트 오브젝트를 사용할 수 있습니다 .
Viewport 생성
ExampleApplication 의 뷰포트 생성부분을 재정의 하기위해 TutorialApplication::createViewports 함수로 갑시다 . 뷰포트를 만들기 위해서는 단순히 RenderWindow 의 addViewport 함수를 호출하고 어떤 카메라가 사용되는지만 알려주면 됩니다 . ExampleApplication 클래스에는 이미 mWindow 클래스가 RenderWindow 와 함께 자리잡고 있으므로 그냥 다음 라인을 추가하세요 :
// Create one viewport, entire window
Viewport* vp = mWindow->addViewport(mCamera);
이제 뷰포트를 만들었습니다만 이걸로 뭘 할수 있을까요 ? 대답 : 별로 할게 없습니다 . 가장 중요한건 setBackgroundColour 함수로 배경색을 자유롭게 정할 수 있다는 것 입니다 . 조명 튜토리얼에서 했던 것 처럼 배경색을 검정으로 지정해 봅시다 :
vp->setBackgroundColour(ColourValue(0,0,0));
ColourValue 는 red, green 그리고 blue 색상값 가지는데 0 과 1 사이의 값을 취합니다 . 마지막으로 꼭 해야할게 있는데 바로 카메라의 화면 종횡비율 설정입니다 . 만약 정상적인 전체화면 뷰포트의 비율과 다르게 설정하면 장면이 이상하게 보일 것 입니다 . 초기값 종횡비율을 사용하게 될 것이지만 일단 한번 설정 해 봅시다 :
// Alter the camera aspect ratio to match the viewport
mCamera->setAspectRatio(Real(vp->getActualWidth()) / Real(vp->getActualHeight()));
긴단한 뷰포트 클래스의 사용법을 끝냈습니다 . 비록 화면에 나오는게 아무것도 없지만 적어도 어플리케이션을 컴파일 하고 실행할 수 있게 되었습니다 (ESC 누르면 종료 ). 다음 과정으로 넘어가기전에 실행이 잘 되는지를 확인하세요 .
오우거에서 지원하는 그림자 타입들
오우거는 현재 3 가지 종류의 그림자를 지원합니다 :
1. Modulative Texture Shadows (SHADOWTYPE_TEXTURE_MODULATIVE) - 3 가지 방식중에서 가장 적은 계산량을 요구합니다 . 흑백 방식의 그림자를 생성한 다음에 장면에 적용합니다 .
2. Modulative Stencil Shadows (SHADOWTYPE_STENCIL_MODULATIVE) - 이 기법은 불투명 객체들이 화면에 다 그려지고 난 다음에 그림자가 있어야 할 자리는 따로 다시 계산하여 최종적으로 출력합니다 . Additive Stencil Shadows 만큼 부하가 걸리지는 않지만 그 만큼 세밀한 그림자 출력을 해주지는 않습니다 .
3. Additive Stencil Shadows (SHADOWTYPE_STENCIL_ADDITIVE) - 이 기법은 각각의 조명이 따로 화면을 그리는 과정으로 처리 됩니다 . 하나의 조명은 한번의 전체렌더링을 그리는 만큼의 처리량을 필요로 하기때문에 그래픽카드에 많은 부하가 걸리게 됩니다 .
Soft shadow 그림자는 엔진의 기본기능으로는 제공하지 않습니다 . 만약 Soft shadow 를 사용하시고 싶으시면 사용자가 따로 vertex 와 fragment 코드를 작성해야 합니다 . 여기서는 이것에 대한 간단한 소개만 할 것입니다 . 오우거 메뉴얼을 참고하시면 오우거에서의 그림자들에 대한 간략한 사용법이 모두 설명되어 있습니다 .
오우거에서 그림자 사용하기
오우거에서 그림자를 쓰는것은 비교적 쉽습니다 . SceneManager 클래스는 어떠한 그림자를 사용할지를 설정해 주는 setShadowTechnique 멤버 함수를 가지고 있습니다 . 그 다음에 언제든지 Entity 를 만들면 setCastShadows 함수를 호출하는것으로 그림자를 생성하거나 숨길 수 있습니다 .
이제 주변광을 완전한 어둠으로 설정하고 사용하게 될 그림자를 선택합니다 . TutorialApplication::createScene 함수로 가서 다음 코드를 입력해 주세요 :
mSceneMgr->setAmbientLight(ColourValue(0, 0, 0));
mSceneMgr->setShadowTechnique(SHADOWTYPE_STENCIL_ADDITIVE);
이제 SceneManager 는 additive stencil shadows 사용하게 됩니다 . 어서 객체를 화면에 띄워놓고 그림자를 발생시켜 봅시다 :
Entity *ent = mSceneMgr->createEntity("Ninja", "ninja.mesh");
ent->setCastShadows(true);
mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(ent);
설명하자면 ExampleApplication 클래스는 미리 ninja.mesh 를 메모리에 로드시켜놓았습니다 . 그것도 좋지만 우리는 닌자가 서 있을만한 뭔가가 필요합니다 ( 그림자가 뿌려질 뭔가를 말이죠 ). 그렇기에 우리는 닌자가 서 있을만한 간단한 평면을 생성할 것 입니다 . MeshManager 를 기반으로 진행되는 튜토리얼이 된다는 의미는 아니지만 평면을 생성하기위해서 최소한의 기본은 사용할 것 입니다 . 가장먼저 평면 객체 자체를 정의하고 원점으로부터 떨어진 거리 , (Normal) 법선벡터를 설정해야 합니다 . 평면을 지형의 일부분으로 사용할 수 있으며 , 이 경우 원점으로부터 어느정도 떨어져서 위치하도록 해야 할 필요도 있습니다 . 우리는 +y 방향을 향한 ( 앞면이 위로 향한 ) 평면이 필요하고 원점에 위치하게 할 것 입니다 :
Plane plane(Vector3::UNIT_Y, 0);
어플리케이션에서 쓸 수 있도록 평면을 등록합시다 . MeshManager 클래스는 어플리케이션에서 읽어드린 모든 메쉬들을 관리합니다 (robot.mesh 나 ninja.mesh 같이 읽어들였던 것 ). createPlane 멤버함수는 평면의 정의와 매개변수로 부터 메쉬를 생성합니다 . 평면을 쓰기위해 등록합니다 :
MeshManager::getSingleton().createPlane("ground",
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, plane,
1500,1500,20,20,true,1,5,5,Vector3::UNIT_Z);
다시 언급하지만 , MeshManager 가 어떻게 사용되는지를 아직은 알려고 하지 마세요 ( 이 매개변수들이 정확히 뭘 하는지 알고 싶다면 API 레퍼런스를 참고하세요 ). 간단하게 보자면 1500 x 1500 크기의 "ground" 라고 불리는 평면메쉬를 등록했습니다 . 이제 이 메쉬로 Entity 를 만들고 장면에 올려봅시다 :
ent = mSceneMgr->createEntity("GroundEntity", "ground");
mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(ent);
깔끔하죠 ? 이 "ground" 를 위해서 해야할게 2 개 더 남았습니다 . 첫째는 SceneManager 에게 그림자가 표시될 영역이기 때문에 이 지면자체가 그림자를 생성할 필요가 없다고 알리는 것 입니다 . 두번째는 텍스쳐를 입히는 것 입니다 . 우리가 쓰는 로봇과 닌자의 메쉬는 재질 스크립트가 자체적으로 이미 설정되어 있습니다 . 우리가 수동으로 "ground" 메쉬를 생성할 때 어떤 텍스쳐가 사용될지는 설정하지 않았습니다 . 이것에는 오우거 샘플에 포함된 "Examples/Rockwall" 이름의 재질 스크립트를 사용 할 것입니다 :
ent->setMaterialName("Examples/Rockwall");
ent->setCastShadows(false);
이제 지면과 닌자를 장면에 올려놨으니 컴파일하고 실행시켜 봅시다 . 자 , 거기 멋진 닌자가 .. 안보입니다 ! 어떻게 된거죠 ? 이전 튜토리얼에서는 로봇을 넣고 잘 보였습니다 . 닌자가 보이지 않는 이유는 주변광을 완전깜깜한 어둠으로 설정했기 때문입니다 . 자 .. 조명을 추가하고 뭐가 어찌 돌아가는지 봅시다 .
문제점 해결
몇몇 분들께서 이 부분을 진행함에 있어서 code::blocks 에서 GCC 를 사용할 경우 문제점이 있다고 알려줬습니다 . 이런 비슷한 에러 메세지를 발견 하신다면 :
variable 'vtable for Ogre::MeshPtr' can't be auto-imported. Please read the documentation for ld's --enable-auto-import for details.
Linker 옵션에 다음줄을 추가해 주세요 :
-Wl,--enable-runtime-pseudo-reloc
codeblocks::ide 를 사용한다면 project name -> build options -> linker 옵션으로 가서 설정하세요 .
조명 종류
오우거는 3 가지 종류의 조명을 제공합니다 .
1. Point (LT_POINT) - 한개 지점에서 사방으로 빛이 퍼져 나옵니다 .
2. Spotlight (LT_SPOTLIGHT) - spotlight 는 정확하게 손전등처럼 동작합니다 . 빛이 나가는 점이 있고 가리키는 방향쪽으로 빛이 진행됩니다 . 안쪽과 바깥쪽의 빛의 퍼짐 각도도 절정할 수 있습니다 ( 손전등은 주변부 빛보다 중심쪽이 더 밝다는 사실을 아십니까 ?)
3. Directional (LT_DIRECTIONAL) - 방향성을 가진 조명은 한개방향으로 빛이 진행되면서 부딛히는 모든것을 시뮬레이트 해 줍니다 . 예를 들면 밤의 배경에서 달빛을 구현하고 싶습니다 . 옅은 주변광으로 비슷하게 구현할 수 있을지 모르겠지만 달빛이 모든 사물을 똑같이 비춘다는 면 ( 해도 똑같습니다 ) 에서는 비현실 적입니다 . 한가지 방법은 달이 빛나는 지점과 방향성을 가진 조명을 설정하는 것 입니다 .
조명이 어떻게 보일지 설정하는데에는 무척이나 넓은 변수가 작용합니다 . 가장 중요한 2 가지 조명속성으로는 산란 (diffuse) 과 정반사 (specular) 색상입니다 . 각각의 재질은 반사될때 어느정도의 산란광 및 정반사광 수치를 가지는지 정의되는데 추후 튜토리얼에서 어떻게 제어하는지를 다룰 예정입니다 .
조명 생성
조명생성을 위해서는 SceneManager 의 createLight 멤버함수에 를 조명이름을 제공하여 만드는데 Entity 나 Camera 를 생성하는것과 배우 유사합니다 . 조명생성후 위치를 설정하는데 수동으로 할것인지 SceneNode 에 붙여서 이동하는데 유리하게 할지를 선택할 수 있습니다 . 카메라 오브젝트와는 다르게 조명은 setPosition 과 setDirection 만 가능합니다 ( 게다가 translate, pitch, yaw, roll 등과 같은 움직임과 관련된 함수들도 없습니다 ). 그래서 만약 정적인 조명을 만들어야 한다면 setPosition 멤버함수를 호출하면 됩니다 . 만약 움직이는 조명이 필요하다면 ( 캐릭터를 따라다니는 조명같은 ) setPosition 호출 대신에 SceneNode 에 붙여야 합니다 .
기본적인 point 조명을 만들어 봅시다 . 첫번째로 할 일은 조명을 만들고 , 타입을 설정하고 , 위치를 설정하는 것 입니다 :
Light *light = mSceneMgr->createLight("Light1");
light->setType(Light::LT_POINT);
light->setPosition(Vector3(0, 150, 250));
산란광과 반사광을 설정할 수 있는 조명이 만들어 졌습니다 . 빨간 조명으로 만듭니다 :
light->setDiffuseColour(1.0, 0.0, 0.0);
light->setSpecularColour(1.0, 0.0, 0.0);
이제 컴파일후 실행시켜 봅시다 . 해냈습니다 ! 이제 닌자와 그림자를 볼 수 있습니다 . 닌자의 정면으로 카메라를 옮겨서 전체적인 모습을 보세요 . 관심있게 보셔야 할 점은 광원을 볼 수 없다는 것 입니다 . 빛이 생성하는 것은 보이지만 빛 그 자체는 보이지 않습니다 . 많은 오우거 튜토리얼에서는 빛이 어디에서 비추고 있는지를 보여주기 위해 간단한 Entity 를 추가합니다 . 만약 조명과 관련해서 어려움이 발생한다면 조명이 정확하게 어디에 있는지를 확인할 수 있도록 뭔가를 생성하는것을 고려할 필요성이 있습니다 .
다음으로 방향성을 가진 조명을 시도해 봅시다 . 닌자의 정면이 정말 새까맣죠 ? 희미한 노란색 조명을 정면방향에서 비춰 봅시다 . 조명을 만들고 색상은 point 조명에서 했던방법 처럼 설정합니다 :
light = mSceneMgr->createLight("Light3");
light->setType(Light::LT_DIRECTIONAL);
light->setDiffuseColour(ColourValue(.25, .25, 0));
light->setSpecularColour(ColourValue(.25, .25, 0));
방향성을 가진 조명은 거리와는 관계없이 동일하게 적용되므로 위치를 설정할 필요없이 방향만 설정해 주면 됩니다 . 지금 생성하는 조명은 +z 에서 -y 방향으로 비추게 될 것입니다 ( 닌자의 45 도 위 - 정면 방향에서 빛이 내려온다고 생각하시면 됩니다 ) :
light->setDirection(Vector3( 0, -1, 1 ));
컴파일하고 실행시켜 봅시다 . 화면상 2 개의 그림자를 볼 수 있습니다 . 방향성 조명이 희마한 만큼 그림자도 희미합니다 . 마지막으로 살펴볼 조명종류는 spotlight 입니다 . 이제 spotlight 를 생성해 봅시다 :
light = mSceneMgr->createLight("Light2");
light->setType(Light::LT_SPOTLIGHT);
light->setDiffuseColour(0, 0, 1.0);
light->setSpecularColour(0, 0, 1.0);
spotlight 가 빛을 낼 방향과 위치 둘다 설정해야 합니다 . 닌자의 오른쪽 어께위로 떠 있는 spotlight 를 생성하고 닌자를 향해서 빛을 발산할 것 입니다 :
light->setDirection(-1, -1, 0);
light->setPosition(Vector3(300, 300, 0));
Spotlight 광선이 얼마나 넓게 퍼질지도 설정할 수 있습니다 . 손전등을 잠시 떠올려 보세요 . 광선의 중심은 주변을 감싸는 빛보다 더 밝습니다 . 이 광선의 넓이요소들을 setSpotlightRange 멤버함수를 호출하여 설정할 수 있습니다 :
light->setSpotlightRange(Degree(35), Degree(50));
컴파일하고 실행시켜 보세요 . 보라색 닌자 ... 살벌하군요 !
Ogre3D 삽질란/Basic Tutorial 2 2008. 11. 8. 17:10
다양한 그림자 타입들
이 데모에서는 SHADOWTYPE_STENCIL_ADDITIVE 방식의 그림자만 설정했습니다 . 다른 2 가지 종류의 그림자 타입도 설정해 보고 어떻게 달라지는지를 살펴보세요 . 게다가 더 찾아보시면 소개되지 않은 그림자관련 SceneManager 함수들도 많이 있습니다 . 다양한 방법들을 시도해 보시고 어떤 결과가 출력되는지 살펴보세요 .
빛 감쇠
조명은 setAttenuation 함수로 빛이 감쇠하되면서 얼마나 멀리까지 뻗어 나갈 수 있게 할 지 설정할 수 있습니다 . point 조명을 호출하는 부분에 감쇠수치를 다르게 해서 설정해 보세요 . 빛에 어떤 영향을 줄까요 ?
SceneManager::setAmbientLight
mSceneMgr 의 setAmbientLight 함수를 시험해 보세요 .
Viewport 배경색
createViewports 의 ColourValue 의 초기치를 바꿔보세요 . 이 상황에서는 검은색이 아닌 다른색으로 하는건 적절한 선택이 아니지만 어떻게 바뀌는지를 알아두시면 도움이 되실 것 입니다 .
Camera::setFarClipDistance
createCamera 에서 근접 클리핑 거리를 설정했었습니다 . setFarClipDistance 함수를 수치값 500 으로 호출하는 부분을 추가해 보고 닌자가 보이지 않을때까지 이동해 보시면서 stencil shadows 가 변하는것을 지켜보세요 . 뭔가 타이밍이 맞지 않음을 보셨습니까 ?
참고 : 이 현상발생을 위해서는 mSceneMgr->setShadowUseInfiniteFarPlane(false) 를 설정해 주셔야 하며 뭔가 어색한 그림자를 보실 수 있을겁니다 .
평면
이번 튜토리얼에서는 평면에 대해서 많은부분을 다루지 못했습니다 ( 이번 튜토리얼의 주제가 아니었기 때문이죠 ). 나중에 있을 튜토리얼에서 이 주제에 대해서 다시 다룰 예정이지만 지금 createPlane 함수와 관련된 정보를 찾아보시고 직접 매개변수를 입력해 보시고 좀 더 다뤄보시길 권합니다 .
Ogre3D 삽질란/Basic Tutorial 1 2008. 11. 8. 16:58
입문자 튜토리얼 1: SceneNode, Entity, SceneManager 의 구성
이 튜토리얼을 진행하다가 문제점이 생기면 Help 포럼 (http://www.ogre3d.org/phpBB2/viewforum.php?f=2) 에 문의하세요 .
이 튜토리얼은 C++ 프로그래밍을 할줄 알고 오우거 어플리케이션을 셋업하고 실행할 줄 안다는 가정하에 진행됩니다 . ( 만약 오우거 어플리케이션 설정에 문제점이 있으면 컴파일러별 프로그램 설정 가이드를 참고하세요 . - 역자주 : 프로그램 설정 가이드는 번역을 생략합니다 ) 이 튜토리얼은 설정가이드에서 설정이외의 사전 지식은 필요 없습니다 ..
이번 튜토리얼에서는 오우거엔진에서 가장 기초적인 구성물을 소개합니다 . ( 장면관리자 -SceneManager, 장면노드 -SceneNode, 엔티티 -Entity 객체 ) 앞으로 코드의 많은부분을 다루지는 않을것이지만 독자가 오우거 엔진을 시작하는데 필요한 일반적인 컨셉들에 촛점을 맞출 것 입니다 .
튜토리얼을 진행하면서 독자는 진행단계에 맞추어서 천천히 코드를 스스로 입력하고 결과물을 지켜 볼 필요성이 있습니다 . 오우거엔진의 개념을 따라잡는대에는 이것만한 방법이 없습니다 ! 대충 눈으로 훑고 넘어가지 마세요 .
최초 코드
이 튜토리얼에서는 미리 작성된 기본코드를 사용할 것입니다 . 나중에 내용을 채우게 될 createScene 함수를 제외하고는 모두 무시하셔도 됩니다 . 나중에 오우거 어플리케이션이 어떻게 동작되는지를 좀 더 자세하게 살펴볼 예정이지만 지금은 이제 막 시작한 단계에 불과합니다 . 독자가 사용하는 컴파일러에서 프로젝트를 생성하고 아래 코드를 포함한 소스파일을 추가하세요 .
#include "ExampleApplication.h"
class TutorialApplication : public ExampleApplication
{
protected:
public:
TutorialApplication()
{
}
~TutorialApplication()
{
}
protected:
void createScene(void)
{
}
};
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else
int main(int argc, char **argv)
#endif
{
// Create application object
TutorialApplication app;
try {
app.go();
} catch( Exception& e ) {
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
MessageBox( NULL, e.what(), "An exception has occurred!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
fprintf(stderr, "An exception has occurred: %s\n",
e.what());
#endif
}
return 0;
}
만약 오우거 SDK 를 Windows 에서 사용한다면 "[OgreSDK_DIRECTORY]\samples\include" 디렉토리 (ExampleApplication.h 파일이 있는곳 ) 를 include 가 가능하도록 프로젝트에 추가해 주세요 . 만약 오우거엔진 소스를 직접 사용하신다면 [OgreSource_DIRECTORY]\Samples\Common\include" 을 추가해 주세요 . 비록 이 코드가 까만 화면에 프레임수를 보여주는 썰렁한 화면만 보여주는 프로그램이지만 일단은 컴파일 및 실행이 되도록 해두세요 . 다음 과정에서 화면에 뭔가를 더 추가할 것 입니다 .
프로그램이 동작되면 WASD 키로 움직이고 마우스로 주변을 둘러보는 기능을 합니다 . ESC 키는 종료키 입니다 .
문제점 해결하기
만약 진행하는데 문제점이 발생하면 'Setting Up An Application'(http://www.ogre3d.org/wiki/index.php/SettingUpAnApplication) 에서 컴파일러 속성을 체크해 보거나 Ogre.log 파일에서 좀 더 자세한 에러정보를 참조하세요 . 만약 도움이 필요하다면 포럼을 검색 (http://www.ogre3d.org/phpBB2/search.php) 해 주세요 . 아마 다른 많은 사람들도 똑같은 문제점을 겪었을 겁니다 . 만약 새로운 이슈라면 포럼규칙 (http://www.ogre3d.org/phpBB2/viewtopic.php?t=11886) 을 읽어보시고 질문 (http://www.ogre3d.org/phpBB2/viewforum.php?f=2) 을 하세요 . Ogre.log 파일에서 에러와 관련된 항목이나 예외사항 , 에러메세지 , 컴파일러 디버거에서 제공하는 정보들을 제공하시는 것이 다른 유저들로부터 답변을 받는데 도움이 될 것입니다 .
나중에 있을 튜토리얼에서는 문제점 해결방법을 포함하지 않을 것 입니다 . 그러니 문제점이 발생되면 여기 이어지는 섹션을 주의깊게 살펴보세요 .
Message Box 문제
유니코드를 지원하는 비주얼스튜디오를 사용한다면 다음 에러가 생길 수 있습니다 :
error C2664: 'MessageBoxW' : cannot convert parameter 2 from 'const char *' to 'LPCWSTR'
Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
이 문제는 MessageBox 함수가 유니코드 (Unicode) 를 받아야 할 시점에 안시 (ANSI) 코드를 받아서 일어난 에러입니다 . 이걸 고치기 위해서는 다음라인을 고쳐 주세요 :
MessageBox( NULL, e.what(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
이 라인을 다음 라인처럼 수정합니다 :
MessageBoxA ( NULL, e.what(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
또는 컴파일러에서 유니코드지원 기능을 끄는 방법도 있습니다 . 그러나 그렇게 하면 유니코드지원 기능 자체를 사용하지 못하게 됩니다 .
이 에러가 발생하는 이유는 "MessageBox" 함수가 자동적으로 MessageBoxA (ANSI) 또는 MessageBoxW (Wide/Unicode) 를 프로젝트 설정에 기준하여 선택하기 때문입니다 . 이 코드에서는 ANSI 버젼을 강제적으로 사용하게끔 하게 하여 에러를 고칩니다 .
환결성정 파일이나 DLL 파일이 없는 문제
새롭게 작성한 프로그램을 빌드하려는데 DLL 파일이 없다거나 환경설정파일 (*.cfg) 이 없다고 빌드가 안된다면 아마 OgreSDK 폴더로부터 복사하지 않았기 때문일 겁니다 . 비주얼스튜디오 에서 릴리즈 모드로 빌드할때 릴리즈 실행파일은 [ProjectFolder]\bin\release 에 생성하고 디버그 실행파일은 [ProjectFolder]\bin\debug 폴더에 생성됩니다 . 즉 OgreSDK 폴더의 "*.dll" 파일과 "*.cfg" 파일을 해당 폴더로 복사해 주어야 합니다 . 다시 말하면 [OgreSDK]\bin\release 폴더로부터 [ProjectFolder]\bin\release 폴더로 복사하고 [OgreSDK]\bin\debug 로부터 [ProjectFolder]\bin\debug 로 복사해야 한다는 말 입니다 . 그리고 resources.cfg 파일이 옳바른 경로를 가르키도록 수정해줄 필요도 있습니다 . 다음 섹션에서 이 부분에 대해서 더 자세하게 다룰 것 입니다 .
리소스나 플러그인 문제점들
Plugin.cfg 파일과 Resources.cfg 파일을 실행파일과 같은 폴더에 두세요 . Plugin.cfg 파일은 오우거엔진에게 어떤 렌더링 라이브러리가 사용 가능한지 (Direct3D9, OpenGL, 기타등등 ..) 를 알려줍니다 . Resources.cfg 파일은 ExampleApplication 클래스나 텍스쳐 , 메쉬 , 그리고 스크립트가 어디에 위치하고 있는지에 대한 상세한 경로를 기술합니다 . 둘다 텍스트 파일기때문에 어렵지 않게 내부에서 가르키는 경로를 옳바른 경로로 수정할 수 있습니다 . 경로가 잘못된다면 오우거엔진 설정 대화상자로 아무런 렌더링 라이브러리를 선택할 수 없게 되거나 화면상으로 에러메세지를 출력하거나 Ogre.log 파일에 다음과 비슷한 에러메세지를 보게 될 겁니다 .:
Description: ../../Media/packs/OgreCore.zip - error whilst opening archive: Unable to read zip file
이 경우에는 Resources.cfg 파일을 열고 폴더 위치를 오우거엔진에 포함된 미디어 폴더가 위치한 포인트로 경로를 바꿔주세요 . 알아 두셔야 할것은 $( 변수 ) 와 같은 환경경로변수 는 이 파일 내부에서 사용할 수 없습니다 .
비주얼 스튜디오에서 프로그램이 실행되지 않는문제
만약 비주얼스튜디오 또는 비주얼 C++ 에서 어플리케이션을 생성하고 실행하는데 환경설정문제가 있다면 다수의 경우 디버거 설정문제 입니다 . 만약 플레이버튼 ( 메뉴에서 Start Debugging 을 누르는것과 동일 ) 을 누르고 환경설정 파일 (*.cfg) 을 찾을 수 없다는 예외처리 메세지를 보게 된다면 작업 디렉토리 (Working Directory) 가 제대로 설정되지 않았다는 뜻 입니다 .
이러한 문제점을 해결하기위한 정확한 해결방법은 비주얼 C++ 버젼에 따라서 다양하기때문에 제가 정확한 해결방법을 제시할 수 는 없지만 기본적인 해결 방법은 동일합니다 . 프로젝트의 Solution explorer 에서 (Solution 자체가 아닙니다 ) 오른쪽 클릭을 하고 properties 메뉴로 갑니다 . 어딘가에 디버깅 옵션을 위한 속성이 있을겁니다 . 디버깅 옵션에는 "Working Directory" 입력창이 있을겁니다 . 그 입력창에 해당 프로젝트의 실행파일이 생성 되는 위치로 바꿔주세요 .
만약 여기다가 뭘 입력해야 할지 모르겠으면 "Command" 입력란에 입력된 내용을 똑같이 "Debugging" 에 써 넣어주세요 . 예를 들면 비주얼 C++ 2003 에서는 "Command" 입력란은 "..\..\bin\$(ConfigurationName)\$(TargetFileName)" 과 비슷해야 합니다 . "Working Directory" 의 내용에서는 파일명 (TargetFileName) 을 제거해야 합니다 . 이런 경우에는 작업디렉토리는 "..\..\bin\$(ConfigurationName)" 가 되어야 합니다 . 정확한 입력은 비주얼 C++ 버젼에 따라 다르고 빌드환경에 따라서도 달라집니다 . 그렇기 때문에 이 작업을 하기전에 Command 입력란을 체크해 두세요 . 작업디렉토리의 릴리즈모드와 디버그 모드 둘다 바꾸는것도 잊지 마세요 .
비주얼 C++ 2005 에서는 전체적으로 많이 다를겁니다 . "..\..\bin\$(ConfigurationName)" 로 먼저 해보시는걸 권장하고 , 여전히 문제가 지속된다면 Help 포럼에서 상담을 받아보세요 .
이렇게 하는 이유는 오우거엔진은 실행파일과 동일한 디렉토리에 특정한 중요한 필수 파일들을 필요로 하기때문에 Working Directory 는 필수적으로 있어야 하는 파일들이 포함된 디렉토리가 되어야 합니다 ..
Ogre3D 삽질란/Basic Tutorial 1 2008. 11. 8. 12:06
광범위한 주제군요 . 앞으로 여러타입의 SceneManager 와 Entity, SceneNode 들과 함께 작업할 것 입니다 . 이 3 가지 클래스들은 오우거 어플리케이션을 구성하는 중요한 구성원들 입니다 ..
SceneManager 기초
화면상에 표시되는 모든것은 SceneManager 에 의해 관리됩니다 ( 상상해 보세요 ). 어떠한 객체들을 화면에 표시하면 SceneManager 는 그 객체들의 위치와 움직임을 관리하는 클래스 입니다 . 장면을 보기위해 카메라를 생성하면 SceneManager 는 그 카메라 ( 추후 튜토리얼에서 다루게 됩니다 ) 의 위치를 관리합니다 . 평면 , 빌보드 , 빛 , 기타등등 .. SceneManager 는 사용자가 생성한 모든것을 관리합니다 .
SceneManager 는 여러가지 타입이 있습니다 . 지형을 그리는 SceneManager, BSP 맵을 그리는 SceneManager, 그리고 더 많은것들이 있습니다 . 다양한 타입의 SceneManager 는 여기서 (http://www.ogre3d.org/wiki/index.php/SceneManagersFAQ) 확인할 수 있습니다 . 튜토리얼을 진행하면서 점점 다양한 SceneManager 들을 다루게 될 것입니다 .
Entity 기초
Entity 는 장면에 그릴 수 있는 객체타입중 하나입니다 . 3D 메쉬로 표현할 수 있는 모든것은 Entity 라고 생각하셔도 됩니다 . 로봇도 Entity 가 될수 있고 물고기 , 걸어다닐 수 있는 넓은 땅떵어리도 넓은 형태의 Entity 가 될 수 있습니다 . 그러나 빛 , 빌보드 , 파티클 , 카메라 및 몇가지 들은 Entity 가 될 수 없습니다 .
알아두셔야 할 것은 장면에 그려질 수 있는 객체들은 그들의 위치와 방향 정보와는 별개로 분리되어 있다는 것 입니다 . 무슨 의미냐면 하나의 Entity 를 단 한번의 과정만으로는 그릴 수 없다는 뜻 입니다 . 그 대신에 Entity 를 SceneNode 객체에 (attach) 붙여야 하며 이 SceneNode 가 붙여진 Entity 의 위치와 방향에 대한 정보를 가지게 됩니다 .
SceneNode 기초
앞서 얘기한대로 , SceneNode 는 attach 된 모든 객체들에 대한 방향과 위치정보를 가지고 있습니다 . Entity 를 생성했을때 SceneNode 에 attach 하기 전에는 실제적으로 그려지지 않습니다 . 추가로 말씀드리자면 SceneNode 자체는 화면에 출력되는 객체가 아닙니다 . SceneNode 를 생성하고 Entity 를 attach 해야만 화면상에 뭔가가 그려지게 됩니다 .
SceneNode 는 attach 되는 객체의 수에 제한을 받지 않습니다 . 예를 들자면 화면을 돌아다니는 캐릭터가 하나 있고 스스로 빛을 내는 후광효과를 내고 싶다고 합시다 . 이것을 구현하기위해서 먼저 SceneNode 를 생성하고 캐릭터 Entity 를 생성한 뒤 해당 SceneNode 에 attach 합니다 . 다음 빛 객체를 생성한 뒤 아까 생성했던 SceneNode 에 attach 합니다 . SceneNode 는 다른 SceneNode 에 attach 가능하며 이것은 node 들의 계층적 구도를 구현할 수 있도록 해 줍니다 . 나중에 있을 튜토리얼에서 더 나은 SceneNode attachment 사용법에 대해서 다룰 예정입니다 .
SceneNode 의 가장 중요한 컨셉중 하나는 SceneNode 의 위치는 항상 부모 SceneNode 에 상대적 이라는 것 과 각각의 SceneManager 들은 최초의 root node를 가지는데 그 root node 로 다른 모든 SceneNode 들이 attach 된다는 것 입니다 .
자 , 이제 앞서 작성해 둔 코드로 돌아갑시다 . TutorialApplication::createScene 멤버함수를 찾으세요 . 이 튜토리얼에서는 이 함수의 내용만 수정할 것 입니다 . 우리가 먼저 해야 할 일은 우리가 뭘 하고 있는지를 확인할 수 있도록 주변광 (ambient light) 를 설정하는 것 입니다 . 우리는 setAmbientLight 을 호출하고 적용할 컬러를 설정할 것 입니다 . 참고로 ColourValue 생성자는 red, green, blue 3 가지 색상값을 받는데 각각은 0 과 1 사이의 실수 범위를 가집니다 . 이 라인을 createScene 에 추가하세요 :
mSceneMgr->setAmbientLight( ColourValue( 1, 1, 1 ) );
다음으로 해야할 일은 Entity 를 생성하는 것 입니다 . SceneManager 의 createEntity 멤버함수호출에서 처리합니다 :
Entity *ent1 = mSceneMgr->createEntity( "Robot", "robot.mesh" );
여기서 잠깐 몇 가지 질문들이 예상되는군요 . 가장먼저 mSceneMgr 은 어디서 온것이며 관련함수 호출시 어떤 매개변수를 줘야 하는걸까요 ? 여기서 mSceneMgr 변수는 현재의 SceneManager 객체 (ExampleApplication 클래스에서 이미 생성되어 져 있습니다 ) 를 가르킵니다 . createEntity 의 첫번째 매개변수는 만들고자 하는 Entity 의 이름을 의미합니다 . 모든 Entity 들은 고유한 이름을 가져야 합니다 . 만약 2 개의 Entity 들이 같은 이름을 가진 다면 에러를 발생시킬 것 입니다 . "robot.mesh" 매개변수는 Entity 로 사용될 메쉬를 의미합니다 . 전체적으로 다시말해 , ExampleApplication 클래스가 앞으로 사용되어질 메쉬를 미리 메모리에 로드시켜놓은 것 입니다 .
지금 우리는 Entity 를 생성했고 이제는 SceneNode 에 attach 해야 합니다 . 모든 SceneManager 는 루트 SceneNode 를 가지고 있고
우리는 그 루트 SceneNode 로부터 자식 node 를 생성해 나갈 것 입니다 .
SceneNode *node1 = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode" );
이 긴 명령은 먼저 현재 사용되는 SceneManager 통해서 getRootSceneNode 를 부릅니다 . 다음 그 결과값 포인터 ( 루트 SceneNode 입니다 ) 로부터 createChildSceneNode 를 호출하게 됩니다 . createChildSceneNode 의 매개변수는 만들어질 SceneNode 의 이름입니다 . Entity 클래스처럼 2 개 의 SceneNode 들이 같은 이름을 가질 수는 없습니다 .
드디어 , 로봇이 그려질 위치를 전달하기위해 Entity 를 SceneNode 로 attach 할 차례입니다 :
node1->attachObject( ent1 );
그리고 끝입니다 ! 컴파일하고 어플리케이션을 실행하세요 . 화면에 서 있는 로봇 하나를 발견하실것 입니다 .
참고 : Robot.mesh 는 OgreCore.zip 파일에 포함되어 있지 않습니다 . 튜토리얼을 따르면서 지금 이 시점에서 어플리케이션이 실행은 되지만 아무것도 표시되지 않을 수 있습니다 . 어플리케이션이 정상적으로 실행되기 위해서 resources.cfg 파일에서 디렉토리를 옳바르게 수정해야 합니다 :
FileSystem=../../media/materials/programs
FileSystem=../../media/materials/scripts
FileSystem=../../media/materials/textures
FileSystem=../../media/models
더 진행하기 전에 , 스크린 좌표계와 오우거 벡터 객체에 대해서 알아야 합니다 . 오우거 ( 다른 그래픽 엔진들과 마찬가지로 ) 는 x, z 축으로 수평면을 표현하고 , y 축은 수직축으로 사용됩니다 . 지금 모니터를 바라보는 시점에서 x 축은 왼쪽에서 오른쪽 방향이며 오른쪽 방향이 x 축 양의 방향입니다 . y 축은 모니터 바닥에서 윗쪽 방향이며 모니터의 상위쪽이 y 축 향의 방향입니다 . z 축은 모니터 내부에서 바깥쪽 방향이며 스크린 바깥쪽 방향이 z 축 양의 방향입니다 .
왜 로봇이 x 축 양의 방향을 바라보고 있을까요 ? 이건 메쉬자체의 속성이자 , 메쉬가 어떻게 디자인 되었는가에 따라 다릅니다 . 오우거는 사용되는 모델의 방향에 대해서는 상관하지 않습니다 . 로드되어지는 각각의 메쉬는 " 기본 시작방향 " 이 제각각 다를 수 있습니다 .
2 차원 (Vector2), 3 차원 (Vector3), 4 차원 (Vector4) 이렇게 다양한 차원을 위해서 벡터 클래스가 정의되어 있으며 이중 Vector3 가 가장 자주 쓰입니다 . 만약 벡터개념에 익숙치 않다면 오우거를 이용해 본격적으로 뭔가를 하기전에 복습 하시기를 권합니다 .
(http://en.wikipedia.org/wiki/Vector_%28spatial%29)
수학적인 벡터 지식은 복잡한 프로그램을 다룰때 무척 유용하게 쓰입니다 .
이제 좌표계가 어떻게 적용되는지 이해했으니 살펴보던 코드로 돌아갑시다 . 작성했던 3 줄의 코드에서 로봇이 나타날 위치를 설정한 부분은 없습니다 . 대부분의 오우거 함수들은 디폴트 매개변수값을 가지고 있습니다 . 예를 들면 SceneNode::createChildSceneNode 멤버함수는 3 개의 매개변수를 가지는데 SceneNode 의 이름 , SceneNode 의 위치 , 마지막으로 SceneNode 가 바라볼 최초 회전값을 가집니다 . 위치값은 보시는것 처럼 (0, 0, 0) 이 적용되었습니다 . 이번에는 원점에서 떨어진 위치에다가 추가적인 SceneNode 를 만들어 봅시다 :
Entity *ent2 = mSceneMgr->createEntity( "Robot2", "robot.mesh" );
SceneNode *node2 = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode2", Vector3( 50, 0, 0 ) );
node2->attachObject( ent2 );
예전과 비교해 별반 다를바 없어 보입니다 . 2 가지 부분을 제외하곤 이전에 했던것과 완전 똑같은 코드를 입력했습니다 . 가장 먼저 Entity 와 SceneNode 이름을 조금 다르게 설정했습니다 . 두번째는 시작위치를 root SceneNode 로 부터 x 축에서 50 단위 거리만큼 떨어지게 설정했습니다 ( 기억해야 할 것은 모든 SceneNode 위치는 그들의 부모와 상대적인 위치를 가집니다 ). 컴파일하고 데모를 실행시켜 보십시오 . 2 개의 로봇이 나란히 있습니다 .
Entity 클래스는 매우 광범위 하며 , Entity 객체의 모든 사용법을 여기서 설명하지는 않을 것 이지만 .. 시작하기에는 충분할 것 입니다 . 지금 바로 쓰기에 유용한 몇몇 Entity 멤버함수들을 여기서 집고 넘어가고 싶습니다 .
첫번째는 Entity::setVisible 와 Entity::isVisible 입니다 . 간단하게 이 함수를 호출하는것만으로 어떠한 Entity 라도 보여지거나 숨겨질 수 있습니다 . 만약 Entity 를 숨기고 나중에 표시해야 한다면 Entity 를 파괴하고 다시 생성하는것 대신에 이 함수를 호출하세요 . 참고로 Entity 들을 위해서 "Pool" 을 준비할 필요는 없습니다 . 메모리에 읽혀질 때마다 모든 객체의 메쉬와 텍스쳐는 단 한번만 실제적으로 메모리에 적재되기 때문에 메모리공간을 절약하기위해 노력하실 필요가 없습니다 . 오직 상대적으로 부담이 적은 Entity 생성과 파괴에 대한 비용부담만 생각하시면 됩니다 .
getName 함수는 Entity 의 이름을 반환하며 , getParentSceneNode 함수는 해당 Entity 가 attach 되어 있는 SceneNode 를 반환합니다 .
SceneNode 클래스는 매우 복잡합니다 . SceneNode 로 할 수 있는 많은것들이 있는데 가장 유용한 몇가지의 기능들을 살펴볼 것입니다 . getPosition 과 setPosition 을 이용해 SceneNode 의 위치를 설정하거나 얻어낼 수 있습니다 . SceneNode 에 연관된 객체의 위치를 translate 명령을 통해서 이동이 가능합니다 . SceneNode 는 객체의 위치를 설정하는것 뿐만 아니라 크기와 회전역시 담당합니다 . Scale 함수를 이용해서 객체의 크기를 설정할 수 있습니다 . pitch, yaw, 그리고 roll 함수로 객체를 회전시킬 수 있습니다 . resetOrientation 를 통해서 수행된 모든 회전을 리셋시킬 수도 있습니다 . setOrientation, getOrientation 그리고 rotate 함수들을 이용해서 좀 더 세부적인 회전을 수행할 수도 있습니다 . Quaternions( 사원수 ) 에 대해서는 많은 튜토리얼을 다루기 전에는 언급하지 않을 예정입니다 .
독자는 이미 attachObject 를 본 적이 있을것입니다 . 만약 SceneNode 에 attach 된 객체에 변화를 주고 싶다면 여기에 소개되는 연관된 함수들이 매우 유용하게 쓰일 것입니다 : numAttachedObjects, getAttachedObject ( 여러가지 상황별 함수들이 있음 ), detachObject ( 이것 역시 다양한 상황별 함수가 있음 ), detachAllObjects. 부모 SceneNode 와 자식 SceneNode 를 다루는 함수들은 서로 동일하게 쌍으로 존재합니다 .
모든 위치이동이 부모 SceneNode 에 의해 연관되어 수행되는 이상 2 개의 SceneNode 들을 함께 이동시키는건 매우 쉽습니다 . 현재 어플리케이션 소스에는 다음코드가 존재합니다 :
Entity *ent1 = mSceneMgr->createEntity( "Robot", "robot.mesh" );
SceneNode *node1 = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode" );
node1->attachObject( ent1 );
Entity *ent2 = mSceneMgr->createEntity( "Robot2", "robot.mesh" );
SceneNode *node2 = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode2", Vector3( 50, 0, 0 ) );
node2->attachObject( ent2 );
만약 5 번째 줄을 이 상태에서 :
SceneNode *node2 = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode2", Vector3( 50, 0, 0 ) );
이렇게 고치게 된다면 ? :
SceneNode *node2 = node1->createChildSceneNode( "RobotNode2", Vector3( 50, 0, 0 ) );
RobotNode 의 자식노드 RobotNode2 를 만들게 됩니다 . node1 을 움직이는것은 node2 도 같이 움직이게 되지만 node2 의 이동은 node1 에 영향을 주지 않습니다 . 예를 들어 이 코드는 RobotNode2 만 이동시킵니다 .
node2->translate( Vector3( 10, 0, 10 ) );
다음의 코드는 RobotNode 를 움직이고 RobotNode2 가 RobotNode 의 자식인 이상 RobotNode2 도 동일하게 같이 움직이게 됩니다 :
node1->translate( Vector3( 25, 0, 0 ) );
이런 수행을 하는데 계산하는데 어려움이 있다면 가장 좋은 방법은 최상위 루트 SceneNode 로 시작하여 아래방향으로 진행하는 것 입니다 . 예를 들면 ( 본문에서 든 예처럼 ), node1 로부터 시작하여 (0, 0, 0) 을 (25, 0, 0) 으로 이동시킴으로 node1 의 위치는 (25, 0, 0) 만큼 부모의 위치로부터 상대적인 공간에 위치하게 됩니다 . node2 는 (50, 0, 0) 에서 시작하여 (10, 0, 10) 으로 이동시 켰으므로 새로운 위치는 (60, 0, 10) 만큼 부모의 위치로부터 상대적인 공간에 위치하게 됩니다 .
자 , 이것들이 실제로 어디에 위치하는지 알아봅시다 . 루트 SceneNode 에서 시작합니다 . 이것의 위치는 항상 (0, 0, 0) 입니다 . 지금 node1 의 위치는 (root + node1) : (25, 0, 0) = (25, 0, 0) 이 되겠죠 . 별거 아니군요 .
자 , 그럼 현재 node2 는 node1 의 자식이므로 이것의 위치는 (root + node1 + node2): (0, 0, 0) + (25, 0, 0) + (60, 0, 10) = (85, 0, 10) 가 됩니다 .
이건 SceneNode 의 계층적인 위치 구조를 설명하기위한 예시에 불과합니다 . 아마 아주 가끔씩 노드들의 절대위치를 계산해야 할 경우가 생길겁니다 .
마지막으로 알아두실 것 은 SceneNode 와 Entity 의 이름은 getSceneNode 와 getEntity 함수 호출로 얻을 수 있기 때문에 만드는 SceneNode 마다 포인터들을 따로 보관해 둘 필요가 없습니다 . 특별히 자주 쓰는것만 쓰기 쉽도록 관리해 주시면 됩니다 .
Ogre3D 삽질란/Basic Tutorial 1 2008. 11. 8. 12:04
이제 여러분은 Entity, SceneNode 그리고 SceneManager 의 기본기를 익혔습니다 . 위에서 언급한 소스로부터 로봇을 추가하고 제거하는것을 해보길 권합니다 . 다 해보셨으면 createScene 내용을 모두 지우고 다음의 소스코드들을 동작시켜 보시기 바랍니다 :
Scale
SceneNode 의 scale 함수를 이용해서 메쉬의 크기를 조절할 수 있습니다 . 스케일 수치를 바꿔보고 어떻게 바뀌는지 확인해 보세요 :
Entity *ent1 = mSceneMgr->createEntity( "Robot", "robot.mesh" );
SceneNode *node1 = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode" );
node1->attachObject( ent1 );
node1->scale( .5, 1, 2 );
Entity *ent2 = mSceneMgr->createEntity( "Robot2", "robot.mesh" );
SceneNode *node2 = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode2", Vector3( 50, 0, 0 ) );
node2->attachObject( ent2 );
node2->scale( 1, 2, 1 );
Rotations
yaw, pitch, roll 함수에 Degree 또는 Radian 수치를 적용해서 객체를 회전시킬 수 있습니다 . Pitch 는 x 축을 주위로 회전합니다 . yaw 는 y 축 그리고 roll 은 z 축을 중심으로 회전합니다 .
오른손으로 방향을 알 수 있습니다 : 엄지손가락을 축으로 둔 다음 다른 손가락이 가르키는 방향이 양의 각도입니다 . 예를 들면 pitch(Degree(90)) 라면 엄지손가락은 오른쪽을 가르키고 다른 손가락은 회전방향을 의미합니다 . 차근차근 생각해 보세요 .
각도 (Degree) 의 수치 조절과 위치변화를 다양하게 조합해 보세요 :
Entity *ent1 = mSceneMgr->createEntity( "Robot", "robot.mesh" );
SceneNode *node1 = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode", Vector3( -100, 0, 0 ) );
node1->attachObject( ent1 );
node1->yaw( Degree( -90 ) );
Entity *ent2 = mSceneMgr->createEntity( "Robot2", "robot.mesh" );
SceneNode *node2 = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode2");
node2->attachObject( ent2 );
node2->pitch( Degree( -90 ) );
Entity *ent3 = mSceneMgr->createEntity( "Robot3", "robot.mesh" );
SceneNode *node3 = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode3", Vector3( 100, 0, 0 ) );
node3->attachObject( ent3 );
node3->roll( Degree( -90 ) );
OgreSDK 의 "bin" 폴더 아래의 debug 또는 release 폴더에서 볼 수 있는 많은 파일들 (*.dll, *.cfg) 을 이 섹션에서 참조할 수 있습니다 . 디버그버젼으로 생성된 프로그램은 OgreSDK 의 debug 폴더내의 파일들을 이용하고 릴리즈버젼의 프로그램은 release 폴더의 파일들을 사용하게 됩니다 .
이 섹션의 대부분은 윈도우즈 환경하에서 다뤄집니다 . 리눅스에서도 기본적인 사항들은 동일하게 적용되지만 몇몇 사항들은 약간 다를 수 있습니다 . 만약 리눅스에서 오우거엔진 사용시 문제가 발생하면 오우거 help 포럼에 글을 남겨주세요 .
DLLs and Plugins
우리는 방금 오우거 환경에서 흥미로운 체험을 해봤습니다 . 이제 평상시에 오우거 라이브러리를 사용할때 어떤 편리한 점이 있는지 설명하고자 합니다 .
오우거는 크게 3 개의 중요그룹으로 나눠지는데 , 메인 라이브러리 , 플러그인 그리고 써드파티 라이브러리로 나뉘어 집니다 .
Main library .
첫번째 그룹은 오우거 라이브러리 그 자체와 밀접하게 연관되는 공유 라이브러리들을 포함합니다 . OgreMain.dll 에 오우거본체 라이브러리가 포함됩니다 . 이 dll 파일은 cg.dll 과 같은 몇몇 다른 라이브러리를 필요로 합니다 . 이 DLL 들은 반드시 항상 오우거 어플리케이션과 함께 포함되어야 합니다 .
Plugins .
두번째 공유 라이브러리는 플러그인 입니다 . 오우거는 효율적인 기능성분할을 위해 공유라이브러리로 기능을 나누었으며 이 라이브러리들은 유저의 어플리케이션이 필요로 하는 정도에 따라서 기능을 제공할 수 도있고 그렇지 않을 수 도 있습니다 . 오우거와 함께 사용되는 기본적 플러그인들은 "Plugin_" 이라는 prefix 로 시작되는 파일명을 가집니다 . 유저가 필요로하는 플러그인은 유저 스스로 새로운 플러그인을 작성할 수 있지만 , 그 어떤 튜토리얼에서도 다루지는 않을 계획입니다 . 오우거는 렌더링 시스템을 위해서도 플러그인을 사용합니다 (OpenGL, DirectX, 기타등등 ). 이러한 플러그인들은 "RenderSystem_" 이라는 prefix 를 가집니다 . 이 플러그인들을 추가하거나 삭제하는것 만 으로도 유저의 어플리케이션의 렌더링 시스템을 설정할 수 있습니다 . 다음과 같은 상황에서는 매우 유용한데 만약 유저가 ( 예를들면 ) OpenGL 에만 해당되는 쉐이더 프로그래밍이나 특별한 작업을 하는경우 또는 DirectX 에서 동작되는 프로그램에서 OpenGL 기능을 강제로 끄고 싶을때 유저는 단순히 해당되는 렌더링시스템 플러그인을 제거하는 것 만으로도 그 기능은 제공되지 않도록 할 수 있십니다 . 추가적으로 만약 유저가 비표준 플랫폼을 염두한다면 튜토리얼에서는 다루지는 않지만 유저만의 렌더링시스템 플러그인을 작성할 수도 있습니다 . 플러그인을 어떻게 제거하는지는 다음 섹션에서 다룰 것 입니다 .
써드파티 라이브러리와 helper 라이브러리 .
세번째 공유라이브러리는 써드파티 라이브러리와 helper 라이브러리입니다 . 오우거 그 자체로는 그냥 그래픽 렌더링 라이브러리일 뿐 입니다 . GUI 시스템이라던지 입력컨트롤 , 물리엔진 , 기타등등 .. 이러한 기능들은 가지고 있지 않습니다 . 다른 라이브러리들을 사용하기위해서 다음과 같은 절차를 거쳐야 합니다 . 오우거 데모들과 SDK 는 소수의 써드파티 helper 라이브러리들을 포함하고 있습니다 . CEGUI 라이브러리는 오우거와 잘 통합된 GUI 시스템이며 "OgreGUIRenderer.dll" 과 "CEGUI*" 로 부터 시작되는 파일들은 GUI 시스템의 일부 입니다 . CEGUI 를 사용하는 방법은 나중에 있을 튜토리얼에서 다루어질 것 입니다 . 키보드와 마우스입력은 OIS( 입력 시스템 ) 을 통해서 최종 처리됩니다 . OIS 는 OIS.dll 을 포함합니다 . 이것들 말고도 다른 기능 ( 물리엔진이나 사운드 ) 들을 제공하는 라이브러리들 역시 존재하며 (SDK 에 포함되지 않은것들 ) 이와 관련된 더 많은 정보는 위키와 같은 다른 포럼에서 찾을 수 있습니다 .
가장 실용적이고 진정한 문제는 바로 눈앞에서 스스로 테스트 해보는 어플리케이션에서 나오며 모든 기능을 켠 상태 ( 아무것도 제거하지 않은상태 ) 하에서 테스트해 볼 수 있습니다 . 유저의 어플리케이션 배포가 준비되었을때 릴리즈모드에서 빌드를 해야 할 것이고 어플리케이션이 필요로 하는 모든 릴리즈용 DLL 파일은 포함되어야 해야 하며 , 쓰지않는 DLL 파일은 제거되어야 할 것 입니다 . 만약 프로그램이 CEGUI 는 쓰지 않지만 OIS 는 쓸때 , CEGUI 관련 DLL 파일들을 포함하는데 고민할 필요는 없지만 OIS DLL 파일은 반드시 포함되어야 합니다 . 그렇지 않으면 어플리케이션은 실행되지 않을 것 입니다 .
환경설정 파일
오우거는 몇몇 환경설정파일을 사용합니다 . 이 파일들은 어떠한 플러그인이 로드되어야 할지 , 어플리케이션의 리소스들은 어디에 위치되었는지 , 기타등등을 제어합니다 . 이제 각각의 환경설정 파일들이 어떠한 일을 하는지 간단하게 살펴볼 것입니다 . 만약 더 세부적인 궁금증이 있다면 바로 오우거 help 포럼으로 보내주세요 .
plugins.cfg 이 파일은 유저의 어플리케이션이 사용하는 플러그인들을 포함합니다 . 만약 유저 어플리케이션에서 플러그인을 추가하거나 제거하고 싶다면 이 파일을 수정하면 됩니다 . 플러그인을 제거하기 위해서는 간단히 해당 라인을 제거하거나 라인 가장 앞에 # 을 붙이는 것으로 주석처리를 하면 됩니다 . 플러그인을 추가하고 싶다면 "Plugin=[PluginName]" 과 같이 라인을 추가하면 됩니다 . 참고로 플러그인 이름 끝에 ".DLL" 을 붙이지 마세요 . 플러그인은 "RenderSystem_" 또는 "Plugin_" 으로 시작되어서도 안됩니다 . 오우거가 플러그인들을 찾는 위치를 수정하기위해 "PluginFolder" 변수를 수정할 수도 있습니다 . 상대경로 , 절대경로 모두 사용가능하지만 $( 변수 ) 의 형태의 환경변수는 사용할 수 없습니다 .
resources.cfg 이 파일은 오우거가 리소스를 찾기위해 검색해야할 디렉토리 리스트를 담고 있습니다 . 리소스는 스크립트 , 메쉬 , 텍스쳐 , 기타 여러가지를 포함하고 있습니다 . 절대경로 , 상대경로 모두 사용 가능하지만 $( 변수 ) 와 같은 환경변수는 사용할 수 없습니다 . 참고로 오우거는 하위폴더를 검색하지 않기때문에 만약 여러단계의 폴더를 입력할 필요가 있다면 일일이 작성해 주어야 합니다 . 예를 들면 "res\meshes" 와 "res\meshes\small" 디렉토리 트리구조를 가진다면 위해 리소스 파일이 포함된 경로의 2 가지 진입점을 리소스 파일에 추가해야 합니다 .
media.cfg 이 파일은 리소스의 일부분에 대한 상세한 정보를 말해줍니다 . 지금 이 시점에서는 수정할 필요가 없으므로 자세한 설명은 생략하겠습니다 . 더 자세한 정보는 메뉴얼과 오우거 포럼에서 찾을 수 있습니다 .
ogre.cfg 이 파일은 오우거 환경설정 화면에서 생성됩니다 . 이 파일은 유저의 컴퓨터와 그래픽 설정 내용을 기술합니다 . 다른 사람들이 각각 다른 설정을 가지는 것 처럼 유저가 어플리케이션을 배포할때는 이 파일이 같이 포함되어서는 안됩니다 . 유의하실 점은 환경설정을 통하지 않고 이 파일을 직접적으로 수정하시면 안됩니다 .
quake3settings.cfg 이 파일은 BSPSceneManager 와 함께 사용됩니다 . BSPSceneManager( 지금 시점에서는 쓰지 않습니다 ) 를 사용하기 전에는 필요하지 않은 파일이므로 무시하세요 . 유저 어플리케이션에서 필요없다면 배포하지 마세요 . 반면에 , 만약 유저가 BSPSceneManager 를 이용한다면 프로그램의 필요성에 의해 포함될 수 있습니다 .
이 모든 환경설정 파일들은 오우거에 직접적인 영향을 줍니다 . 오우거는 "plugins.cfg", "resources.cfg", 그리고 "media.cfg" 파일들이 실행시 반드시 참조 가능해야합니다 . 나중에 있을 튜토리얼에서는 이 파일들에 관한 내용과 더 세부적인 작업을 위해 어떻게 그들의 위치정보와 내용을 수정해야 할지를 다룰 것 입니다 .
어플리케이션 테스트를 위한 더 나은 환경 구성하기
윈도우즈에서 비주얼 C++ 을 사용하는 사람에게만 해당되는 내용입니다 .
앞서 여러번 언급했던것 처럼 ( 문제해결 섹션을 포함하여 ), 오우거는 유저의 프로그램에서 사용되는 각종 환경설정파일 , DLL, 미디어 리소스 자원들에 대한 접근이 가능해야 합니다 . 많은 사람들은 이 문제를 위해 OgreSDK 의 bin 폴더로 부터 유저의 프로젝트 디렉토리로 필요한 DLL 파일들을 실행파일과 같은 위치로 복사하여 해결합니다 . 이 방법이 아마 게임이든 다른종류의 프로그램이든 배포에 있어서 최고의 방법일 지도 모릅니다 . 배포를 위해서 각종 플러그인을 넣고 빼고 하는 귀찮은 과정을 생략할 수 있기 때문이죠 . 모든 DLL 파일들을 각각의 오우거 프로젝트 폴더로 복사해 넣는것은 공간적 , 시간적 낭비임에도 불구하고 많은 상황에서 발생되고 있습니다 . 여기 그런 낭비를 줄일 수 있는 방법이 몇가지 있습니다 .
한가지 대안은 OgreSDK 의 DLL 파일들을 ( 플러그인 빼고 ) 윈도우즈 시스템 폴더로 복사하는 것 입니다 . 이건 오우거를 이용한 실행파일이 어디에 있던간에 필요한 DLL 파일에 접근할 수 있는게 장점입니다 . 이 작업을 수행하려면 resources.cfg, plugins.cfg 파일에서 media 폴더와 plugins 폴더의 절대경로를 알맞게 각각 수정해 줘야 합니다 . 그럼 어디서 프로젝트를 생성하던지 수정된 환경설정 파일들만 bin\debug 와 bin\release 디렉토리로 부터 복사하기만 하면 됩니다 . 그러나 저는 OgreSDK DLL 파일들의 위치관리가 안될것 같아서 개인적으로는 쓰지 않는 방식입니다 . 오우거는 자주 새버젼이 발표되는데 이 방법으로 업데이트 하려면 좀 귀찮아 집니다 .
더 나은 대안으로는 OgreSDK 파일들은 그대로 놔두고 프로젝트의 작업디렉토리를 OgreSDK 의 bin\release 나 bin\Debug 디렉토리로 설정하는 것 입니다 . 이렇게 하려면 프로젝트의 속성 ( 디버깅 옵션에서 ) 에서 "Working Directory" 입력창 내용을 "C:\OgreSDK\bin\$(ConfigurationName)" 로 바꿔 주세요 . 기본값이 아닌 다른경로로 오우거를 설치했다면 "C:\OgreSDK" 부분을 실제로 설치한 경로로 바꿔줘야 합니다 . 그렇게 하신다면 오우거 프로그램 동작을 위해서 아무런 파일도 복사하지 않아도 됩니다 . 이 접근방법이 제가 개인적으로 사용하는 방법입니다 . 한 가지 단점은 환경설정파일을 수정해야 할 경우 , 모든 오우거 프로젝트를 수정해야 한다는것인데 .. 좀 안좋긴 합니다 . 만약 이 접근방법을 이용하고 유저 프로젝트를 위해 환경설정파일을 수정해야 한다면 그 방법 대신에 하던대로 모든 파일을 프로젝트로 복사해 넣으시고 작업디렉토리를 원래 지정되어졌던 내용으로 바꿔주세요 .
이제 독자는 SceneManager, SceneNode 그리고 Entity 클래스에 대한 기초지식을 습득했습니다 . 여기서 소개드린 모든 함수들에 대해서 억지로 익숙해질 필요는 없습니다 . 소개드린것은 가장 기초적인 객체들이며 앞으로 자주 쓰이게 될 것입니다 . 다음에 있을 몇몇 튜토리얼에서 여기서 소개드린 모든 것이 좀 더 자연스럽게 친숙해 질 것입니다 .
그리고 오우거 환경을 설정하는데에도 익숙해져야 합니다 .