블로그 이미지
대갈장군

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. 16. 05:11 프로그래밍/DirectX

이건뭘까... Color Key라. 이번 강의에서는 텍스쳐를 로딩하는 새로운 방법을 보여준다는 구나. 바로 color key를 사용하여 그림의 일부만 투명하게 하기도 하도 그리기도 하고 그런다는 구나...

흠, 투명하게 만든다는 부분은 좀 이해가 안가고 일부만 그린다는 것은 OpenGL에서의 subimage 형식으로 이해할수 있겠다만 일단 color key 에 대해서 짚고 넘어가자.

1. About Color-Keys
그렇다면 우선 근본 적인 질문인, '왜 우리는 그림의 일부만 다시 그리고 싶은 걸까?' 에 대해 대답해보자.

예를들어 다음 그림처럼 둥근 도넛 모양을 화면에 그린다고 해보자. 256X256을 사용한다.

Image 12.1 - A Blue-Circle Texture

Image 12.1 - A Blue-Circle Texture

그런데 이상하게도 우리는 이 모양의 사각형을 회색 배경을 가진 화면에서 그리고 싶다고 하자... 다음 그림과 같이 되겠죠.

Image 12.2 - The Blue Circle As Displayed

Image 12.2 - The Blue Circle As Displayed

뭔가 이상하다. 우리가 그리고자 하는 것은 사실 파란 부분만인데 여기서는 다 보여지고 있다. 자 여기서 바로 color-key의 개념이 사용된다.

color key는 텍스쳐에 있는 특정한 색상을 렌더링시 그리지 않는 것을 말한다. 만약 우리가 검정색을 color key로 설정하면 이것은 렌더링 될때 아래와 같이 사라지게 된다.

Image 12.3 - The Blue Circle Using a Color Key

Image 12.3 - The Blue Circle Using a Color Key

상당히 특이한 방법인데, OpenGL에서는 본적이 없는 것 같다. 헌데 문제라면 게임상에서 그려질만한 색상을 선택해서는 안된다는 것인데 그 색상을 표현되지 않을것이다. 고로 잘 골라야함. 절대 쓰지 않을 색상으로... 예를 들면 핫 핑크.... 같은거... 헐헐헐

2. D3DXCreateTextureFromFileEx()
아따 그놈 이름도 참말로 기네잉. 이름만 긴게 아니고라.... 인자수도 졸라게 많다.

HRESULT D3DXCreateTextureFromFileEx(LPDIRECT3DDEVICE9 pDevice,
                                    LPCTSTR pSrcFile,
                                    UINT Width,
                                    UINT Height,
                                    UINT MipLevels,
                                    DWORD Usage,
                                    D3DFORMAT Format,
                                    D3DPOOL Pool,
                                    DWORD Filter,
                                    DWORD MipFilter,
                                    D3DCOLOR ColorKey,
                                    D3DXIMAGE_INFO* pSrcInfo,
                                    PALETTEENTRY* pPalette,
                                    LPDIRECT3DTEXTURE9* ppTexture);

끄아.. 길다만... 자세히 한번 살펴보자.. 차근 차근..

LPDIRECT3DDEVICE9 pDevice : 쉬운거다. d3ddev를 말한다.

LPCTSTR pSrcFile : 이것도 아는 거군. 파일 이름 지정하는 포인터다. L"Whatever.bmp" 같은 식으로 넣어주면 된다.

UINT Width, UINT Height : 뭐, 이름에서 딱 티가 난다. 단순히 생각해서 텍스쳐의 크기라고 생각하겠지만 아니다. 이것은 텍스쳐 이미지를 요 크기대로 늘리거나 줄이라는 말인데, 만약 이미지 그 자체의 크기로 유지하고 싶다면 원래 이미지 크기를 넣어도 된다?  정말? 원하는 크기로 맘대로 늘리고 줄일수 있단다.

헌데, 이것은 추천되지 않는 방법인데 이유는 일단 성능 문제이고 둘째는 모든 비디오 카드가 지원하는건 아니기 때문이라는데... 모든 환경에서 작동하가 위해서는 텍스쳐를 2의 승수로 줄이거나 늘리고, 그런다음 색상을 감추기 위해서 color key를 사용하는 것이 좋단다. D3DX_DEFAULT 를 사용하여 텍스쳐 이미지를 2의 승수로 늘리던가 아니면 D3DX_DEFAULT_NONPOW2 를 사용해 원래 이미지 사이즈를 사용할 수도 있단다.

