Subscribe

RSS Feed (xml)

Powered By

Powered by Blogger

Google
 
xnahelp.blogspot.com

Kamis, 03 April 2008

Creating a Camera

That is enough theory for a bit. We are going to create a camera so we can view our
world. Now we can create a new Windows game project to get started with this section.
We can call this project XNADemo. To begin, we will need to create the following private
member fields:
private Matrix projection;
private Matrix view;
private Matrix world;
We will then need to add a call to InitializeCamera in the beginning of our
LoadGraphicsContent method. The InitializeCamera method will have no return
value. We will begin to populate the method, which can be marked as private, in the next
three points.
Projection
The Matrix struct has a lot of helper functions built in that we can utilize. The
Matrix.CreatePerspectiveFieldOfView is the method we want to look at now.
float aspectRatio = (float)graphics.GraphicsDevice.Viewport.Width /
(float)graphics.GraphicsDevice.Viewport.Height;
Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, aspectRatio,
0.0001f, 1000.0f, out projection);
First we set up a local variable aspectRatio. This is to store, you guessed it, the aspect
ratio of our screen. For the Xbox 360 the aspect ratio of the back buffer will determine
how the game is displayed on the gamer’s TV. If we develop with a widescreen aspect ratio
and the user has a standard TV, the game will have a letterbox look to it. Conversely, if we
develop with a standard aspect ratio and the user has a wide screen, the Xbox 360 will
stretch the display. To avoid this we should account for both situations and then adjust
the value of our aspect ratio variable to the default values of the viewport of the graphics
device like in the preceding code. If we needed to query the default value to which the
gamer had his or her Xbox 360 set, we can gather that information by querying the
DisplayMode property of the graphics device during or after the Initialization method
is called by the framework.
Creating a Camera 61
4
However, if we want to force a widescreen aspect ratio on the Xbox 360 we could set the
PreferredBackBufferWidth and PreferredBackBufferHeight properties on the graphics
object right after creating it. Many gamers do not care for the black bars, so we should use
this with caution. To force a widescreen aspect ratio on Windows is a little more complicated,
but the XNA Game Studio Express documentation has a great “How to” page
explaining how to do it. Once in the documentation, you can find “How to: Restrict
Graphics Devices to Widescreen Aspect Ratios in Full Screen” under the Application
Model in the Programming Guide.
Second, we create our field of view. The first parameter we pass in is 45 degrees. We could
have used MathHelper.ToRadians(45.0f) but there is no need to do the math because the
MathHelper class already has the value as a constant. The second parameter is the aspect
ratio, which we already calculated. The third and fourth parameters are our near and far
clipping planes, respectively. The plane values represent how far the plane is from our
camera. It means anything past the far clipping plane will not be drawn onto the screen.
It also means anything closer to us than the near clipping plane will not be drawn either.
Only the points that fall in between those two planes and are within a 45-degree angle of
where we are looking will be drawn on the screen. The last parameter is where we populate
our projection matrix. This is an overloaded method. (One version actually returns
the projection, but we will utilize the overload that has reference and out parameters, as
they are faster because it doesn’t have to copy the value of the data.)
View
Now that we have our projection matrix set, we can set up our view matrix. To do this we
are going to use another XNA matrix helper method. The Matrix.CreateLookAt method
takes three parameters. Let’s create and initialize these private member fields now.
private Vector3 cameraPosition = new Vector3(0.0f, 0.0f, 3.0f);
private Vector3 cameraTarget = Vector3.Zero;
private Vector3 cameraUpVector = Vector3.Up;
Now we can actually call the CreateLookAt method inside of our InitializeCamera
method. We should add the following code at the end of the method:
Matrix.CreateLookAt(ref cameraPosition, ref cameraTarget,
ref cameraUpVector, out view);
The first parameter we pass in is our camera position. We are passing in the coordinates
(0,0,3) for our camera position to start with, so our camera position will remain at the
origin of the x and y axis, but it will move backward from the origin 3 units. The second
parameter of the CreateLookAt method is the target of where we are aiming the camera.
In this example, we are aiming the camera at the origin of the world Vector3.Zero
(0,0,0). Finally, we pass in the camera’s up vector. For this we use the Up property on
Vector3, which means (0,1,0). Notice we actually created a variable for this so we can pass
it in by reference. This is also an overloaded method, and because we want this to be fast
we will pass the variables in by reference instead of by value. Fortunately, we do not lose
much readability with this performance gain.
62 CHAPTER 4 Creating 3D Objects
World
At this point if we compiled and ran the demo we would still see the lovely blank cornflower
blue screen because we have not set up our world matrix or put anything in the
world to actually look at. Let’s fix that now.
As we saw, the templates provide a lot of methods stubbed out for us. One of these very
important methods is the Draw method. Find this method and add this line of code right
below the TODO: Add your drawing code here comment:
world = Matrix.Identity;
This simply sets our world matrix to an identity matrix, which means that there is no
scaling, no rotating, and no translating (movement). The identity matrix has a translation
of (0,0,0) so this will effectively set our world matrix to the origin of the world.
At this point we have our camera successfully set up but we have not actually drawn
anything. We are going to correct that starting with the next section.
Vertex Buffers


