Tải bản đầy đủ (.pdf) (30 trang)

Microsoft XNA Game Studio Creator’s Guide- P4 pot

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (323.41 KB, 30 trang )

This chapter has shown how to draw 3D graphics. The vertices and primitive sur-
faces drawn with these simple shapes are the foundation for all 3D game graphics.
Even fiery effects and 3D models begin with vertices and primitive surfaces.
C
HAPTER 5 REVIEW EXERCISES
To get the most from this chapter, try out these chapter review exercises:
1. Implement the step-by-step examples presented in this chapter, if you have
not already done so.
2. Using primitive objects, create the face of a cat with ears and whiskers.
3. Use line and triangle primitives to create a small house with a roof and
fence around it. You can use triangle strips, triangle lists, line strips, and
line lists.
MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE
68
CHAPTER
CHAPTER
6
6
Shaders
Shaders
70
XNA
uses shader-based rendering to convert vertex data into pixel out-
put. This method achieves high performance because shaders per-
form graphics processing at breakneck speed on your graphics card. Whether you use
your own shader or XNA’s BasicEffect shader, you must use some type of shader
to draw 3D graphics from your XNA code. The shader also gives you the power to
customize the way your vertices are displayed. Shaders can be used to manipulate all
vertex properties (for example, color, position, and texture). The ability to provide
additional vertex processing through the shader makes it possible to use them for im-
plementing lighting, blending effects such as transparency, and multitexturing. For


some effects—such as point sprites for fire, multitexturing, and custom light-
ing—you will need to write your own shader to implement the effect.
G
RAPHICS PIPELINE
In discussions about shaders, you will often hear references to the graphics pipeline.
The graphics pipeline refers to the process of converting vertex and primitive input
into pixel output. Vertex and pixel shaders, of course, play a key role in this process-
ing. The vertex shader applies transformations to the vertex inputs. When the trans-
formed vertices are passed to the shader, the output that is not visible to the player is
clipped and the back faces are removed (this is called culling). Rasterization is per-
formed to convert the vector data to an output image. And interpolation is per-
formed between vertices to uniformly distribute vertex data between coordinates. In
the pixel shader, coloration and texturing are applied before outputting pixels to the
screen. Figure 6-1 provides a high-level summary of the graphics pipeline operations.
Shaders
Shaders offer you some control over how processing is done in the graphics pipeline.
In most cases, you will want to write your own shader code. This section explains
why and shows you how to do it.
FIGURE 6-1
Graphics pipeline summary
71
CHAPTER 6
Shaders
Shader Structure
The shader shown here does nothing more than receive vertices that contain color and
position data. The vertex shader receives this data, and then outputs the position data
to the graphics pipeline. The vertex shader output (that can be modified by the pixel
shader) is interpolated before it reaches the pixel shader. The pixel shader receives the
color data and outputs it as pixels in your screen. The shader code shown here is actu-
ally the same code that is contained in the PositionColor.fx file in your game project:

float4x4 wvpMatrix : WORLDVIEWPROJ;
struct VSinput{
float4 position : POSITION0;
float4 color : COLOR0;
};
struct VStoPS{
float4 position : POSITION0;
float4 color : COLOR0;
};
struct PSoutput{
float4 color : COLOR0;
};
// alter vertex inputs
void VertexShader(in VSinput IN, out VStoPS OUT){
// transform vertex
OUT.position = mul(IN.position, wvpMatrix);
OUT.color = IN.color;
}
// alter vs color output
void PixelShader(in VStoPS IN, out PSoutput OUT){
float4 color = IN.color;
OUT.color = clamp(color, 0, 1); // range between 0 and 1
}
// the shader starts here
technique BasicShader{
pass p0{ // declare & initialize ps & vs
vertexshader = compile vs_1_1 VertexShader();
pixelshader = compile ps_1_1 PixelShader();
}
}

MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE
72
Vertex Shaders
A vertex shader is the portion of the shader that performs operations on each vertex
received from your XNA code. You can use the vertex shader to alter and draw any
vertex property. It can be used for per-vertex lighting, modifying position, changing
color, or adjusting image coordinates. For example, the vertex shader may alter this
information using other inputs such as transformations or filtering. When alterations
to each vertex are complete, the color and texture output is sent to a pixel shader for
further processing (if desired). Position and normal data may also be passed to the
pixel shader if any calculations require this information. However, the position and
normal data cannot be altered once it leaves the vertex shader.
At the very least, the vertex shader must output position data. Elements that are
passed to the pixel shader are interpolated across the polygon before they are sent to
the pixel shader.
Pixel Shaders
Pixel shaders convert vertex data from the vertex shader into colored pixel data. The
pixel shader cannot manipulate the position or normal vector information, but it can
perform per-pixel operations to implement lighting, coloration, texture sampling,
and blending. In terms of volume, per-pixel operations are more expensive than
per-vertex operations. However, effects such as lighting are noticeably richer when
you do them in the pixel shader, so there are times when the performance hit is worth
it. When processing in the pixel shader is complete, the pixel shader outputs colored
pixels for display in the window.
Technique and Passes
A technique defines the vertex shaders and pixel shaders used during each pass
through the pixel-rendering process. In most cases, drawing is done in one pass.
However, you might want to specify more than one pass if you have to implement
blended textures (through multitexturing). Chapter 12 shows an example of a
multipass technique used to create running water.

High Level Shader Language
For XNA games, most shaders are written in Microsoft’s High Level Shader Lan-
guage (HLSL). HLSL syntax resembles C syntax, and because C# is also a member of
the C family, the data types, conditional structures, loops, functions, and other syn
-
tax used in HLSL code are easy transitions for an XNA coder.
73
CHAPTER 6
Shaders
You could write your shaders in assembly language, but assembly syntax is more
difficult to read and is more prone to incompatibilities between graphics cards. Also,
because HLSL and XNA were designed for implementation on the Xbox 360, and
they were both created by Microsoft, you are certainly going to want to write most (if
not all) of your shader code in HLSL.
Initially, game programmers only wrote shaders in assembly language, but assem-
bly code is specific to video card hardware and this caused issues. Graphics card man-
ufacturers such as NVIDIA, AMD (formerly ATI), and others have similar assembly
code instruction sets, but differences between video cards sometimes cause shader in-
compatibilities. Because of this, games that use cutting-edge shader code, or shader
code that is unique to a graphics card vendor, may not port well to machines that use
other types of graphics cards. If you are only developing for the Xbox 360, you could
use the latest HLSL features as long as they run on your Xbox 360. If you are writing
code to run on PCs, you should consider potential differences in graphics cards when
writing your shaders.
For the XNA platform, Microsoft recommends that your PC graphics card sup-
port at least Shader Model 2.0. However, shaders written using Shader Model 1.1
will run on the Xbox 360.
Shader Inputs and Outputs
Parameters that are received and returned from the vertex and pixel shaders can be
passed either through parameter lists in the shader headers or through structs. Either

way, the data fields are denoted with semantics to bind the inputs and outputs passed
between shaders and to bind the data from the shader to the graphics pipeline.
Shader Semantics
A shader semantic binds shader input to vertex data that is output from your XNA
code. Shader semantics are also used to bind inputs and outputs together for passing
data between shaders. In other words, a semantic is a syntactical element that de-
notes a piece of data that is passed between your XNA code, shaders, and the graph-
ics pipeline. You can specify shader semantics for color, texture coordinates, normal
vectors, position data, and more. Because it is possible to input more than one in-
stance of a specific data type, you must use a numeric suffix to define the data type in-
stance when referencing it more than once.
Common Vertex Shader Input Semantics Here are some common vertex shader in-
puts that allow you to pass vertex properties from your XNA code to the vertex shader:
COLOR[n] // color
NORMAL[n] // normal vector
POSITION[n] // vertex position
PSIZE[n] // point size for point sprites
TEXCOORD[n] // texture coordinates
The number, denoted by [n], specifies the instance of the data type since you can
have more than one field storing data of the same type.
Common Vertex Shader Output Semantics Vertex shader output semantics denote
the data that is passed from a vertex shader to the pixel shader, where more process-
ing can be performed on the vertex inputs, or to the graphics pipeline, where the ver-
tex data is channeled for display in the window. You use semantics to bind the data
that is passed between the vertex shader and the pixel shader. The outputs from the
vertex shader use these semantics:
COLOR[n] // color
POSITION[n] // position
PSIZE // size for point sprites
TEXCOORD[n] // texture coordinates

