이건뭘까... Color Key라. 이번 강의에서는 텍스쳐를 로딩하는 새로운 방법을 보여준다는 구나. 바로 color key를 사용하여 그림의 일부만 투명하게 하기도 하도 그리기도 하고 그런다는 구나...
흠, 투명하게 만든다는 부분은 좀 이해가 안가고 일부만 그린다는 것은 OpenGL에서의 subimage 형식으로 이해할수 있겠다만 일단 color key 에 대해서 짚고 넘어가자.
1. About Color-Keys
그렇다면 우선 근본 적인 질문인, '왜 우리는 그림의 일부만 다시 그리고 싶은 걸까?' 에 대해 대답해보자.
예를들어 다음 그림처럼 둥근 도넛 모양을 화면에 그린다고 해보자. 256X256을 사용한다.
Image 12.1 - A Blue-Circle Texture
그런데 이상하게도 우리는 이 모양의 사각형을 회색 배경을 가진 화면에서 그리고 싶다고 하자... 다음 그림과 같이 되겠죠.
Image 12.2 - The Blue Circle As Displayed
color key는 텍스쳐에 있는 특정한 색상을 렌더링시 그리지 않는 것을 말한다. 만약 우리가 검정색을 color key로 설정하면 이것은 렌더링 될때 아래와 같이 사라지게 된다.
Image 12.3 - The Blue Circle Using a Color Key
상당히 특이한 방법인데, OpenGL에서는 본적이 없는 것 같다. 헌데 문제라면 게임상에서 그려질만한 색상을 선택해서는 안된다는 것인데 그 색상을 표현되지 않을것이다. 고로 잘 골라야함. 절대 쓰지 않을 색상으로... 예를 들면 핫 핑크.... 같은거... 헐헐헐
2. D3DXCreateTextureFromFileEx()
아따 그놈 이름도 참말로 기네잉. 이름만 긴게 아니고라.... 인자수도 졸라게 많다.
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]
Value | Description |
---|---|
D3DPOOL_DEFAULT | This flag indicates that the texture should be created in the most appropriate memory for what settings and resources are available. This however, imposes some limits which are not always good for games. |
D3DPOOL_MANAGED | This indicates that the texture will be located in the video RAM. |
D3DPOOL_SYSTEMMEM | This indicates that the texture will be located in the system memory. Vertex buffers located here cannot usually be accessed by the Direct3D Device, but can be accessed by other, more advanced means. |
D3DPOOL_SCRATCH | This also indicates the texture will be located in system memory, however, there is no way the video RAM to access this. This type is useful for storing graphics information that is not currently being used (but will be used later), such as graphics belonging to other maps a player hasn't reached yet, but might in the near future. |
[Close Table] |
DWORD Filter : 특별한 결과를 만들어내기 위한 (텍스쳐를 이용한) 기술인데, (특별한 상황에서만) 현재는 사용하지 않는다. 고로 기본 인자값인 D3DX_DEFAULT 로 설정해서 사용.
DWORD MipFilter : 이것도 Filter인자와 매우 유사한 것인데 단지 차이점이라면 이 인자의 설정은 오직 MipMap에만 영향을 미친다는 점이다. 이것도 기본값 사용.
D3DCOLOR ColorKey : 드디어 관계 되는 인자가 나왔구나. D3DCOLOR_XRGB() macro 를 이용하여 가려버리고 싶은 색을 지정하자.
D3DXIMAGE_INFO* pSrcInfo : 이것은 D3DXIMAGE_INFO struct에 대한 포인터로 로딩된 이미지에 대한 모든 정보를 담고 있는 구조체다.
A D3DXIMAGE_INFO struct는 이미지 정보를 담는데 사용하는 아주 유용한 구조체이다. 다음 테이블이 바로 구조체 내부에 자리잡고 있는 인자들에 대한 설명이다.
Value | Description |
---|---|
UINT Width | An unsigned integer containing the width of the original image in pixels. |
UINT Height | Same, but for the height. |
D3DFORMAT Format | A D3DFORMAT value indicating the pixel format of the original image. |
D3DXIMAGE_FILEFORMAT ImageFileFormat | A D3DXIMAGE_FILEFORMAT allowing you to recall the original format of the file and do work with it. File formats return as flags such as D3DXIFF_BMP, D3DXIFF_JPG or D3DXIFF_PNG. |
[Close Table] |
PALETTEENTRY* pPalette : 256 색상을 사용하는 경우 사용되는 인자인데 여기서는 주로 사용하지 않는다. 32 비트 세상인 지금 거의 쓸일이 없다고 봐도 무방하죠.
LPDIRECT3DTEXTURE9* ppTexture : 드디어 마지막 인자. 우리가 로딩하고자 하는 주소 위치를 찍어주는 인자. 어따 넣을건지 알려주는 거죠..
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
#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;
}
'프로그래밍 > DirectX' 카테고리의 다른 글
[DX Tutorial - Mesh] Lesson 2: Loading Meshes From X Files (0) | 2008.02.19 |
---|---|
[DX Tutorial - Mesh] Lesson 1: Using Basic Meshes (0) | 2008.02.16 |
[DX Tutorial] Lesson 11: Blending Colors for Special Effects (0) | 2008.02.16 |
[DX Tutorial] Lesson 10: More on Lighting (0) | 2008.02.15 |
[DX Tutorial] Lesson 9: Rendering WIth Vertex Lighting (0) | 2008.02.14 |