블로그 이미지
대갈장군

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. 20. 04:37 프로그래밍/DirectX
이 sprites라는 놈은 뭐길래 이렇게 내 눈에 자주 띄는건가? 2D 그래픽이 필요 없을 것 같아도 3D 영상에도 항상 2D는 사용되고 있으며 2D 가 많이 이곳 저곳에 사용되고 있단다.

이번 강의에서는 2D 그래픽을 그리는데 가장 많이 사용되는 sprites라는 기술에 대해서 알아 볼 것이다. 

1. Loading and Dislpaying Sprites
sprites를 사용하는 방법은 매우 간단하고 쉽단다. 구차하게 설명하기 보다 바로 어떻게 하는지 살펴보자.

1. D3DXSprite interface 초기화
2. texture 로딩
3. texture 그리기

헐, 너무 간단하게 말하는걸? :)

1-1. Initializing the D3DXSprite Interface
D3DXSprite는 COM 객체로써 2D 그래픽을 쉽게 만들도록 해준다. 시스템에서 관리를 해주는 아주 적절하고 좋은 방법이라는데... 문제는 COM 객체니까 초기화가 되어야 하는데 이것을 초기화 하는 방법은 다른 DirectX interface를 초기화 하는 방법과 유사하다. 우선 인터페이스에 대한 포인터를 만들고 함수를 호출하여 인터페이스를 생성(객체화) 한단다.

예전에 Direct3D interface를 초기화 했던 방법을 살펴보면,

LPDIRECT3D9 d3d;    // the pointer to our Direct3D interface

d3d = Direct3DCreate9(D3D_SDK_VERSION);    // create the Direct3D object

인데 interface 포인터를 만들고 함수를 호출하여 생성하였다. 이것 외에도 Direct3D Device를 생성할때 이런 비슷한것을 했다는데 그건 좀 더 복잡했단다.
 
D3DXSprite도 위와 거의 유사한 방식인데 조금더 쉽다.

LPD3DXSPRITE d3dspt;    // the pointer to our Direct3D Sprite interface

D3DXCreateSprite(d3ddev, &d3dspt);    // create the Direct3D Sprite object

첫번째 인자는 늘 보던거고 두번째 인자가 생성된 인터페이스 객체를 받을 주소인갑다.

1-2. Loading a Texture
Sprites는 이미지를 그리기 위해서 텍스쳐를 사용한단다. 고로 sprite를 디스플레이 하기 위해서는 반드시 텍스쳐가 로딩되어야 한다는데... sprite 찾아보니..  (헐헐 찾는데 2시간 넘게 걸렸다. 졸고 졸고 놀고 놀고... 미친놈) 스프라이트는 음료수가 아니라 쪽화면이라는 뜻이다. 화면상의 일부를 말한다.

LPDIRECT3DTEXTURE9 sprite;    // the pointer to the sprite

D3DXCreateTextureFromFile(d3ddev, L"graphic.bmp", &sprite);

음, 이전에도 했던 것과 똑같은데... 그냥 텍스쳐 포인터 선언하고 파일로 부터 읽어 들인다. 아무런 변화는 없다. 다만 조금 느낌이 이상하다면... 흠... 아니다, 이상할게 없다. 스프라이트건 아니건 간에 텍스쳐를 읽어오고 메모리상에 저장하는 과정은 다를 이유가 없지 않나.

여기서 하나 집고 갈것은 2의 승수가 아닌 텍스쳐는 컴퓨터가 알아서 스트레칭을 하는데 스프라이트의 경우 스트레칭이 일어나면 상당히 어글리하다는 점... 고로 사이즈를 잘 잡아야 한다는 점.

1-3. Drawing the Texture
아시다시피 3차원 물체를 그리는 것은 두가지 함수로 시작하고 멈춘다. BeginScene() 함수와 EndScene() 함수다. 헌데 이 스프라이트를 그리는 것은 2차원 영상을 그리는 것으로써 3차원 영상을 그리는 함수와 구별된다. 그래서 일반적으로 위의 두 함수 사이에 스프라이트를 그리고 멈추는 함수가 따로 한벌로 존재한다. 다음 예를보자.

d3ddev->BeginScene();

// perform 3D rendering here

d3dspt->Begin(NULL);    // begin sprite drawing

// perform sprite drawing here

d3dspt->End();    // end sprite drawing

d3ddev->EndScene();

여기 보면 스프라이트 그리기 함수에 인자값으로 널이 들어가 있는데 이 것은 다양한 그리기 옵션이란다. 현재로써는 그냥 널을 사용하는 것이 좋고 다음에 한번 자세히 알아보자.

위의 구조를 잠깐 보아하니 우선 3차원 영상을 먼저 그려내고 그다음에 2차원 영상을 그려내는 구나.. 흠.. 이유가 있나? :)

