Subscribe

RSS Feed (xml)

Powered By

Powered by Blogger

Google
 
xnahelp.blogspot.com

Kamis, 03 April 2008

Textures

We have a triangle finally drawn on the screen, but it does not look particularly
good. We can fix that by adding a texture. Copy the texture from the
Chapter4\XNADemo\XNADemo folder (texture.jpg) and paste that into our project.
This invokes the XNA Content Pipeline, which we discuss in Part III, “Content Pipeline”.
For now, we just need to know that it makes it available as loadable content complete
with a name by which we can access it. The asset will get the name “texture” (because that
is the name of the file). We need to declare a private member field to store our texture:
68 CHAPTER 4 Creating 3D Objects
private Texture2D texture;
We define our texture as a Texture2D object. This is another class that XNA provides for
us. Texture2D inherits from the Texture class, which allows us to manipulate a texture
resource. Now we need to actually load our texture into that variable. We do this in the
LoadGraphicsContent method. Inside of the loadAllContent condition, add this line of
code:
texture = content.Load(“texture”);
Now we have our texture added to our project and loaded into a variable (with very little
code), but we have yet to associate that texture to the effect that we used to draw the
triangle. We will do that now by adding the following two lines of code right before our
call to effect.Begin inside of our Draw method:
effect.TextureEnabled = true;
effect.Texture = texture;
This simply tells the effect we are using that we want to use textures, and then we actually
assign the texture to our effect. It is really that simple. Go ahead and compile and run
the code to see our nicely textured triangle!

Index Buffers

We have covered a lot of ground so far, but we aren’t done yet. We want to create a
rectangle on the screen and to do this we need another triangle. So that means we need
three more vertices, or do we? Actually, we only need one more vertex to create our
square because the second triangle we need to complete the square shares two of our existing
points already. Feel free to review a couple of sections earlier where we talked about
vertex buffers. We set up three points to make the triangle we just saw. To use the code as
is we would need to create another three points, but two of those points are redundant
and the amount of data it takes to represent the VertexPositionNormalTexture struct is
not minimal, so we do not want to duplicate all of that data if we do not need to.
Fortunately, we do not. XNA provides us with index buffers. Index buffers simply store
indices that correspond to our vertex buffer. So to resolve our current dilemma of not
wanting to duplicate our heavy vertex data we will instead duplicate our index data, which
is much smaller. Our vertex buffer will only store four points (instead of six) and our index
buffer will store six indices that will correspond to our vertices in the order we want them
to be drawn. We need to increase our vertex array to hold four values instead of three.
Make the following change:
vertices = new VertexPositionNormalTexture[4];
An index buffer simply describes the order in which we want the vertices in our vertex
buffer to be drawn in our scene.
Find the InitializeVertices method in our code and add the last point we need for our
rectangle. Try to do this before looking at the following code.
Index Buffers 69
4
//top right
position = new Vector3(1, 1, 0);
textureCoordinates = new Vector2(1, 0);
vertices[3] = new VertexPositionNormalTexture(position, Vector3.Forward,
textureCoordinates);
As you were writing the code, I imagine you were wondering about the texture coordinates
for the points. We finally talked about textures, but not really how we mapped the
texture to the vertices we created. We will take a moment and do that now before we
continue our understanding of index buffers.
Texture coordinates start at the top left at (0,0) and end at the bottom right at (1,1). The
bottom left texture coordinate is (0,1) and the top right is (1,0). Take a look at Figure 4.4
to see an example.

If we wanted to map a vertex to the bottom center pixel of a texture, what should the
values be? The horizontal axis is our x axis and the vertical axis is our y axis. We know we
need a 1 in our y coordinate to get to the very bottom of the texture. To get to the middle
of that bottom row, we would need to take the value in between 0 and 1, which is 0.5. So
if we wanted to map a vertex to the bottom center pixel of a texture, we would map it at
(0.5, 1). Back to our demo: Because the vertex we just added was the top right point of
the rectangle, the texture coordinate we assigned to it was (1,0).
Now that we have a better understanding of why our texture mapped to our triangle
correctly, we can get back to our index buffer. We have added a vertex to our code and
now we need to create an index buffer to reference these four points. We need to create
another private member field called indices:
private short[] indices;
Notice that we declared this as an array of short. We could have used int, but short takes
up less room and we aren’t going to have more than 65,535 indices in this demo. The
next thing we need to do is actually create our method that will initialize our indices.
We will name this InitializeIndices and we will call this method from inside our
LoadGraphicsContent method right after we make the call to InitializeVertices. Make
sure that the vertex was added right before we initialize our vertex buffer and after we
created all of the other vertices. This way the code for InitializeIndices shown next
will work for us. It assumes the latest addition to our list of vertices is at the bottom of
the list.
private void InitializeIndices()
{
//6 vertices make up 2 triangles which make up our rectangle
indices = new short[6];
//triangle 1 (bottom portion)
indices[0] = 0; // top left
indices[1] = 1; // bottom right
indices[2] = 2; // bottom left
//triangle 2 (top portion)
indices[3] = 0; // top left
indices[4] = 3; // top right
indices[5] = 1; // bottom right
}
In this method we know we are going to create two triangles (with three points each) so
we create enough space to hold all six indices. We then populate our indices. We took
care to add our vertices in a clockwise order when adding them to the vertex list, so we
can simply set our first three indices to 0, 1, and 2. The second triangle, however, needs a
little more thought. We know we have to add these in clockwise order, so we can start
with any vertex and work our way around. Let us start with the top left vertex (the first
vertex we added to our list—index of 0). That means we need to set our next index to be
the top right vertex, which is the one we just added to the end of the list. We set that
index to 3. Finally, we set the last point to the bottom right vertex, which was added to
the vertex buffer second and has the index of 1.
Now we have our vertices created, complete with textured coordinates and position and
even normals. We have our indices set up to use the vertex buffer in a way where we did
not have to duplicate any of the complex vertex data. We further saved memory by using
short instead of int because we will only have a few indices we need to store to represent
our 3D object (our rectangle). Also, some older graphic cards do not support 32-bit (int)
index buffers. The only thing left for us to do is to actually modify our code that draws
the primitive to tell it we are now using an index buffer. To do that, find the Draw method
and locate the call to DrawUserPrimitives. We will want to replace that line with the
following line:
graphics.GraphicsDevice.DrawUserIndexedPrimitives(
PrimitiveType.TriangleList, vertices, 0, vertices.Length,
indices, 0, indices.Length / 3);
Index Buffers 71
4
Notice that we changed the method we are calling on the graphics device. We are now
passing in both vertex and index data. Let’s break down the parameters we are passing in.
We still pass in a triangle array as the first parameter and our array of vertices as the
second parameter and we are leaving our vertex offset at 0. The next parameter is new
and simply needs the number of vertices that is in our vertex array. The fifth parameter is
our array of indices (this method has an override that accepts an array of int as well). The
sixth parameter is the offset we want for our index buffer. We want to use all of the
indices so we passed in 0. The final parameter, primitive count, is the same as the final
parameter in the method we just replaced. Because we only have four vertices we needed
to change that to our index array. Our indices array has six references to vertices in it and
we take that value and divide it by three to get the number of triangles that are in our
triangle list. When we compile and run the code we should see a rectangle that was
created with our modified vertex buffer and our new index buffer!
As an exercise, modify the points in our vertex to have the rectangle slanted into the
screen. This will require modifying a couple of our z values from 0 to something else.
Give it a try!

0 komentar: