How To: Create Custom Texture Effects

Demonstrates how to create an effect to apply a texture to a 3D primitive object. This topic builds on the demonstrations How To: Draw Points, Lines, and Other 3D Primitives and How To: Create and Apply Custom Effects.

The Complete Sample

The code in the topic shows you the technique. You can download a complete code sample for this topic, including full source code and any additional supporting files required by the sample.

The major steps for this example are listed below:

Composing the Texture

To compose the texture effect

  • Compose the effect description using either shader assembly language (ASM) or high-level shader language (HLSL).

    In this example, the effect file contains a vertex shader to transform the object and a pixel shader to apply the texture to the object.

    In this effect file, even though the vertex shader does not manipulate the texture coordinate, it returns the texture coordinate so that it will be available to the pixel shader.
    uniform extern float4x4 WorldViewProj : WORLDVIEWPROJECTION;
    uniform extern texture UserTexture;
    struct VS_OUTPUT
        float4 position  : POSITION;
        float4 textureCoordinate : TEXCOORD0;
    sampler textureSampler = sampler_state
        Texture = <UserTexture>;
        mipfilter = LINEAR; 
    VS_OUTPUT Transform(
        float4 Position  : POSITION, 
        float4 TextureCoordinate : TEXCOORD0 )
        VS_OUTPUT Out = (VS_OUTPUT)0;
        Out.position = mul(Position, WorldViewProj);
        Out.textureCoordinate = TextureCoordinate;
        return Out;
    float4 ApplyTexture(VS_OUTPUT vsout) : COLOR
        return tex2D(textureSampler, vsout.textureCoordinate).rgba;
    technique TransformAndTexture
        pass P0
            vertexShader = compile vs_2_0 Transform();
            pixelShader  = compile ps_2_0 ApplyTexture();

Associating Vertices with Texture Coordinates

