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:
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 Direct3DSo 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.
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:
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. To get you started on tutorials for some of those features I have the following links:
|