필자는 D3DX_DEFAULT를 항시 사용하는데 이것이 가장 효율적이고 테스트에 적합하단다.

UINT MipLevels : 밉맵 레벨을 지정하는 것인데 이것은 OpenGL에서 많이 봤던 내용이다. 기본적으로 밉맵은 하나의 텍스쳐의 여러개의 복사본이다. 단, 사이즈가 다르다. 왜 다른 사이즈의 텍스쳐가 필요하냐면, 기본적으로 큰 텍스쳐를 입힌 큰 물체가 시야에서 멀어져서 작아지면 텍스쳐가 찌개져 보이는 (약간 찌개짐) 효과가 발생하며 또한 효율성에 있어서도 멀리 있는 작은 물체를 무진장 큰 텍스쳐로 커버하는 비효율성을 보이게 된다. 이런 점을 커버하기 위해서 여러 가지 크기의 텍스쳐를 준비해서 거리와 크기에 따라 다른 텍스쳐를 사용하는 것이다.

DWORD Usage : Advanced.  The only way that we intend to use it is to draw it, so we don't need to do anything with this.  We'll just set it to NULL.

D3DFORMAT Format : 픽셀 포멧 설정하는 거구나. color key를 사용할 것이므로 알파 채널이 필요하고 고로 D3DFMT_A8R8G8B8 를 사용해야 한다.

D3DPOOL Pool : 이전에 한번 다룬적이 있는 인자인데, 이것은 D3D에게 어디다 텍스쳐를 어떻게 만들것인지를 알려주는 역활을 한다. 현재 사용할 것은 MANAGED인데 video ram에 텍스쳐 이미지를 저장 보관 한다.

[Table 12.1 - D3DPOOL Values]

DWORD Filter : 특별한 결과를 만들어내기 위한 (텍스쳐를 이용한) 기술인데, (특별한 상황에서만) 현재는 사용하지 않는다. 고로 기본 인자값인 D3DX_DEFAULT 로 설정해서 사용.

DWORD MipFilter : 이것도 Filter인자와 매우 유사한 것인데 단지 차이점이라면 이 인자의 설정은 오직 MipMap에만 영향을 미친다는 점이다. 이것도 기본값 사용.

D3DCOLOR ColorKey : 드디어 관계 되는 인자가 나왔구나. D3DCOLOR_XRGB() macro 를 이용하여 가려버리고 싶은 색을 지정하자.

D3DXIMAGE_INFO* pSrcInfo : 이것은 D3DXIMAGE_INFO struct에 대한 포인터로 로딩된 이미지에 대한 모든 정보를 담고 있는 구조체다.

A D3DXIMAGE_INFO struct는 이미지 정보를 담는데 사용하는 아주 유용한 구조체이다. 다음 테이블이 바로 구조체 내부에 자리잡고 있는 인자들에 대한 설명이다.

[Table 12.2 - D3DXIMAGE_INFO Values]

PALETTEENTRY* pPalette : 256 색상을 사용하는 경우 사용되는 인자인데 여기서는 주로 사용하지 않는다. 32 비트 세상인 지금 거의 쓸일이 없다고 봐도 무방하죠.

LPDIRECT3DTEXTURE9* ppTexture : 드디어 마지막 인자. 우리가 로딩하고자 하는 주소 위치를 찍어주는 인자. 어따 넣을건지 알려주는 거죠..

D3DXCreateTextureFromFileEx(d3ddev,    // the device pointer
                            L"BlueCircle.png",    // the file name
                            D3DX_DEFAULT,    // default width
                            D3DX_DEFAULT,    // default height
                            D3DX_DEFAULT,    // no mip mapping
                            NULL,    // regular usage
                            D3DFMT_A8R8G8B8,    // 32-bit pixels with alpha
                            D3DPOOL_MANAGED,    // typical memory handling
                            D3DX_DEFAULT,    // no filtering
                            D3DX_DEFAULT,    // no mip filtering
                            D3DCOLOR_XRGB(255, 0, 255),    // the hot-pink color key
                            NULL,    // no image info struct
                            NULL,    // not using 256 colors
                            &texture_1);    // load to texture_1

위의 예가 실제에 사용되는 예인데... 후미 길다.. 거의 WndMain 수준인데? 헐헐헐.. 하지만 알고 보면 간단한 것들이다.

3. The Finished Program
우선 결과는..