Common Pixel Shader Input Semantics
The pixel shader can modify the color and
texture data; it receives this information through the semantics shown here. You will
notice that the position semantic is absent. The pixel shader can receive position in-
formation to implement calculations for effects such as lighting. However, the pixel
shader cannot alter the position information because it is sent to the graphics pipeline
from the vertex shader.
COLOR[n] // color
TEXCOORD[n] // texture coordinates
Common Pixel Shader Output Semantics
In most cases—and throughout this
book—the only output returned from the pixel shader is the color of a pixel. Fit-
tingly, the main output semantic for the pixel shader is the COLOR semantic:
COLOR[n] // output color
Shader Data Types
When looking at HLSL code, you will notice that the shader data types are very simi-
lar in syntax to XNA data types. Table 6-1 compares the XNA data types with the
HLSL data types used in this book.
MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE
74
75
CHAPTER 6
Shaders
HLSL Intrinsic Functions
HLSL provides several functions, and they are fully documented on Microsoft's
MSDN website (www.msdn.com). Table 6-2 is a reference for the intrinsic func-
tions used in this book. They are explained in more detail as they are used in each
chapter.
Flow Control Syntax
Shaders implement C-like syntax for loops and conditional structures. Loop struc-

tures include for-loops, do-while loops, and while-loops. HLSL if-else syntax is the
same syntax used for any C-style language.
Referencing the Shader in Your XNA Project
To use shaders, your XNA application needs to load and reference them. The XNA
platform makes this task easy by providing an Effect class with methods for load-
ing and compiling the shader. Your XNA code can modify global shader variables
through the EffectParameter class.
XNA Data Type HLSL Data Type
Matrix float4x4
Texture2D Texture
struct struct
int int
float float
Vector2 float2 // array with two elements
Vector3 float3 // array with three elements
Vector4 float4 // array with four elements
Color float3 (with no alpha blending) or float4 (with alpha blending)
Comparison of XNA Data Types with Shader Data Types
TABLE 6-1
Referencing the Shader File in Your XNA Project
Shaders are loaded by the content pipeline, so you need to reference the *.fx file,
which contains the shader code, under the Content node in the Solution Explorer.
The base code in this book groups all .fx files in a subfolder called Shaders (under the
Content node). To create a new shader in your project, right-click the Content node,
in the Solution Explorer (or any subfolder where you keep your shaders). Then, select
Add | New Item. In the Add New Item dialog, select the Effect File template. You can
overwrite the shader name, which defaults to Effect1.fx. Note that the *.fx extension
is the required extension for your shader file. Once the name is entered, click Add to
MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE
76

HLSL Intrinsic Functions Inputs Component Type Outputs
abs(a) a
is a scalar, vector,
or matrix.
float, int
Absolute value of
a
clamp(a, min, max) clamp(a, min, max) float, int
Clamped value for
a
cos(a) a
is a scalar, vector,
or matrix.
float
Same dimension as
a
dot(a, b) a
and
b
are vectors.
float
A scalar vector (dot product
)
mul(a, b) a
and
b
can be vectors
or matrices, but the
a
columns must match the

b
rows.
float
Matrix or vector, depending
on the inputs
normalize(a) a
is a vector.
float
Unit vector
pow(a, b) a
is a scalar, vector,
or matrix.
b
is the
specified power.
a
is a float.
b
is an
integer.
a
b
saturate(a) a
is a scalar, vector,
or matrix.
a
is a float.
a
clamped between 0 and 1
sin(a) a

is a scalar, vector,
or matrix.
float
Same dimension as
a
tan(a) a
is a scalar, vector,
or matrix.
float
Same dimension as
a
tex2D(a,b) a
is a sampler2D.
b
is a vector.
a
is a sampler2D.
b
is a two-dimen-
sional float.
Vector
HLSL Intrinsic Functions
TABLE 6-2
77
add this file to your project. Game Studio will add code to generate a shader code
shell, so you will need to manually delete this code to start fresh.
Alternatively, you can add a prewritten shader to your project from the Solution
Explorer by right-clicking your project’s Content node or any subdirectory under
this node and selecting Add | Existing Item. From there, you can select the *.fx file
from the Add Existing Item dialog that appears.

Effect
An Effect object allows you to load and compile the shader code, to finalize any
variable changes that you made to the shader, and, of course, to send vertex data
from your XNA code to the shader. The Effect class is used for declaring the Ef-
fect object:
private Effect effect;
When the shader is referenced in your project from the Solution Explorer, it can be
read using the Load() method. HLSL shader files traditionally are named with an
.fx extension. However, when the shader is referenced in the Solution Explorer, the
.fx extension is dropped from the filename in the load statement:
effect = content.Load<Effect>("DirectoryPath\\ShaderName");
EffectParameter
EffectParameter objects allow you to set global variables in the shader from
your XNA code. The EffectParameter class is used when declaring this object:
private EffectParameter effectParameter;
When you have defined the EffectParameter object in your XNA code, you
can then use it to reference global shader variables. An Effect object’s Parame-
ters collection stores references to all the global shader variables. The collection is
indexed by the global variable name. Thus, the following line stores a reference to a
global shader variable:
effectParameter = effect.Parameters[string shaderVariableName];
Once the EffectParameter objects have been declared and initialized, you as-
sign the value using the SetValue() method:
effectParameter.SetValue(DataValue);
CHAPTER 6
Shaders
The parameter used in SetValue() must match the data type of the variable be-
ing set.
Drawing with the Shader
When you draw with the shader, you must select the shader (and the shader’s tech-

nique) to execute your vertex and pixel processing code. There are several syntax
variations for this. Here is a common approach that we use throughout the book:
Effect effect.Begin();
Effect effect.Techniques[0].Passes[0].Begin();
As soon as the shader is finished drawing, deselect it with the End() method:
Effect effect.End();
Effect effect.Techniques[0].Passes[0].End();
Committing Changes to the Shader
For performance reasons, the preferred way to set variable values within the shader
(using the EffectParameter’s SetValue() method) is to assign the values be-
fore calling the Begin() method for the Effect object. Calling Begin() finalizes
the changes to the values.
CommitChanges()
You may encounter situations where you want to assign values to your shader vari-
ables after you have called Effect.Begin(). As soon as you finish setting any
shader values, though, you must use the CommitChanges() method to finalize
these changes in your shader:
effect.CommitChanges();
Position Color Shader Example: Referencing
the Shader
This example demonstrates one of the most basic shaders. This shader does nothing
more than output a primitive surface that uses a set of vertices for storing color and
position. You will make adjustments to the shader so you can use your XNA code to
change the color and position of the vertices that are drawn from the shader. In this
case, the blue component of the rectangular surface will be set to automatically incre-
ment and decrement between 0 (for no blue) and 1 (for full blue)—this will create a
flashing effect. The rectangular surface’s position on the X axis will also be automati-
MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE
78
79

cally incremented and decremented from the shader using a timescale, which will
make it slide back and forth.
In Chapter 5, we covered graphics basics for drawing primitive surfaces that use
vertices for storing position and color. The example in this chapter takes the material
discussed in Chapter 5 a little further by showing how to control the vertex data out-
put from the shader. On the surface, this example may not appear to offer anything
remotely useful for a video game implementation, but remember that this example
has been kept simple to introduce the topic. Shaders will be discussed again in this
book, and you will definitely benefit from your efforts to understand this example.
Chapter 9 shows how to use the shader to texture your primitive surface with images;
Chapter 12 shows how to create multitexturing effects using shaders; Chapter 20 ex-
plains how shaders can be implemented for fiery effects; and Chapter 22 demon-
strates how to create advanced lighting using shaders.
All code discussed in the “Referencing the Shader” portion of the “Position
Color Shader Example” is already added to your base code.
If you are trying this example for the first time, this demonstration begins with ei-
ther the MGHWinBaseCode project or the MGH360BaseCode project—both can be
found in the BaseCode folder in the download from the book’s website. However, if
you are following the steps in Chapter 17, continue with the project that you started
in Chapter 17 to build the base code from scratch.
Adding Your Shader Code
The PositionColor.fx file must be referenced in your project from the Solution Ex-
plorer. This shader receives color and position data from your XNA code and ren-
ders points, lines, and surfaces with it.
All of the code in this shader is from the shader sample at the start of this chapter.
This is the same PositionColor.fx file that is in the base code project. However, if you
are following the “Building the Base Code From Scratch Example,” you will need to
add it to your Shaders folder under your project’s Content node.
Referencing Your Shader from Your XNA Code
To reference a shader in your XNA code, you need an Effect object. Also, when

