Export (0) Print
Expand All

How To: Create Custom Texture Effects

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

The Complete Sample

The code in this tutorial illustrates the technique described in the text. A complete code sample for this tutorial is available for you to download, 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.

Bb195020.note(en-US,XNAGameStudio.20).gifNote
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

Associate texture coordinates with vertices

  1. Create a vertex declaration of type VertexPositionTexture.

    cubeVertexDeclaration = new VertexDeclaration(
        graphics.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(graphics.GraphicsDevice,
        VertexPositionTexture.SizeInBytes * cubeVertices.Length,
        BufferUsage.None
        );
    
  7. Add the data to the vertex buffer.

    vertexBuffer.SetData<VertexPositionTexture>(cubeVertices);
    
  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(graphics.GraphicsDevice,
        sizeof(short) * cubeIndices.Length,
        BufferUsage.None,
        IndexElementSize.SixteenBits
        );
    
  10. Add the data to the index buffer.

    indexBuffer.SetData<short>(cubeIndices);
    

Initializing the Texture

Initialize the texture

  1. Add the texture to the Content directory of your game project. From Solution Explorer, right-click the Content node of your game project, click Add, and then click Existing Item. In the Files of type: drop-down list, select All Files. 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.

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

Initializing the Effect

Set effect parameters

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. Here, these parameters are set to the corresponding objects in the application: the Matrix object named worldViewProjection, and the Texture2D object named texture.

effect.Parameters["WorldViewProj"].SetValue(worldViewProjection);
effect.Parameters["UserTexture"].SetValue(texture);

Applying the Effect Technique

Choose and apply a texture technique

  1. 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"];
    
  2. Set the device vertex stream, indices, and vertex declaration to correspond to the vertices for which the texture is to be applied.

    graphics.GraphicsDevice.VertexDeclaration = cubeVertexDeclaration;
    graphics.GraphicsDevice.Indices = indexBuffer;
    graphics.GraphicsDevice.Vertices[0].SetSource(
        vertexBuffer,
        0,
        VertexPositionTexture.SizeInBytes);
    
  3. 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.
    effect.Begin();
    foreach (EffectPass pass in effect.CurrentTechnique.Passes)
    {
        pass.Begin();
    
        graphics.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList,
            0,
            0,
            cubeVertices.Length,
            0,
            12
        );
    
        pass.End();
    }
    effect.End();
    

Community Additions

ADD
Show:
© 2014 Microsoft