Well, XNA is a lot different than OpenGL.
I like it, though.
* XNA does NOT have glVertex3f() or glColor3f() equivalents. That is, you can’t specify a drawing by specifying a series of vertex points and colors like you might be used to in OpenGL.
glBegin( GL_LINES ) ; // has glVertex3f( 0, 0, 0 ) ; // no glVertex3f( 1, 0, 0 ) ; // xna glEnd() ; // equivalent.
* Strangely (and goodly, I suppose), the above method is used when batching calls to draw textures. Kind of neato, actually.
spriteBatch.Begin() ; spriteBatch.Draw( texture1 ... ) ; spriteBatch.Draw( texture2 ... ) ; spriteBatch.End() ;
* You can’t specify line width in XNA.
glLineWidth( 4.0f ) ; // sorry, no xna equivalent.
* To simulate this (and accomodate for the deficiency), take a look at Manders vs machine thick lines demo, or consider drawing rotated rectangular textures instead of true lines, if working strictly in 2d.
* Here is how you handle it. The following Game1 class can be copy/pasted into a new project.
#region using...
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
#endregion
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
////
// To draw, we'll need 3 basic objects:
// 1. A BasicEffect
// 2. A VertexBuffer
// 3. A VertexDeclaration
// The BasicEffect will draw the vertices in the
// VertexBuffer using the VertexDeclaration to
// understand the type of vertices that are in
// the VertexBuffer.
//
////
//// The BasicEffect.
// The BasicEffect object will be our rendering workhorse.
// It will remember all the information about our model,
// view and projection matrices.
// Basically the renderer will take points from anywhere in space
// and translate them into the canonical view volume (-1,-1,0), (1,1,1)
BasicEffect renderer ;
//// The VertexBuffer
// Ah, a "vertex buffer". What is a vertex buffer?
// Its just a fancy name for "arrayful of vertices".
// Isn't that just an arrayful of Vector3's, you say?
// Well, no. It will actually be an arrayful of
// VERTICES which EACH have a COLOR __and__ a position
// in space.
// What's more is whatever you store inside this vertexbuffer
// will actually go down and sit IN THE GPU. That's right.
// You will write code that downloads vertex data
// directly into your graphics card! Isn't this exciting?
// When you call the renderer object to draw
// your shapes, you will pass it your collection of
// vertices that you place inside the vertex buffer.
VertexBuffer vb ;
//// The VertexDeclaration.
// Ok, you know how above we said that the VertexBuffer
// was going to be this collection of vertices that
// sat in the GPU?
// Well, the GPU isn't THAT smart. UNless you tell it,
// it will actually NOT KNOW which points in your VertexBuffer
// are meant to be positions, and which points are meant to be colors.
// So you need to tell it. The VertexDeclaration
// says WHAT KIND OF DATA is in the VertexBuffer.
// Does each vertex have:
// * Just a position?
// * A position and a color?
// * A position and a color AND a texture coordinate?
// The vertex declaration object is how you specify
// exactly what data is IN that VertexBuffer.
VertexDeclaration vd ;
// And that's all! With these 4 objects, we'll
// be able to draw basic primitives to the screen.
////
public Game1()
{
graphics = new GraphicsDeviceManager( this );
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
/////
// Initialization.
// This is a really long set of steps we need to do
// to load vertex data INTO THE GPU.
// The way drawing happens in XNA is you kind of
// "preload" the GPU up with all the vertices
// you want it to draw. Like, you place the
// data DIRECTLY into the GPU card itself.
// Then, in your Draw() function, you just
// tell it what to draw, using data that you
// previously already loaded into its memory.
//////
// Initialize the renderer as a BasicEffect:
renderer = new BasicEffect(
GraphicsDevice, // represents the GPU. We must tie
// our renderer object to the physical GPU.
null // We do not need pooling.
) ;
renderer.VertexColorEnabled = true ; // make it actually
// use the colors we specify to color the verticies.
//
//////
//////
// Next, we will create and set up a bunch of vertices
// that we can draw.
// I've decided to go with 3 vertices.
// You can change this if you like.
/////////////////////////////
int NUM_VERTICES = 3; //
/////////////////////////////
// Now to make some vertices. Here we go!
// I'm using the native C# type VertexPositionColor because
// I want each Vertex to have BOTH a POSITION IN SPACE,
// AND a color specified for it. There are other types,
// like VertexPositionColorTexture if you want to add in
// some tex coords.
VertexPositionColor[] pointList = new VertexPositionColor[ NUM_VERTICES ];
// Now, I'm going to actually go in and give each vertex
// some point value.
pointList[ 0 ] = new VertexPositionColor(
new Vector3( 0, 0, 0 ),
Color.Black
);
pointList[ 1 ] = new VertexPositionColor(
new Vector3( 0.5f, 0, 0 ),
Color.Red
);
pointList[ 2 ] = new VertexPositionColor(
new Vector3( 0, 0.5f, 0 ),
Color.GreenYellow
);
//
///////
// ARE WE DONE YET??? We specified the vertices. Can't
// we just draw them?
// NOT QUITE!! We specified the vertices in C#, system
// memory. However, the GPU doesn't have them yet.
// So, how are we gonna flush out the vertex
// specification above out the GPU itself?
/////
// We first need to allocate memory for (ON THE GPU!!) our vertices.
// Notice how this memory allocation statement doesn't
// seem to have have normal C# form.
// Yes we are creating a VertexBuffer object, but nowhere
// do you see normal array creation (new VertexColor[5] or something
// like that). However given the parameters, enough memory
// will be allocated to store an arrayful of vertices,
// each with its own position and color.
vb = new VertexBuffer(
GraphicsDevice, // this is the GPU onto which to store
// this actual vertex buffer. Remember, we're writing
// directly to the GPU here!
VertexPositionColor.SizeInBytes * NUM_VERTICES, // AWKWARD!!
// What an awkward way to say how many vertices we
// want this vertex buffer to eventually hold. Nonetheless,
// this is how you do it.
// Its pretty straightforward, no?
BufferUsage.None // This doesn't mean we AREN'T GOING TO
// USE the buffer. No. Instead, it means that we have
// "no special instructions" about how this buffer will
// be used.
// The two other options are BufferUsage.Points, which
// says to XNA that the VertexBuffer we're creating here
// is actually going to be used to render POINTS in space,
// and not triangles or lines, so to be prepared for that.
// The OTHER option is BufferUsage.WriteOnly, which is
// to say to XNA that our software program will only
// WRITE to this buffer and never directly read from it
// (though the GPU itself will obviously read from it
// to actually draw what we put in there).
// Though .WriteOnly is a reasonable setting to use here
// for this example, we're not going to bother. In case
// you're wondering, there is a method VertexBuffer.GetData<>
// which you would use to "read" from a vertex buffer.
);
////
// Finally we need to SET IN that set of vertices we
// declared above into the actual GPU VertexBuffer.
// So the way we do that is by using
vb.SetData<VertexPositionColor>( pointList ) ;
//
/////
// OK ALMOST done here. Just ONE LAST THING.
// At draw time, the GPU will want to be notified AGAIN
// what kind of vertex data you're trying to draw.
// For this reason, we must initialize a VertexDeclaration
// object:
/////
// The VertexDeclaration will tell
// the GPU what KIND of vertex data it
// shall be reading when it attempts to read
// the VertexBuffer we just sent it in the
// lines of code above
vd = new VertexDeclaration(
GraphicsDevice, // tie to the GPU
VertexPositionColor.VertexElements // its
// going to be a bunch of vertices that
// have a Position and a Color.
// Autocomplete help is a bit misleading here:
// "An array of vertex elements" - but this is
// NOT the place to pass in your vertex data.
// We already did that above.
);
// And DONE!
//////
}
protected override void Update( GameTime gameTime )
{
// Esc. to exit.
if( Keyboard.GetState().IsKeyDown(Keys.Escape) )
this.Exit();
base.Update( gameTime );
}
protected override void Draw( GameTime gameTime )
{
GraphicsDevice.Clear( Color.DarkGray );
////
// Tell GPU again what kind of vertices it should
// expect to be getting.
renderer.GraphicsDevice.VertexDeclaration = vd ;
renderer.Begin(); // start rendering.
// Enter a loop. You'll see this loop a lot,
// basically the reason for it is some "EFFECTS" (aka "shaders")
// have multiple passes.. that is, they actually draw out
// each vertex more than one time. They do this to achieve
// neato effects like weird shell hulls in a light shaded purple
// color around an object, etc.
foreach( EffectPass pass in renderer.CurrentTechnique.Passes )
{
// begin this rendering pass.
pass.Begin();
// Set the size of the points
renderer.GraphicsDevice.RenderState.PointSize = 8.0f ;
// Tell the GPU what vertex array to pull vertex data from.
renderer.GraphicsDevice.Vertices[0].SetSource( vb, 0, VertexPositionColor.SizeInBytes );
// Actually draw.
#region note
// !! Note: There is a mistake in the MSDN sample @
// http://msdn.microsoft.com/en-us/library/bb196414.aspx
// In that example, they use GraphicsDevice.DrawUserPrimitives<VertexPositionColor>
// and that's wrong because DrawUserPrimitives doesn't draw
// from a vertex buffer.
#endregion
renderer.GraphicsDevice.DrawPrimitives(
PrimitiveType.PointList, // they are points ...
0, // ... start at the beginning of the list ...
3 // ... and pull 3 points out of it.
);
// end the rendering pass.
pass.End();
}
// Stop rendering.
renderer.End();
////
base.Draw( gameTime );
}
static void Main( string[] args )
{
Game1 game = new Game1() ;
game.Run();
}
}
3 Comments
Awesome man thanks a million
Amazing tutorial man, thank you!
Could you do one on 3D please ;)
Hi and thank you for the hints!
Anyway if i draw lines with this method, or the one suggested by msdn, it slow down really bad the rendering process (it really sucks :()
Do you have any tips to tune up the rendering process?
One Trackback/Pingback
[...] READING, this post explains vertex buffers and how you download vertices to the GPU using .SetData in greater [...]