drawing your object with this shader, you need an EffectParameter object to set
the world-view-projection (WVP) matrix. This positions your object, so it can be
viewed properly in your window. The WVP matrix is explained in Chapter 17. The
Effect and EffectParameter objects should be added at the top of the game
CHAPTER 6
Shaders
MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE
80
class so they can be used in your project. These declarations must be added to your
“Building the Base Code From Scratch Example” project from Chapter 17:
private Effect positionColorEffect; // shader object
private EffectParameter positionColorEffectWVP; // set window view
After your shader has been referenced in the Solution Explorer, you need to load it
from the InitializeBaseCode() method. This allows you to compile and refer-
ence your shader when the program begins. Also, the EffectParameter object,
positionColorEffectWVP (declared earlier), is initialized to reference the
wvpMatrix global variable in the shader. Note that the file path is hard-coded to the
PositionColor.fx file in the Shaders folder:
positionColorEffect = Content.Load<Effect>("Shaders\\PositionColor");
positionColorEffectWVP = positionColorEffect.Parameters["wvpMatrix"];
Preparing your XNA Code for Drawing with the
PositionColor.fx Shader
A VertexDeclaration object prepares the GraphicsDevice to retrieve and
draw your data with a specific format. In this case, we need one to set the device for
retrieving and drawing position and color vertices. This declaration is made at the
top of the game class:
private VertexDeclaration positionColor;
Next, the VertexDeclaration instance, positionColor, is referenced in
the InitializeBaseCode() method:
positionColor = new VertexDeclaration(graphics.GraphicsDevice,

VertexPositionColor.VertexElements);
All drawing that uses this new shader must be triggered between the Begin() and
End() statements for the Effect object positionColorEffect. The vertex
shaders and pixel shaders that you will use are set inside each pass. But, in this case,
only one pass is used in the shader. All drawing is done from within the Begin() and
End() methods for the pass. The following method is added to the base code for
drawing with the PositionColor.fx shader:
private void PositionColorShader(PrimitiveType primitiveType,
VertexPositionColor[] vertexData, int numPrimitives){
positionColorEffect.Begin(); // begin using PositionColor.fx
positionColorEffect.Techniques[0].Passes[0].Begin();
81
CHAPTER 6
Shaders
// set drawing format and vertex data then draw primitive surface
graphics.GraphicsDevice.VertexDeclaration = positionColor;
graphics.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(
primitiveType, vertexData, 0, numPrimitives);
positionColorEffect.Techniques[0].Passes[0].End();
positionColorEffect.End(); // stop using PositionColor.fx
}
The PositionColor.fx shader is now defined and is initialized in your code, and the
PositionColorEffect() method is in place to draw with it. If you are following
the “Building the Base Code From Scratch Example” from Chapter 17, your base
code project is now complete and it is identical to the one we have been using
throughout the book.
Position Color Shader Example:
Drawing with Your Shader
This section shows how to draw a surface with the PositionColor.fx shader. You
need to declare a set of vertices that can use our newly referenced shader. Because the

shader is designed to modify only position and color, it makes sense that the vertex
definition is also set to store only color and position. This declaration in the game
class at the module level will enable its use throughout the class:
private VertexPositionColor[] vertices = new VertexPositionColor[4];
A method is required to initialize the vertices that you will use to build the rectan-
gular surface. Therefore, you’ll add the InitializeVertices() method to the
game class to define each corner vertex of this rectangle:
private void InitializeVertices(){
Vector3 position = Vector3.Zero;
Color color = Color.White;
// initialize coordinates used to draw white surface
position = new Vector3(-3.0f, 3.0f, -15.0f); // top left
vertices[0] = new VertexPositionColor(position, color);
position = new Vector3(-3.0f,-3.0f, -15.0f); // bottom left
vertices[1] = new VertexPositionColor(position, color);
position = new Vector3(3.0f, 3.0f, -15.0f); // top right
vertices[2] = new VertexPositionColor(position, color);
position = new Vector3(3.0f,-3.0f, -15.0f); // bottom right
vertices[3] = new VertexPositionColor(position, color);
}
To initialize the vertices that will be used to build the rectangle when the program
starts, InitializeVertices() is called from the Initialize() method:
InitializeVertices();
The code used to draw the rectangle from the vertices that have been declared fol-
lows the same five steps described in the preceding chapter. Step 4 of this method
makes use of the new EffectParameter by setting the matrix in the new shader
for positioning the rectangle relative to the camera.
private void DrawRectangle(){
// 1: declare matrices
Matrix world, translation;

// 2: initialize matrices
translation = Matrix.CreateTranslation(0.0f, -0.9f, 0.0f);
// 3: build cumulative world matrix using I.S.R.O.T. sequence
// identity, scale, rotate, orbit(translate & rotate), translate
world = translation;
// 4: set the shader variables
positionColorEffectWVP.SetValue( world * cam.viewMatrix
* cam.projectionMatrix);
// 5: draw object - select primitive type, vertices, # of primitives
PositionColorShader(PrimitiveType.TriangleStrip, vertices, 2);
}
To make our output a little more dramatic, replace the existing Draw() method
with this revision. This new version of the Draw() method makes the background of
our world black, so all we’ll see is the rectangle that is drawn with the new shader:
protected override void Draw(GameTime gameTime){
graphics.GraphicsDevice.Clear(Color.Black); // clear screen
DrawRectangle(); // build graphics
base.Draw(gameTime); // display buffer
}
If you ran the project with the current modifications, it would show a white sta-
tionary rectangle with a black background.
MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE
82
83
One of the first modifications you will make is to modify the blue component of
the RGB colors that are rendered. In the shader, you can do this by creating a global
variable at the top of the PositionColor.fx file:
float blueIntensity;
Next, you need a function inside the PositionColor.fx file to change the blue com-
ponent of the RGB color that is drawn from the shader. Note that you use the color

