프로그래밍 언어 입문서가 아닌 프로그래밍 기초 개념 입문서
문과생, 비전공자를 위한 프로그래밍 입문책입니다.
jobGuid 꽃미남 프로그래머 "Pope Kim"님의 이론이나 수학에 치우치지 않고 실무에 곧바로 쓸 수 있는 실용적인 셰이더 프로그래밍 입문서 #겁나친절 jobGuid "1판의내용"에 "새로바뀐북미게임업계분위기"와 "비자관련정보", "1판을 기반으로 북미취업에 성공하신 분들의 생생한 경험담"을 담았습니다.
Posted by 밥을먹는선비


0. 그냥 일단 변명부터 ㅜ.ㅜ;

안녕하세요 마감일을 지키려했지만 결국 실패한듯하네요. 원래는 21일날 올리려한 글이지만 6일로 미뤄서 오픈합니다.
작년 12월23일부터 4주간 매일 8시간 html5 수업을 강행군을 했더니 몸과 마음이 모두 지친거같네요 ㅜ.ㅜ; 

이번 시간에는 화면에 간단한 도형을 띄워보는 예제를 다뤄가면서 일리히트의 기본 랜더링 파이프라인 대해서 알아보겠습니다.

1. 디바이스 생성하기

일단 일리히트엔진의 시동을 걸기위해서 다음과 같이 한줄의 코드가 필요합니다.

    irr::IrrlichtDevice *pDevice = irr::createDevice(irr::video::EDT_OPENGL);


방금 여러분은 오픈지엘 랜더러를 사용하는 디바이스(게임기 비스므리?)를 생성한것입니다.
자~ 그럼 일단 시동을 걸어야하는데 일단 연료를 주입할 먼가가있어야 하겟죠?
연료 주입구라고 할수 있는 비디오드라이버 객체를 얻는것입니다. 

irr::video::IVideoDriver *pVideo = pDevice->getVideoDriver();

pVideo 이것이 바로 연료 주입구 입니다.

게임엔진을 차에 비유해서 굳이 억지로 설명하자면 일단 엔진을 돌리기위해선 연료가 필요한데 그게 바로 그래픽 데이터들입니다. 엔진을 그것을 먹고 결과를 출력하죠 자동차엔진이 화석연료를 태워 스피드라는 결과 물을 출력하듯이요.

여기서 우리의 약간의 딜래마가 있습니다. 엔진의 성능이라는 이슈인데요.

좀허접한 엔진에 최고급 항공유를 넣은것과 완전 최고급엔진에 허접한 유사 휘발유를 쓰는 것인데요.

성능이 좋은 엔진이라면 유사휘발유를 먹고도 아주 좋은 스피드를 내길 바래야겟죠. ^^?
머 아님말구요.

그러나 현실은 좀 엔진이 허접하더라도 연료가 우수(폭팔성강한)하면 결과가 거의 무조건 일반인들(스피드라는 것..)이 보기엔 만족 스럽다는겁니다.(이건 자동차 애기입니다. 게임엔진은 알아서 생각하시고요^^) 물론 나중에 항공유의 폭발성을 견디지못하고 엔진이 터지고말겠지만 머 당장은 그럴듯하죠.

그리고 또 어떤게 성능일것이냐 하는 문제도 있습니다. 즉 얼마나 폭발성 강한 연료를 잘소비하느냐에 관점을 둔 엔진이 있겠고 얼마나 연료를 아껴가며 연비를 최대한 높이려는 관점의 엔진이있을 수 있습니다. 
게임엔진도 비슷합니다. 상용엔진은 전자의 경우겠고 일리히트엔진처럼 오픈소스 공개엔진의 경우엔 후자에 좀 가깝습니다. 전자에 가까운 공개엔진도있고 후자에 가까운 사용엔진도 물론있습니다. 그냥 그런 경향이 있다는 것이라 생각하시면 될듯합니다.

암튼 이야기가 잠시 삼천포로 갔었는데요 일단은 pVideo를 통해서 엔진에 에너지를 주는거라고 생각하시면 됩니다.


2. 버텍스 데이터 만들기

