Tutorial 5: Using Texture Maps
The Textures tutorial project adds texture to Microsoft Direct3D objects. While lights and materials add a great deal of realism to a scene, nothing adds more realism than adding textures to surfaces. Textures can be thought of as wallpaper that is shrink-wrapped onto a surface. You could place a wood texture on a cube to make it look like the cube is actually made of wood. This tutorial project adds a "banana peel" texture to the cylinder object created in Tutorial 4: Using Materials and Lights.
Path
Source location: (SDK root)\Samples\Managed\Direct3D\Tutorials\Tutorial5
Procedure
Note: For information about initializing Direct3D, handling Microsoft Windows messages, rendering, or shutting down, see Tutorial 1: Creating a Device.
Tutorial 4: Using Materials and Lights created materials and lighting on the Direct3D object. This tutorial adds to the Tutorial 4 code with procedures for loading textures, setting up vertices, and displaying objects with texture.
Creating the Texture
To be able to use textures, a vertex buffer must be created that has texture coordinates in its custom vertex format. Texture coordinates tell Direct3D where to place a texture for each vector in a primitive. Texture coordinates range from 0.0 to 1.0, where (0.0, 0.0) represents the upper left side of the texture and (1.0, 1.0) represents the lower-right side of the texture.
After initializing a new Texture object, the following sample code sets the format of the vertex buffer using the CustomVertex.PositionNormalTextured structure. This initialization prepares the vertex buffer to receive (x, y, z) position and (x, y, z) normal data, and one set of (tu, tv) texture coordinates.
[C#]
public class Textures : Form
{
// Our global variables for this project
Device device = null; // Our rendering device
VertexBuffer vertexBuffer = null;
Texture texture = null;
.
.
.
public void OnCreateDevice(object sender, EventArgs e)
{
Device dev = (Device)sender;
// Now create the vertex buffer
vertexBuffer = new VertexBuffer(
typeof(CustomVertex.PositionNormalTextured),
100,
dev,
Usage.WriteOnly,
CustomVertex.PositionNormalTextured.Format,
Pool.Default);
vertexBuffer.Created += new System.EventHandler(
this.OnCreateVertexBuffer);
this.OnCreateVertexBuffer(vertexBuffer, null);
}
}
Loading Texture Data
The render state settings (display parameters) are similar to those of Tutorial 4, except that Direct3D lighting is disabled to allow the colors of the cylinder object to be displayed, and a "banana peel" texture is loaded from a bitmap file using the TextureLoader.FromFile method.
[C#]
public void OnResetDevice(object sender, EventArgs e)
{
Device dev = (Device)sender;
// Turn off culling, so the user sees the front and back of the triangle
dev.RenderState.CullMode = Cull.None;
// Turn off Direct3D lighting
dev.RenderState.Lighting = false;
// Turn on the z-buffer
dev.RenderState.ZBufferEnable = true;
// Now create the texture
texture = TextureLoader.FromFile(dev,
Application.StartupPath + @"\..\..\banana.bmp");
}
The procedure for loading the vertex buffer with the cylinder object's properties is identical to the OnCreateVertexBuffer application-defined method in Tutorial 4, with the addition of texture coordinates (tu, tv) for each point, as shown in the following code sample. This procedure causes the texture to wrap smoothly around the cylinder when rendered, and the texture remains pinned to the original cylinder.
[C#]
public void OnCreateVertexBuffer(object sender, EventArgs e)
{
VertexBuffer vb = (VertexBuffer)sender;
// Create a vertex buffer
// Lock the buffer (which will return the structures)
CustomVertex.PositionNormalTextured[] verts =
(CustomVertex.PositionNormalTextured[])vb.Lock(0,0);
for (int i = 0; i < 50; i++)
{
// Fill up the structures
float theta = (float)(2 * Math.PI * i) / 49;
verts[2 * i].Position = new Vector3(
(float)Math.Sin(theta), -1, (float)Math.Cos(theta));
verts[2 * i].Normal = new Vector3(\
(float)Math.Sin(theta), 0, (float)Math.Cos(theta));
verts[2 * i].Tu = ((float)i)/(50-1);
verts[2 * i].Tv = 1.0f;
verts[2 * i + 1].Position = new Vector3(
(float)Math.Sin(theta), 1, (float)Math.Cos(theta));
verts[2 * i + 1].Normal = new Vector3(
(float)Math.Sin(theta), 0, (float)Math.Cos(theta));
verts[2 * i + 1].Tu = ((float)i)/(50-1);
verts[2 * i + 1].Tv = 0.0f;
}
}
Rendering the Scene
Texture stages enable you to define the behavior of how a texture or textures are to be rendered. For example, you could blend multiple textures together. This tutorial project starts with the private Render method of Tutorial 4 and sets to 0 the texture stage that the Direct3D device will use to render. A device can have up to eight set textures, so the maximum stage value is 7, but this tutorial project has only one texture and places it at stage 0. Zero is used as both the stage parameter of the Device.SetTexture method and as the array value for the Device.TextureState property.
Constants of the TextureOperation and TextureArgument enumerations control how the texture renders. For example, the settings in this code cause the output color to be the texture color multiplied by the diffuse color. For more information on color blending in textures.
[C#]
private void Render()
{
.
.
.
// Set up our texture. Using textures introduces the texture stage states,
// which govern how textures get blended together (in the case of multiple
// textures) and lighting information. In this case, modulate (blend)
// the texture with the diffuse color of the vertices.
device.SetTexture(0,texture);
device.TextureState[0].ColorOperation = TextureOperation.Modulate;
device.TextureState[0].ColorArgument1 = TextureArgument.TextureColor;
device.TextureState[0].ColorArgument2 = TextureArgument.Diffuse;
device.TextureState[0].AlphaOperation = TextureOperation.Disable;
.
.
.