DirectX
Graphics:
2D
Graphics Part 2
Author
: Jack Hoxley
Written : 30th November 2000
Contact : [Email]
Download : Graph_04.Zip
[8 Kb]
Contents
of this lesson:
1. Introduction
2. Some more theory
3. Loading A Texture and displaying it
4. Transparencies
1.
Introduction
Welcome
back to the fourth lesson on learning DirectX graphics. I've decided to alter
the way I'm writing things; This tutorial was originally going to include a
basic look at texturing for 2D primatives. But I realised that it'd be very
similiar to the later lesson I'd planned on introducing textures; so rather
than do two half lessons, it's all going to be done here - well the basics anyway.
Ignore my ramblings if this is a long time after 7th December....
Textures
in Direct3D are a massive topic, EVERY game that you play on your computer will
use lots and lots of textures - almost everything that's rendered is likely
to be textured in some way. Textures add incredible realism to an object - something
that shape and colour would never be able to achieve.
Simple
texturing, once learned, is extremely simple and you'll be able to do it in
your sleep within a few lessons; but you HAVE TO understand the basics in order
to get anywhere with the clever stuff a little later on. Texture blending, animation
and skinning all come under the heading of textures - and without the basic
knowledge you can never hope to realise that dream of writing the next Quake
3 engine (if that's what you think of).
Enough
talk, time to learn some theory.... hehe
2.
Some more Theory
It
wouldn't surprise me if you were getting bored of all the theory you're learning
- but if you have any intentions of getting good with DirectX theory will be
your best friend (maybe). The main things that you'll need to know before we
get started are outlined below:
What
is a texture?
A texture is a 2D bitmap that is loaded into memory and then mapped across triangles.
Up till this point you will have seen coloured triangles - where the colour
is there will be part of a texture. Textures tend to take up large amounts of
memory if either large in number or large in size - which is one of the main
reasons why we've seen the rapid increase in on-board memory for 3D accelerators.
Texture
Coordinates
Texture coordinates are NOT in pixels - remember that. All texture coordinates
are on a 0.0 to 1.0 scale; were 0.0 is the minimum value (pixel 0) and 1.0 is
the maximum (255 for a 256x256 texture, 511 for a 512x512 texture and so on...).
If you have to still use pixels for your measurements you can use a simple formula
to convert : TexCoord = (1 / MaxTexSize) * PixelCoord; which if you work it
out is perfect : (1/256) * 128 = 0.5 (correct, as 128 is half way between them).
This isn't greatly difficult, more of something to remember; specifying 256
as a texture coordinate will either not work or give you some really really
weird results....
Texture
Sizes
Textures sizes are often a real pain in the back side. All sizes must be 2^n
that is, any integer value to the power of n: 2^1, 2^2, 2^3, 2^4 and so on....
This means that textures get larger and larger as the integer goes up - which
becomes awkward if you wish to use an 800x800 textures (nearest values are 512x512
or 1024x1024). Another thing to note - it's usually impractical to use textures
greater than 256x256 in size. Some hardware doesn't support it, other hardware
just runs much slower when using large textures. Also, be practical when drawing
your textures - there is NO point in designing a super detailed texture that's
always going to be seen as very very small on screen - the detail will never
be visible (as well as it being slow to map large textures onto small triangles).
Lighting
Whilst we haven't really covered lighting yet (and we're not going to here)
you should bare in mind that lighting has an affect on how the textures will
be seen. This will be demonstrated in this lesson by the colours of the vertices
that make up the triangles. In the previous lesson we gave each vertex a colour,
this colour is basically light; and the texture will be tinted according to
the vertex colour. For example; a green vertex will make the texture appear
green....
3.
Loading a Texture and displaying it
So,
you want to play around with textures now. We're going to modify our Lesson
03 code so that instead of nice colourful shapes we get nice textured shapes.
This first part is extremely easy actually; but you'll see how it gets more
complicated later on....
First,
we need two new declarations to be added:
Dim
D3DX
As
D3DX8 '//A helper library
Dim
Texture
As
Direct3DTexture8 |
|
|
|
Not
too complicated really; the D3DX8 object is something that you'll come across
again and again in the future - so I may as well introduce it. D3DX is a helper
class, it has lots and lots of useful functions included in it to make certain
tasks easier. The main ones being shape (Geometry) creation, Mesh manipulation,
texture loading and some simple maths things - such as bounding boxes and ray
intersections (which aren't really that simple).
Second,
we need to add a few lines into our initialisation function:
'This next line can go after we create the DirectX 'Object and the Direct3DDevice8 object
Set
D3DX =
New
D3DX8 '//Create our helper library...
'Either way, the above line must come before this line....
'//We now want to load our texture;
Set
Texture
=
D3DX.CreateTextureFromFile(D3DDevice,
App.Path
&
"\ExampleTexture.bmp") |
|
|
|
Assuming
alls-well, we now have a texture loaded into memory. At this level it's not
really very complicated at all; the D3DX.CreateTextureFromFile is a very simple
call to use, and I'd be very surprised if anyone out there could get it wrong!
Later on we'll be using CreateTextureFromFileEx - which allows us much more
control over things - at the expense of being much more complicated....
The
penultimate thing we need to do is reconfigure our geometry for texture coordinates.
In this particular instance, when we're dealing with squares, creating texture
coordinates is extremely easy. Assuming you want the square to have the entire
texture you just set corners 1,2,3 to have U or V components of 1.0 (yes, U
and V - just think of them as X and Y). Should you end up with more obscure
shapes it'll get much more complicated:
Even
the above diagram for the pentagon may well be wrong - It's how I'd do it, but
depending on
the positions of you're vertices you may well need them to be different.
To
alter the coordinates we just pass the values in the last two parameters of
the CreateTLVertex( ) function that we saw in the last lesson. For our square
we need the lines to look like this:
'vertex 0
TriStrip(0) = CreateTLVertex(10, 10, 0, 1, RGB(255, 255, 255), 0, 0, 0)
'vertex 1
TriStrip(1) = CreateTLVertex(210, 10, 0, 1, RGB(255, 0, 0), 0, 1, 0)
'vertex 2
TriStrip(2) = CreateTLVertex(10, 210, 0, 1, RGB(0, 255, 0), 0, 0, 1)
'vertex 3
TriStrip(3) = CreateTLVertex(210, 210, 0, 1, RGB(0, 0, 255), 0, 1, 1) |
|
|
|
The
code above is the same as the square we set up in the last lesson; but with
the added texture coordinates.
Finally
we need to render our primatives using the texture we just loaded. This is extremely
simple actually - as shown by the following code:
D3DDevice.SetTexture
0,
Texture '//Tell the device which texture we want to use...
D3DDevice.DrawPrimitiveUP
D3DPT_TRIANGLESTRIP,
2,
TriStrip(0),
Len(TriStrip(0)) |
|
|
|
The
second line you should already know about, the first one is hardly very complicated.
The "0" parameter can be replaced with different values when you do
multiple texture blending and other special texture effects - but right now
we dont want to play around with that stuff. You should see, as you're result
something looking like this:
[Cut
down and 75% of full size]
You
may well notice in the above picture that a) we have two squares, b) they're
different colours. In the sample code for this lesson it actually creates two
squares to illustrate the next point, but as the code is almost identical I
didn't put it in above. The point is that lighting/vertex colours affect the
way that textures are displayed. Normally the texture is grey (like the right-hand
square), but when you apply the texture to a triangle where the vertices are
not white we get this colorisation. This is the basis of Direct3D lighting -
It alters the vertex colour which in turn alters the texture. This can be incredibly
useful to you, whilst white vertices result in a normal texture, grey vertices
will darken the texture; coloured ones, as you can see, colorise the final texture.
There is one thing that you must bare in mind - if there is no green around
a green vertex then the resulting colour will be black. You should be aware
of how colours are generated - red green and blue components, if the colour
at a vertex is 0.5 (half) then all the pixels nearby will have each colour channel
halved (making it darker). What happens when the texture is almost all red,
yet the light is green? The light multiplies all red and blue components by
0 and all green components by 0.75 (or whatever value); which will mean that
the red part of the texture will be multipled by 0, which will give 0, the blue
will be multiplied by 0, as there is no blue anyway we're left with blue; finally
the green is multiplied by 0.75 - but a red texture has no green, so it's 0*0.75,
which again results in 0 - the final colour being 0,0,0 - black. There are ways
around this, but we'll discuss them in a later lesson (about lighting).
4.
Transparencies
At
this point you should be perfectly capable of creating a 2D game using Direct3D8
- it would probably be quite basic, but it's not impossible. You can create
2D geometry and you can apply textures to it. The final thing that you need
to know about textures in order to do nice 2D graphics is transparencies.
Transparencies
are where the renderer doesn't render part of a texture based on the colour.
This allows you to create incredibly detailed textures without incredibly high
triangle/vertex counts. You could quite easily create a circle shape with triangles,
yet you'd need 100's of them; using transparencies you could make a texture
with transparent parts and apply it to a square (with only 2 triangles remember)
and it would look the same (or better).
To
use transparencies we must first add three new lines into our initialisation
function:
D3DDevice.SetRenderState
D3DRS_SRCBLEND,
D3DBLEND_SRCALPHA
D3DDevice.SetRenderState
D3DRS_DESTBLEND,
D3DBLEND_INVSRCALPHA
D3DDevice.SetRenderState
D3DRS_ALPHABLENDENABLE,
True |
|
|
|
These
three lines allow you to use transparent textures; first we tell Direct3D what
type of transparencies we want (there are a few possible options) then we enable
the usage of alpha blending. First thing to note - Alpha blending is a fairly
advanced topic - it just so happens that we need it to do transparencies; but
we'll come back to it later on in more detail. Another thing to note is that
we enable it's usage from the start; this adds a slight performance overhead
to our program - it's much better to leave it disabled and only turn it on when
you need it (ie, on before the DrawPrimativeUP call, and off again afterwards).
As you will find out later, when you use different types of SRCBLEND and DESTBLEND
factors it'll affect all textures and rendering; so having it on all the time
is not a great idea...
Now,
to load in a transparent texture we need to use a new function - this time more
complicated than the original one.
'//Choose
one of
the
following
depending
on
what
you
'
should
need.
Other
colours
can be
made
up,
but
these
'
ones
should
be
okay
for
most
uses...
'ColorKeyVal = &HFF000000 '//Black
'ColorKeyVal = &HFFFF0000 '//Red
ColorKeyVal = &HFF00FF00 '//Green
'ColorKeyVal = &HFF0000FF '//Blue
'ColorKeyVal = &HFFFF00FF '//Magenta
'ColorKeyVal = &HFFFFFF00 '//Yellow
'ColorKeyVal = &HFF00FFFF '//Cyan
'ColorKeyVal = &HFFFFFFFF '//White
Set TransTexture = D3DX.CreateTextureFromFileEx(D3DDevice, App.Path &
"\transtexture.bmp", 64, 64, _
D3DX_DEFAULT, 0, D3DFMT_UNKNOWN, _
D3DPOOL_MANAGED, D3DX_FILTER_POINT, _
D3DX_FILTER_POINT, ColorKeyVal, _
ByVal 0, ByVal 0) |
|
|
|
Not
too complicated really. The first part is just for convenience - by commenting
and uncommenting these lines you can use different colours as the transparent
one. The D3DX.CreateTextureFromFileEx function has quite a few parameters, you
should get to know this function, as it is quite likely that you'll use this
function for almost all of your texture loading.
object.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 |
|
|
|
From
the top then:
Object
: An initialised D3DX8 object.
Device : Your 3D rendering Device
SrcFile : A string indicating where the texture is to be loaded from
Width : width in pixels, if specified it must be a valid power of 2
value (64, 128, 256 and so on...). You can use "D3DX_DEFAULT" to
let Direct3D decide what size to load it as (whatever size it was saved at)
Height : Same as width, but for the height (funny that)
MipLevels : How many mip-map levels are generated; these will be discussed
later on in this series. For now just leave it as D3DX_DEFAULT (A complete
chain is created) or 1 (Only 1 level is generated) where 1 will require the
least amount of memory.
Usage : You can make this texture a render target (useful for some
special effects) but for normal use make this value 0.
Format : What format the surface is in; same as when you specify what
backbuffer format you want during initialisation. If you're doing anything
clever you may well need to know what this format is; but most of the time
just leave it as "D3DFMT_UNKNOWN"
Pool : Whereabouts this memory should be allocated; certain areas have
less space and different areas are faster for different uses. D3DPOOL_MANAGED
should be suitable for most uses, other uses will be explained later on in
this series.
Filter : What texture filter we want to use. This only really affects
things that are stretched or squashed - which when you move into proper 3D
will happen to almost every texture used. Normally you'll want to use D3DX_FILTER_LINEAR
, but for simple things you can use D3DX_FILTER_POINT and for best quality
you can use D3DX_FILTER_TRIANGLE. Experiment with them as you choose - some
look better than others in certain situations
MipFilter : Same as the above argument, but for mip maps. These will
be explained later on in the series.
ColorKey : What colour is going to be transparent black. This is a
32 bit ARGB code - best represented by a hexidecimal number. You should be
familiar with RGB notation (3 sets of 2 figures (0 to 9) or letters (A to
F)); this value is basically &HFF<normal rgb val>. All it does is
make all pixels of the specified colour fully transparent.
SrcInfo : Allows you to specify a D3DXIMAGE_INFO structure giving any
information about the source file - height, width, depth and format. Not really
needed - leave as "ByVal 0"
Palette : Leave as ByVal 0. Palettes are on the way out, with the ever
increasing hardware (even the now "old" cards support better) capabilites
there is very little speed advantage to be gained by using them - and they
look pretty rubbish anyway.
Thats
about all folks!
I
strongly advise you to get familiar with ALL the information covered in this
lesson; there's quite a lot of important stuff to digest - almost all of it
important if you want to advance you're knowledge of DirectX8 graphics.
You
can download the source code and see it working - as well as modifying it
and playing around. Grab it from the top of this page....
Assuming
you've swallowed all of this it's onto Lesson 05
: Basic 3D Geometry
|