How to: Create and Apply Custom Effects
This example demonstrates how to create a simple effect to set the diffuse color and world, view, and projection transformation of a 3D object.
Note
The steps described here apply to effects created with the Effect class. In contrast, the BasicEffect class is provided for users who desire basic lighting, materials, texture, and transformation functionality but do not wish to write custom effects. For more information, see How to: Use BasicEffect.
To apply an 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 named Transform and a technique named TransformTechnique, which applies the vertex shader in a single pass.
uniform extern float4x4 WorldViewProj : WORLDVIEWPROJECTION; struct VS_OUTPUT { float4 position : POSITION; float4 color : COLOR0; }; VS_OUTPUT Transform( float4 Pos : POSITION, float4 Color : COLOR0 ) { VS_OUTPUT Out = (VS_OUTPUT)0; Out.position = mul(Pos, WorldViewProj); Out.color = Color; return Out; } float4 PixelShader( VS_OUTPUT vsout ) : COLOR { return vsout.color; } technique TransformTechnique { pass P0 { vertexShader = compile vs_2_0 Transform(); pixelShader = compile ps_1_1 PixelShader(); } }
Add the effect file to your XNA Framework game. Right-click your game project and point to Add, and then click Existing Item. In the Files of type: drop-down list, click All Files. Navigate to your .fx file and click Add.
Tip
To create a new .fx file, point to Add, and then New Item, and then choose the Text File template. For the name of the file, use the .fx file extension.
Create a new Effect by using the ContentManager.Load<Effect> method to load the asset.
effect = content.Load<Effect>( "ReallySimpleEffect" );
In the effect file, there is a parameter named WorldViewProj, which is a matrix used by the vertex shader. The following code looks up the WorldViewProj parameter in Effect.Parameters and calls EffectParameter.SetValue to initialize the value of the transform matrix.
effect.Parameters["WorldViewProj"].SetValue( worldViewProjection );
Set Effect.CurrentTechnique to the technique from the effect file you wish to apply. In this case, we use Effect.Techniques to look up the technique named TransformTechnique from the effect file.
effect.CurrentTechnique = effect.Techniques["TransformTechnique"];
Call the Effect.Begin to begin applying the technique. For each pass in the technique, draw the desired geometry between calls to EffectPass.Begin and EffectPass.End. Call Effect.End to stop applying the technique.
// The effect is a compiled effect created and compiled elsewhere // in the application. 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();
The Complete Example
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
ContentManager content;
Matrix worldViewProjection;
Effect effect;
VertexDeclaration cubeVertexDeclaration;
VertexPositionColor[] cubeVertices;
VertexBuffer vertexBuffer;
short[] cubeIndices;
IndexBuffer indexBuffer;
public Game1()
{
graphics = new GraphicsDeviceManager( this );
content = new ContentManager( Services );
}
protected override void Initialize()
{
base.Initialize();
}
private void InitializeTransform()
{
float tilt = (float)Math.PI / 8.0f;
// Use the world matrix to tilt the cube along x and y axes.
Matrix world = Matrix.CreateRotationX( tilt ) *
Matrix.CreateRotationY( tilt );
Matrix view = Matrix.CreateLookAt( new Vector3( 0, 0, 5 ), Vector3.Zero,
Vector3.Up );
Matrix projection = Matrix.CreatePerspectiveFieldOfView(
(float)Math.PI / 4.0f, // 2 PI Radians is 360 degrees, so this is 45 degrees.
(float)graphics.GraphicsDevice.Viewport.Width /
(float)graphics.GraphicsDevice.Viewport.Height,
1.0f, 100.0f );
worldViewProjection = world * view * projection;
}
private void InitializeEffect()
{
effect = content.Load<Effect>( "ReallySimpleEffect" );
effect.Parameters["WorldViewProj"].SetValue( worldViewProjection );
effect.CurrentTechnique = effect.Techniques["TransformTechnique"];
}
protected override void LoadGraphicsContent( bool loadAllContent )
{
InitializeTransform();
if (loadAllContent)
{
InitializeEffect();
InitializeCube();
}
}
private void InitializeCube()
{
cubeVertexDeclaration = new VertexDeclaration(
graphics.GraphicsDevice, VertexPositionColor.VertexElements );
cubeVertices = new VertexPositionColor[24];
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 );
cubeVertices[0] = new VertexPositionColor( topLeftFront, Color.Red );
cubeVertices[1] = new VertexPositionColor( bottomLeftFront, Color.Red );
cubeVertices[2] = new VertexPositionColor( topRightFront, Color.Red );
cubeVertices[3] = new VertexPositionColor( bottomRightFront, Color.Red );
cubeVertices[4] = new VertexPositionColor( topLeftBack, Color.Orange );
cubeVertices[5] = new VertexPositionColor( topRightBack, Color.Orange );
cubeVertices[6] = new VertexPositionColor( bottomLeftBack, Color.Orange );
cubeVertices[7] = new VertexPositionColor( bottomRightBack, Color.Orange );
cubeVertices[8] = new VertexPositionColor( topLeftFront, Color.Yellow );
cubeVertices[9] = new VertexPositionColor( topRightBack, Color.Yellow );
cubeVertices[10] = new VertexPositionColor( topLeftBack, Color.Yellow );
cubeVertices[11] = new VertexPositionColor( topRightFront, Color.Yellow );
cubeVertices[12] = new VertexPositionColor( bottomLeftFront, Color.Purple );
cubeVertices[13] = new VertexPositionColor( bottomLeftBack, Color.Purple );
cubeVertices[14] = new VertexPositionColor( bottomRightBack, Color.Purple );
cubeVertices[15] = new VertexPositionColor( bottomRightFront, Color.Purple );
cubeVertices[16] = new VertexPositionColor( topLeftFront, Color.Blue );
cubeVertices[17] = new VertexPositionColor( bottomLeftBack, Color.Blue );
cubeVertices[18] = new VertexPositionColor( bottomLeftFront, Color.Blue );
cubeVertices[19] = new VertexPositionColor( topLeftBack, Color.Blue );
cubeVertices[20] = new VertexPositionColor( topRightFront, Color.Green );
cubeVertices[21] = new VertexPositionColor( bottomRightFront, Color.Green );
cubeVertices[22] = new VertexPositionColor( bottomRightBack, Color.Green );
cubeVertices[23] = new VertexPositionColor( topRightBack, Color.Green );
cubeIndices = new short[] { 0, 1, 2, // red front face
1, 3, 2,
4, 5, 6, // orange back face
6, 5, 7,
8, 9, 10, // yellow top face
8, 11, 9,
12, 13, 14, // purple bottom face
12, 14, 15,
16, 17, 18, // blue left face
19, 17, 16,
20, 21, 22, // green right face
23, 20, 22 };
vertexBuffer = new VertexBuffer(
graphics.GraphicsDevice,
cubeVertices.Length * VertexPositionColor.SizeInBytes,
ResourceUsage.None,
ResourceManagementMode.Automatic );
vertexBuffer.SetData<VertexPositionColor>( cubeVertices );
indexBuffer = new IndexBuffer( graphics.GraphicsDevice,
sizeof( short ) * cubeIndices.Length,
ResourceUsage.None,
ResourceManagementMode.Automatic,
IndexElementSize.SixteenBits );
indexBuffer.SetData<short>( cubeIndices );
}
protected override void UnloadGraphicsContent( bool unloadAllContent )
{
if (unloadAllContent == true)
{
content.Unload();
}
}
protected override void Update( GameTime gameTime )
{
// Allows the default game to exit on Xbox 360 and Windows.
if (GamePad.GetState( PlayerIndex.One ).Buttons.Back == ButtonState.Pressed)
this.Exit();
base.Update( gameTime );
}
protected override void Draw( GameTime gameTime )
{
graphics.GraphicsDevice.Clear( Color.CornflowerBlue );
graphics.GraphicsDevice.VertexDeclaration =
cubeVertexDeclaration;
graphics.GraphicsDevice.RenderState.CullMode =
CullMode.CullClockwiseFace;
graphics.GraphicsDevice.Indices = indexBuffer;
graphics.GraphicsDevice.Vertices[0].SetSource( vertexBuffer, 0, VertexPositionColor.SizeInBytes );
// The effect is a compiled effect created and compiled elsewhere
// in the application.
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();
base.Draw( gameTime );
}
}