Now we are going to return once more to the subject of textures. As I mentioned
in Lesson 8, there is enough you can do with textures to create an entire tutorial
about them. I won't, but I will tell you quite a bit about them.
In this lesson, we're going to look into a new way to load textures. To add a practical
use for this new way, we're going to learn about color-keys, which allow you to
have part of an image transparent and part of it drawn. First we're going
to learn what exactly color-leys are, and then we'll go into the new function.
And why would we want to draw part of an image? Let's take an example:
Let's say we wanted to draw a simple blue circle on our screen.
We could create
a square, and place a texture of a blue circle on it. Let's say we use this
256x256 texture:

Image 12.1 - A Blue-Circle Texture
Now let's take this textured square and draw it onto a gray background (for whatever
reason).

Image 12.2 - The Blue Circle As Displayed
Well, this certainly won't do. What we want to do here is draw only the blue
and hide all the black. Direct3D fortunately has a way of doing this.
It is called a color-key.
And so what is a color-key? A color-key is a specified color in a texture
that is not drawn when rendering occurs. For example, if we had specified
black as our color key in this image, here is what we would have gotten:

Image 12.3 - The Blue Circle Using a Color Key
Now that is, I'm sure, what was originally intended when that texture was made.
When making textures intended to be "color-keyed", it is usually not wise to select
black for the transparent color, as black is too often used in the texture itself.
Instead use some arbitrary color that you would never use in your game, such as
pink (unless you're thinking of competing with The Sims). As I can't
imagine you using 255, 0, 255 (hot pink) in your game, it's probably safe to use
as a color-key.
What a function name! Not only is it a long name, but it has more parameters
than any function we have covered so far!
But don't get discouraged. This lesson and the next few will be devoted to
explaining it in detail, in addition to the many different techniques it supports.
However, let's start with a simple function prototype, examine the parameters, and
find out how we can use color-keys. Then we'll worry about the rest.
So here we go:
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);
Breathe!
All right, let's dive in!
Well, it's a good start. We know this one already. d3ddev. Easy.
We know this one too. It's the filename of the texture we wish to load.
L"Whatever.bmp" is what we need here. (or no L if you're into that kind of
thing).
These two are not as straightforward as they seem. You would at first think
they were the width and height of the texture, but they are not. What they
do is tell Direct3D to stretch the texture to the size given. You
can stretch it to any size (or give the image's actual size if you don't want Direct3D
to stretch the width or the height to a power of 2).
Note that this is unwise for performance reasons, and also because not all video
devices support this feature. To work with everyone, extend the borders of
your texture to a power of two and use a color key to make the extra space invisible.
Then, use D3DX_DEFAULT to have the image be stretched to powers of 2 (hopefully
it is already this way) or use D3DX_DEFAULT_NONPOW2 to use the texture's original
size.
I will be using D3DX_DEFAULT throughout this tutorial, and providing textures of
an appropriate size so that we don't have ugly, stretched textures.
This parameter indicates the number of mip levels the texture will have.
Mip levels are basically copies of textures that intentionally have less detail
than the original. The purpose of having mip levels is so that Direct3D can
display textures that are far away with correct accuracy. Drawing textures
that are far away during runtime makes the textures look incorrect.
To correct this, Direct3D, while loading a texture, can make several copies of the
texture in memory, each having less quality, but still being accurate. This
process is called mip mapping. As doing this takes a little time,
Direct3D can do it beforehand and have the images ready to draw during runtime.
But back to the beginning, this parameter indicates the number of mip levels the
texture will have. We won't be using it in this lesson, so we'll set it to
D3DX_DEFAULT.
This is a parameter which we can use to set advanced details regarding how the texture
will be used. 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.
Here we have the format of the pixels in the texture. Because we are planning
to use transparency (for the color-key), we'll need to have 8 bits of alpha in the
texture, so we'll set this to D3DFMT_A8R8G8B8.
We have gone over this parameter before, although it has been some time. What
it does is tell Direct3D where to create the texture and how. For the length
of this tutorial, we will only be using D3DPOOL_MANAGED, which tells Direct3D to
save the texture in video RAM.
I've copied the table we used before up to here so you can have a look at it again.
[
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] |
A filter is a technique that can be used to create special results with textures
to make them look better under particular circumstances. We'll be going into
this in more detail in future lessons. For now, we'll set this to D3DX_DEFAULT
to get the most efficient filters.
This parameter is very similar to DWORD Filter, only it applies to mip maps, rather
than the base texture. We'll also set this one to D3DX_DEFAULT.
This one is easy, and even relevant! This parameter is the color-key that
is to be used for the transparent parts of the image. Use a D3DCOLOR_XRGB()
macro here to indicate which color.
This parameter is a pointer to a D3DXIMAGE_INFO struct, in which will be stored
all the information about the image loaded.
A D3DXIMAGE_INFO struct is a very useful tool in obtaining information about an
image. The following table lets you in on some of the information it provides:
[
Table 12.2 - D3DXIMAGE_INFO
Values]
|
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] |
If using this struct is not needed, we can just set it to NULL.
This parameter is only used when programming with 256 colors (quite obsolete for
3D gaming) and so we will not get into it here. Just set it to NULL.
The last parameter! This is simply a pointer to the texture we are loading
to. It is exactly the same as it was in the smaller version of the function.
And that's it! Let's take a look at the function as we'll use it in this next
sample program:
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
When using this function, it is always a relief to me that almost none of the parameters
are actually used. :)
And without further ado, let's build the program. This one uses the exact
same matrices as the last program, and produces a similar result. Only this
time, we're using color-key transparency instead of alpha transparency.
Note however, that the alpha channels are still required. Taking these out
would put the pink border around the two circles, which isn't very good looking.
I've bolded the lines needed from the last lesson, as well as the
new lines added here.
Also, before your run the program, you should download and include this texture:
BlueCircle.png
[Show Code]
// 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;
}
If you run this code, here is what you will get:

Image 12.4 - Blue Circles in Action
Well, it doesn't look quite as exciting as it is...
This is just the beginning of the power of textures. There are many many other
things that can be done comming up in the next couple of lessons.
For now, however, try doing these for exercises:
1. Experiment with image stretching, and figure out when Direct3D will or
will not stretch your image.
2. Make one of the circles red.
3. Try to get the two circles to rotate through various colors during runtime.
4. Design your own texture that requires a color-key and use it in the program.
For now, that's all there is in the Direct3D Basics tutorial. However, do
go on and do the Direct3D Meshes tutorial. If thought you could do a lot with
3D now, you'll do much more by the time you are through with it.
Next Tutorial: Direct3D Meshes
GO! GO! GO!
© 2006-2009 DirectXTutorial.com. All Rights Reserved.