Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
101 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

This article is for people trying to make a 2D graphics engine using Direct3D 9. It assumes you already have a basic Direct3D 9 application up and running, possibly using ID3DXSprite functions.

If you do not, the code for a fully working application using the methods presented in this article is included.

This article is divded into the following sections:

  1. The concept
  2. The vertex format
  3. Initialization code
  4. Loading a texture
  5. Drawing a texture
  6. The CTexture class

1. The concept

So you've decided to write a 2d game. Great, I love 2d games. You've also decided to use Direct3D as your API of choice. Groovy, you get to use cool tricks like fully hardware-supported alpha blending. If you're using Direct3D 8 instead, the code is pretty much the same (just change all the 8's to 9's and update CreateVertexBuffer() and SetStreamSource() calls). Now, you're probably wondering "How exactly do I put a 2d graphic on screen using an API designed exclusively for 3d graphics?"

Don't worry. It's not particularly hard. Anyways, you only have to actually write the code once ;-)

The basic idea is to draw a rectangular polygon on screen textured with your 2d image. The great thing about this is that the image can have its own alpha channel, so you don't get the opaque rectangular frame around your image (accomplishing the same thing as colour-keying) and at the same time, you can have certain areas of the image be partially transparent.

In order to draw a quad on the screen, you have to give the positions of the four corners of the quad. This means with a little toying around with the coordinates, you can easily add transformations like skewing and perspective effects. Those, however, are left as an exercise for the reader.

The main benefit, however, to using textured quads for a 2d engine is speed. You're actually using the 3d hardware to do exactly what it was designed for: spitting out a bunch of textured shapes on screen really fast.

Of course, with so many benefits to using textured quads, there must be some drawbacks. And there are a few.

The most significant drawback to using 3d hardware for drawing is that most 3d cards cannot support textures whose side lengths are not powers of 2. Furthermore, some cards cannot support non-square textures. What this means is that you should try to make your graphics have dimensions such as: 1x1, 2x2, 4x4, 8x8, 16x16, 32x32, 64x64, 128x128, etc. The maximum size you should use is 512x512. Most video cards can support textures up to 2048x2048, but some can't, and a texture that big would consume a massive amount of memory on the video card.

The CTexture class I provide at the end of this article will provide a work-around for these limitations, allowing you to use rectangular textures of weird dimensions. Even so, using non-square, non-power-of-2 textures is pretty much guaranteed to result in more processing time and more memory consumption.

2. The vertex format

It's time to get down to business: putting a quad on the screen.

Every quad is made up of two triangle and four vertices as shown below:

  v0               v1
   |\              |
   |  \            |
   |    \          |
   |      \        |
   |        \      |
   |          \    |
   |            \  |
   |              \|
  v3               v2

I like to keep that particular diagram as a comment in my code as it comes in handy when I want to change the colour tint of a particular corner on a quad I'm drawing (more on this later).

The first thing you need in order to write your own quad-based engine for 2d drawing is a flexible vertex format. The vertex format decides exactly what information is available about the vertices that make up the quads you draw. This one should do you well:


Put it somewhere where all of your graphics code can see it. It's very important.

Here's a quick run-down of exactly what the each of the flags in the above vertex format does:

D3DFVF_XYZRHW - Specifies what position information the vertex will hold. In this case, X, Y, Z coordinates, as well as a "reciprocal of homogenous W" coordinate. All you really need to know about that is it lets you use screen coordinates to draw your quads instead of 3d-space coordinates. As you may have guessed, the Z coordinate is irrelevant. It will always be set to 0.

D3DFVF_DIFFUSE - Allows you to specify a colour and alpha for the vertex, letting you shade the corners of a quad in different colours. ID3DXSprite offers limited support for this, allowing you to shade the whole a quad a certain colour. The code in this article allows you to change the colour and alpha value of each corner of a quad.

D3DFVF_TEX1 - Specifies that the vertex is to hold texture coordinates. You need this to put a texture on the quads you draw.

Now that you've got your vertex format defined, you need a structure to hold a vertex. This one will work swimmingly with the above vertex format:

//Custom vertex
    float x;
    float y;
    float z;
    float rhw;
    D3DCOLOR colour;
    float u;
    float v;

You'll need this structure to be available to all of your drawing code as well.

A brief description of the members:

x, y, z - The coordinates the vertex is located at. x and y are the screen coordinates at which the vertex is located. In 2d-land, z is always 0.0f. While x and y are floats, they should contain integral values, or they will be rounded when it comes time to draw the quad.

rhw - This is always 1.0f. It allows you to use screen coordinates to specify the position of the vertex rather than 3d-space coordinates,

colour - The colour/alpha value of the vertex in standard 32-bit AARRGGBB format.

u, v - The texture coordinates of the vertex. For the vertices on the left, u is 0.0f. On the right, u is 1.0f. On the top, v is 0.0f. On the bottom, v is 1.0f.

If you didn't get all of that, don't worry about it. As long as you copy-and-pasted the const and struct properly, you'll have no problem using the code accompanying this article ;)

Initialization code

  The concept
  Initialization code
  Drawing a texture
  The CTexture class

  Source code
  Printable version
  Discuss this article