일리히트엔진은 자체적으로 컨테이너(array,map,list)를 가지고 있습니다. STL과 거의 유사한 사용법을 가지고있으므로 stl을 평소에 사용하시는 분들이라면 쉽게 사용하실수 있습니다.

irr::core::array<irr::video::S3DVertex> Vertices;

위와같이 버텍스 배열을 선언해줍니다. 사용법은 std::vector와 거의 같습니다.

 1---------2

 |              |

 0---------3


이런식의 도형을 그려 보도록 하겠습니다.


일리히트엔진은 버택스포멧이 기본적으로 3종류가 있습니다. 그중에서 가장 기본이 되는 것이 S3DVertex 입니다.
여기서는 이것을 사용했습니다.

정점위치,법선,컬러,텍스춰 좌표 등을 가지고 있는 포멧입니다.

형식은 다음과 같습니다.

irr
::video::S3DVertex(
                                 정점X죄표,정점Y
죄표,정점Z좌표,
                                 법선X죄표,법선Y죄표,법선Z좌표,
                                 정점컬러,
                                 텍스춰 U,텍스춰 V);

다른 두가지포멧과 이것을 확장하는 법은 다음에 다루도록 하겠습니다. 

그 다음으로 인덱스 정보를 만듭니다.


 
위와 같이 삼각형 두개를 조합해서 사각형을 만들어 보았습니다.


자 이렇게 해서 일단 기본 도형을 그리는 데필요한 정점(
Vertices)과 인덱스(Indice4TriList) 데이터를 만들어 보았습니다. 


3. 변환 다루기

변환은 일반적으로 월드,뷰,투영 변환 이렇게 3가지로 나누어집니다. 변환에 대한 자세한 내용은 김용준 교수님이 쓰신 명저 해골책을 참고 하시면됩니다. 
일리히트엔진에서 변환을 다루는 함수는 irr::video::IVideoDriver의 
setTransformsetTransform 입니다.
이 함수는 변환의 종류와 변환이 들어있는 행렬을 매개 변수로 받아서 해당 변환을 파이프라인에서 적용하도록 처리를 해줍니다.

irr::video::ETS_VIEW
irr::video::ETS_PROJECTION
irr::video::ETS_WORLD 

첫번째 인자로는 이와 같이 3개의 변환을 선택해서 넣어줄수있습니다.

두번째 인자에 변환의 내용이 담겨있는 행렬을 넘겨줍니다.

이제 행렬을 만들어서 넘겨줘야하는데요.
행렬객체를 직접 초기화한다는건 매우 귀찬은 일중에 하나겠죠?
그래서...
일리히트엔진은 다이랙스엑스에 있는 행렬만들기 함수와 같은 행렬생성 유틸리티 함수들을 가지고 있습니다.

먼저 투영행렬을 만들어 파이프라인에 등록하는 것을 해봅시다.


buildProjectionMatrixPerspectiveFovLH 함수는 왼손좌표계 기반의 원근투영 행렬을 만들어 주는 함수입니다.

첫번째 인자인 fov는 시야각입니다.  값이 하나 이므로 가로 세로 시야각을 따로 계산한 피라미드형이 아니라 원뿔형의 시야각을 생각하시면됩니다.

두번째 인자인 ratio 는 화면의 종횡 비율입니다. 정비율을 만들고 싶다면 현재 화면해상도의 넓이/높이 값을 넣어주면됩니다.

마지막 두개의 인자는 절두체 범위의 원근값입니다. 1은 가장 가까운 거리이고 3000은 절두체의 최대끝 범위입니다. 이 이상거이에 있는 물체는 잘려서 않보이게 됩니다.


그 다음은 뷰변환 들어갑니다.

일명 카메라 변환 입니다.

buildCameraLookAtMatrixLH 함수로 왼손좌표계 기반의 뷰변환 행렬이 만들어 집니다.
눈치 체셨겠지만  LH는 토지 주택공사의 약자가 아니라 왼손의 약자입니다.

첫번째 인자는 카메라의 위치 입니다.

두번째 인자는 바라보는 시점의 위치입니다.
방향이 아닙니다. 
시야의 방향벡터를 구하려면 두번째에서 첫번째를 빼주시면됩니다.

마지막은 상방벡터입니다.

