블로그 이미지
대갈장군

calendar

1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

Notice

2008. 2. 7. 03:37 프로그래밍/DirectX

이 내용은 www.directxtutorial.com 사이트의 내용을 번역한 하여 참조하였습니다.

앞서서 2차원 삼각형을 만들어 봤는데 한가지 특이한 점은 그것은 pre-transformed 였다는 것인데 그 의미는 그것을 3차원 공간 좌표로 전혀 바꾸지 않았다는 것을 의미한단다. 3차원 공간의 좌표를 주는 대신 우리는 D3D에게 screen 좌표를 바로 주었다는 점이랜다.

1. The Geometry Pipeline

Image 5.1 - The Geometry Pipeline

Image 5.1 - The Geometry Pipeline

자 openGL에서 봤던 바로 그 트랜스 포메이션이 그대로 나온다. 일반적으로 물체가 첫 등장 할때는 중심을 (0,0,0)에 두고 있다. 하지만 우리가 원하는것은 서로 다른 물체를 서로 다른 위치에 놓는 것이므로 물체를 만들면서 위치를 잡아 주게 된다. 이때가 바로 World Transformation이다. 아마도 초기 좌표 쯤으로 생각하면 될 듯하다.

자, 이제 우리가 원하는 공간의 점에 이런 저런 물체를 놓았다. 이때까지 좌표계는 절대적 좌표계로 아직 움직이지 않았다. 하지만 우리가 원하는 시점에서 물체를 봐야 하므로 이 좌표계를 원하는 방향으로 돌리고 움직여야 하는데 바로 이것이 View Transformation 이다. 카메라의 위치를 잡는 것이다.

자, 이제는 원하는 위치에 카메라도 놨고 사진만 찍으면 되는데 이때는 화면에 보여질수 있는 부분만 잘라서 뒤에 있는 물체는 가리고 앞에 있는 물체만 보이게 해서 2차원 영상을 만들면 된다. 바로 이것이  Projection Transformation이 되는 것이다.

이렇게 설명하니까 훨씬 이해하기가 쉽다. 물론 내가 OpenGL을 해봐서 그럴수도 있지만...

World Transformation

Image 5.2 - Model Translation

Image 5.2 - Model Translation

좀더 자세한 설명인데, WT (World Transformation)은 model space의 좌표를 world space 좌표로 바꾸는 것을 말한다. 뭐 간단히 말해서 절대적 좌표계에서 사용자가 원하는 지점으로의 이동이다.

Image 5.3 - Model Rotation

Image 5.3 - Model Rotation

Image 5.4 - Model Scaling

Image 5.4 - Model Scaling

그림 5.3 과 5.4는 각각 rotation과 scaling의 예를 보여주고 있다.

View Transformation
View transformation은 3D Coordinate system을 적절한 방향을 향해 셋업 시키는 것을 말한다. 흠, OpenGL에서는 이렇게 설명하지 않았는데 여기서의 설명은 더욱 이해하기 쉽다.

우선, 아래 그림처럼 우리가 절대 좌표계에 임의의 상자를 놓고 Virtual Camera를 어떤 공간에 놓고 그 물체를 바라보고 있다고 하자.

Image 5.5 - 3D Axes Unaligned With Camera

Image 5.5 - 3D Axes Unaligned With Camera

이제 카메라의 중심에 맞게 x와 z축을 움직여야 하는데 그 이유는 그렇게 해야 처리 속도가 개선되기 때문이라고 한다. 바로 아래 그림처럼 좌표 축을 옮기게 된다.

Image 5.6 - 3D Axes Aligned With Camera

Image 5.6 - 3D Axes Aligned With Camera

보다시피 카메라에 찍히게 되는 물체의 모습은 이전과 전혀 변함이 없지만 다만 바뀐것은 좌표 축과 바뀐 좌표축에 따른 새로운 물체의 좌표들이다.