이제 본격적으로 스프라이트를 그리는 명령인 Draw를 알아볼 차례이다. 제법 긴 함수다.

HRESULT Draw(LPDIRECT3DTEXTURE9 pTexture,
             CONST RECT* pSrcRect,
             CONST D3DXVECTOR3* pCenter,
             CONST D3DXVECTOR3* pPosition,
             D3DCOLOR Color);

LPDIRECT3DTEXTURE9 pTexture : 우리가 사용하고자 하는 텍스쳐로의 포인터.

CONST RECT* pSrcRect : 텍스쳐의 어떤 부분을 사용할지 알려주는 좌표 구조체 포인터. 애니매이션을 사용하지 않는 이상은 주로 전체 텍스쳐를 다 사용하게 되는데 이때는 NULL을 넣어주면 전체를 가져온다.

CONST D3DXVECTOR* pCenter : 이 벡터는 이미지의 중심을 표시한다. 헌데 이 중심이라 함은 말그대로 진짜 이미지의 중심이라기 보다는 이미지의 포터스 포인트이다.

설명을 조금 이상하게 해놨는데 마우스 커서를 예로 들었는데 일반적으로 마우스 커서의 왼쪽 끝이 '중심점'이 된다. 내 생각에 글쓴이가 의도하는 설명은, 그림에 따라 중심을 둬야 하는 위치가 제 각각일 수 있다는 것 같다.

근데 한가지 더 남아있는 의문은 왜 z 축 좌표도 필요하냐는 건데 나중에 설명한단다.

CONST D3DXVECTOR3* pPosition :
이것은 스크린에서 이미지 중심의 좌표를 지시하는 벡터 이다. 일반적으로 이미지가 스크린의 50, 50 에 있다고 말하면 그것은 이미지의 중심이 그 좌표에 있다는 것을 의미한다. 흠, 그래서 이미지의 중심이 어디인가가 중요하겠군.

이제 z 축의 사용법에 대해서 알아보면 3차원 영상과는 달리 2차원 영상에서는 z 축의 값이 0.0에서 1.0 까지 밖에 없다. 그리고 그 사이에서의 거리에 따른 크기 감쇄도 전혀 없다. (왜? 2차원 영상 이니까~)

다만 2차원 이미지는 더 가까운 이미지가 뒤에것에 겹쳐 보인다. 아직 이 글쓴이도 어떻게 이것을 사용해야 할지 모르겠다는데... 그럴만도 한게... 2차원 이미지가 겹쳐보일 일이 잘 없지 않은가?

D3DCOLOR Color : 이미지를 칠하고 싶은 색상을 정하는 인자.

어떻게 사용하는지 실제로 살펴보면,

d3dspt->Begin(NULL);    // begin sprite drawing

D3DXVECTOR3 center(0.0f, 0.0f, 0.0f);    // center at the upper-left corner
D3DXVECTOR3 position(50.0f, 50.0f, 0.0f);    // position at 50, 50 with no depth
d3dspt->Draw(sprite, NULL, &center, &position, D3DCOLOR_XRGB(255, 255, 255));  // draw it!

d3dspt->End();    // end sprite drawing

2. The Finished Program
// 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  640
#define SCREEN_HEIGHT 480
#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 file
#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
LPD3DXSPRITE d3dspt;    // the pointer to our Direct3D Sprite interface

// sprite declarations
LPDIRECT3DTEXTURE9 sprite;    // the pointer to the sprite


// 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

// 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"WindowClass1";

    RegisterClassEx(&wc);

    hWnd = CreateWindowEx(NULL,
                          L"WindowClass1",
                          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);

    D3DXCreateSprite(d3ddev, &d3dspt);    // create the Direct3D Sprite object

    D3DXCreateTextureFromFile(d3ddev, L"Panel1.png", &sprite);


    return;
}


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

    d3ddev->BeginScene();    // begins the 3D scene

    d3dspt->Begin(NULL);    // begin sprite drawing

    // draw the sprite
    D3DXVECTOR3 center(0.0f, 0.0f, 0.0f);    // center at the upper-left corner
    D3DXVECTOR3 position(50.0f, 50.0f, 0.0f);    // position at 50, 50 with no depth
    d3dspt->Draw(sprite, NULL, &center, &position, D3DCOLOR_XRGB(255, 255, 255));

    d3dspt->End();    // end sprite drawing

    d3ddev->EndScene();    // ends the 3D scene

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

    return;
}


// this is the function that cleans up Direct3D and COM
void cleanD3D(void)
{
      sprite->Release();
    d3ddev->Release();
    d3d->Release();

    return;
}

Image 1.1 - DirectGrid.png Displayed in DirectX

Image 1.1 - Panel1.png Displayed in DirectX



posted by 대갈장군