vector’s b component to adjust the blue intensity by assigning to it the global vari-
able blueIntensity:
float4 AdjustBlueLevel(){
float4 color;
color.r = 0.0f; color.g = 0.0f;
color.b = blueIntensity; color.a = 1.0f;
return color;
}
Shader functions must be declared before they are used in the shader;
otherwise, the shader file will not compile. This requirement, of course,
applies when placing AdjustBlueLevel() in the shader, and the same logic applies
for the ChangePosition() function that follows.
The code that outputs the color from the vertex shader to the pixel shader must
now change to handle the modifications to the blue component. Note that when you
are multiplying vectors in HLSL, the product becomes (a
1
*b
1
,a
2
*b
2
,a
3
*b
3
,a
4
*b
4

). Re-
place the existing color assignment in the vertex shader with this version to alter the
blue component:
OUT.color = IN.color * AdjustBlueLevel();
To reference the blueIntensity shader variable used to adjust the blue compo-
nent from your XNA code, you declare the EffectParameter
positionColorEffectBlue. In the Game1.cs file, add a module declaration
for it:
private EffectParameter positionColorEffectBlue;
Next, to initialize this object, add the following line to the
InitializeBaseCode() method of your game class (after the code where the
Effect object, positionColorEffect, has been loaded and initialized):
positionColorEffectBlue =
positionColorEffect.Parameters["blueIntensity"];
CHAPTER 6
Shaders
Class-level variables are used to adjust the blue component of the color that is out-
put every frame. Also, a Boolean value is used to track whether the floating point is
increasing or decreasing, and a float is used to track the actual value:
private float blue = 0.0f;
private bool increaseBlue = true;
A method is used to increase or decrease the value of the blue component each
frame. The blue portion of the RGB color ranges between 0 and 1, where 0 is no color
and 1 is full blue. Each frame, this value is incremented or decremented by a scaled
amount based on the time difference between frames. This time scalar ensures the
color change is at the same rate regardless of the system that shows the animation.
Add the UpdateBlueLevel() method to your game class to implement this
routine:
void UpdateBlueLevel(GameTime gameTime){
// use elapsed time between frames to increment color for

// a smooth animation at same speed on all systems
if(increaseBlue)
blue += (float)gameTime.ElapsedGameTime.Milliseconds/1000.0f;
else
blue -= (float)gameTime.ElapsedGameTime.Milliseconds/1000.0f;
if (blue <= 0.0f) // decrease blue till blue < 0
increaseBlue = true;
else if (blue >= 1.0f) // increase blue till blue > 1
increaseBlue = false;
positionColorEffectBlue.SetValue(blue); // set blue in shader
}
To update the blue color each frame, you call UpdateBlueLevel() from the
Update() method:
UpdateBlueLevel(gameTime);
If you run the code now, you will notice that the blue color component changes; it
will range from between 0 and 1. Because the red and green colors are set at 0, the
color range for the rectangle is between black and dark blue.
Next, you will make another change to automatically adjust the position of the
rectangle—to move it side to side on the X axis. To enable this, in the global variable
MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE
84
85
section of the shader, you declare a variable to store the value of the position on the X
plane:
float positionX;
This function, added to the shader, changes the value of the X position:
float4 ChangePosition(float4 position){
position.x += positionX;
return position;
}