Projection Transformation
사실 View transformation은 종종 카메라의 위치 잡기에 비유가 되는데 Projection Transformation은 종종 렌즈에 비유가 된다. 이제 남은 일은 카메라 셔터를 눌러 3차원 이미지를 2차원 이미지로 바꾸는 일인데 바로 이것이 Projection Transformation이다.

Image 5.7 - Projection Transformation

Image 5.7 - Projection Transformation

2. Untransformed Vertices
앞서 우리는 FVF를 정의 했는데 다음과 같이 정의 했다.

struct CUSTOMVERTEX {FLOAT X, Y, Z, RHW; DWORD COLOR;};
#define CUSTOMFVF (D3DFVF_XYZRHW | D3DFVF_DIFFUSE)

이것이 바로 pre-transformed vertices인데 그 의미는 이것들은 3D pipeline을 통과하지 않는다는 의미다. (그랬던가?)

3D form을 가지는 vertice는 다음과 같이 정의 해야 된다.

struct CUSTOMVERTEX {FLOAT X, Y, Z; DWORD COLOR;};
#define CUSTOMFVF (D3DFVF_XYZ | D3DFVF_DIFFUSE)

차이 점이라고는 D3DFVF_XYZ와 RHW가 더 있나 없나 차이일 뿐인데? 하하, 이렇게 바꾸고 프로그램을 실행해보라 화면에는 아무것도 나타나지 않을 것이다. 왜? 왜냐면 pipeline을 전혀 통과시키지 않았기 때문에 점들이 결정되지 않았기 때문이다.

3. What is the Matrix?
알다시피 매트릭스다. C++에서 매트릭스 선언은 대충 다음과 같이 한다.

float TheMatrix [4][4] =
{
    1.0f, 0.0f, 0.0f, 0.0f,
    0.0f, 1.0f, 0.0f, 0.0f,
    0.0f, 0.0f, 1.0f, 0.0f,
    0.0f, 0.0f, 0.0f, 1.0f
};

The Identity Matrix
뭐 설명할 게 없다. 바로 위의 매트릭스다. 초기화 상태의 매트릭스다. 항상 매트릭스를 사용하기 전에는 초기화 해줘야 한다. LoadIdentity(); 함수를 OpenGL에서 사용했던 바로 그 이유다.

The Direct3D Defined Matrix
C++에서 사용하는 2차원 배열은 상당히 불편한 점이 있으니, 이것을 개선하기 위해서는 D3D에서 정의하는 새로운 형태의 매트릭스를 사용하는 것이 편리하다.

typedef struct _D3DMATRIX {
    union {
        struct {
        float        _11, _12, _13, _14;
        float        _21, _22, _23, _24;
        float        _31, _32, _33, _34;
        float        _41, _42, _43, _44;
        };
        float m[4][4];
    }
} D3DMATRIX;

흠, 공용체를 사용한 구조체의 타입이라... 이것을 초기화 하는 함수가 있으니 바로 D3DXMatrixIdentity() 이다.

D3DXMATRIX* D3DXMatrixIdentity(D3DXMATRIX* pOut);

이 함수 인자로써 우리가 초기화하기 원하는 매트릭스 주소값을 주면 알아서 초기화 해준다.

4. Setting the World Transfromation
우선 순서를 보면 1. matrices를 생성한다. 2. D3D에게 그것을 사용한다고 알려준다. (어떤 순서인지도 함께) OpenGL에서는 생성이 아니라 선택이었는데.

4-1. Translation

D3DXMATRIX matTranslate;    // a matrix to store the translation information

// build a matrix to move the model 12 units along the x-axis and 4 units along the y-axis
// store it to matTranslate
D3DXMatrixTranslation(&matTranslate, 12.0f, 4.0f, 0.0f);

// tell Direct3D about our matrix
d3ddev->SetTransform(D3DTS_WORLD, &matTranslate);

위 코드는 간단한 translation 코드인데 SetStreamSource() 함수와 DrawPrimitive() 함수 사이에서 사용이 가능하다.

하나하나 살펴보면,

D3DMATRIX matTranslate; : 이 함수는 바로 위에서 봤던 그 D3D 매트릭스 타입이다. 이것이 4 x 4 매트릭스를 만들게 된다.

