브라이스는 독특하게도 지형 모델링 + 렌더링에 특화된 전문툴로써 MAX처럼 폴리곤이 아닌 2D 이미지를 그려대면 그것을 기초로 멋진 3D 지형을 만들어 주었다. 기본적인 텍스쳐까지 말이다!
브라이스는 스스로를 Fractal 어쩌구 하면서 자랑을 하는 툴이었고,
한눈에 봐도 엄청나보이는 알고리즘과 무지막지할 수학 공식이 엿보였으므로,
"난 평생 이런거 짤일 없을꺼야~"
라는 부끄러운 자위를 하며 분석과 흉내란 도전에서 도망쳤었다.
그러나 시간은 흘러 게임에서 지형은 삼각형 양탄자에서 쿼드트리를 거쳐 높이맵, 라이트맵이 나오고 지형엔진이란 말까지 오래 전에 나와버렸다. Rhea君의 경우, MMORPG보다는 MO를 더 좋아하기에 솔직히 지형에 대한 공부는 별로 하지 않았다. Seamless 월드보단 메탈기어솔리드2같이 한정된 공간 사이사이에서 뛰어노는 게임이 더 좋았고 콘솔게임에 익숙하다보니 월드를 이동할때 생기는 로딩과 연출, 거기에서 나오는 게임의 성격을 자연스레 받아들인 탓이었다.
아뭏든 그래도 직업이 직업이다보니 공부는 계속 하게될수 밖에 없고 실전(=상용) 프로젝트에서 사용하든 접히든(- -;;;;) 계속 삽질을 해야하는게 개발자의 사명이거니 했다. 물론 그 과정 속에서 브라이스가 보여준 독특한 지형 제작 방식이 높이맵(Heghtmap)이란 것도 저절로 알게되었다.
모르는 사람들을 위한 높이맵 이야기 산, 바다, 언덕, 계곡...등과 같이 높낮이가 있는 지형을 만드는 것이 높이맵이다. 상식적으로 모든 3D는 삼각형들로 이루어져 있으며 지형 역시 삼각형 덩어리이다. 이 삼각형들이 같은 높이로 깔려있으면 그야말로 평지일 것이고 높이 방향에 대해 튀어 나오고 들어가면 산이 되고 바다가 된다.
이래뵈도 VS 2.0으로 뽑아낸 폴리곤들이랍니다. 단순한 LineTo()가 아니라고요!
그럼 튀어나오게 하고 들어가게 하는 정의가 필요한데 주로 사용되는 것이 256 단계를 지닌 Gray Scale 비트맵 이미지가 되겠다. 알다시피 컴퓨터에서 채도가 없는 회색은 0~255의 단계를 지니고 있기에 256 그레이 스케일 이미지는 높낮이 편집에 아주 유용한 데이터 정의가 된다. 바로 브라이스가 이걸 보여줬었던 것이다. 0이면 솟아오르고 255면 꺼지면 된다.
지구를 만드는 신의 기분으로~
이 이미지를 바탕으로 정점들을 올리고 내리자. 그냥 작업하면 너무 밋밋하니까 잔디 무늬가 있는 텍스쳐도 하나 같이 올리자.
헐퀴~~ 뭔가 튀어나왔다~!
위에 있는 회색 이미지와 비교해보라.
텍스쳐를 더 올리면 부드러운 지형을 얻을 수 있다.
포스트 공간도 부족하고, 높이맵 따라하기~ 스터디용이 아니니까,
이 싯점에서 굳히 소스까지 뿌려가며 설명할 필요는 없으리라 생각한다.
아뭏든 여기서 중요한 것은, 이제 LOD를 구현하고 원금감을 위해 포그(fog)를 넣어야 한다 3D 모델링이 원래 높낮이가 있다는 것이 아니라 읽어들인 한장의 이미지에 의해 높낮이가 만들어졌다는 사실이다.
더 정확히 말하자면 높낮이를 위해서 1장, 텍스쳐 세장, 그리고 텍스쳐의 경계선을 만들기 위해 1장, 총 5장의 이미지가 사용되었다. 그러나 가장 중요한 3D를 만들기 위해서는 정말 1장이 쓰여졌다.
결론 :
높이맵은 모션포트레이트가 핵심으로 내걸고 있는 "한장의 이미지로 방향을 갖고 움직이게 한다"라는 사실과 일치한다.
모션포트레이트 액션스크립트 3.0에서 flash.geom 네임스페이스가 추가되었다.
(http://help.adobe.com/ko_KR/AS3LCR/Flash_10.0/flash/geom/package-detail.html)
아, 정말 플래시 너무하는구나. 이것으로 본격 3D 개발툴이 되었다. GPU까지 직접 쓸수 있게 되면... 우~~ 놀라워라~~!
현재로는 소프트웨어 렌더러라고 생각되지만 아뭏든 플래시의 행보는 무섭다.
비단 플래시 뿐만 아니라 포토샵 CS4 역시 Dimension이 번들되면서 GPU 지원까지 하고 있는데 플래시도 조만간 GPU를 적극 활용할 것 같다.
WPF도 Direct3D를 불러쓰고있지만 WPF가 보여주는 아웃풋은 플래시의 그것과는 자뭇 달라서 실망감이 컸던게 사실이었다. 물론 가장 필요한 킬러 타이틀이 없어서 그렇기도 하지만, 현재로 보기에 WPF는 처량해보이는게 사실이다. 차라리 Silverlight에게 희망을 걸어보고 싶지만... 수많은 디자이너들이 액션스크립트에서 C#으로 넘어가는 것도 결코 쉽지 않을 것이라 HD 스트리밍을 제외하면 플래시가 앞으로도 우위를 지킬 것 같다.
...또 말이 꽤나 빗나갔군. 모션 포트레이트, 다시 한번 더 봅니다.
우리 단장님.
역시 이게 만들자~ 식의 강좌는 아니고 포스트 길이의 압박으로 과정과 소스 분석은 생략하고... 지난 회에서 역컴파일 했을 때, 회색으로 나온 높이맵 이미지를 일단 올려보았다.
원본 높이맵 소스
일단 3D 높낮이가 만들어졌다.
흠좀무... 텍스쳐를 그대로 사용했더니 마치 화성의 안면암이 떠오른다. - -;;;;;;;;;;;;;
(보통 책에서는 텍스쳐를 없애버리고 음영만 준다.)
......ㅎㄷㄷ;;;;
한장의 높이맵 이미지로 3D를 만들었으니, 일단 이것으로 모션포트레이트의 비밀은 증명이 되었다.
텍스쳐 스케일링을 변경해 기본 텍스쳐를 먼저 올려보았다.
무섭다. ㅠㅠ
솔직히 나는 정교한 MAX 모델링(예컨데 얼굴 + 눈알이 있는)을 보고도 ㅎㄷㄷ;;; 거린다.
텍스쳐 이미지를 보면 인간의 살가죽을 벚겨놓은 듯해 코딩하면서도 조낸 무서워한다. ㅠㅠ;;;;
카메라에 대한 멀미도 하는데... 어찌보면 게임 프로그래머로써 최악의 조건을 지닌 셈이다.
우선 매핑을 더 올려보았다.
이리 돌리고~
저리 돌리고~
보기 드믄 하루히 옆모습...
코가 아주 못생겨졌는데 이는 아직 전체에 대한 원근감이 남아있기 때문에 색이 두드러져 보이기 때문이다.
이제 노말 적용, 직교행렬을 이용한 카메라 설정, 커서 위치 판단...등등이 남았다.
시간 관계상, 오늘은 일단 여기까지 적용해본다.
워낙 텍스쳐가 뛰어나니 높낮이를 없애고 직교행렬만 적용시키면 정말 상기의 모션포트레이트처럼 나오리라 확신한다.
이것으로 한동안 화제였던 모션포트레이트의 기본적인 비밀을 밝혔다.
(원래 생각은 한꺼번에 다 구현해볼 생각이었는데 먹고 사는 문제 때문에... ㅠㅠ)
모션포트레이트를 이렇게 적어보았고, Rhea君이 조금 충격을 먹었던 것은, 기술에 대한 선입견을 없앤다는 것이었다.
"높이맵 = 외부 지형, 라이트맵 = 내부 지형"이란 선입견의 제약으로
조금만 더 아이디어를 올리면 무언가를 해볼수 있는 기회를 우리는 그냥 놓아버리게 되는 것이 아닐까 한다.
모션포트레이트도 보다 모에하도록 구현을 해볼 것이고 위에 느낀 감정을 기억하며 하나의 기법으로 새로운 효과를 만들어내는 습관을 지녀야 할 것같다.
요즘 개발자라면 대부분 Dual 모니터 환경에서 작업을 할 것이다. 간혹 어떤 사연으로 Primary 모니터가 아닌 Secondary 모니터로 Full Screen으로 된 DX 화면을 띄우고 싶을 때가 있을 것이다(예컨데, 창모드 지원이 되지 않는 프레임워크에서 디버깅을 위해서나 그냥 그렇게 보는 게 더 편하다거나.).
그렇다면 IDirect3D9::CreateDevice() 에서 흔히 사용하는 D3DADAPTER_DEFAULT 대신 다른 방법을 써야 한다. D3DADAPTER_DEFAULT는 Primary 모니터를 가르키기 때문이다. 안타깝게도 이를 위한 매크로는 따로 없다.
모니터 식별은 IDirect3D9::GetAdapterIdentifier()로 얻어낼 수 있는데 이름에서 알수 있듯, DX에게 중요한 것은 모니터가 아니라 비디오 카드의 출력단자인 것이다.
// 윈도우 모드일때 정보 얻음 HR(m_pD3DObject->CheckDeviceType(iAdapterOrdinal, m_D3DTypeDef, VGAAdapter.m_mode.Format, VGAAdapter.m_mode.Format, TRUE));
// 풀스크린일때 정보 얻음 HR(m_pD3DObject->CheckDeviceType(iAdapterOrdinal, m_D3DTypeDef, D3DFMT_X8R8G8B8, D3DFMT_X8R8G8B8, FALSE));
// 벡터에 저장 m_vecVGAAdapter.push_back(VGAAdapter); }
마지막 push_back에서 알 수 있듯이 CVGAAdapter는 vector로 관리했는데, 엽기적으로 그래픽카드를 여러개 꽂아 3개 이상의 모니터를 사용하는 변태도 있기 때문이다.
이제 m_vecVGAAdapter[0]은 Primary 모니터를 의미하고 m_vecVGAAdapter[1]은 Secondary 모니터를 의미하게 된다.
이제 IDirect3D9::CreateDevice()의 1, 2번 인자에는 각각 m_vecVGAAdapter[0].m_iAdapterOrdinal과 m_vecVGAAdapter[0].m_DevType 를 적어주면 된다.
만약 풀스크린과 윈도우 모드를 동시 지원하는 프레임워크라면 분명 각 모드에 대한 D3DPRESENT_PARAMETERS 구조체를 채우는 함수가 있을 것이다. 여기에 CVGAAdapter.m_mode의 값을 넣어주자. 이때 해상도를 변경하고 싶으면 사용가능한 해상도 정보를 얻기 위해 IDirect3D9::EnumAdapterModes()를 이용하여 위의 for() 내에서 한 아답터 정보를 얻어마자 다시 사용 가능한 해상도를 얻어낸다.
이는 어차피 DX 기본 프레임워크에 구현되어 있으니 참조하도록 하며 상기의 내용 정도라면 개발시 Secondary 모니터로 풀스크린을 만드는데에는 큰 불편함이 없을 것이다.
비스타에서 추가된 DwmExtendFrameIntoClientArea(), 이거 아주 유용하다. 창을 없애고 이래저래 작업을 해보니, 드디어 폴리곤만 바탕화면에 띄울 수 있었다. 이것으로 비스타 전용의 무언가를 해볼수 있지 않을까...싶다. 폴리곤 그릴때 최적화를 잘하면 가젯을 활용한 것보다 더 유용하지 않을까 싶다... . 아직 더 연구해봐야겠지만.