Image 12.4 - Blue Circles in Action

Image 12.4 - Blue Circles in Action

그리고 코드는

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

// texture declarations
LPDIRECT3DTEXTURE9 texture_1;    // the texture

// 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; FLOAT U, V; };
#define CUSTOMFVF (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1)

// 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;
    d3dpp.EnableAutoDepthStencil = TRUE;
    d3dpp.AutoDepthStencilFormat = D3DFMT_D16;

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

    d3ddev->SetRenderState(D3DRS_LIGHTING, FALSE);    // turn on the 3D lighting
    d3ddev->SetRenderState(D3DRS_ZENABLE, TRUE);    // turn on the z-buffer

    // keep the alpha blending in
    d3ddev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);    // turn on the color blending
    d3ddev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);    // set source factor
    d3ddev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);    // set dest factor
    d3ddev->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);    // set the operation

    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->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

    d3ddev->BeginScene();

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

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

    // set the view transform
    D3DXMATRIX matView;    // the view transform matrix
    D3DXMatrixLookAtLH(&matView,
    &D3DXVECTOR3 ((float)sin(index) * 20.0f, 2.0f, 25.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

    // set the projection transform
    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

    // set the stream source
    d3ddev->SetStreamSource(0, t_buffer, 0, sizeof(CUSTOMVERTEX));

    // set the texture
    d3ddev->SetTexture(0, texture_1);


    // set the first world transform
    D3DXMATRIX matTranslate;
    D3DXMatrixTranslation(&matTranslate, 0.0f, 0.0f, -10.0f);
    d3ddev->SetTransform(D3DTS_WORLD, &(matTranslate));    // set the world transform

    // draw the first circle
    d3ddev->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);

    // set the second world transform
    D3DXMatrixTranslation(&matTranslate, 0.0f, 0.0f, 0.0f);
    d3ddev->SetTransform(D3DTS_WORLD, &(matTranslate));    // set the world transform

    // draw the second circle
    d3ddev->DrawPrimitive(D3DPT_TRIANGLESTRIP, 4, 2);

    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
    texture_1->Release();    // close and release the texture
    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)
{

    // Load the BlueCircle.png texture
    D3DXCreateTextureFromFileEx(d3ddev,    // the device pointer
                                L"BlueCircle.png",    // the file name
                                D3DX_DEFAULT,    // default width
                                D3DX_DEFAULT,    // default height
                                D3DX_DEFAULT,    // no mip mapping
                                NULL,    // regular usage
                                D3DFMT_A8R8G8B8,    // 32-bit pixels with alpha
                                D3DPOOL_MANAGED,    // typical memory handling
                                D3DX_DEFAULT,    // no filtering
                                D3DX_DEFAULT,    // no mip filtering
                                D3DCOLOR_XRGB(255, 0, 255),    // the hot-pink color key
                                NULL,    // no image info struct
                                NULL,    // not using 256 colors
                                &texture_1);    // load to texture_1

    // create the vertices using the CUSTOMVERTEX struct
    CUSTOMVERTEX t_vert[] =
    {
        // square 1
        { -3.0f, 3.0f, 3.0f, D3DCOLOR_ARGB(255, 255, 255, 255), 0, 1, },
        { -3.0f, -3.0f, 3.0f, D3DCOLOR_ARGB(255, 255, 255, 255), 0, 0, },
        { 3.0f, 3.0f, 3.0f, D3DCOLOR_ARGB(255, 255, 255, 255), 1, 1, },
        { 3.0f, -3.0f, 3.0f, D3DCOLOR_ARGB(255, 255, 255, 255), 1, 0, },

        // square 2
        { -3.0f, 3.0f, 3.0f, D3DCOLOR_ARGB(255, 255, 255, 255), 0, 1, },
        { -3.0f, -3.0f, 3.0f, D3DCOLOR_ARGB(255, 255, 255, 255), 0, 0, },
        { 3.0f, 3.0f, 3.0f, D3DCOLOR_ARGB(255, 255, 255, 255), 1, 1, },
        { 3.0f, -3.0f, 3.0f, D3DCOLOR_ARGB(255, 255, 255, 255), 1, 0, },
    };

    // create a vertex buffer interface called t_buffer
    d3ddev->CreateVertexBuffer(8*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;
}

이제 드디어 D3D basic이 끝나고 다음으로 D3D mesh로 넘어간다. 좀더 멋진걸 해보자! 유후!
posted by 대갈장군