Matrices

Saturday, April 07, 2012

12:37 PM

 

Applied Order

Matrix multiplication is right to left (i.e. the right most matrix is applied to the model first). So typically the 'do everything in one go' matrix we generate for a model will be doEverything = scaling * rotation * transformation

 

Well Known Matrixes

·         View Matrix
Transforms world space into view space. The world as seen by a camera.

Description: Machine generated alternative text: up=O
Camera
bject

Matrix.CreateLookAt(cameraPostionXYZ, subjectPositionXYZ, up) create a view matrix with the camera at cameraPositionXYZ pointed to something interesting at subjectPositionXYZ. up controls the rotation of the camera around the line joining the two.

 

·         Projection Matrix
Transforms 3D space into 2D space

 

Description: Machine generated alternative text: Far Plane
Field of view
Camera

 

Matrix.CreatePerspectiveFieldOfView(fieldOfView, aspectRatio, nearPlaneDistance, farPlaneDistance)

Only Items between the near plane and the far plan are rendered.

A typical field of view is 45-60 degrees. 180 = fish eye lens.
 

The enclosed area between the near plane and far plane is known as the Frustum (and is very useful for clipping)

A good default projection is matrix is Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), GraphicsDevice.Viewport.AspectRatio, 0.1f, 10000.0f);

 

A bounding frustrum can be created from the view and projection matrixes: new BoundingFrustum(viewMatrix * projectMatrix);

 

 

·         World Matrix
Transforms a local object to a world space. Used to place models in the world space. This is typically the last matrix applied to the model.

 

Helpers

·         Matrix m = Matrix.CreateFromYawPitchRoll(yaw, pitch, row)
Creates a matrix that applies a rotation
 

·         Vector3 v = Vector3.Transform(vector, rotation)
Applies a rotation to a vector.
 

·         Vector3.Up, .XUnit, YUnit, Zunit
 

·         Vector3 v = vector.Normalize()
Creates a vector with the same orientation as the original, but with a length of 1.
 

·         Angle between two vectors (say the direction the playing is facing (f) and the direction to the of monster (m))

a.       The dot product (f.m) is |F||Q|cos(angle), so

b.      Normalize all vectors (so |F| and |Q| are one)

c.       double angle = Vector3.dot(f,q);

 

 

Content

Saturday, April 07, 2012

12:44 PM

 

Overview

Models (and other assets) are added to the Content project rather than the runtime project. The Content project takes care of converting the various assets into common XNA format. The content project is about conversion ??and is a build time thing rather than run-time??

 

So good we reference it twice

Content dependencies (e.g. textures for models) are automatically detected. Adding the texture to Content directly will cause the file to be included twice.

 

Loading

At runtime the Content assets are exposed via the ContentManager class. In simple games (where all assets are simply loaded at start up)  the programmer can simply load each asset individual in their override of Game::LoadContent(). When the model is loaded, a copy is usually take of the model's bones so that they can be manipulated later.
 

        Model model;
        
Matrix[] transforms;

        /// <summary>LoadContent will be called once per game and is the place to load
        
/// all of your content.</summary>
        
protected override void LoadContent()
        {
            
// …
 

            // TODO: use this.Content to load your game content here
 
            
// The mesh
            
Model model = Content.Load<Model>("ship");
 
            
// Make a copy of where the bones of the model are
            
Matrix[] transforms = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(transforms);
        }

 

Common types for Content.Load<T> are Model, Effect, TODO

 

By default, the loader assigns the BasicEffect shader to the model's mesh parts.

 

Minimum to Display a Model

In addition to the snippet above:

        /// <summary>/// This is called when the game should draw itself.</summary>
        
/// <param name="gameTime">Provides a snapshot of timing values.</param>
        
protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(
Color.CornflowerBlue);
            
float ang45 = (floatMath.PI / 4.0f;
            
Matrix view = Matrix.CreateLookAt(new Vector3(333, 333, 1000), new Vector3(0, 50, 0), 
                                                                                     
Vector3.Up);
            
Matrix projection = Matrix.CreatePerspectiveFieldOfView(ang45, 
                                            GraphicsDevice.Viewport.AspectRatio, 0.1f, 10000.0f);
            
Matrix world = Matrix.CreateScale(0.5f) * Matrix.CreateRotationY(ang45);
 
            
foreach (ModelMesh mesh in model.Meshes)
            {
                
foreach (ModelMeshPart part in mesh.MeshParts)
                {
                    
BasicEffect e = (BasicEffect)part.Effect;
                    e.EnableDefaultLighting();
                    e.View = view;
                    e.Projection = projection;
 
                    
// Matrix math go right to left, so apply the world transform, then 
                    
// the mesh's relative-to-the-other-meshes-model-transform
                    e.World = transforms[mesh.ParentBone.Index] * world; ;
                }
                mesh.Draw();
            }
 
            
base.Draw(gameTime);
        }

 

 

