Dynamic Buffers

  CU_MDX_DynamicBuffer.zip (64.3 KiB, 2,995 hits)


  CUnitFramework.zip (101.1 KiB, 20,443 hits)

Extract the C-Unit Framework
inside the project (not solution) directory.

In the Vertex and Index Buffers tutorial, we worked with static buffers. We didn’t alter the vertex data once we put it in the buffer. Dynamic vertex buffers allow us to alter data in a vertex buffer, which lets objects deform and change shape. When we use dynamic buffers, we can change the contents of the buffer each frame. Some uses for dynamic buffers include particle engines and skinned meshes. Both of which have geometry that is constantly moving within itself.

Static buffers are filled up once and then sent off to be rendered. Dynamic buffers, on the other hand, are filled up, rendered, filled up again with updated geometry, rendered, and so on. This tutorial shows you how to use a dynamic vertex buffer to create_ a basic particle effect. Each frame we will fill up the buffer with particles that are moved slightly from their previous positions.

/// Initialize application-specific resources, states here.
/// True on success, false on failure
private bool Initialize()
{
// Initialize positions and random speeds
m_positions = new Vector3[1000];
m_speeds = new Vector3[m_positions.Length];
Random random = new Random( 1000 );
for ( int i = 0; i < m_positions.Length; i++ )
{
float x = (float)random.Next( –500, 500 ) / 50.0f;
float y = (float)random.Next( –500, 500 ) / 50.0f;
m_speeds[i] = new Vector3( x, y, 0 );
m_positions[i] = new Vector3( 0.0f, 0.0f, 0.0f );
}

return true;
}

First, we’ll create_ two Vector3 arrays to store each particles’ position and velocity vector. The speeds are all randomly generated. We need to keep track of each particles’ position and speed in order to update_ the particles every frame.

///
/// This event will be fired immediately after the Direct3D device has been
/// reset, which will happen after a lost device scenario, a window resize, and a
/// fullscreen toggle. This is the best location to create_ Pool.Default resources
/// since these resources need to be reloaded whenever the device is reset. Resources
/// created here should be released in the OnLostDevice callback.
///

/// The Direct3D device
public override void OnResetDevice( Device device )
{
// Clip…

// Create the buffer
m_vb = new VertexBuffer( device, m_positions.Length * PositionColored.StrideSize, Usage.Dynamic | Usage.WriteOnly, PositionColored.Format, Pool.Default, null );

// Set render states
device.RenderState.Lighting = false;
device.RenderState.FillMode = m_framework.FillMode;
device.RenderState.PointSize = 3.0f;
}

Second, we create_ the vertex buffer. Dynamic vertex buffers cannot be made in the Pool.Managed memory pool, therefore, we’ll create_ in in the Pool.Default memory pool. Since it is in the Default memory pool, we create_ it in the OnResetDevice method. To create_ a dynamic buffer, we have to specify the Usage.Dynamic usage flag.

We can control the size of each particle by setting the PointSize property of the render state.

/// Updates a frame prior to rendering.
/// The Direct3D device
/// Time elapsed since last frame
public override void OnUpdateFrame( Device device, float elapsedTime )
{
// Create the new vertices
PositionColored[] verts = new PositionColored[m_positions.Length];
for ( int i = 0; i < m_positions.Length; i++ )
{
m_positions[i] += m_speeds[i] * elapsedTime;
// Constrain points
if ( m_positions[i].X < –7.0f || m_positions[i].X > 7.0f ||
m_positions[i].Y < –5.0f || m_positions[i].Y > 5.0f )
{
m_positions[i].X = 0.0f;
m_positions[i].Y = 0.0f;
}
// Form colors based on distance from origin
// Lots of parabola shift and squeeze math going on.
double distanceFromOrigin = Vector3.Length( m_positions[i] );
int red = -(int)( distanceFromOrigin * distanceFromOrigin * 15 ) + 255;
red = Math.Max( 0, red );
red = Math.Min( 255, red );
int green = -(int)( ( distanceFromOrigin – 5 ) * ( distanceFromOrigin – 5 ) * 15 ) + 255;
green = Math.Max( 0, green );
green = Math.Min( 255, green );
int blue = -(int)( ( distanceFromOrigin – 8 ) * ( distanceFromOrigin – 8 ) * 15 ) + 255;
blue = Math.Max( 0, blue );
blue = Math.Min( 255, blue );
Color color = Color.FromArgb( red, green, blue );
verts[i] = new PositionColored( m_positions[i].X, m_positions[i].Y, m_positions[i].Z, color.ToArgb() );
}
// Fill up the buffer with the new vertices
GraphicsBuffer buffer = m_vb.Lock( 0, 0, LockFlags.Discard );
buffer.Write( verts );
m_vb.Unlock();
buffer.Dispose();
}

This is the heart of dynamic buffers: updating it’s contents. Each frame, we update_ the particles’ position with the corresponding velocity vector. To create_ the illusion of particles being emitted, we reset the position of a particle to the origin whenever the particle goes outside of the bounding rectangle. The color is calculated based on the distance of the particle from the origin. The exact numbers took some tweeking until I got the effect that I wanted. Basically, I used the parabola equation y = x^2 for each color component. Using our clever math skills, we know we can shift a parabola with y = (x-shift)^2, and we can squeeze a parabola with y = (squeeze)x^2. Color components must have a value between 0 and 255 so we can use the Min and Max methods to constrain the value to this range. Once all the positions and color values are set, we can insert_ the vertex data into the vertex buffer.

/// Renders the current frame.
/// The Direct3D device
/// Time elapsed since last frame
public override void OnRenderFrame( Device device, float elapsedTime )
{
device.Clear( ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0 );
device.BeginScene();

// Clip…

// Render buffers
device.SetStreamSource( 0, m_vb, 0, PositionColored.StrideSize );
device.VertexFormat = PositionColored.Format;
device.DrawPrimitives( PrimitiveType.PointList, 0, m_positions.Length );

// Clip…

device.EndScene();
device.Present();
}

Since we want to render a series of points, we’ll use the PointList primitive type.

Dynamic buffers can be used to render geometry very quickly. Both my GUI system and BitmapFont system use dynamic buffers to render their contents and as a result, they perform quite fast.