[Irrlicht] 3D Cube 구현



이전글에 이어서 3D Cube의 구현에 대해 써보자 합니다.

이전글을 보시면 아시겠지만, 큐브의 조작은 마우스로만 이루어집니다.
즉, 마우스 입력을 큐브회전으로 변환하는 것이 핵심이죠.
그러므로 잡다한 자료구조나, 구조는 제외하고 이것에 대해서만 포스팅하겠습니다.



우선 하나의 면을 클릭해보죠.
스샷에서 하얀색으로 표시된 부분이 클릭한 면입니다.
그리고 이때  마우스로 조작할 수 있는 회전은 3가지입니다. (반대방향은 제외)
하늘색면을 회전, 녹색면을 회전, 분홍색면을 회전이죠.


하늘색면의 회전에 대해서 생각해 보도록 합시다.
아까의 위치를 클릭하였을 때, 4개의 벡터를 얻을 수 있습니다.
이 4개의 벡터중 하나를 이용해서 회전하고자 하는 면을 선별하는 것입니다.
이 벡터들은 클릭한 조각의 각각 면의 법선을 의미합니다.


그리고 조각을 선택한 상태에서 왼쪽으로 마우스를 움직여보죠. 
그러면 처음에 마우스를 클릭한 지점부터, 마우스를 땐 지점까지
붉은색 화살표와 같은 벡터를 얻을 수 있습니다.
그리고 이 벡터를 기초로, 아까구한 4개의 벡터 중, 가장 유사한 하나의 벡터를 선별합니다.
[선별하는건 일반적인 피킹과정과 유사합니다. 붉은 화살표는 카메라좌표계의 벡터이므로
 이것을 선택한 조각의 로컬좌표계로 이동시킨후, 가장 가까운 벡터를 찾는 것이죠. ]
[가장 가까운 벡터를 찾는 것도 간단합니다. 각각의 노멀벡터를 하나의 위치로서 사용하여
 변환한 붉은 벡터의 노멀(이것또한 위치로 사용)과 가장 가까운 벡터를 찾으면 됩니다.)


선별된 벡터와 클릭지점의 노멀벡터를 사용하면 회전하고자 하는 면들을 구합니다.
상단에 검은 사각형들이 바로 이번 입력에 의해 회전될 사각형들입니다.


그렇다면 회전축을 찾아야겠죠?
회전축 찾은 것또한 간단하죠.
단순히 위에서 구한 벡터 2개의 외적으로 구해지는 범선벡터를 사용하는 것입니다.
외적으로 회전축을 구하는 것은 또 하나의 이득이 있습니다.
반대로 드래그했다면 파랑색벡터는 반대방향이 되어있을 것입니다.
이들의 외적은 녹색벡터와 반대방향이 되어있겠죠.
당연히 녹색벡터의 반대방향 벡터로 회전을 하면 반대로 회전하게 됩니다.


이러식으로 구해진 큐브조각을 정해진 회전축으로 회전시키면 큐브가 회전하게 됩니다.
이렇게 만든 루틴은 녹색을 회전할때도 같은 방법으로 회전이 가능합니다.


이러한 방식이 통하지 않는 건 분홍색면을 회전할 때가 됩니다.
일반적으로 분홍색면을 클릭하고 분홍색을 회전한다면, 마우스로 원을 그리는게 타당하겠죠.
만약 이 기능을 넣는다면 마우스의 회전을 감지하고, 클릭한 면의 노멀로 회전.
그리고 다른 면의 노멀로, 회전할 조각을 검색하는것으로 구현이 가능하겠죠.

그러나 분홍색과 녹색을 포함한 조각을 밑으로 내리는게 마우스 동선이 더 짧기도 합니다.
또 별개의 마우스 움직임을 추가하면, 움직임 판정이 미묘해 질 가능성도 있겠죠.
그래서 이러한 회전은 구현에서 제외하였습니다. (슬슬 다음계획으로 넘어가고 싶었고;;)


이렇게 회전처리를 하면 카메라 위치와 관계없이, 동일한 룰로 회전을 처리할 수 있습니다.
카메라의 위치와 관계없이 직관적으로 큐브를 조각할 수 있는 것이죠.



3DCubeSource.zip 소스입니다.
일리히트 엔진을 이용하였습니다. 일리히트엔진을 알아보는 김에 한번 작업해봤습니다.
처음 써본거라 조작에 미숙한 점이 있더라도 향해바랍니다 ^^;