3D objects are made up of triangles. Every object is one triangle or more. For example, a
sphere is just made up of triangles; the more triangles, the more rounded the sphere is.
Take a look at Figure 4.1 to see how this works. Now that we know that every 3D object
we render is made up of triangles and that a triangle is simply three vertices in 3D space,
we can use vertex buffers to store a list of 3D points. As the name implies, a vertex buffer
is simply memory (a buffer) that holds a list of vertices.
Vertex Buffers 63
4
FIGURE 4.1 All 3D objects are made up of triangles.
XNA uses a right-handed coordinate system. This means that the x axis goes from left to
right (left being negative and right being positive), the y axis goes up and down (down
being negative and up being positive), and z goes forward and backward (forward being
negative and backward being positive). We can visualize this by extending our right arm
out to our right and positioning our hand like we are holding a gun. Now rotate our wrist
so our palm is facing the sky. At this point our pointer finger should be pointing to the
right (this is our x axis going in a positive direction to the right). Our thumb should be
pointing behind us (this is our z axis going in a positive direction backward). Now, we
uncurl our three fingers so they are pointing to the sky (this represents the y axis with a
positive direction upward). Take a look at Figure 4.2 to help solidify how the right-handed
coordinate system works.


Now that we know what the positive direction is for each axis we are ready to start plotting
our points. XNA uses counterclockwise culling. Culling is a performance measure
graphic cards take to keep from rendering objects that are not facing the camera. XNA has
three options for culling: CullClockwiseFace, CullCounterClockwiseFace, and None. The
default culling mode for XNA is CullCounterClockwiseFace, so to see our objects we have
to set up our points in the opposite order—clockwise.
TIP
It is helpful to use some graph paper (or regular notebook paper for that matter) to
plot out points. Simply put points where we want them and make sure when we put
them into the code, we do it in a clockwise order.
Let’s plot some points. Ultimately, we want to make a square. We know that all 3D
objects can be made with triangles and we can see that a square is made up of two triangles.
We will position the first triangle at (-1,1,0); (1,-1,0); (-1,-1,0). That means the first
point (-1,1,0) will be positioned on the x axis 1 unit to the left and it will be 1 unit up the
y axis and will stay at the origin on the z axis. The code needed to set up these points is
as follows:
private void InitializeVertices()
{
Vector3 position;
Vector2 textureCoordinates;
vertices = new VertexPositionNormalTexture[3];
//top left
position = new Vector3(-1, 1, 0);
textureCoordinates = new Vector2(0, 0);
vertices[0] = new VertexPositionNormalTexture(position, Vector3.Forward,
textureCoordinates);
//bottom right
position = new Vector3(1, -1, 0);
textureCoordinates = new Vector2(1, 1);
vertices[1] = new VertexPositionNormalTexture(position, Vector3.Forward,
textureCoordinates);
//bottom left
position = new Vector3(-1, -1, 0);
textureCoordinates = new Vector2(0, 1);
vertices[2] = new VertexPositionNormalTexture(position, Vector3.Forward,
textureCoordinates);
}
As we look at this function we can see three variables that have been created: vertex,
position, and textureCoordinates. The vertex variable will store our vertex (point) data.
XNA has different structs that describe the type of data a vertex will hold. In most cases
for 3D games we will need to store the position, normal, and texture coordinates. We
discuss normals later, but for now it is sufficient to know they let the graphics device
know how to reflect light off of the face (triangle). The most important part of the vertex
variable is the position of the point in 3D space. We saw earlier that XNA allows us to
store that information in a Vector3 struct. We can either set the data in the constructor as
we did in this code, or we can explicitly set its X, Y, and Z properties.
We skip over explaining the texture coordinates momentarily, but notice it uses the
Vector2 struct that XNA provides for us.We need to add the following private member
field to our class that we have been using to store our vertices:
private VertexPositionNormalTexture[] vertices;
We need to call this method in our application. The appropriate place to call the
InitializeVertices method is inside of the LoadGraphicsContent method.
Vertex Buffers 65
4
If we compile and run our application now we still do not see anything on the screen.
This is because we have not actually told the program to draw our triangle! We will want
to find our Draw method and before the last call to the base class base.Draw(gameTime),
we need to add the following code:
graphics.GraphicsDevice.VertexDeclaration = new
VertexDeclaration(graphics.GraphicsDevice,
VertexPositionNormalTexture.VertexElements);
BasicEffect effect = new BasicEffect(graphics.GraphicsDevice, null);
world = Matrix.Identity;
effect.World = world;
effect.Projection = projection;
effect.View = view;
effect.EnableDefaultLighting();
effect.Begin();
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Begin();
graphics.GraphicsDevice.DrawUserPrimitives(
PrimitiveType.TriangleList, vertices, 0,
vertices.Length / 3);
pass.End();
}
effect.End();
You might think there is a lot of code here just to draw the points we have created on the
screen. Well, there is, but it is all very straightforward and we can plow on through.
Before we do, though, let’s take a minute and talk about effects.

0 komentar: