Introduction to 3D Game Programming with DirectX.9.0 - F. D. Luna
.pdf
114 Chapter 6
Example:
Device->SetTexture(0, _stonewall);
Note: In Direct3D, you can set up to eight textures that can be combined to create a more detailed image. This is called multitexturing. We do not use multitexturing in this book until Part IV; therefore we always set the textures stage to 0, for now.
To disable a texture at a particular texturing stage, set pTexture to 0. For instance, if we don’t want to render an object with a texture, we would write:
|
|
|
|
|
|
Y |
If our scene has triangles that use different textures, we would have to |
||||||
|
|
|
|
|
L |
|
do something similar to the following code: |
||||||
|
|
|
|
|
F |
|
|
Device->SetTexture(0, |
tex0); |
|
|
||
|
drawTrisUsingTex0(); |
|
M |
|
||
|
Device->SetTexture(0, |
|
|
|||
|
tex1); |
|
|
|||
|
|
|
A |
|
||
|
|
E |
|
|
||
|
|
T |
|
|
|
|
Device->SetTexture(0, 0); renderObjectWithoutTexture();
drawTrisUsing ex1();
As mentioned previously, textures are mapped to triangles in screen space. Usually, the texture triangle is not the same size as the screen triangle. When the texture triangle is smaller than the screen triangle, the texture triangle is magnified to fit. When the texture triangle is larger than the screen triangle, the texture triangle is minified to fit. In both cases, distortion will occur. Filtering is a technique Direct3D uses to help smooth out these distortions.
Direct3D provides three different types of filters; each one provides a different level of quality. The better the quality, the slower it is, so you must make the trade-off between quality and speed. Texture filters are set with the IDirect3DDevice9::SetSamplerState method.
Nearest point sampling—This is the default filtering method and produces the worst-looking results, but it is also the fastest to compute. The following code sets nearest point sampling as the minification and magnification filter:
Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
Linear filtering—This type of filtering produces fairly good results and can be done very fast on today’s hardware. It is
Team-Fly®
Texturing 115
recommended that you use linear filtering as a minimum. The following code sets linear filtering as the minification and magnification filter:
Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
Anisotropic filtering—This type of filtering produces the best results but also takes the longest to compute. The following code sets anisotropic filtering as the minification and magnification filter:
Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_
ANISOTROPIC);
Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_
ANISOTROPIC);
When using anisotropic filtering, we must also set the D3DSAMP_ MAXANISOTROPY level, which determines the quality of the anisotropic filtering. The higher this value, the better the results. Check the D3DCAPS9 structure for the valid range that your device supports. The following code sets this value to 4:
Device->SetSamplerState(0, D3DSAMP_MAXANISOTROPY, 4);
P a r t I I
6.4 Mipmaps
As said in section 6.3, the triangle on the screen is usually not the same size as the texture triangle. In an effort to make the size difference less drastic, we can create a chain of mipmaps for a texture. The idea is to take a texture and create a series of smaller lower resolution textures but customize the filtering for each of these levels so it preserves the detail that is important for us (see Figure 6.4).
Figure 6.4: A chain of mipmaps; notice that each successive mipmap is half the size of the previous mipmap.
116 Chapter 6
6.4.1 Mipmap Filter
The mipmap filter is used to control how Direct3D uses the mipmaps. You can set the mipmap filter by writing:
Device->SetSamplerState(0, D3DSAMP_MIPFILTER, Filter);
where Filter is one of the following three options:
D3DTEXF_NONE—Disables mipmapping
D3DTEXF_POINT—By using this filter, Direct3D will choose the mipmap level that is closest in size to the screen triangle. Once that level is chosen, Direct3D will filter that level based on the specified min and mag filters.
D3DTEXF_LINEAR—By using this filter, Direct3D will take the two closest mipmap levels, filter each level with the min and mag filters, and linearly combine these two levels to form the final color values.
6.4.2Using Mipmaps with Direct3D
Using mipmaps with Direct3D is easy. If the device supports mipmaps, D3DXCreateTextureFromFile will generate a mipmap chain for you. In addition, Direct3D automatically selects the mipmap that matches the screen triangle the best. So mipmapping is pretty much used and set up automatically.
6.5 Address Modes
Previously, we stated that texture coordinates must be specified in the range [0, 1]. Technically, that is not correct; they can go outside that range. The behavior for texture coordinates that go outside the [0, 1] range is defined by the Direct3D address mode. There are four types of address modes: wrap, border color, clamp, and mirror, which are illustrated in Figures 6.5, 6.6, 6.7, and 6.8, respectively.
Figure 6.5: Wrap mode
Texturing 117
Figure 6.6: |
Figure 6.7: |
Border |
Clamp |
color mode |
mode |
P a r t I I
Figure 6.8:
Mirror mode
In these figures, the texture coordinates for the four unique quad vertices are defined as (0, 0), (0, 3), (3, 0), and (3, 3). From the three in both the u-axis and v-axis direction, the quad is subdivided into a 3 3 area matrix. If, for instance, you wanted the texture to be tiled 5 5 across the quad, you would specify the wrap address mode and texture coordinates (0, 0), (0, 5), (5, 0), and (5, 5).
The following code snippet taken from the AddressModes sample illustrates how the four address modes are set:
// set wrap address mode
if( ::GetAsyncKeyState('W') & 0x8000f )
{
Device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP); Device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
}
// set border color address mode
if( ::GetAsyncKeyState('B') & 0x8000f )
{
Device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER); Device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER); Device->SetSamplerState(0, D3DSAMP_BORDERCOLOR, 0x000000ff);
}
// set clamp address mode
if( ::GetAsyncKeyState('C') & 0x8000f )
{
Device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
118 Chapter 6
Device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
}
// set mirror address mode
if( ::GetAsyncKeyState('M') & 0x8000f )
{
Device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_MIRROR); Device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_MIRROR);
}
6.6 Sample Application: Textured Quad
The sample for this chapter demonstrates how to texture a quad and set a texture filter (see Figure 6.9). A mipmap chain is created automatically with the D3DXCreateTextureFromFile function if the device supports mipmapping.
Figure 6.9:
A screen shot of a textured quad taken from the TexQuad sample
Note: There are two additional samples on the web page for this chapter. One sample textures a cube with the crate texture, as shown in Figure 6.1. The second demonstrates texture address modes.
The tasks required for adding textures to a scene are:
1.Construct the vertices of the objects with the texture coordinates specified.
2.Load a texture into an IDirect3DTexture9 interface using the
D3DXCreateTextureFromFile function.
3.Set the minification, magnification, and mipmap filters.
4.Before you draw an object, set the texture that is associated with the object with IDirect3DDevice9::SetTexture.
Texturing 119
We begin by instantiating several global variables; one is the vertex buffer that stores the vertices of the quad and the other is the texture that we map to the quad:
IDirect3DVertexBuffer9* Quad = 0;
IDirect3DTexture9* |
Tex = 0; |
The Setup routine is fairly straightforward; we construct a quad from two triangles with texture coordinates defined. We then load the bitmap file dx5_logo.bmp into an IDirect3DTexture9 interface. Next we enable the texture using the SetTexture method. Finally, we set the minification and magnification filters to linear filtering, and we also set the mipmap filter to D3DTEXF_POINT:
bool Setup()
{
Device->CreateVertexBuffer( 6 * sizeof(Vertex), D3DUSAGE_WRITEONLY, Vertex::FVF, D3DPOOL_MANAGED, &Quad,
0);
Vertex* v;
Quad->Lock(0, 0, (void**)&v, 0);
// quad built from two triangles, note texture coordinates:
v[0] = Vertex(-1.0f, -1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f); v[1] = Vertex(-1.0f, 1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f); v[2] = Vertex( 1.0f, 1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f);
v[3] = Vertex(-1.0f, -1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f); v[4] = Vertex( 1.0f, 1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f); v[5] = Vertex( 1.0f, -1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f);
Quad->Unlock();
//Load texture data. D3DXCreateTextureFromFile(
Device, "dx5_logo.bmp", &Tex);
//Enable the texture. Device->SetTexture(0, Tex);
//Set texture filters.
Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
Device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT);
// set projection matrix D3DXMATRIX proj; D3DXMatrixPerspectiveFovLH(
P a r t I I
120 Chapter 6
&proj,
D3DX_PI * 0.5f, // 90 - degree (float)Width / (float)Height, 1.0f,
1000.0f); Device->SetTransform(D3DTS_PROJECTION, &proj);
// don't use lighting for this sample Device->SetRenderState(D3DRS_LIGHTING, false);
return true;
}
We can now render our quad as normal, and the currently set texture is mapped to it:
bool Display(float timeDelta)
{
if( Device )
{
Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);
Device->BeginScene();
Device->SetStreamSource(0, Quad, 0, sizeof(Vertex));
Device->SetFVF(Vertex::FVF);
// Draw primitives using presently enabled texture. Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);
Device->EndScene(); Device->Present(0, 0, 0, 0);
}
return true;
}
6.7Summary
Texture coordinates are used to define a triangle on the texture that gets mapped to the 3D triangle.
We can create textures from image files stored on disk using the
D3DXCreateTextureFromFile function.
We can filter textures by using the minification, magnification, and mipmap filter sampler states.
Address modes define what Direct3D is supposed to do with texture coordinates outside the [0, 1] range. For example, should the texture be tiled, mirrored, clamped, etc.?
Chapter 7
Blending
In this chapter we examine a technique called blending that allows us to blend (combine) pixels that we are currently rasterizing with pixels that have been previously rasterized to the same pixel locations. In other words, we blend primitives over previously drawn primitives. This technique allows us to achieve a variety of effects (in particular, transparency).
Objectives
To understand how blending works and how to use it
To learn about the different blend modes that Direct3D supports
To find out how the alpha component can be used to control the transparency of a primitive
7.1The Blending Equation
Consider Figure 7.1, where we have a red teapot drawn in front of a wooden crate background.
Figure 7.1: An opaque teapot
121
122 Chapter 7
Suppose that we want to draw the teapot with a level of transparency so that we could see the background crate through the teapot (see Figure 7.2).
Figure 7.2: A transparent teapot
How would we accomplish this? As we are rasterizing the teapot’s triangles on top of the crate, we need to combine the pixel colors being computed for the teapot with the pixel colors of the crate in such a way that the crate shows through the teapot. The idea of combining the pixel values that are currently being computed (source pixel) with the pixel values previously written (destination pixel) is called blending. Note that the effect of blending is not limited to ordinary glass-like transparency. We have a variety of options that specify how the colors are combined, as seen in section 7.2.
It is important to realize that the triangles currently being rasterized are blended with the pixels that were previously written to the back buffer. In the example figures, the crate image is drawn first so that the crate’s pixels are on the back buffer. We then draw the teapot so that the teapot’s pixels are blended with the crate’s pixels. Thus, the following rule should be followed when using blending:
Rule: Draw objects that do not use blending first. Then sort the objects that use blending by their distance from the camera; this is most efficiently done if the objects are in view space so that you can sort simply by the z-component. Finally, draw the objects that use blending in a back-to-front order.
The following formula is used to blend two pixel values:
OutputPixel = SourcePixel SourceBlendFactor + DestPixel DestBlendFactor
Blending
Each of the above variables is a 4D color vector (r, g, b, a), and the symbol denotes component-wise multiplication.
OutputPixel—The resulting blended pixel
SourcePixel—The pixel currently being computed that is to be blended with the pixel on the back buffer
SourceBlendFactor—A value in the interval [0, 1] that specifies the percent of the source pixel to use in the blend
DestPixel—The pixel currently on the back buffer
DestBlendFactor—A value in the interval [0, 1] that specifies the percent of the destination pixel to use in the blend
The source and destination blend factors let us modify the original source and destination pixels in a variety of ways, allowing for different effects to be achieved. Section 7.2 covers the predefined values that can be used.
Blending is disabled by default; you can enable it by setting the
D3DRS_ALPHABLENDENABLE render state to true:
Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
Tip: Blending is not a cheap operation and should only be enabled for the geometry that needs it. When you are done rendering that geometry, you should disable alpha blending. Also try to batch triangles that use blending and render them at once so that you can avoid turning blending on and off multiple times per frame.
123
P a r t I I
7.2 Blend Factors
By setting different combinations of source and destination blend factors, you can create dozens of different blending effects. Experiment with different combinations to see what they do. You can set the source blend factor and the destination blend factor by setting the D3DRS_ SRCBLEND and D3DRS_DESTBLEND render states, respectively. For example, we can write:
Device->SetRenderState(D3DRS_SRCBLEND, Source);
Device->SetRenderState(D3DRS_DESTBLEND, Destination);
where Source and Destination can be one of the following blend factors:
D3DBLEND_ZERO—blendFactor=(0, 0, 0, 0)
D3DBLEND_ONE—blendFactor=(1, 1, 1, 1)
D3DBLEND_SRCCOLOR—blendFactor=(rs, gs, bs, as)