Cameras

Saturday, April 07, 2012

2:05 PM

 

Target Camera

Should be trival: Matrix.CreateLookAt(c=camera, s=subject, u=Vector3.Up), but...

 

When the vector (v) representing the line from camera (c) to subject (s) is exactly the same as the camera's up direction (u), XNA can't calculate a plane from which to 'up' should apply.

 

·         If your camera flips over when it crosses an axis or nothing renders when you exactly align to an axis, it is likely this problem is the cause.
 

·         When we use (for example) u=(0,1,0) for the third parameter, what we're really saying is 'the final up vector must have a positive Y'.

 

The work around for this is to calculate 'up' as being perpendicular to v (imaging a plane in 3d space defined by c & s. u would be up from that).

 

The cross product of two vectors is a new vector that is perpendicular to those vectors*. This can be used to calculate the Up vector for the camera.

 

Vector3 subjectPosition, cameraPosition;

 

// Calculate the vector from camera to subject

Vector3 forward = subjectPosition - cameraPosition;

 

// Ensure the camera's up is up

Vector3 side = Vector3.Cross(forward, Vector3.up);

Vector3 cameraUp = Vector3.Cross(forward, side);

Matrix view = Matrix.CreateLookAt(camera, subject, cameraUp)

 

[*Since two vectors fulfill the requirement (one pointing 'up' the other pointing 'down') which one the cross product returns depends on whether you are using left or right handiness. XMA is right handed (-Z goes into the screen). Interestingly Direct3D is right handed.]

 

 

Over the Shoulder, Chase, Arc Cameras

These are created by applying a relative offset to the model and applying the models transformation to the camera as well. However, when following (for example) a banking plane it is often desired that the camera bank as well.

 

 

Collision Detection & Clipping

Saturday, April 07, 2012

2:39 PM

 

Bounding Sphere

Quickest way to check for collision. Also doesn't need rotation :-)

Models have built in bounding spheres (model.Meshes.BoundingSphere), which can be added together to create one big sphere for the whole model:

BoundingSphere sphere = new BoundingSphere(Vector3.Zero, 0);