D3DMatrixTranslation(&matTranslate, 12.0f, 4.0f, 0.0f); : 이 함수는 매트릭스를 적당한 값으로 초기화 시키는 함수인데 우리가 이동시키기 원하는 좌표 점을 뒤에 넣어준것이 보인다.

d3ddev->SetTransform(D3DTS_WORLD, &matTranslate); : 이제 이것이 아마도 Transform 하라는 직접적인 명령 함수인것 같은데, 이렇게 명령을 줌으로써 D3D에게 바로 이 매트릭스를 모든 임의의 vertices 프로세싱에서 사용하라는 의미가 된다. 흠.. 그러면 각 물체별로 다른 matrix를 적용해야 할 경우에는 어떻게 한단 말인가? (그럴 필요가 없나?)

그럴 필요가 없다라고 생각하는 이유는 처음에 vertices 좌표를 잡고 시작하면 고정된 물체에 대해서 전체적인 이동만 하게 되므로 개별 적용이 필요없고 필요가 있다고 생각하는 이유는 내가 짠 OpenGL 코드처럼 물체에 따라 다른 matrix 적용이 필요한 경우가 있기 때문이다.

위 함수의 첫번째 인자를 보면 알수 있겠지만 바로 저것이 World Transformation 매트릭스를 선택한다는 의미다. OpenGL에서는 뭐였더라.. 헐헐헐.. 기억이.... -_-

4-2. Rotation

D3DXMATRIX matRotateX;    // a matrix to store the rotation information

// build a matrix to rotate the model 3.14 radians
D3DXMatrixRotationX(&matRotateX, 3.14f);

// tell Direct3D about our matrix
d3ddev->SetTransform(D3DTS_WORLD, &matRotateX);

뭐 거의 같은데 한가지 주의 할 건 rotation의 경우 축을 기준으로 회전하므로 3가지 회전 함수가 존재한다. 이경우에는 X 축에 대해서 회전하는 거다.

물론 각도로 회전 시킬수도 있다. 아래처럼,

D3DXMatrixRotationX(&matRotateX, D3DXToRadian(180.0f));

4-3. Scaling

D3DXMATRIX matScale;    // a matrix to store the scaling information

// build a matrix to double the size of the model
// store it to matScale
D3DXMatrixScaling(&matScale, 2.0f, 2.0f, 2.0f);

// tell Direct3D about our matrix
d3ddev->SetTransform(D3DTS_WORLD, &matScale);

각 축에 대해서 몇배로 확대 할 것인가를 설정해줌.

4-4. Combining the Translation, Rotation and Scaling matrices
이게 중요하다. 문제는 SetTransform()함수는 마지막에 변형된 매트릭스만 기억할뿐 앞 단계는 까먹어 버린다는 것이 문제점. 고래서 필요한 것이 Matrix Multiflication 이죠.

D3DXMATRIX matRotateX;    // a matrix to store the rotation information
D3DXMATRIX matScale;    // a matrix to store the scaling information

D3DXMatrixScaling(&matScale, 2.0f, 2.0f, 2.0f);    // double the size of the model
D3DXMatrixRotationX(&matRotateX, D3DXToRadian(90.0f));    // rotate the model 90 degrees

// set the world transform to the two matrices multiplied together
d3ddev->SetTransform(D3DTS_WORLD, &(matRotateX * matScale));

가장 단순한 예로 돌리고 확대하는 예를 보자. 위처럼 두개의 D3DXMATRIX 타입을 선언하고 각각의 매트릭스 타입을 각각의 변환에 맞게 정의한 다음 SetTranform의 두번째 인자로써 둘을 곱해서 넣어주는 것이다.

순서가 중요한데 설명에서는 1번이 90도 회전, 2번이 확대라고 되어 있다.

우선 다음 예를 보자.

D3DXMATRIX matRotateX;
D3DXMATRIX matRotateY;
D3DXMATRIX matRotateZ;
D3DXMATRIX matScale;
D3DXMATRIX matTranslate;