To implement the change in the vertex shader, replace the assignment for posi-
tion with the instruction that follows. This takes the current position and shifts it on
the X axis by the amount stored in the
positionX
global variable:
OUT.position = mul(ChangePosition(IN.position), wvpMatrix);
Back in your XNA code, an EffectParameter is required to reference the
positionX
global variable in the shader:
private EffectParameter positionColorEffectX;
To initialize this effect parameter inside InitializeBaseCode(), after the
Effect object is set up, add the following instruction to reference the
positionX
shader variable from the Effect’s collection:
positionColorEffectX = positionColorEffect.Parameters["positionX"];
With a reference to a shader variable that is used to modify the position of the rect-
angle, some XNA code can be added to actually track the X value and reset it. Add
the float X declaration to store the current X increment for the rectangle. Also, add
the
increasingX
Boolean variable to track whether the variable is to be incre-
mented or decremented:
private float X = 0.0f;
private bool increasingX = true;
The code for updating the X increment is added to the game class:
void UpdatePosition(GameTime gameTime){
// use elapsed time between frames to increment X to create animation
// at same speed on all systems that run this code
CHAPTER 6
Shaders

if (increasingX)
X += (float)gameTime.ElapsedGameTime.Milliseconds/1000.0f;
else
X -= (float)gameTime.ElapsedGameTime.Milliseconds/1000.0f;
if (X <= -1.0f) // decrease X till less than -1
increasingX = true;
else if (X >= 1.0f) // increase X till greater than 1
increasingX = false;
positionColorEffectX.SetValue(X); // set new X value in shader
}
Updates to the X increment for the rectangle are triggered from the Update()
method every frame:
UpdatePosition(gameTime);
When you run this version of the code, you will see a flashing blue rectangle that
moves from side to side.
XNA’s BasicEffect Class
XNA offers the BasicEffect class, which actually is a built-in shader that you can
use to render your 3D graphics. On one hand, compared to writing your own HLSL,
the BasicEffect class does not offer you as much flexibility to customize the way
your vertex data is filtered, blended, and displayed as pixel output. However, on the
other hand, you can rely on the BasicEffect class to quickly and simply imple-
ment lighting, and it is especially useful for rendering 3D models. Chapter 14 demon-
strates the use of the BasicEffect shader to render and light 3D models. The
BasicEffect class lighting properties are explained and implemented in Chapter 22.
The code for the BaseEffect shader has been shared by Microsoft so that XNA
coders can learn from how it was developed (see />basiceffectshader).
A BasicEffect object is instantiated with the BasicEffect class:
BasicEffect basicEffect
= new BasicEffect(GraphicsDevice device, EffectPool effectPool);
MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE

86
87
Setting Properties Within the BasicEffect Class
When drawing objects using the BasicEffect class, you will need to set the World,
View, and Projection matrices to implement object movement, scaling, and rotations
and to position your objects in the window so they can be seen properly as your play-
ers view the 3D world. These matrices are explained in more detail in Chapter 17.
When you are implementing a custom shader (as demonstrated earlier in the chap-
ter), an EffectParameter is used to set these matrix values. With the
BasicEffect class, you don’t have to create an EffectParameter object for
each variable you want to set. Instead, you can assign these values to the
BasicEffect’s World, View, and Projection properties:
Matrix basicEffect.World = Matrix worldMatrix;
Matrix basicEffect.View = Matrix viewMatrix;
Matrix basicEffect.Projection = Matrix projectionMatrix;
Similar to the custom shader, whenever you change the state of the BasicEffect
shader—by assigning a value to one of the BasicEffect’s attributes—you have to
finalize the change by calling the CommitChanges() method (although only if you
have changed attributes between a Begin() and End() pair):
basicEffect.CommitChanges();
Techniques and Passes within
the BasicEffect Class
Similar to a custom shader that you would write on your own, the BasicEffect
class uses a technique to define the vertex and pixel shaders, and to set the total num-
ber of passes used to render an object each frame. To use the BasicEffect shader
when drawing your objects, you use the following construct to select the technique
and pass(es) within it. All drawing is performed between the Begin() and End()
methods for the BasicEffect object:
basicEffect.Begin();
foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes){

pass.Begin();
// rendering is done here
pass.End();
}
basicEffect.End();
CHAPTER 6
Shaders
MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE
88
BasicEffect Class Example
This demonstration shows how to convert the existing base code to use the
BasicEffect class to draw the ground with a grass texture. Currently, the base
code uses the shader Texture.fx. Note that this demonstration uses texturing to apply
images to the surfaces you draw before a proper explanation is given. Chapter 9 pro-
vides a more detailed explanation of how the texturing works.
This example begins with either the MGHWinBaseCode or the
MGH360BaseCode base code from the BaseCode folder in the download from this
book's website. Converting it to use the BasicEffect class to draw the textured ground
can be done in two steps.
First, you will need to declare an instance of the BasicEffect at the top of the
game class so that you can use it throughout the class:
BasicEffect basicEffect;
The BasicEffect instance should be initialized when the program begins in
Initialize(). Regenerating this BasicEffect instance anywhere else on a con-
tinuous basis will slow down your program considerably, so you should only do it
when the program starts:
basicEffect = new BasicEffect(graphics.GraphicsDevice, null);
Next, replace the existing DrawGround() method in the game class with this new
version, which uses a BasicEffect object to do the rendering. Most of the routine
remains the same. However, in step 4, the World, View, and Projection proper-

ties for the BasicEffect class are set to provide it with information about the cam-
era. This way, it can render the ground and be seen properly by the camera. Also in
step 4, the texture is set to the grass texture. These changes are then committed to the
shader. In step 5, the BasicEffect technique is selected and the rendering is done
in the passes that have been automatically selected by the technique.
Replace the existing version of DrawGround() with this routine to draw the
ground with the BasicEffect shader:
private void DrawGround(){
// 1: declare matrices
Matrix world, translation;
// 2: initialize matrices
translation = Matrix.CreateTranslation(0.0f, 0.0f, 0.0f);
89
// 3: build cumulative world matrix using I.S.R.O.T. sequence
// identity, scale, rotate, orbit(translate & rotate), translate
world = translation;
// 4: set the shader variables
basicEffect.World = world;
basicEffect.View = cam.viewMatrix;
basicEffect.Projection = cam.projectionMatrix;
basicEffect.TextureEnabled = true;
basicEffect.Texture = grassTexture;
// 5: draw object - select shader, vertex type, then draw surface
basicEffect.Begin();
foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes){
pass.Begin();
graphics.GraphicsDevice.VertexDeclaration = positionColorTexture;
graphics.GraphicsDevice.DrawUserPrimitives
<VertexPositionColorTexture>(
PrimitiveType.TriangleStrip, groundVertices, 0, 2);

pass.End();
}
basicEffect.End();
}
When you run this code, the textured ground will appear as before, but this time it
will be rendered using the BasicEffect class.
If you have ever seen the NVIDIA or AMD shader demos, you would agree that
shaders can be used for some incredibly slick graphics effects. The shader examples in
this chapter have intentionally been kept simple by comparison, but will gradually
become more complex as the chapters progress.
C
HAPTER 6 REVIEW EXERCISES
To get the most from this chapter, try out these chapter review exercises:
Implement the step-by-step examples in this chapter, but make the following
changes:
1. In the first example, try adding a field with a POSITION semantic to the
pixel shader output struct and notice that it can’t be done. Why is this?
CHAPTER 6
Shaders
2. In the first example, replace the instruction in the vertex shader that defines
the color output with the following:
OUT.color = IN.color
3. In the pixel shader, replace the instruction that defines the color output
with this:
OUT.color = IN.color * ChangeBlueValue();
When you run the code after this change, the output will be the same as
before, but the color transformation will be performed on a per-pixel basis
rather than a per-vertex basis.
MICROSOFT XNA GAME STUDIO CREATOR’S GUIDE
90

CHAPTER
CHAPTER
7
7
Animation
Animation
Introduction
Introduction
92
AFTER
working through Chapter 5, you should now be comfort-
able building structures with lines and triangles. If you
create a butterfly, a bird, a car, an airplane, or even a monster out of lines and trian-
gles, you will surely want to animate your 3D object. Animation is the key ingredient
that will bring life to your creations.
Animating an object requires that you have the ability to rotate, move, or even
resize the object. The process of scaling and moving objects is referred to as a trans-
formation. Transformations include:

Setting the identity as a default for the World matrix if no transformations
are specified.

Scaling to resize the object.

Using translations for horizontal, vertical, up, down, and diagonal
movements on the X, Y, and Z planes.

Rotating an object about its own axis. This is referred to as a revolution.

Rotating an object about an external fixed point. This is referred to as the

object’s orbit.
XNA offers several methods for performing transformations. Familiarity with
these methods is crucial since they greatly reduce the amount of code you have to
write to bring movement to your 3D environment.
While this chapter may seem to be somewhat trivial, the topic of transformations is
extremely important. For new graphics programmers, understanding this chapter and
Chapter 8 is essential to harnessing XNA and taking control of your 3D animations.
R
IGHT HAND RULE
Like all 3D graphics libraries, XNA allows you to create 3D worlds on a Cartesian
graph with X, Y, and Z axes. The orientation of each axis on the graph, with respect to
the other axes, defines the direction for positive and negative translations and rota-
tions. With an upright Y axis, it is possible to have an X axis that runs either outward
to the left or to the right, and a Z axis that increases either in a forward or backward di-
rection. The XNA developer team has implemented the Cartesian coordinate system
from a Right Hand Rule perspective. Figure 7-1 shows the Right Hand Rule imple-
mentation used in this book, where positive movement in the X, Y, and Z planes is:

Toward the right on the X axis

Upward on the Y axis

Toward the viewer on the Z axis

×