태그 : DirectX

[3D][DirectX] Xi5 소스

Xi5.zip

XI5의 움직임을 구현했던 프로그램의 소스입니다.

DXUT를 기반으로 작성되어 있습니다.
소스파일들(DXUT포함)과 이미지만 첨부했습니다.
컴파일 하고 싶으시면 프로젝트 만들어서 넣어주세요.

3D 처음배울 때 짠 습작이라, 여러모로 엉성합니다 --;;


구현방법에 대한 것은 예전에 아래 페이지에 설명한 것을 참고바랍니다.
[3D][DirectX] 평면위에 굴러가는 주사위


by hellz | 2009/10/27 01:49 | Workshop | 트랙백 | 덧글(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)

[3D][DirectX] Cylinder World

과제로 나온 Cylinder를 사용한 프로그램을 작성했봤습니다.


Cyliner를 만드는건 간단합니다.
우선 원의 한 지점을 구하기 위해서 세타값을 구합니다.
이것은 간단히 원통의 단계를 PI로 나누어 단위세타를 구하고, 단계마다 곱해줍니다.
그리고 이것을 높이의 양 끝에 배치해서 순서대로 넣어주면 됩니다.
렌더링엔 TRIANGE_STRIP을 사용합니다.
    for(int CreateCount = 0;
        CreateCount < Division;
        CreateCount++) {
            float Theta = 2 * D3DX_PI * CreateCount / (Division - 1);
            this->m_pVertex[CreateCount * 2    ] =
                CVertex(sinf(Theta) * CircleRadius,  Height, cosf(Theta) * CircleRadius, UpColor);
            this->m_pVertex[CreateCount * 2 + 1] =
                CVertex(sinf(Theta) * CircleRadius, -Height, cosf(Theta) * CircleRadius, DownColor);
    }

그리고 XZ를 평면으로 가정하고 그 위에서 Cyliner가 굴러가도록 구현했습니다.
상하좌우키로 평면위를 움직이는데, 다른 방향의 입력이 들어오면 회전합니다.
회전엔 제한을 걸었습니다. 회전을 Cyliner의 중점을 기준으로 회전하는 것이죠.
원래 회전하고자 하는 방향의 끝을 기준으로 회전인데, 코드가 매우 늘어나더군요.
게다가 시험기간이라서 간단히 회전했습니다. 덧붙여 상하<->좌우회전시 회전방향도
고정해버렸습니다. 고정을 하지 않으면 원통이 뒤집힐 수 있기때문이죠.
  이동(방향에 따라 회전축이 바뀝니다. 아래소스는 한방향만 표시)
D3DXMatrixTranslation(&Trans, 0.0f, 0.0f, CylinderMovement);
D3DXMatrixRotationY(&Rot, CylinerRoll); 
this->mRollCylinder.m_mtxWorldTranslation *= Trans;
this->mRollCylinder.m_mtxWorldRotation = Rot * this->mRollCylinder.m_mtxWorldRotation;
this->mRollCylinder.CalculationWorld();
  회전

if(this->mBreadth== true)

this->mRollCylinder.m_mtxWorldRotation*= this->mAnimationBreadthRollMatrix;

else

this->mRollCylinder.m_mtxWorldRotation*= this->mAnimationNonBreadthRollMatrix;


굴러가는 범위제한은 두가지 방법으로 해봤습니다.
하나는 버텍스별로 월드에 배치시켜 해당범위를 체크하는 것이고,
또 다른 하나는 중점을 월드에 배치시켜 범위를 체크하는것입니다.
전자의 경우, 이동에 사용해봤고, 후자는 회전에 사용했습니다. 사실 후자가 간단하죠;;

D3DXMatrixTranslation(&Trans, MovementX, 0.0f,MovementZ);

Trans = this->mRollCylinder.m_mtxWorld* Trans;

// 필요한 버텍스마다 체크

D3DXVec3TransformCoord(&TestVec3, &TestVec3,&Trans);

// 이동 범위검사.

