MSDN Library

Textures

Applies to: Windows Phone 7

Published: August 2011

Author: Rod Stephens

WROX Tech Editors for Windows Phone 7 Articles

Wrox Windows Phone 7 Books

This topic contains the following sections.

Summary: This article explains how to use textures to make objects appear more realistic. It explains how to load a texture and specify texture coordinates to map parts of a texture to parts of a three-dimensional triangle. The results are more realistic than those given by lighting models alone.

The preceding article in this series explained how to use lighting to shade the objects in a 3-D scene. Instead of explicitly calculating the colors of every triangle in a scene, your program can let the graphics system do it more quickly and easily. By adjusting vertex normals, the graphics system can even interpolate across the faces of triangles to produce an object that appears smooth. The result still looks fairly artificial, however, because each object still has a single color, even if it is drawn in different shades of that color.

This article explains how to add textures to objects. The graphics system still uses a lighting model to shade different parts of an object, but instead of making an object out of a single solid color, the object’s underlying appearance is determined by an image. Figure 1 shows a textured triangle and four textured cubes sitting on a wooden floor.

Figure 1. Textured objects are much more realistic than those that are simply colored.

Referenced Image

Displaying textured surfaces is very similar to displaying surfaces colored with a lighting model. You need to learn only three new techniques: setting the texture sampling state, specifying textures, and specifying texture coordinates for triangles.

The sampler state tells the rendering engine how to map points in a texture image to points on a triangle. In particular it determines such factors as what color a pixel should have if it maps to a point outside of the texture’s area and how colors near the edges of the texture should be combined.

Setting the texture sampling state for this example is easy. Just set the GraphicsDevice object’s first SamplerStates value to LinearClamp as shown in the following code:

// Set texture sampling state.
GraphicsDevice.SamplerStates[0] = SamplerState.LinearClamp;

This setting tells the graphics system how to sample textures. You can experiment with other values for the sampler state, but if you do not set a value or set it to a value that is not valid, you will get a confusingly generic “An unexpected error has occurred” message in the call to the drawing method.

Before you can use a texture, you must load it into the program. Start by adding the texture graphic file to the project’s content directory. Look in the directory hierarchy for a directory with a name similar to MyProjectContent, and copy the file there. Next find the content project in Solution Explorer, right-click it, open the Add context menu, select Existing Item, and select the file. For textures you can use files with the .bmp, .dds, .dib, .hdr, .jpg, .pfm, .png, .ppm, and .tga extensions.

Now that you have added the texture file to the program’s content, you need to load it into a Texture2D object at run time. Do that by using the ContentManager class’s Content.Load method in the LoadContent method of your Game class.

Finally, to use the texture, find the Effect object that you will use to draw an object, and set the Effect object’s Texture property to the texture. This might sound complicated, but it is actually fairly easy.

The example program TexturedCube, which is shown in Figure 1, uses a new TexturedTriangleListDrawable class to draw 3-D objects with texture. It is very similar to the TriangleListDrawable class used in previous examples, but it stores a texture (and texture coordinates, which I will talk about shortly) for its objects.

The following code shows how the main TexturedCube program makes its objects.

// Make textured cubes and the floor.
Texture2D cubeTextures = Content.Load<Texture2D>("CubeTextures");
TexturedTriangleListDrawable3D cube =
    new TexturedTriangleListDrawable3D(GraphicsDevice, cubeTextures);
cube.AddBox(4, 8, -2, 2, -2, 2, 0.67f, 1, 0, 0.5f);
cube.AddBox(-2, 2, -2, 2, 4, 8, 0, 0.33f, 0.5f, 1);
cube.AddBox(-8, -4, -2, 2, -2, 2, 0.34f, 0.66f, 0, 0.5f);
cube.AddBox(-2, 2, -2, 2, -8, -4, 0.34f, 0.66f, 0.5f, 1);
cube.AddRectangle(
    new Vector3(-9, -2, 9), new Vector3(-9, -2, -9), new Vector3(9, -2, -9),
    new Vector3(9, -2, 9), new Vector2(0, 0.5f), new Vector2(0, 0),
    new Vector2(0.3f, 0), new Vector2(0.3f, 0.5f));

// Add a simple triangle.
cube.AddTriangle(
    new Vector3(-2.5f, 4.2f, 0),
    new Vector3(0, 7, 0),
    new Vector3(2.5f, 4.2f, 0),
    new Vector2(0, 0.5f),
    new Vector2(0.16f, 0),
    new Vector2(0.33f, 0.5f));

cube.Prepare();
drawables.Add(cube);

This code first creates a texture named cubeTexture and uses Content.Load to load it from the program’s content.

