If you want an in depth explanation of how matrix palette effects work, click here: http://msdn.microsoft.com/msdnmag/issues/01/06/Matrix/. This article is helpful, but is starting to become outdated and does assume you know how non-skinned animation works.

Note that you can modify the effect however you please, and add your own normal maps, lighting algorithms, etc.
  • In your effect file, first declare the variables we will use:
    // These are required by the Animation library
    float4x4 MatrixPalette[56];
    float4x4 World;

    // These are not
    float4x4 View;
    float4x4 Projection;
    texture BasicTexture;

    // Stores sampler state info for the texture
    sampler TextureSampler = sampler_state
    {
       Texture = (BasicTexture);
    };

Creating the Vertex Shader

  • Now, lets create the vertex shader component of our effect. First, we declare the structure passed from Xna to our vertex shader:
    // This is passed into our vertex shader from Xna
    struct VS_INPUT
    {
        // This is the position of the vertex in the model file
	float4 position : POSITION;
        // The vertex normal
	float3 normal : NORMAL0;
	// This is the texture coordinate for the vertex in the model file
	float2 texcoord : TEXCOORD0;
	// These are the indices (4 of them) that index the bones that affect
	// this vertex.  The indices refer to the MatrixPalette.
	half4 indices : BLENDINDICES0;
	// These are the weights (4 of them) that determine how much each bone
	// affects this vertex.
	float4 weights : BLENDWEIGHT0;
    };
  • Next, create the structure passed from our vertex shader back to the hardware:
    // This is passed out from our vertex shader once we have processed the input
    struct VS_OUTPUT
    {
        // The final position of the vertex in world space
	float4 position : POSITION;
	// The texture coordinate associated with the vertex
	float2 texcoord : TEXCOORD0;
    };
  • Create a structure that will store the vertex position after it is transformed by the model bones:
    // This is the output from our skinning method
    struct SKIN_OUTPUT
    {
        float4 position;
        float4 normal;
    };
  • Now, create a method that takes in a vertex position and returns it's position as affected by the bones:
    // This method takes in a vertex and applies the bone transforms to it.
    SKIN_OUTPUT Skin4( const VS_INPUT input)
    {
        SKIN_OUTPUT output = (SKIN_OUTPUT)0;
        // Since the weights need to add up to one, store 1.0 - (sum of the weights)
        float lastWeight = 1.0;
        float weight = 0;
        // Apply the transforms for the first 3 weights
        for (int i = 0; i < 3; ++i)
        {
            weight = input.weights[i];
            lastWeight -= weight;
            output.position     += mul( input.position, MatrixPalette[input.indices[i]]) * weight;
            output.normal       += mul( input.normal, MatrixPalette[input.indices[i]]) * weight;
        }
        // Apply the transform for the last weight
        output.position     += mul( input.position, MatrixPalette[input.indices[3]])*lastWeight;
        output.normal       += mul( input.normal, MatrixPalette[input.indices[3]])*lastWeight;
        return output;
    };
  • Finally, we can create our vertex shader method:
    void TransformVertex (in VS_INPUT input, out VS_OUTPUT output)
    {
        // Calculate the skinned position
        SKIN_OUTPUT skin = Skin4(input);
        // This is the final position of the vertex, and where it will be drawn on the screen
        float4x4 WorldViewProjection = mul(World,mul(View,Projection));
        output.position = mul(skin.position, WorldViewProjection);
        // This is not used by is included to demonstrate how to get the normal in world space
        float4 transformedNormal = mul(skin.normal, WorldViewProjection);
        output.texcoord = input.texcoord;
    }

Creating the Pixel Shader

  • Like the Vertex Shader, we need to declare new structures. First, lets declare the structure passed from the hardware to the pixel shader (which occurs after the vertices have been processed by the vertex shader):
    // This is passed into our pixel shader
    struct PS_INPUT
    {
        float2 texcoord : TEXCOORD0;
    };
  • Now, lets declare the structure passed from our pixel shader to the screen:
    // This is the final color rendered on the screen
    struct PS_OUTPUT
    {
        float4 color : COLOR;
    };
  • For our purposes, the pixel shader will be simple since we are not doing any lighting:
    void TransformPixel (in PS_INPUT input, out PS_OUTPUT output)
    {
        output.color = tex2D(TextureSampler,input.texcoord);
    }
  • Finally, we need to make the technique, which declares what new render state information will be used for the effect (not applicable in our case), and which pixel and vertex shader functions to use:
    technique TransformTechnique
    {
        pass P0
        {
            VertexShader = compile vs_2_0 TransformVertex();
            PixelShader  = compile ps_2_0 TransformPixel();
        }
    }

Last edited Mar 7, 2007 at 11:00 PM by dastle, version 4

Comments

No comments yet.