마지막으로 도형을 그리기 전에 최종적으로 도형의 위치를 정하는 월드 변환입니다.

왜 마지막에 월드변환인가 하면 랜더링 루프내에서 대부분 투영변환과 카메라 변환은 월드 변환에 비해서 빈번하게 바뀌지 않습니다.
그래서 코드 순서는 그리기 직전에 월드 변환을 하는것이 일반적인 순서입니다.



월드 변환은 이동,회전,크기 변환을 조합하여 만들수있습니다.
위예제에서는 단순히 원점에 위치시키는 변환아닌 변환이 되었습니다. 

setTranslation 에서 인자로 받는 벡터클래스는 3차원 위치를 나타낼때 빈번하게 사용됩니다.

irr::core::vector3df(x,y,z)

이렇게 해서 변환을 위한 준비는 모두 마쳤습니다.
 

4. 재질 다루기

3D하면 또 하나 중요한것이 재질입니다. 지난 20세기가 폴리건의 시대였다면 21세기는 재질과 물리 레이트래이싱 머 그런 등등의 시대일것입니다.


일리히트엔진은 SMertial 구조체로 재질에 대한 모든것을 다 처리합니다. 이 안에 쉐이더라든지 텍스춰등의 모든 정보가 들어 있습니다.

특별히 값을 지정하지않아도 선언과 동시에 초기값들이 지정이됩니다.
일단 조명은 나중에 사용할것이므로 조명속성을 OFF 시켜둡니다.

그리고

텍스춰를 지정합니다. 일리히트엔진은 기본적으로 텍스춰 4개를 동시에 조합할수있습니다.
더 필요하다면 TextureLayer 의 배열수를 늘려주고 엔진을 다시 빌드해주면됩니다.

일단 1개를 기본으로 사용하므로 첫번째 레이어에 텍스춰를 로드해줍니다.

비디오드라이버의 
getTexture함수는 텍스춰 파일이름으로 텍스춰정보를 관리합니다. 만약 이미로딩이 되어있다면 중복하여 로딩하지않고 이미 로딩된 데이터를 map구조로된 컨테이너에서 찾아 반환합니다.

setMaterial 함수로 재질을 등록 해줍니다. 그러면 다른재질로 이함수를 호출하기전까지 이 재질의 속성이 그대로 유지됩니다.


5. 프리미티브 그리기 함수 사용하기

모든게 준비 되었고 이제는 그리는 것만 남았습니다.




 첫번째 인자는 정점 버퍼의 포인터 입니다.
 (irr::video::S3DVertex *) (Vertices.pointer())  를 사용해도 같은 결과를 얻을수 있습니다.

두번째 인자는 정점갯수입니다.

세번째 인자는 인덱스버퍼의 포인터입니다.
네번째 인자는 인덱스의 갯수입니다.

다섯번째 인자는 정점형식입니다.
여섯번째 인자는 정점을 이어주는 방식입니다.


위와 같은 종류 들이 있습니다.


이번에는 여기까지 하도록 하겠습니다.

소스 파일 첨부합니다. 조만간 구글 코드 같은곳에 SVN을 하나 열어두겠습니다.





 

댓글을 달아 주세요

  1. 하늘향기 2012.02.06 11:29  댓글주소  수정/삭제  댓글쓰기

    타 모사이트에서만 뵙다가 여기서 연재글 보니까 반갑네요.

    매번 좋은정보 주셔서 감사합니다. :)

  2. 쏠구 2012.02.06 15:02  댓글주소  수정/삭제  댓글쓰기

    예전부터 일리히트 관심이 많아서 도플광어님 글도 많이 보곤했는데
    여기서 또 이렇게 볼수있어서 반갑습니다 :)

  3. Favicon of https://gamedevforever.com 죠쉬 2012.02.07 16:34 신고  댓글주소  수정/삭제  댓글쓰기

    ㅡ,.ㅡa
    어렵습니다.
    인쇄해서 정독해야 할지도 모르겠습니다.

    • 도플광어 2012.02.12 15:14  댓글주소  수정/삭제

      좀 길어진면이 있었던거 같네요.
      좀더 간결하게 예제를 만들도록하겠습니다.