It then makes a TexturedTriangleListDrawable object to hold all of the objects shown in Figure 1. It calls that object’s AddBox method four times to make four cubes and then calls its AddRectangle method to make the floor. The parameters to these calls give the coordinates of the boxes’ and floor’s corners, plus their texture coordinates, which are described in the next section.

Next the code creates the triangle shown at the top of Figure 1. The code finishes by preparing the object and adding it to the drawables list just as the previous examples did.

It takes time to switch textures while drawing, so you should not use more textures than necessary. A useful technique that the TexturedCube program uses to improve performance is to store the surface images for many objects in a single texture file. The program can then draw many triangles with the same Texture2D object, using a different part of the texture for each. Figure 2 shows the single texture used by every object that the program displays. The different objects shown in Figure 1 all use different parts of this image. (The sand and wicker parts of the image are not used by any object in this program.)

Figure 2. It is more efficient to store many images in a single texture file.

Referenced Image

The following code shows the TexturedTriangleListDrawable class’s constructor.

// Initialize the device and effect.
public TexturedTriangleListDrawable3D(GraphicsDevice device, Texture2D texture)
{
    Device = device;
    Effect = new BasicEffect(Device);

    // Enable texture.
    Effect.EnableDefaultLighting();
    Effect.TextureEnabled = true;
    Effect.Texture = texture;
}

This code saves a reference to the graphics device and creates a new BasicEffect object. It then enables default lighting so that the graphics system can use a lighting model. It enables textures and sets the effect’s Texture property to the Texture2D object that it will use when drawing.

Having specified the texture that an effect should use when drawing, the only remaining task is specifying what part of the texture should be used to draw each triangle. To do that, a program can use the predefined VertexPositionNormalTexture structure to store vertex data.

As its name suggests, this structure holds information about a vertex’s position, normal vector, and texture coordinates. You should be familiar with the position and normal pieces from earlier examples.

Texture coordinates are given by Vector2 structures with X and Y coordinates ranging from (0, 0) in the texture’s upper-left corner to (1, 1) in the texture’s lower-right corner.

For example, the following code shows again how the TexturedCube example program creates its triangle.

// Add a simple triangle.
cube.AddTriangle(
    new Vector3(-2.5f, 4.2f, 0),
    new Vector3(0, 7, 0),
    new Vector3(2.5f, 4.2f, 0),
    new Vector2(0, 0.5f),
    new Vector2(0.16f, 0),
    new Vector2(0.33f, 0.5f));

The AddTriangle method simply creates a new VertexPositionNormalTexture structure and fills in its position, normal (calculated from the vertices), and texture coordinates. The first three parameters shown here give the triangle’s corners. The last three give the texture coordinates corresponding to those corners.

In this example, the first vertex’s texture coordinates are (0, 0.5f), meaning that the vertex should correspond to the point halfway down the texture’s left edge, at the bottom of the wood grain part of Figure 2. The second vertex’s texture coordinates are (0.16f, 0), or roughly 1/6 of the way along the texture’s top edge, in the middle of the wood grain texture. The third point’s texture coordinates are (0.33f, 0.5f), or 1/3 of the way to the right and halfway down the texture, at the wood grain’s lower-right corner. If you compare the texture shown in Figure 2 with the results shown in Figure 1, you can see how those coordinates determine how the triangle is drawn.

The rest of the process is figuring out exactly how you want to map textures to objects. For simple triangles and cubes, this is easy. For complex shapes such as those drawn by the TexturedSphere example program shown in Figure 3, this can be quite difficult.

Figure 3. Mapping textures to complex shapes can be difficult.

Referenced Image

The TexturedSphere program’s methods that create spheres, tori, cones, and cylinders map an area on a texture to an object’s triangles. For example, when it creates the triangles that define a sphere, the code also calculates the part of the texture that should correspond to the triangle’s vertices. To see how these methods work in detail, download the program and look at the code.

Drawing with textures is fairly similar to drawing with plain colors and a color model. The only differences are that you must set the graphics device’s sampling state, specify an effect’s texture, and specify vertex texture coordinates. The basic idea is straightforward, although specifying texture coordinates for the vertices in a complex scene can be a lot of work.

At this point, you can make a scene that looks reasonably realistic. You can transform objects in the scene and display the scene at various angles. You can even transform the scene repeatedly to make objects move. Many of the examples described so far make their objects rotate around the Y axis so that you can see them from different angles.

Together these techniques give you the tools you need to display a game environment as a player manipulates it. One key piece that is missing, however, is the ability to interact with the user. The next article in this series explains how a program can receive user input and take appropriate action, such as moving objects or changing the scene’s point of view.

Previous Article: Lighting

Continue on to the Next Article: Interacting with Players

Show:
© 2016 Microsoft