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
66 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:

Contents
 Using Textures
 in Direct3D

 Loading 3D Models
 into Direct3D

 Using Direct3D
 Lighting


 Printable version
 Discuss this article
 Get the source

The Series
 Part 1
 Part 2
 Part 3

Welcome back for part 3 in my 3 part series on DirectXGraphicsÖ First off, Iím sorry for the rather long delay between parts 2 and 3 (8 months or so!), Iíve just been phenomenally busy lately. Hopefully to make up for this gap Iíll cover everything you need to know to give you a strong foundation in Direct3D8 programming. Direct3D9 / DirectX9 is about to enter itís first beta-testing stage, so it may seem that DX8/D3D8 is getting a little old (itís a 1 year old API now), but you would be foolish to think like that. From what Iíve seen here-and-there about D3D9 it seems to be not much more than an extension of D3D8, whereas v7 to v8 was a big jump, v8 to v9 is more of a revision. Also, D3D9 wont be much use for a long time yet as there will be very little hardware to support its new features, and very few end-users owning this hardware. Direct3D8ís revolutionary pixel/vertex shader technology only exists on 2 or 3 cards (GeForce 3 and the ATI Radeonís) and isnít really being that extensively used yet, so if we havenít even caught up with that properly, why do we need Direct3D9Ö?

Iíll stop moaning now, and get on with what this article is actually supposed to be about. Part 2 was quite a complicated and fast paced article, and donít expect any let-off just yet - Iím going to be keeping up the pace for this article too. This is the outline:

  1. Using textures in Direct3D
  2. Loading 3D Models from files
  3. Using the Direct3D lighting engine

Doesnít look like much does it? Haha, more fool you if thatís what your thinking. These 3 topics alone deserve an article (or two) eachÖ less talk, more learning!

Using Textures in Direct3D

So far weíve seen some basic 3D geometry - a spinning cube, you should be aware that the colour of the vertices depicts what colours you actually see when itís finally rendered. Yet you should also be aware that you cant really do more than create pretty-coloured gradients with it. Say we want to turn our 3D Box into something more interesting - say a wooden crate for example.

I donít think I need to tell you that itís almost impossible to create a decent wooden box appearance using just vertices and their colours. So weíre going to use a bitmap to display the colours. As you should be aware, 3D geometry is made up of triangles, and a simple fact of a triangle defined in 3 dimensions is that it is planar - a 2D surface that doesnít have curves or anything like that. Thus it is perfectly suited for projecting a 2D bitmap image onto. This is the basis of texturing - we use a 2D bitmap applied to the 3D triangles in order to make the overall model appear to look more detailed / look like something.

The first step to using textures is to load the texture from the hard-drive / CD-ROM into texture memory. This causes one slight complication already - texture memory is a finite resource, yet art-work tends to happily consume an infinite amount of space! Therefore we can only fit a potentially small amount of art work into memory at any one time. This amount is indicated by the amount of memory the graphics card has "onboard". 32mb is common these days, with 16mb being a past favourite and 64mb being standard on all the new high-tech boards. It is quite easy to work out how much space you are using - based on the internal texture format and the dimensions of the texture itself, also dependent on any additional space required for mip-mapping.

I discussed the CONST_D3DFORMAT enumeration in the previous article - what the letters mean, what the numbers are forÖ if you cant remember that then go read the previous article. As you are aware, a bitmap is made up of a 2D grid of pixels, we need to use the CONST_D3DFORMAT enumeration to tell Direct3D how to store the colour for each of those pixels - 32 bit, 16 bitÖ As you should be aware, 32 bit = 4 bytes, 16 bits = 2 bytes. If we store a standard 256x256 bitmap with 32 bits per pixel weíll need 256kb of texture memory, however, if we store it at 16 bits per pixel weíll only need 128kb of texture memory. This may seem fairly trivial for only one texture - and it is; but if you have 200 textures itís the difference between 50mb and 25mb - suddenly it means a lot more! 25mb will fit into most recent video cards, 50mb will only fit into the (current) top of the range 3D cards. The bottom line being that you must be clever with your choice of texture format. As a general note, you will tend to find that your game runs much faster if the display mode format and the texture formats are the same - as it saves any last minute format conversions from being done (which is just a tiny bit more work to be done).

This following little piece of code will allow you to check what texture formats can be used by the currently installed 3D board. The last parameter (D3DFMT_X8R8G8B8 in this case) indicates the texture format you want to test.

If D3D.CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, _
                         D3DWindow.BackBufferFormat, 0, D3DRTYPE_TEXTURE, _
                         D3DFMT_X8R8G8B8) = D3D_OK Then

  Debug.Print "32 Bit textures with no alpha are supported"