D3DXVECTOR3 TestVec3(0.0f, 0.0f, 0.0f);  

D3DXVec3TransformCoord(&TestVec3, &TestVec3, &this->mRollCylinder.m_mtxWorld);

// 회전 범위검사



추가로 움직이는 Cylinder를 향해 총알이 발사되도록 해봤습니다.
우선 총알을 배치해야 하는데, 이것은 XZ평면 위쪽에서 일정범위 안에서
무작위로 생성되도록 해봤습니다. (New-에서 랜덤좌표를 구해넣습니다.)
그리고 이것이 움직일 수 있는 Cylinder를 행해 이동할수 있는 이동행열을 구합니다.

D3DXVECTOR3 Target(0.0f, 0.0f, 0.0f);        

D3DXVec3TransformCoord(&Target, &Target, &this->mRollCylinder.m_mtxWorld);

D3DXVECTOR3 Bullet(NewX, NewY, NewZ);

D3DXVECTOR3 TargetDirection = Target - Bullet;

D3DXVec3Normalize(&TargetDirection, &TargetDirection);

D3DXMatrixTranslation(&mSpeedMatrix[Index], TargetDirection.x* Speed,

                           TargetDirection.y* Speed, TargetDirection.z * Speed);                         



그리고 이 총알을 해당방향으로 회전해야 합니다.
카메라의 역행열은 월드배치이므로
D3DXMatrixLookAtLH을 이용했습니다.
위에서 시작점과 목표점을 구했으니 이 함수에 직접 인자를 넣을수 있으니깐요.
물론 이전포스팅처럼 RIGHT, UP, LOOK벡터를 구해서 행열에 넣어도 가능합니다.
모델좌표의 Look는 Z축이고,  실제총알의 Look는 Y축이므로 X축 회전이 추가됩니다.

D3DXMATRIX NewTrans, NewRot1, NewRot2;           

D3DXMatrixTranslation(&NewTrans, NewX, NewY, NewZ);

D3DXVECTOR3 Pos(NewX, NewY, NewZ);

D3DXVECTOR3 LOOK = Target;

D3DXVECTOR3 UP;

D3DXVec3Cross(&UP, &Pos, &LOOK);

D3DXMatrixLookAtLH(&NewRot1, &Pos, &LOOK,&UP);

NewRot1._41 = 0.0f;          NewRot1._42= 0.0f;          NewRot1._43 = 0.0f;

D3DXMatrixInverse(&NewRot1, NULL, &NewRot1);

D3DXMatrixRotationX(&NewRot2, D3DX_PI / 2);

this->mBulletCylinder[Index].m_mtxWorld = NewRot2 *NewRot1  * NewTrans; 


이렇게 총알변환행열을 구했으면 카메라를 총알을 따라가게 하는 것은 쉽죠.
월드의 역변환이 카메라의 행열이 되니깐요.

D3DXMatrixInverse(&this->mView,NULL, &this->mBulletCylinder[0].m_mtxWorld);





결과
과제문서로 제출했던 그림을 다시 따오니 압축이 좀 심하게 들어가있네요.. ex2.exe


어쨌건 첫번째 과제는 이렇게 넘어가네요. 총알을 구현한 덕분에 텀프로젝트에선
탄환도 추가할수 있을듯 싶습니다. 물론 회전을 무식하게 구현한만큼 좀더 공부해서
다듬어야 겠지만요 --;

by hellz | 2009/04/23 12:36 | Workshop | 트랙백 | 덧글(0)

[3D][DirectX] 평면위에 굴러가는 주사위

PS2초기 타이틀중 XI5라는 게임이 있습니다.
평면위에 주사위를 굴려서 같은 면이 만나면 없어지는 퍼즐게임이죠.

MT갔다가 한 친구가 이거 구현했다는 이야기듣고 한번 따라해봤죠;;;
생각해보니 3D물체의 회전과 이동을 확실히 익히는데 좋은 예제같더군요.