참고로 만약에 DirectX나 OPEN GL등의 과제로 쓴다면 거의 다 뜯어고쳐야 할겁니다.
(보통 3D수업은 API레벨일테니, 과제용으로 써먹기 힘들거 같아서 공개하는거죠 ㅎㅎ)



학기말 텀프로젝트때, 큐브만든다는 친구가 있었는데, 재미있어보여서 한번 짜봤습니다.
혹시 다음학기때, 인공지능땜에 써먹을 수 있을지 모르겠네요;;

by hellz | 2009/07/09 01:04 | Workshop | 트랙백 | 덧글(0)

[Irrlicht] 3D Cube

3DCube.zip
3D 큐브입니다.
마우스로 원하는 면을 드레그하면 회전하도록 되어있습니다.
오른쪽클릭으로 큐브를 회전할 수 있습니다.

제목 그대로 큐브의 움직임만 재현한 프로그램입니다.
목표가 움직임 재현이어서, 큐브를 섞거나, 푸는 등의 부가기능은 넣지 않았습니다.
사실 남은 부가기능은 잔 작업이니 말이죠 --; ( 푸는 법은 좀 연구해야겠지만;;)

구현에 관한 사항은 별도로 올릴 생각입니다.


by hellz | 2009/07/08 23:59 | Workshop | 트랙백 | 덧글(0)

[3D][DirectX] Picking

과제로 작성한 피킹입니다. 상당히 예전에 작성한거죠..






LabProject.exe

클릭하면 카메라가 해당오브젝트의 뒤로 이동하게 되어, 제어할 수 있습니다.
피킹방법이야 여러 책에서 단골로 등장하니깐요.
간단히 요약해 놓자면
스크린좌표를 카메라좌표계로 변환하여 카메라에서 해당방향으로 뻗어나가는 Ray를
생성하고 이것을 필요에 따라 월드좌표계 -> 로컬좌표계로 변환시켜서 사용하게 됩니다.

DX샘플보면 오브젝트의 전체 폴리곤에 대해 피킹하는 예제를 볼 수 있습니다.

D3DX에서 제공하는 D3DXIntersectTri함수를 사용하면 쉽게 구현이 가능합니다.


아무래도 각각의 폴리곤에 대해서 검사하는건 비용문제가 클꺼라고 생각해서
오브젝트를 감싸는 바운드 박스를 계산하고 바운드박스로 피킹검사를 했습니다.

by hellz | 2009/06/27 23:12 | 트랙백 | 덧글(0)

[3D][DirectX] 물에 잠기는 알파오브젝트

드디어 한학기가 끝났습니다.
시험이다 과제때문에 한동안 포스팅이 없었네요. (정기방문보다 검색방문이 많겠지만 ;;;)
기말고사는 저번주에 끝났지만, 늦게 내는 텀이 남아서 방학(!)이  한주 더 걸렸네요.
이제 남은건 성적공개뿐이군요....



알파오브젝트는 일반적인 오브젝트를 그린 후,
알파 오브젝트끼리 카메라에서의 거리로 먼곳부터 그려줘야 합니다.

문제는 지형에 커다란 사각형으로 물을 씌울때 발생합니다.
물도 알파오브젝트인데, 다른 작은 알파오브젝트와 비교해 언제 그려줘야 할까요?




빌보드로 알파값을 가진 나무입니다.
나무를 먼저 그려주고, 물을 그려준 상태입니다.
보시다시피 빌보드의 투명부분에 물이 그려지지 않았습니다.
나무의 Z버퍼가 물보다 앞에 있기때문에 그려지지 않는것이죠.



이번엔 반대로 물을 그려주고, 나무를 그려줬습니다.
얼핏보면 제대로 그져진듯 하지만, 물에 잠긴 나무밑둥이 그려지지 않습니다.
아까와 반대로 물의 Z버퍼가 잠긴 나무앞에 있기 때문에 그려질 수 없는것이죠.
만약 물이 투명하지 않는 거라면 이정도로도 충분할 겁니다.
하지만 투명히 비추는 물에 잠긴 오브젝트를 원한다면 불충분하죠.



해결법은 나무를 2번 그리고, Z버퍼쓰기 상태를 이용하는 겁니다.
먼저 나무를 그려줍니다.
중요 포인트는 Z버퍼쓰기를 꺼주는 겁니다.
SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
이렇게하면 출력은 되지만, Z버퍼에는 반영이 되지 않죠.