D3DXMatrixRotationX(&matRotateX, D3DXToRadian(50.0f));
D3DXMatrixRotationY(&matRotateY, D3DXToRadian(50.0f));
D3DXMatrixRotationZ(&matRotateZ, D3DXToRadian(50.0f));
D3DXMatrixScaling(&matScale, 5.0f, 1.0f, 1.0f);
D3DXMatrixTranslation(&matTranslate, 40.0f, 12.0f, 0.0f);

d3ddev->SetTransform(D3DTS_WORLD,
                     &(matRotateX * matRotateY * matRotateZ * matScale * matTranslate));

자 이 것은 x축으로 50도 돌리고 -> y 축으로 50도 돌리고 -> z축으로 50도 돌리고 -> x축으로 5배 확대하고 -> 이동시키기다.

자, 다음 그림을 보면 왜 순서가 중요한지 나온다.

Image 5.8 - Rotating Before Translating

Image 5.8 - Rotating Before Translating

Image 5.9 - Rotating After Translating

Image 5.9 - Rotating After Translating

5. Setting the View Transformation

D3DXMATRIX* D3DXMATRIXLookAtLH(D3DXMATRIX* pOut,
                               CONST D3DXVECTOR3* pEye,
                               CONST D3DXVECTOR3* pAt,
                               CONST D3DXVECTOR3* pUp);

gluLookAt 함수와 거의 유사하며 눈이 어디서 무엇을 보는지 그리고 윗 방향이 어딘지 설정하는 것 까지 똑같다.

우선 처음 보는 타입인 D3DXVECTOR3는 아래와 같다.

typedef struct D3DXVECTOR3
{
    FLOAT x;    // contains an x-axis coordinate
    FLOAT y;    // contayns a y-axis coordinate
    FLOAT z;    // containz a z-axis coordinate
} D3DXVECTOR3, *LPD3DXVECTOR3;    // go speling

 각각의 파라미터를 살펴보면,

D3DXMATRIX* pOut, : 이 인자는 우리가 채워 넣고자 하는 매트릭스 타입의 변수를 넣어주면 된다.

CONST D3DXVECTOR3* pEye,: 이 인자는 눈의 위치다.

CONST D3DXVECTOR3* pAt, : 이것은 어디를 보고 있는지를 위한 위치다.

CONST D3DXVECTOR3* pUp, : 이것은 어디가 위인지를 알려준다.
 

D3DXMATRIX matView;    // the view transform matrix

D3DXMatrixLookAtLH(&matView,
                   &D3DXVECTOR3 (100.0f, 100.0f, 100.0f),    // the camera position
                   &D3DXVECTOR3 (0.0f, 0.0f, 0.0f),    // the look-at position
                   &D3DXVECTOR3 (0.0f, 1.0f, 0.0f));    // the up direction

d3ddev->SetTransform(D3DTS_VIEW, &matView);    // set the view transform to matView
실제 코드를 보면 위와 같다.  한가지 더 유의할 것은 SetTranform 함수의 첫번째 인자가 VIEW로 바뀌었다는 점. ;)

6. Setting the Projection Tranformation
The Field of View(FoV)
Image 5.10 - Field of View: 45 Degrees

Image 5.10 - Field of View: 45 Degrees
 
Image 5.11 - Field of View: 22.5 Degrees

Image 5.11 - Field of View: 22.5 Degrees

뭐 굳이 설명 하지 않아도 안다... Fov...

View-Plane Clipping
Image 5.12 - The Viewing Frustum

Image 5.12 - The Viewing Frustum

뭐 이것도 사실 OpenGL에서 많이 했던 것인데, gluPerspective 함수로 했었다.

Setting Up the Projection Matrix
D3DXMATRIX* D3DXMatrixPerspectiveFovLH(D3DXMATRIX* pOut,
                                       FLOAT fovy,
                                       FLOAT Aspect,
                                       FLOAT zn,
                                       FLOAT zf);