End If

The other rule for textures is their size. Whilst itís not so important with new 3D cards, it is very important if you want to be backwards compatible. Itís also generally much faster to stick to using the old style texture size conventions.

  1. Stick to using 2n texture dimensions, that is 2,4,8,16,32,64,128,256 and so onÖ anything above 256x256 is getting a little risky - the very popular Voodoo3 chipset doesnít support textures above 256x256 in size, which instantly causes a problem with compatibility. 256x256 is also the optimal size for a texture and tends to give the best all round performance.
  2. Textures donít have to be square. This may well cause some problems with very, very old graphics, but we cant be compatible with everyone nowÖ
  3. Where possible, group small textures onto one larger texture, this is known as texture-paging sometimes. For example 64 32x32 tile pictures will be okay as 64 different textures, but itíll run much faster to store them all as one 256x256 texture.

This following piece of code retrieves the maximum texture sizes available to the device:

D3D.GetDeviceCaps D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, DevCaps
Debug.Print "MAX TEX SIZE: ", DevCaps.MaxTextureWidth & "x" & DevCaps.MaxTextureHeight

Enough talking now, let's load a texture into memory. Textures are stored in a Direct3DTexture8 object, and can be loaded using one of two main functions (provided by the D3DX8 library):

CreateTextureFromFile( _
     Device As Direct3DDevice8, _
     SrcFile As String) As Direct3DTexture8

CreateTextureFromFileEx( _
    Device As Direct3DDevice8, _
    SrcFile As String, _
    Width As Long, _
    Height As Long, _
    MipLevels As Long, _
    Usage As Long, _
    Format As CONST_D3DFORMAT, _
    Pool As CONST_D3DPOOL, _
    Filter As Long, _
    MipFilter As Long, _
    ColorKey As Long, _
    SrcInfo As Any _
    Palette As Any) As Direct3DTexture8

As you can see, the first function is much simpler than the second. This is deliberate - sometimes you really donít need that much control over the texture creation process. However, I strongly suggest that you get used to using the second function from square 1. Many of the parameters are fairly simple and donít change much between different uses. The following code is the fairly general implentation:

Set CubeTex = D3DX.CreateTextureFromFileEx(D3DDevice, _
                        App.Path & "\cube_tex.jpg", _
                        256, 256, 1, 0, _
                        DispMode.Format, D3DPOOL_MANAGED, _
                        D3DX_FILTER_LINEAR, D3DX_FILTER_LINEAR, _
                        0, ByVal 0, ByVal 0)

Looks a little complicated doesnít it? Well, the first parameter associates the texture with our device (simple enough), the second parameter points to the file where the data is stored (BMP, TGA, JPG allowed). The third and fourth textures indicate the size of the texture in memory, if the file is of different dimensions then D3DX will resize it for you. The fifth and sixth parameters indicate the mip-map levels and the usage - leave these both to 0 in most cases, although in this case Iíve set the MipLevels parameter to be 1 - I only want one iteration in the mipmap sequenceÖ if I let it do more (setting it to 0 indicates a full chain) then itíll start chewing up my memory! The seventh parameter indicates the format of the texture, as I said earlier, keeping it the same as the device format is best - so thatís what Iíve done. The eighth parameter indicates the memory pool - managed (copied to video memory when needed/moved back to system memory when not needed), default (lets the driver decide where it should go) and system memory (stores it in system memory, which isnít accessible to the 3D Device, yet can be used for some other functions). The ninth parameter and tenth parameters indicate how D3DX filters the input data to fit the memory data - should the two sizes be different; D3DX_FILTER_LINEAR will do fine here unless youíre resizing the image by more than 2x or 3x the original. The eleventh parameter is the colorkey and isnít being used here - but will be explained later. The last two parameters arenít really that interesting and have been known to cause errors on some systems - so leave them as ByVal 0 unless you really need to.

The above code will now have loaded the following image into texture memory:


Cube_Tex.Jpg

Itís not an amazingly interesting texture, but itíll look alright on our box. The next thing that I need to discuss is texture coordinates.

Texture coordinates are a fun topic, well actually, theyíre not - because you either get it or you donít; if you donít then youíre screwed! Iím only going to go over it briefly here - hopefully you will follow, otherwise, ask some people in the forums on this site, or go in search of some other more in-depth texturing tutorials. The following diagram is required for reference:

The above diagram can be imagined as the Cube_Tex.jpg shown above. You should be familiar with coordinates in a normal 2D image - X and Y, measured in pixels. We now replace this coordinate system with a scalar system - all pixels are referred to on a 0.0 to 1.0 scale; this is unaffected by the actual pixel dimensions - 256x256 or 128x256, it doesnít matter - they both still use the 0.0 to 1.0 scale. This makes things surprisingly easier actually. Both for us, and for the 3D accelerator. It means that we can interchangeably use different sized textures (a low-res version and high-res version) with the same piece of code, and expect to get an almost identical result. It also makes it much easier to algorithmically generate texture coordinates (a bit more advanced).

In the above diagram the four corners are labelled with their respective coordinates. Iíve also drawn a simple triangle on the diagram marked with three vertices, A B and C. At a guess, Iím thinking that A will have coordinates of [0.4,0.3], B will be [0.6,0.1] and C will be [0.75,0.3] - itís only a rough guess, and you could calculate it exactly if you wantedÖ but I didnít! If this new coordinate system really confuses you still you can use a simple conversion formula: (1/Width)*X, (1/Height)*Y, where width, height, x and y are all pixel measurements. On a final note, texture coordinates are usually denoted using U,V and W rather than X,Y and Z - however U=X, V=Y, W=Z. It is advised to stick to convention so that other people understand what youíre doing.

Now that weíve covered loading textures and their coordinate system we can actually try rendering something with it! Iím going to use the cube from the second part of this series - the one with no indices and no vertex/index buffers. There is a good reason for this - I want each vertex to have itís own, different, texture coordinate. This gets difficult when using indices, as in the case of the cube, 3 sides share each vertex, between 3 and 6 triangles as well; therefore Iíd need to express up to 6 texture coordinates as a single coordinate - not easy, or in this case, just not possible. Therefore Iím going to have to use the lots-of-vertices cube.

The first step is to assign texture coordinates to each of the vertices, Iíve only copied out the code for the first face, because itís identical for the other 5, and only takes up lots of space:

CubeVerts(0) = CreateLitVertex(-1, 1, -1, Corner010, 0, 0, 0)
CubeVerts(1) = CreateLitVertex(1, 1, -1, Corner110, 0, 1, 0)
CubeVerts(2) = CreateLitVertex(-1, 1, 1, Corner011, 0, 0, 1)

CubeVerts(3) = CreateLitVertex(1, 1, -1, Corner110, 0, 1, 0)
CubeVerts(4) = CreateLitVertex(1, 1, 1, Corner111, 0, 1, 1)
CubeVerts(5) = CreateLitVertex(-1, 1, 1, Corner011, 0, 0, 1)

The parameters in bold are the two texture coordinates.

The second step is to actually render the cube with the texture applied - which is actually very very easy.

The final result looks like this:

Hmm, so whatís gone wrong here then? Itís red and yellow? Not much like the picture of the texture aboveÖ well, actually, nothing has gone wrong - youíve just seen the effects of Direct3D lighting. As you may remember, the original cube geometry had 8 different colours for the corners, well itís these colours that are blending with the texel data to form the final rendered image. This can be used to create brilliant effects - as we shall see later on in the lighting section. If we replace the vertex colours with white then the original texture wont be affected at all - and youíll get an image like this next one:

Which probably looks much more like what you expected.

Okay, so thatís texturing covered. Well, as much as you need for a foundation. There is literally tonnes and tonnes more to learn about texturing - but leave it alone till you have this part sorted out in your head. The main areas of advanced texturing come under these headings:

Alpha channel effects - opacity/transparency effects in textures.
Altering pixel data - generating procedural textures, or applying per-pixel effects.
Compression - by default the D3DFMT_DXT* formats.
The Texture cascade - where you can apply up to 8 textures to each triangle - capable of creating some stunning effects (bump mapping, specular lighting to name two).
Pixel shaders - very, very advanced texture effects - brand new to Direct3D8, and quite likely to become a big part of Direct3D 9 and 10Ö

To get you started on tutorials for some of those features I have the following links:
http://www.ancientcode.f2s.net - has a good article on binary manipulation, and itís uses in altering pixel data.
http://www.vbexplorer.com/directx4vb/Tutorials/DirectX8/GR_Lesson12.asp - a tutorial on the texture cascade and the effects it presents.
http://www.vbexplorer.com/directx4vb/Tutorials/DirectX8/GR_Lesson14.asp - a tutorial on accessing and manipulating texture memory.
http://www.vbexplorer.com/directx4vb/Tutorials/DirectX8/GR_Dot3Bump.asp - a tutorial on using the texture cascade to do Dot-3 bump mapping.



Next : Loading 3D Models into Direct3D