
| Textured Quad |
|
Introduction Up until now we've coloured our objects by setting the per-vertex diffuse component. This is fine for simple examples, but overall it's very limiting. In this lesson we're going to take the next step. We're going to load an image from disk and apply it to a quad. The image is called a texture and the process of applying it to an object is texturing. Sometimes in artist circles it's called skinning, but since that term can also mean a number of other things we won't use it here. To keep things simple, we're going to use a screen space quad as our object. |
|
Loading A Texture With D3DX there are a variety of ways to load a texture. In this lesson we'll use D3DXCreateTextureFromFile. HRESULT WINAPI D3DXCreateTextureFromFile( LPDIRECT3DDEVICE9 pDevice, LPCTSTR pSrcFile, LPDIRECT3DTEXTURE9 *ppTexture );
IDirect3DTexture9 *g_texture=NULL;
D3DXCreateTextureFromFile(g_d3d_device, //Direct3D Device
"DH.png", //File Name
&g_texture); //Texture handle
Like all DirectX resources you have to remember to call the texture's Release method when you are done with it. Back in the Vertex Buffer tutorial we discussed memory pools. Textures created with D3DXCreateTextureFromFile are automatically put in the Managed pool. If you need more control over the texture loading you can use D3DXCreateTextureFromFileEx, which is an advanced version of D3DXCreateTextureFromFile. D3DXCreateTextureFromFileEx will be covered in a later lesson. |
|
Setting The Active Texture Now that the texture is loaded, you need a way to tell Direct3D to use it. Setting the active texture is done with a call to the SetTexture method of the device. Each texture is bound to a Stage or Sampler. HRESULT SetTexture( DWORD Sampler, IDirect3DBaseTexture9 *pTexture );
IDirect3DTexture9 *g_texture; IDirect3DDevice9 *g_d3d_device; g_d3d_device->SetTexture(0,g_texture); |
|
Texture Stage States The Texture Stage States declare how the colour (and alpha, not covered here) channels are to be processed. We haven't had to set these in the past because they default to taking the colour from the diffuse colour. Each stage has an operation and 2 arguments that are used to define the processing that will occur. In this lesson we want the colour to come completely from the texture with no other processing. To do this we set the Operation (D3DTSS_COLOROP) to select the first argument (D3DTOP_SELECTARG1) and we set the first argument (D3DTSS_COLORARG1) to be the texture (D3DTA_TEXTURE). The second argument (D3DTSS_COLORARG2) isn't needed in this case, but we set it anyway to a safe value (D3DTA_DIFFUSE). Some drivers misbehave when both arguments aren't set to good values even if they aren't used. HRESULT SetTextureStageState( DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value );
Here's how we set it up in the example code. g_d3d_device->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_SELECTARG1); g_d3d_device->SetTextureStageState(0,D3DTSS_COLORARG1,D3DTA_TEXTURE); g_d3d_device->SetTextureStageState(0,D3DTSS_COLORARG2,D3DTA_DIFFUSE); //Ignored |
|
Texture Coordinates At this point the texture is loaded and the device states are set up for texturing. But we still haven't specified how the texture is supposed to be applied to our quad. This is where texture coordinates come in. Here is our new vertex definition.
struct textured_vertex{
float x, y, z, rhw; // The transformed(screen space) position for the vertex.
float tu,tv; // Texture coordinates
};
//Transformed vertex with 1 set of texture coordinates
const DWORD tri_fvf=D3DFVF_XYZRHW|D3DFVF_TEX1;
The diffuse component is gone and 2 new members have been added:tu, tv. These 2 coordinates specify where in the texture the given vertex is mapped. The top left of the texture is referenced as (0,0), the bottom right is referenced as (1,1). These 2 coordinates are the texture space equivalent of x,y. Given that, you can probably work out that the texture coordinates for the top right of the texture are (1,0) while the bottom left is (0,1). No matter how large your texture is, the values range from 0.0 to 1.0. Also, there is no requirement that the entire texture be used. If you set the top left corner of the quad to have texture coordinates of (0,0) and the bottom right corner to have (0.5,0.5) then the top left quarter of your texture would be used to cover the entire quad. Similarly you can use values outside of the 0.0 to 1.0 range for other effects. I recommend playing with the texture coordinates in the example code to get a good feeling for them. |
|
Limitations There are size limits on the textures you can load. Old cards such as the Voodoo 2 had a maximum size of 256x256. Modern cards can handle textures larger than 1024x1024. This limit is a hardware limitation. You can check the size limits by querying the device caps with the GetDeviceCaps method of your device. D3DCAPS9 caps; g_d3d_device->GetDeviceCaps(&caps); To check the maximum size you card can handle, check the MaxTextureWidth member of the D3DCAPS9 structure. There is also a MaxTextureHeight field, though I've never seen it differ from the MaxTextureWidth. Also, if the TextureCaps field has the D3DPTEXTURECAPS_SQUAREONLY flag set, then you textures must be square as well. Another common limitation is that the texture size must be a power of 2: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, etc. If the device has D3DPTEXTURECAPS_POW2 flag set, then textures must have their width and height as a power of 2. There is an exception though. A card that sets the D3DPTEXTURECAPS_POW2 flag can also set the D3DPTEXTURECAPS_NONPOW2CONDITIONAL flag which indicates that in certain circumstances, a non-power of 2 texture can be created. The list of limits on D3DPTEXTURECAPS_NONPOW2CONDITIONAL is fairly long, so I won't get into it here. The documentation spells it out fairly clearly anyway. Here is a quick code snippet to check the caps of your card for the above capabilities.
D3DCAPS9 caps;
int max_tex_size;
g_d3d_device->GetDeviceCaps(&caps);
max_tex_size=caps.MaxTextureWidth;
if(caps.TextureCaps & D3DPTEXTURECAPS_SQUAREONLY){
//Textures have to be square
}else{
//Texture do not have to be square
}
if(caps.TextureCaps & D3DPTEXTURECAPS_POW2){
//Textures must be a power of 2 in size
if(caps.TextureCaps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL){
//But, in certain cases textures can ignore the power of 2 limitation
}
}else{
//Textures do not need to be a power of 2 in size
}
|
|
Lesson Downloads
|
|
MSDN Links For Functions/Concepts Discussed Here |
|
|