헐, 이것도 OpenGL과 똑같은것 같은데? 첫번째 인자는 뭐 보나마나 채워넣을 매트릭스 변수 받는 것이고 fovy는 fov, aspect는 화면 비율, zn은 가까운쪽 깊이, zf는 먼쪽 깊이지 뭐.

D3DXMATRIX matProjection;    // the projection transform matrix

D3DXMatrixPerspectiveFovLH(&matProjection,
                           D3DXToRadian(45),    // the horizontal field of view
                           (FLOAT)SCREEN_WIDTH / (FLOAT)SCREEN_HEIGHT,    // aspect ratio
                           1.0f,    // the near view-plane
                           100.0f);    // the far view-plane

d3ddev->SetTransform(D3DTS_PROJECTION, &matProjection);    // set the projection transform

7. Lighting
어라 이것도 여기서 언급하나? 일단 앞선 코드에서도 라이트를 켜주는 옵션이 있었나 모르겠는데, (없었는데?) 이게 없으면 뵈는게 없단다. 일단 다음 코드를 삽입하자.

d3ddev->SetRenderState(D3DRS_LIGHTING, FALSE);    // turn off the 3D lighting

헐, 보다시피 light를 끄는 옵션인데? 결국 OpenGL처럼 라이트가 없으면 전부다 균질하게 보이는 옵션이 될것이다.

8. A Quick Review
자, 이제는 앞에서 배운걸 총정리 해보는 시간이다.

우선 헤더 부분에서 vertex format을 변경하였다. 이전에 사용했던 것은 pre-transformed vertices 였으나 이번부터 사용한 것은 not transformed 된 vertices들이었으며 다음과 같이 선언했다.

struct CUSTOMVERTEX {FLOAT X, Y, Z; DWORD COLOR;};
#define CUSTOMFVF (D3DFVF_XYZ | D3DFVF_DIFFUSE)

그리고나서 initD3D()함수에 다음과 같이 light를 끄는 명령을 넣는다.

d3ddev->SetRenderState(D3DRS_LIGHTING, FALSE);    // turn off the 3D lighting

그런 다음 init_graphics() 함수에 보면 CUSTOMVERTEX 타입 array를 사용하여 다음과 같이 사용할 점들의 정보를 저장한다.

// create the vertices using the CUSTOMVERTEX struct
CUSTOMVERTEX t_vert[] =
{
    { 2.5f, -3.0f, 0.0f, D3DCOLOR_XRGB(0, 0, 255), },
    { 0.0f, 3.0f, 0.0f, D3DCOLOR_XRGB(0, 255, 0), },
    { -2.5f, -3.0f, 0.0f, D3DCOLOR_XRGB(255, 0, 0), },
};

이제 마지막으로 render() 함수를 보면,

// this is the function used to render a single frame
void render_frame(void)
{
    d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

    d3ddev->BeginScene();

    // select which vertex format we are using
    d3ddev->SetFVF(CUSTOMFVF);

    // SET UP THE PIPELINE

    D3DXMATRIX matRotateY;    // a matrix to store the rotation information

    static float index = 0.0f; index+=0.05f;    // an ever-increasing float value

    // build a matrix to rotate the model based on the increasing float value
    D3DXMatrixRotationY(&matRotateY, index);


    // tell Direct3D about our matrix
    d3ddev->SetTransform(D3DTS_WORLD, &matRotateY);

    D3DXMATRIX matView;    // the view transform matrix

    D3DXMatrixLookAtLH(&matView,
                       &D3DXVECTOR3 (0.0f, 0.0f, 10.0f),    // the camera position
                       &D3DXVECTOR3 (0.0f, 0.0f, 0.0f),    // the look-at position
                       &D3DXVECTOR3 (0.0f, 1.0f, 0.0f));    // the up direction

    d3ddev->SetTransform(D3DTS_VIEW, &matView);    // set the view transform to matView

    D3DXMATRIX matProjection;     // the projection transform matrix

    D3DXMatrixPerspectiveFovLH(&matProjection,
                               D3DXToRadian(45),    // the horizontal field of view
                               (FLOAT)SCREEN_WIDTH / (FLOAT)SCREEN_HEIGHT, // aspect ratio
                               1.0f,    // the near view-plane
                               100.0f);    // the far view-plane

    d3ddev->SetTransform(D3DTS_PROJECTION, &matProjection);    // set the projection

    // select the vertex buffer to display
    d3ddev->SetStreamSource(0, t_buffer, 0, sizeof(CUSTOMVERTEX));

    // copy the vertex buffer to the back buffer
    d3ddev->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);

    d3ddev->EndScene();

    d3ddev->Present(NULL, NULL, NULL, NULL);

    return;
}