다시 Z버퍼쓰기를 활성화한 후,  물을 그려줍니다.
물 위쪽에 그려진 나무가 물과 알파블렌딩되서 흐려진것을 알 수 있습니다.
당연히 물에 잠긴 밑둥은 물과 블렌딩된 상태입니다.


마지막으로 나무를 다시 그려줍니다.
물을 그릴때 Z버퍼가 변하여, 밑둥이 다시 그려지지 않고,
잠긴 효과 그대로 유지되는것을 알 수 있습니다.
물 위 가지부분은  물보다 카메라에 가까우므로, 덮어 써지는것을 알 수 있습니다.


정리하면 다음과 같습니다.
  1. Z버퍼쓰기를 끄고 알파 오브젝트를 그린다. (Z버퍼 사용[D3DRS_ZENABLE])이 아님.)
  2. Z버퍼쓰기를 다시 키고 물을 그린다.
  3. 알파 오브젝트를 다시 그린다.


이걸 생각해내기 전에는 요상하게 작성해봤었죠.
Z버퍼쓰기를 끄고, 물을 그리고, Z버퍼쓰기 키고 오브젝트를 그리고 알파블렌딩을
D3DBLENDOP_MIN로 지정한뒤 물을 다시 출력했었습니다.
이렇게하면 오브젝트의 알파부분과 물과 약간의 색차가 존재합니다.
그래도 멀리서 보면, 어느정도 그럴듯 했었죠;;;;

원래 과제에 덤으로 넣은 물효과인데, 그냥 초안대로 작성해서 빨리 내버렸습니다.
근데 제출한 다음날에 이방법이 떠올랐다죠 orz...
다행히 기말 실기시험에서 이 소스를 사용해서, 기말고사는 수정된 버전으로 제출~




by hellz | 2009/06/26 23:51 | Programming | 트랙백 | 덧글(0)

[3D][DirectX] Solar System

과제로 나온 태양계입니다. 과제한지는 꽤 됐는데, 그다지 시간여유가 없었습니다 --;


우선 태양계를 살펴보면 항성인 태양. 지구같은 행성. 달과 같은 위성이 있습니다.
지구는 태양주위를 공전하며, 달은 지구 주위를 공전하죠.
또한 자신조차도 스스로 공전을 합니다.

즉, 혹성은 부모주위를 공전하며, 자전을 하죠.
이것을 정리하여 항성과 행성과 위성을 나타내는 혹성클레스를 정의합니다.
혹성안에는 공전과 자전을 위한 자료. 그리고 자식들의 리스트를 가지고 있습니다.
그리고 혹성의 움직임, 그리기등의 작업은 부모를 통해 자식에게 전해지게 됩니다.

그리기 코드는 다음과 같습니다.

bool CPlanet::Draw(LPDIRECT3DDEVICE9 pD3DDevice, D3DXMATRIXMotherTranslation) {

             pD3DDevice->SetTransform(D3DTS_WORLD,

 &(SelfRotation* MotherRotation * MotherTranslation));

             this->m_pMesh->Render(pD3DDevice);

 

             for(int Count = 0;Count < Child.size(); Count++)

Child[Count]->Draw(pD3DDevice, this->MotherRotation * MotherTranslation);   

return true;

}

부모의 위치좌표를 넘겨 받습니다.

그리고 자전회전 * 공전회전 * 부모위치을 차례로 곱해서 혹성의 위치를 구합니다.

자신을 그리면 자전을 제외한 위치행열을 자식에게 전달합니다.


이렇게하면 모든 혹성을 루프를 통해 일괄적으로 넣을수도 있죠.




실행파일 (LabProject.exe)

숫자키 1~8을 누르면 각 행성으로 카메라가 이동되며
9번은 상단카메라. 0번은 위에서 비스듬하게 내려다 보는 카메라입니다.
구는 버텍스와 인덱스를 실린더 공식을 이용해서 직접 만들어 주었는데,
회전을 명확히 보기 위해 색상에 노이즈를 넣어봤습니다.


원래는 구 코드도 올릴까 했는데, 이건 나중에 시간되면 올리죠 --;
일단 다음과제 올리는건 건너뛰고(제공된 소스를 활용한 수준이라)
다음에 올릴건 피킹이 되겠군요.. 일단 구현은 완료된 상태...

by hellz | 2009/05/25 00:16 | Workshop | 트랙백 | 덧글(0)

◀ 이전 페이지          다음 페이지 ▶