12. TerrainRendering
Posted 2007/09/15 19:27 by 수달-------------------------------------------------------------------------
튜토리얼 : 12. 지형 그리기
------------------------
이번 예제는 IRRLICHT에서 지형(terrain)을 어떻게 그리는지 간단히 보여줄 것이다. 또한 지형 충돌 확인을 위한 '지형 삼각형 선택자(the terrain renderer triangle selector)'를 보일 것이다. 주지할 것은 IRRLICHT의 지형 그리기는 Sprintz의 GeoMipMapSceneNode에 기반한다는 것이며, 매우 감사하게 생각한다.
-------------------------------------------------------------------------
시작
----
시작은 특별한 것이 없다. 헤더 파일을 넣고, 사용자가 와이어 프레임을 보려 할때 쓸 'W'와 지형을 세밀하게 볼지 안 볼지 결정케 하는 'D' 키를 받는 이벤트 리시버만 있으면 된다.
#include <irrlicht.h>
#include <iostream>
using namespace irr;
#pragma comment(lib, "Irrlicht.lib")
class MyEventReceiver : public IEventReceiver
{
public:
MyEventReceiver(scene::ISceneNode* terrain)
{
//지형 포인터를 저장해 원하는데로 그리기 모드를 바꿀 수 있게 한다.
Terrain = terrain;
}
bool OnEvent(SEvent event)
{
// check if user presses the key 'W' or 'D'
if (event.EventType == irr::EET_KEY_INPUT_EVENT && !event.KeyInput.PressedDown)
{
switch (event.KeyInput.Key)
{
case irr::KEY_KEY_W: // switch wire frame mode
Terrain->setMaterialFlag(video::EMF_WIREFRAME, !Terrain->getMaterial(0).Wireframe);
return true;
case irr::KEY_KEY_D: // toggle detail map
Terrain->setMaterialType(
Terrain->getMaterial(0).MaterialType == video::EMT_SOLID ?
video::EMT_DETAIL_MAP : video::EMT_SOLID);
return true;
}
}
return false;
}
private:
scene::ISceneNode* Terrain;
};
main()함수의 시작은 다른 예제와 같다. 사용자에게 원하는 그리기 방식을 묻고 장치를 설정하자.
int main()
{
// let user select driver type
video::E_DRIVER_TYPE driverType = video::EDT_DIRECT3D9;
printf("Please select the driver you want for this example:\n"\
" (a) Direct3D 9.0c\n (b) Direct3D 8.1\n (c) OpenGL 1.5\n"\
" (d) Software Renderer\n (e) Apfelbaum Software Renderer\n"\
" (f) NullDevice\n (otherKey) exit\n\n");
char i;
std::cin >> i;
switch(i)
{
case 'a': driverType = video::EDT_DIRECT3D9;break;
case 'b': driverType = video::EDT_DIRECT3D8;break;
case 'c': driverType = video::EDT_OPENGL; break;
case 'd': driverType = video::EDT_SOFTWARE; break;
case 'e': driverType = video::EDT_SOFTWARE2;break;
case 'f': driverType = video::EDT_NULL; break;
default: return 1;
}
// create device
IrrlichtDevice* device = createDevice(driverType, core::dimension2d<s32>(640, 480));
if (device == 0)
return 1; // could not create selected driver.
우선 자질구레한 것부터 추가하자. IRRLICHT 엔진 로고를 넣고, 작은 도움말을 넣으며, 마우스 커서를 없애는 대신 사용자가 이용 할 수 있는 카메라를 만들자.
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
gui::IGUIEnvironment* env = device->getGUIEnvironment();
driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true);
// add irrlicht logo
env->addImage(driver->getTexture("../../media/irrlichtlogoalpha.tga"),
core::position2d<s32>(10,10));
// add some help text
gui::IGUIStaticText* text = env->addStaticText(
L"Press 'W' to change wireframe mode\nPress 'D' to toggle detail map",
core::rect<s32>(10,453,200,475), true, true, 0, -1, true);
// add camera
scene::ICameraSceneNode* camera =
smgr->addCameraSceneNodeFPS(0,100.0f,1200.0f);
camera->setPosition(core::vector3df(1900*2,255*2,3700*2));
camera->setTarget(core::vector3df(2397*2,343*2,2700*2));
camera->setFarValue(12000.0f);
// disable mouse cursor
device->getCursorControl()->setVisible(false);
'지형 그리기' 씬 노드를 구성할 차례다. ISceneManager::addTerrainSceneNode()를 사용해 다른 씬 노드처럼 더하기만 하면 된다. 하나뿐인 인자는 사용할 높이맵의 파일 이름이다. 높이맵은 보통 흑백의 텍스처다. '지형 그리기'는 그것을 읽어 3D 지형으로 변환할 것이다.
지형을 좀 더 크게 하기 위해 크기를 (40, 4.4, 40)으로 바꾼다. 이번 예제에선 동적 광원을 쓰지 않을 것이기 때문에, 광원을 끄고 지형 텍스처로 terrain-texture.jpg를 설정하고, 두번째 텍스처로 detailmap3.jpg를 두자. (이렇게 쓰이는 두번째 맵을 이른바 상세 맵 detail map이라고 한다.) 마지막으로 텍스처에 대해 크기값을 넣는다. 첫번째 텍스처는 전체 지형에 한번만 쓰이고, 두번째 상세 맵은 20배로 쓰일 것이다.
// add terrain scene node
scene::ITerrainSceneNode*terrain = smgr->addTerrainSceneNode(
"../../media/terrain-heightmap.bmp");
terrain->setScale(core::vector3df(40, 4.4f, 40));
terrain->setMaterialFlag(video::EMF_LIGHTING, false);
terrain->setMaterialTexture(0, driver->getTexture("../../media/terrain-texture.jpg"));
terrain->setMaterialTexture(1, driver->getTexture("../../media/detailmap3.jpg"));
terrain->setMaterialType(video::EMT_DETAIL_MAP);
terrain->scaleTexture(1.0f, 20.0f);
지형에 대해 충돌 확인을 하기 위해, 삼각형 선택자(triangle selector)를 만든다. 지금 어떤 삼각형이 선택 되었는지 알기 위해, 충돌 예제에 있던 식을 따라하자. 지형 삼각형 선택자는 지형과 카메라의 관계를 정하는 것이다. 예제를 실행해 보면 카메라는 지형을 뚫지 못하고 마치 지형 위를 걷거나 나는 것 처럼 표현된다. 이를 구현하기 위해선 애니메이터와 연결된 충돌 확인을 만든 후 카메라에 붙여 준다. (역자 주 : 글로는 어려우나 직접 예제 exe를 실행해 보라. 마치 1인칭 캐릭터가 지형 위에 서 있거나 나는 듯 보인다.)
// create triangle selector for the terrain
scene::ITriangleSelector* selector
= smgr->createTerrainTriangleSelector(terrain, 0);
terrain->setTriangleSelector(selector);
selector->drop();
// create collision response animator and attach it to the camera
scene::ISceneNodeAnimator* anim = smgr->createCollisionResponseAnimator(
selector, camera, core::vector3df(60,100,60),
core::vector3df(0,0,0),
core::vector3df(0,50,0));
camera->addAnimator(anim);
anim->drop();
사용자가 일반 모드와 와이어(wireframe) 모드를 바꿔보기 위해, 이벤트 리시버를 만들고, IRRLICH 엔진이 그 내용을 할게 해야 한다. 추가로 스카이 박스를 만들어 보자.
// create event receiver
MyEventReceiver receiver(terrain);
device->setEventReceiver(&receiver);
// create skybox
driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
smgr->addSkyBoxSceneNode(
driver->getTexture("../../media/irrlicht2_up.jpg"),
driver->getTexture("../../media/irrlicht2_dn.jpg"),
driver->getTexture("../../media/irrlicht2_lf.jpg"),
driver->getTexture("../../media/irrlicht2_rt.jpg"),
driver->getTexture("../../media/irrlicht2_ft.jpg"),
driver->getTexture("../../media/irrlicht2_bk.jpg"));
driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);
모든게 다 되었다. 이제 IRRLICHT에서 지형 만드는 법을 알게 된 것이다.
int lastFPS = -1;
while(device->run())
if (device->isWindowActive())
{
driver->beginScene(true, true, 0 );
smgr->drawAll();
env->drawAll();
driver->endScene();
// display frames per second in window title
int fps = driver->getFPS();
if (lastFPS != fps)
{
core::stringw str = L"Terrain Renderer - Irrlicht Engine [";
str += driver->getName();
str += "] FPS:";
str += fps;
device->setWindowCaption(str.c_str());
lastFPS = fps;
}
}
device->drop();
return 0;
}
-------------------------------------------------------------------------
일단 애매한 것이 render, draw 가 모두 '그린다'라는 것이다. 여기에 display라고 까지 하면 모니터에 대한 우리 눈의 활동을 표현하는데 많은 표현 방법이 있음을 느낀다. IRRLICHT나 JUPITER엔진에서 보면 draw는 직접 (메모리에?) 찍는다는 의미로 쓰이고, Render는 그것을 (총괄적으로) 화면에 보인다는 의미로 쓰인 듯 하다. 마치 3D 공간상의 점인 vertex와 모니터에 궁극적으로 찍히는 2D 점을 말하는 pixel처럼 말이다. (물론 여전히 이 비교도 정확하진 않다.)
또한 헤깔릴 것이 그냥 render를 서술적으로 '그리기'라고 하는데, 나는 renderer라는 '그리는 것' 역시 명사적으로 '그리기'로 표현했다는 점이다. 알아서 봐야하고, 반드시 소스와 영어 원문을 참조할 일이다.
기술에 있어서 용어의 정립은 개인의 개발에서나 타인과의 협조에 있어서도 매우 중요하다. 이런 실정에서 억지로 한글화 하는 것은 우스운 일이다. 허나 어쩌랴 한글로 해석해 넣지 않으면 그냥 익힐 때보다 잘 안 익혀지는 않으니 말이다.
여기서 Terrain은 일반적인 '지형'의 의미이면서, 기술적 '지형'을 대표하는 단어다. 보통 터레인이라고 하면, 건물이나 도로와 대비되는 넓은 필드를 의미한다. 이 예제에 의하면 디자이너가 2D로 등고선을 그려놓으면 IRRLICHT엔진은 그것을 바로바로 3D 지형화 해준다는 것을 의미한다. 현재 위치라던가, 멀리 있는 지형에 대한 감소(LOD)는 이 예제에선 논외지만, 터레인을 이렇게 쉽게 만들 수 있음을 보인 것은 멋진 일이다.
'기본 카테고리' 카테고리의 다른 글
Irricht엔진튜토리얼 14. Win32Window (0) | 2009.03.19 |
---|---|
Irricht엔진튜토리얼 13. RenderToTexture (+06.08.04) (0) | 2009.03.19 |
Irricht엔진튜토리얼 11. PerPixelLighting (0) | 2009.03.19 |
Irricht엔진튜토리얼 10. Shaders (0) | 2009.03.19 |
Irricht엔진튜토리얼 09. Mesh Viewer (0) | 2009.03.19 |