흠. 다 되었군. 생각보다 쉽다. OpenGL과 거의 유사하나 단 차이점이 있다면 Flexible Vertex Format이다. 매우 편리한 기능인 것 같다.

마지막으로 완성 코드와 실행 결과를 보자.

// include the basic windows header files and the Direct3D header file

#include <windows.h>

#include <windowsx.h>

#include <d3d9.h>

#include <d3dx9.h>

// define the screen resolution and keyboard macros

#define SCREEN_WIDTH 1680

#define SCREEN_HEIGHT 1050

#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)

#define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)

// include the Direct3D Library files

#pragma comment (lib, "d3d9.lib")

#pragma comment (lib, "d3dx9.lib")

// global declarations

LPDIRECT3D9 d3d; // the pointer to our Direct3D interface

LPDIRECT3DDEVICE9 d3ddev; // the pointer to the device class

LPDIRECT3DVERTEXBUFFER9 t_buffer = NULL; // the pointer to the vertex buffer

// function prototypes

void initD3D(HWND hWnd); // sets up and initializes Direct3D

void render_frame(void); // renders a single frame

void cleanD3D(void); // closes Direct3D and releases memory

void init_graphics(void); // 3D declarations

struct CUSTOMVERTEX {FLOAT X, Y, Z; DWORD COLOR;};

#define CUSTOMFVF (D3DFVF_XYZ | D3DFVF_DIFFUSE)

// the WindowProc function prototype

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);


// the entry point for any Windows program

int WINAPI WinMain(HINSTANCE hInstance,

HINSTANCE hPrevInstance,

LPSTR lpCmdLine,

int nCmdShow)

{

HWND hWnd;

WNDCLASSEX wc;

ZeroMemory(&wc, sizeof(WNDCLASSEX));

wc.cbSize = sizeof(WNDCLASSEX);

wc.style = CS_HREDRAW | CS_VREDRAW;

wc.lpfnWndProc = (WNDPROC)WindowProc;

wc.hInstance = hInstance;

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

wc.lpszClassName = L"WindowClass";

RegisterClassEx(&wc);

hWnd = CreateWindowEx(NULL, L"WindowClass", L"Our Direct3D Program",

WS_EX_TOPMOST | WS_POPUP, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT,

NULL, NULL, hInstance, NULL);

ShowWindow(hWnd, nCmdShow);

// set up and initialize Direct3D

initD3D(hWnd);

// enter the main loop:

MSG msg;

while(TRUE)

{

DWORD starting_point = GetTickCount();

if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))

{

if (msg.message == WM_QUIT)

break;

TranslateMessage(&msg);

DispatchMessage(&msg);

}

render_frame();

// check the 'escape' key

if(KEY_DOWN(VK_ESCAPE))

PostMessage(hWnd, WM_DESTROY, 0, 0);

while ((GetTickCount() - starting_point) < 25);

}

// clean up DirectX and COM

cleanD3D();

return msg.wParam;

}


// this is the main message handler for the program

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

switch(message)

{

case WM_DESTROY:

{

PostQuitMessage(0);

return 0;

} break;

}

return DefWindowProc (hWnd, message, wParam, lParam);

}


// this function initializes and prepares Direct3D for use

void initD3D(HWND hWnd)