그래서 이 주사위의 움직임만 DX로 구현해봤습니다.


구현한 것을 대충 요약하자면
일단 8x8보드를 Y축으로 -1만큼 내려서 배치시켰습니다.
그리고 주사위는 Y축으로 +1위에서 굴러다니는 것이죠.
주사위를 XZ평면의 위치를 가지게 되고요.
키입력에 따라 위치를 조정해 줍니다.

중요한건 주사위의 위치와 움직임의 구현하는 두가지 방법을 떠올렸습니다.
1. Translation과 Rotation 행열을 저장해서 움직일때마다 구현하는 방법.

2. Position, Look, UP 벡터를 저장해서 움직일때마다 벡터를 변환하고
   변환 행렬을 생성하는 방법.

두번째 방법은 주사위의 위쪽면을 쉽게 구할 수 있는듯 싶었지만,
배치와 굴리는 애니메이션 구현에선 첫번째 방법이 간단해서 이걸로 구현했습니다.

변환행열을 구하는건 두가지로 구분했습니다.
하나는 90도씩 완전히 이동시키는 것과 지정된 각도로 회전만 시키는 것입니다.
사실 하나로 합쳐도 되지만, 애니메이션은 임시변환행렬을 사용했기때문에
두개로 나누었습니다.

90도씩 회전은 간단합니다.
  D3DXMatrixTranslation(&Trans, X축이동, 0, Z축이동);
  D3DXMatrixRotationX or Z(&Rot, 회전각도);
주사위가 다음에 다음한칸에 이동할 변환행열을 구합니다.
그리고 다음에 회전될 변환행열을 구해주죠.
  Object.SetTranslation(*Object.GetTranslation() * Trans);   
  Object.SetRotation(*Object.GetRotation() * Rot);
이것들을 저장하고 있던 주사위의 Traslation과 Rotation에 각각 곱해서
다음에 굴러간 위치의 변환행열을 새로 구해줍니다.


애니메이션을 위한 임의회전은 회전작업에 약간 추가가 필요합니다.
주사위를 굴린다는건 한쪽 모서리를 기준으로 회전시키기 때문이죠.
왼쪽그림처럼 주사위 중심을 기준으로 회전시키면 원하는 회전모양이 아닐것입니다. 
그래서 오른쪽그림처럼 회전할려는 축으로 이동시킨뒤, 일정방향만큼 회전시키고
다시 원점으로 이동시켰습니다.
이걸 이전단계에 가지고 있던 변환행열과 곱해서 위치를 만들어줬죠.

    Result = (*Object.GetRotation()) * Trans * Rot;
    D3DXMatrixInverse(&Trans, 0, &Trans);
    Result = Result * Trans * (*Object.GetTranslation());
여기서 Trans는 회전할려는 변을 원점에 맞추는 Translation이며
이것을 다시 돌려놓기 위해 역행열을 사용했습니다.


남은 구현을 일반적인 게임프로그래밍 구조이니 넘어가고. 결과는 이렇습니다.
Dice.zip
나중에 과제로 쓸 수도 있어서 소스는 올리지 않습니다. 학기끝나고 시간날때 올리죠 뭐;;

배경판의 경우는 정점색으로 그리면 구분이 어렵고 텍스쳐 발라주긴 귀찮아서
WireFrame으로 그려줬습니다.
그외도 기본값대로 주사위의 텍스쳐를 그리니 정점과 섞여서
COLOROP를 텍스쳐로만 구성했습니다.
키보드의 방향키로 주사위를 각 방향으로 굴릴 수 있습니다.


여기서 게임을 완전히 구현한다면 현제 위쪽을 향하는 면이 무었인지 구해야 할것입니다.
이때는 월드변환 후 각 면의 법선벡터를 구해서 법선의 단위벡터가
0.1.0인 면을 찾으면 그 면이 위쪽을 바라보고 있는 면이 되겠지요...



by hellz | 2009/04/08 14:36 | Workshop | 트랙백 | 핑백(1) | 덧글(6)

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