To associate texture coordinates with vertices

  1. Create a vertex declaration of type VertexPositionTexture.

    cubeVertexDeclaration = new VertexDeclaration(
        GraphicsDevice, VertexPositionTexture.VertexElements);
  2. Initialize the points to be used to draw each side of the cube.

    Vector3 topLeftFront = new Vector3(-1.0f, 1.0f, 1.0f);
    Vector3 bottomLeftFront = new Vector3(-1.0f, -1.0f, 1.0f);
    Vector3 topRightFront = new Vector3(1.0f, 1.0f, 1.0f);
    Vector3 bottomRightFront = new Vector3(1.0f, -1.0f, 1.0f);
    Vector3 topLeftBack = new Vector3(-1.0f, 1.0f, -1.0f);
    Vector3 topRightBack = new Vector3(1.0f, 1.0f, -1.0f);
    Vector3 bottomLeftBack = new Vector3(-1.0f, -1.0f, -1.0f);
    Vector3 bottomRightBack = new Vector3(1.0f, -1.0f, -1.0f);
  3. Initialize the texture coordinates.

    Vector2 textureTopLeft = new Vector2(0.0f, 0.0f);
    Vector2 textureTopRight = new Vector2(1.0f, 0.0f);
    Vector2 textureBottomLeft = new Vector2(0.0f, 1.0f);
    Vector2 textureBottomRight = new Vector2(1.0f, 1.0f);
  4. Declare an array to hold the list of vertices.

    This array will be used to assign data to the vertex buffer.

    cubeVertices = new VertexPositionTexture[36];
  5. Assign the position and texture coordinate data to each element of the vertex array.

    // Vertices for the front of the cube.
    cubeVertices[0] =
        new VertexPositionTexture(
        topLeftFront, textureTopLeft); // 0
    cubeVertices[1] =
        new VertexPositionTexture(
        bottomLeftFront, textureBottomLeft); // 1
    cubeVertices[2] =
        new VertexPositionTexture(
        topRightFront, textureTopRight); // 2
    cubeVertices[3] =
        new VertexPositionTexture(
        bottomRightFront, textureBottomRight); // 3
    // Vertices for the back of the cube.
    cubeVertices[4] =
        new VertexPositionTexture(
        topLeftBack, textureTopRight); // 4
    cubeVertices[5] =
        new VertexPositionTexture(
        topRightBack, textureTopLeft); // 5
    cubeVertices[6] =
        new VertexPositionTexture(
        bottomLeftBack, textureBottomRight); //6
    cubeVertices[7] =
        new VertexPositionTexture(
        bottomRightBack, textureBottomLeft); // 7
    // Vertices for the top of the cube.
    cubeVertices[8] =
        new VertexPositionTexture(
        topLeftFront, textureBottomLeft); // 8
    cubeVertices[9] =
        new VertexPositionTexture(
        topRightBack, textureTopRight); // 9
    cubeVertices[10] =
        new VertexPositionTexture(
        topLeftBack, textureTopLeft); // 10
    cubeVertices[11] =
        new VertexPositionTexture(
        topRightFront, textureBottomRight); // 11
    // Vertices for the bottom of the cube.
    cubeVertices[12] =
        new VertexPositionTexture(
        bottomLeftFront, textureTopLeft); // 12
    cubeVertices[13] =
        new VertexPositionTexture(
        bottomLeftBack, textureBottomLeft); // 13
    cubeVertices[14] =
        new VertexPositionTexture(
        bottomRightBack, textureBottomRight); // 14
    cubeVertices[15] =
        new VertexPositionTexture(
        bottomRightFront, textureTopRight); // 15
    // Vertices for the left side of the cube.
    cubeVertices[16] =
        new VertexPositionTexture(
        topLeftFront, textureTopRight); // 16
    cubeVertices[17] =
        new VertexPositionTexture(
        bottomLeftFront, textureBottomRight); // 17
    cubeVertices[18] =
        new VertexPositionTexture(
        topRightFront, textureTopLeft); // 18
    cubeVertices[19] =
        new VertexPositionTexture(
        bottomRightFront, textureBottomLeft); // 19
  6. Create a vertex buffer to hold the vertex data.

    vertexBuffer = new VertexBuffer(GraphicsDevice,
        VertexPositionTexture.SizeInBytes * cubeVertices.Length,
  7. Add the data to the vertex buffer.

  8. Create indices to index into the cubeVertices array.

    cubeIndices = new short[] {
                             0,  1,  2,  // front face 
                             1,  3,  2,
                             4,  5,  6,  // back face
                             6,  5,  7,
                             8,  9, 10,  // top face
                             8, 11,  9,
                            12, 13, 14,  // bottom face
                            12, 14, 15,
                            16, 13, 17,  // left face
                            10, 13, 16,
                            18, 19, 14,  // right face
                             9, 18, 14 };
  9. Create an index buffer to hold the index array data.

    indexBuffer = new IndexBuffer(GraphicsDevice,
        sizeof(short) * cubeIndices.Length,
  10. Add the data to the index buffer.


Initializing the Texture

Now you will want to add texture to the Content directory of your game project.

To initialize the texture

  1. From Solution Explorer, right-click the Content node of your game project, click Add, and then click Existing Item.

  2. In the Files of type: drop-down list, select All Files.

  3. Navigate to your texture file and click Add.

    In this example, a texture named xna.jpg is added and assigned a corresponding asset name of xna. For more information about game asset properties, see Game Asset Properties.

  4. Create a new Texture2D object using the ContentManager.Load<Texture2D> method to load the asset.
Texture2D texture = Content.Load<Texture2D>("xna");

Initializing the Effect

This effect has two parameters, a transformation matrix named WorldViewProj to use in the vertex shader, and the texture named UserTexture to use in the texture sampler to be applied to the pixel shader. You need to set these parameters to the corresponding objects in the application: the Matrix object named worldViewProjection, and the Texture2D object named texture.


Applying the Effect Technique

There is only one technique available in the effect file, so in this example CurrentTechnique is set to TransformAndTexture, which is the name of the technique in the effect file.

effect.CurrentTechnique = effect.Techniques["TransformAndTexture"];

To select and apply a texture technique

  1. Set the device vertex stream, indices, and vertex declaration to correspond to the vertices for which the texture is to be applied.

    GraphicsDevice.VertexDeclaration = cubeVertexDeclaration;
    GraphicsDevice.Indices = indexBuffer;
  2. Apply the technique by placing the drawing calls between EffectPass.Begin and EffectPass.End inside an Effect.Begin ... Effect.End block.

    // This code would go between a device BeginScene-EndScene block.
    foreach (EffectPass pass in effect.CurrentTechnique.Passes)

Community Additions