{

d3d = Direct3DCreate9(D3D_SDK_VERSION);

D3DPRESENT_PARAMETERS d3dpp;

ZeroMemory(&d3dpp, sizeof(d3dpp));

d3dpp.Windowed = FALSE;

d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;

d3dpp.hDeviceWindow = hWnd;

d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;

d3dpp.BackBufferWidth = SCREEN_WIDTH;

d3dpp.BackBufferHeight = SCREEN_HEIGHT;

// create a device class using this information and the info from the d3dpp stuct

d3d->CreateDevice(D3DADAPTER_DEFAULT,

D3DDEVTYPE_HAL,

hWnd,

D3DCREATE_SOFTWARE_VERTEXPROCESSING,

&d3dpp,

&d3ddev);

init_graphics(); // call the function to initialize the triangle

d3ddev->SetRenderState(D3DRS_LIGHTING, FALSE); // turn off the 3D lighting

return;

}


// this is the function used to render a single frame

void render_frame(void)

{

d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

d3ddev->BeginScene();

// select which vertex format we are using

d3ddev->SetFVF(CUSTOMFVF);

// SET UP THE PIPELINE

D3DXMATRIX matRotateY; // a matrix to store the rotation information

static float index = 0.0f; index+=0.05f; // an ever-increasing float value

// build a matrix to rotate the model based on the increasing float value

D3DXMatrixRotationY(&matRotateY, index);

// tell Direct3D about our matrix

d3ddev->SetTransform(D3DTS_WORLD, &matRotateY);

D3DXMATRIX matView; // the view transform matrix

D3DXMatrixLookAtLH(&matView,

&D3DXVECTOR3 (0.0f, 0.0f, 10.0f), // the camera position

&D3DXVECTOR3 (0.0f, 0.0f, 0.0f), // the look-at position

&D3DXVECTOR3 (0.0f, 1.0f, 0.0f)); // the up direction

d3ddev->SetTransform(D3DTS_VIEW, &matView); // set the view transform to matView

D3DXMATRIX matProjection; // the projection transform matrix

D3DXMatrixPerspectiveFovLH(&matProjection,

D3DXToRadian(45), // the horizontal field of view

(FLOAT)SCREEN_WIDTH / (FLOAT)SCREEN_HEIGHT, // aspect ratio

1.0f, // the near view-plane

100.0f); // the far view-plane

d3ddev->SetTransform(D3DTS_PROJECTION, &matProjection); // set the projection

// select the vertex buffer to display

d3ddev->SetStreamSource(0, t_buffer, 0, sizeof(CUSTOMVERTEX));

// copy the vertex buffer to the back buffer

d3ddev->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);

d3ddev->EndScene();

d3ddev->Present(NULL, NULL, NULL, NULL);

return;

}


// this is the function that cleans up Direct3D and COM

void cleanD3D(void)

{

t_buffer->Release(); // close and release the vertex buffer

d3ddev->Release(); // close and release the 3D device

d3d->Release(); // close and release Direct3D

return;

}


// this is the function that puts the 3D models into video RAM

void init_graphics(void)

{

// create the vertices using the CUSTOMVERTEX struct

CUSTOMVERTEX t_vert[] =

{

{ 3.0f, -3.0f, 0.0f, D3DCOLOR_XRGB(0, 0, 255), },

{ 0.0f, 3.0f, 0.0f, D3DCOLOR_XRGB(0, 255, 0), },

{ -3.0f, -3.0f, 0.0f, D3DCOLOR_XRGB(255, 0, 0), },

};

// create a vertex buffer interface called t_buffer

d3ddev->CreateVertexBuffer(3*sizeof(CUSTOMVERTEX),

0,

CUSTOMFVF,

D3DPOOL_MANAGED,

&t_buffer,

NULL);

VOID* pVoid; // a void pointer

// lock t_buffer and load the vertices into it

t_buffer->Lock(0, 0, (void**)&pVoid, 0);

memcpy(pVoid, t_vert, sizeof(t_vert));

t_buffer->Unlock();

return;

}

Image 5.13 - The Rotating Triangle

Image 5.13 - The Rotating Triangle
posted by 대갈장군