foreach(ModelMesh mesh in model.Meshes) {

sphere = BoundingShere.CreateMerged(sphere, mesh.BoundingSphere.Transform(transforms[mesh.ParentBone.Index]);

}

[Note that if the model is animated, a sphere based on the original bone positions may not be appropriate]

 

Bounding Frustrum

A bounding frustrum can be created from the view and projection matrixes:

new BoundingFrustum(viewMatrix * projectMatrix);

 

 

Bounding Tests

instanceOfBoundingType1.Contains(instanceOfBoundingType2) == ContainmentType.[Contains|Disjoint|Intersects]

ContainmentType

Meaning

Disjoint

No overlap

Contains

Completely contains (?? But which is the bigger ??)

Intersects

Partial overlap

 

 

Bones

Saturday, April 07, 2012

3:25 PM

 

Animation

Model model = …

Model.Bones["boneName"].Transform = ...

 

 

Controller IO

Saturday, April 07, 2012

4:17 PM

 

Keyboard

KeyboardState k = Keyboard.GetState();

If (keyState.IsKeyDown(Keys.W)) {…

 

 

 

 

Shaders & Effects

Tuesday, April 10, 2012

8:29 PM

 

BasicEffect

Each ModelMeshPart can have its own shader. By default, the content pipeline assigns a new instance of BasicEffect shader to each part.

 

Useful information loaded by the pipeline can be extracted via the shader:

 

foreach (ModelMesh mesh in model.Meshes) {

    foreach(ModelMeshPart part in mesh) {

        (BasicEffect) part.Effect ->

DiffuseColor

RGB color that is applied if the part has no texture
?? May tint texture if has texture? ??

SpecularPower

 

Texture

 

 

 

Custom Effects

Custom effect classes are generated via .fx files added to the Content project (Content project -> Add new -> Effect File), loaded through Content.Load<Effect>("myEffect"), and applied through assign the effect (or more likely a .Clone() of the affect) to each model.Meshes[].MeshParts[].Effect.

 

See subpages for example code.

 

Ambient Lighting

Applying a float3 coefficient to the return value returned PixelShaderFunction provides ambient lighting that illuminates the model evenly. The magnitude of the coefficient represents the intensity of the ambient lighting.

 

Point Lighting

Point lighting (such as a light bulb) is just ambient lighting whose coefficient is reduced by the distance between the light source and the model. Curved drop off (i.e. 1/distance) works better than straight distance.

 

Directional (Lambertian) Lighting

A face (triangle) in the model that is flat on to a light source should reflect more light (i.e. appear brighter) than a face that is at an angle to the light source. We can use the dot product to calculate the angle between a normal to the face (i.e. which way the face is pointing) and a vector drawn from the face to the light source. The closer the angle is to zero, the brighter (in relative terms) that face should be.

 

Dot product is fine here since we don't care only about the magnitude of the angle and not which direction the angle is in.

 

Description: C:\0A3AD8A5\131801C3-3A89-4045-8B30-BED36FFCB44D_files\image003.png

Description: C:\0A3AD8A5\131801C3-3A89-4045-8B30-BED36FFCB44D_files\image004.png

 

The math is pretty straightforward, but expressing it in the effects language requires some magic I'm too lazy to repeat / reinvent here.

 

A coefficient applied the returned brightness can be used to simulate how 'shiny' the object is, but this works much better with Phong shading.

 

Spot Light

Lambertian lighting that has a cut off point for the angle.

 

Shiny Lighting (Phong Shading aka Phong Specular Highlighting)

Makes bright spots on (say) a kettle. Similar in theory to Lambertian lighting except that brightness is determined by the angle between the reflected light and the camera rather than between the light source and the model.

 

Since at the triangle level we are flat, calculating the vector of the reflected light is simple a case of calculating (as before) the angle between the face and the light source, then applying the old truism that the angle of incidence is equal to the angle of reflection. The dot product trick can then be applied to this vector and one drawn from the face to the observer and again, the closer to zero the angle is the brighter the face should be drawn.

 

As mentioned above, a coefficient can be applied to make the surface appear more shiny or dull.

 

Prelighting

A way of having many light sources cheaply. Logic makes my head hurt, so at this point I recommend using a pre-rolled shader. This is where depth and normal maps come in.

 

SimpleEffect.cs

Tuesday, April 10, 2012

9:59 PM

 

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
 
namespace Minimal
{
    
/// <summary>This is the main type for your game</summary>
    
public class Game1 : Microsoft.Xna.Framework.Game
    {
        
private GraphicsDeviceManager graphics;
        
private SpriteBatch spriteBatch;
        
private Model model;
        
        
public Game1()
        {
            graphics = 
new GraphicsDeviceManager(this);
            Content.RootDirectory = 
"Content";
        }
 
        
/// <summary>
        
/// Allows the game to perform any initialization it needs to before starting to run.
        
/// This is where it can query for any required services and load any non-graphic
        
/// related content.  Calling base.Initialize will enumerate through any components
        
/// and initialize them as well.
        
/// </summary>
        
protected override void Initialize()
        {
            
// TODO: Add your initialization logic here
 
            
base.Initialize();
        }
 
        
/// <summary>LoadContent will be called once per game and is the place to load all of your content.</summary>
        
protected override void LoadContent()
        {
            
// Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = 
new SpriteBatch(GraphicsDevice);
 
            
// TODO: use this.Content to load your game content here
            
this.model = Content.Load<Model>("ship");
 
            
// Apply custom effect
            
Effect simpleEffect = Content.Load<Effect>("SimpleEffect");
            
foreach (ModelMesh mesh in this.model.Meshes)
            {
                
foreach (ModelMeshPart part in mesh.MeshParts)
                {
                    
Effect effect = simpleEffect.Clone();
                    
Texture meshPartTexture = ((BasicEffect)part.Effect).Texture;
                    effect.Parameters[
"MyTexture"].SetValue(meshPartTexture);
                    part.Effect = effect;
                }
            }
        }
 
        
/// <summary>UnloadContent will be called once per game and is the place to unload all content.</summary>
        
protected override void UnloadContent()
        {
            
// TODO: Unload any non ContentManager content here
        }
 
        
/// <summary>Allows the game to run logic such as updating the world, checking for collisions, gathering input, and playing audio.</summary>
        
/// <param name="gameTime">Provides a snapshot of timing values.</param>
        
protected override void Update(GameTime gameTime)
        {
            
// Allows the game to exit
            
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                
this.Exit();
 
            
// TODO: Add your update logic here
 
            
base.Update(gameTime);
        }
 
        
/// <summary>This is called when the game should draw itself.</summary>
        
/// <param name="gameTime">Provides a snapshot of timing values.</param>
        
protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(
Color.CornflowerBlue);
            
float ang45 = (float)Math.PI / 4.0f;
            
Matrix view = Matrix.CreateLookAt(new Vector3(333, 333, 1000), new Vector3(0, 50, 0), Vector3.Up);
            
Matrix projection = Matrix.CreatePerspectiveFieldOfView(ang45, GraphicsDevice.Viewport.AspectRatio, 0.1f, 10000.0f);
            
Matrix world = Matrix.CreateScale(0.5f) * Matrix.CreateRotationY(ang45);
 
            
foreach (ModelMesh mesh in model.Meshes)
            {
                
foreach (ModelMeshPart part in mesh.MeshParts)
                {
                    part.Effect.Parameters[
"View"].SetValue(view);
                    part.Effect.Parameters[
"Projection"].SetValue(projection);
                    part.Effect.Parameters[
"World"].SetValue(world);
                }
                mesh.Draw();
            }
 
            
base.Draw(gameTime);
        }
    }
}

 

SimpleEffect.fx

Tuesday, April 10, 2012

10:01 PM

 

// SimpleEffect.fx - Apply a red tinge to the model's textures
 
// Properties are exposed at run time through model.Meshes[].MeshParts[].Effect.Parameters[name]
// These three are standard
float4x4 World;                        // World Matrix public property
float4x4 View;                        // View Matrix public property
float4x4 Projection;        // Projection Matrix public property
 
// My custom texture
texture MyTexture;                // MyTexture public property
 
sampler MyTextureSampler = sampler_state {
        texture = <MyTexture>;

        // This class has additional properties that can be set for better (and more expensive)

        // pixel sampling options such as anisotropic filtering and mip-mapping.

};
 
struct VertexShaderInput
{
        // This is standard
        float4 Position : POSITION0;
 
        // TODO: add input channels such as texture
        // coordinates and vertex colors here.
        float2 UV : TEXCOORD0;
};
 
struct VertexShaderOutput
{
        // This is standard
        float4 Position : POSITION0;
 
        // TODO: add vertex shader outputs such as colors and texture
        // coordinates here. These values will automatically be interpolated
        // over the triangle, and provided as input to your pixel shader.
        float2 UV : TEXCOORD0;
};
 
VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{

        // This is standard

        VertexShaderOutput output;
        float4 worldPosition = mul(input.Position, World);
        float4 viewPosition = mul(worldPosition, View);
        output.Position = mul(viewPosition, Projection);
        
        // TODO: add your vertex shader code here.
        // Passthrough
        output.UV = input.UV;
 
        return output;
}
 
float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
        // Make the entire image grey
        // return float4(.5, .5, .5, 1);
 
        // Dim the green & blue channels, leaving red as is
        float3 output = tex2D(MyTextureSampler, input.UV) * float3(1, 0.3, 0.3);
        return float4(output, 1);
}
 
technique Technique1
{
        pass Pass1
        {
            VertexShader = compile vs_2_0 VertexShaderFunction();
           PixelShader = compile ps_2_0 PixelShaderFunction();
        }
}

 

Definitions

Saturday, April 07, 2012

12:33 PM

 

Axis

Axis

-ve

+ve

X

Left

Right

Y

Down

Up

Z

In

Out

 

HLSL

High Level Shader Language

 

ModelMeshPart

A piece of a mesh. Each mesh part can have its own shader (for example, shiny metal verses dull skin).

 

Mesh

A piece of a model. A mesh can be attached to 0 or 1 bones. When the bone is manipulated, the mesh moves with it.

 

Model

A model is made up of one or more meshes. Meshes can have transformations of their own relative to the model, allowing (for example) a tank turret to rotate. For meshes to be manipulated this way, they require bones. The collection of bones for all the meshes in a model is called the skeleton.

 

Quaternion

A matrix storing only a rotation.

 

Rotation

·         Pitch
Rotation around the X-Axis

 

·         Yaw
Rotation around the Y-Axis
 

·         Roll
Rotation around the Z-Axis

 

Shaders

·         Vertex Shaders
Transforms  game space into world space. Includes the view, projection and world matrixes.
 

·         Pixel Shaders
Generates the pixel color. Includes lighting, texture, etc.