CREATING A VERTEX BUFFER

Before you can specify a vertex stream, you'll need to create a vertex buffer. There is a large selection of possible formats, so we'll just pick a fairly simple one to start with. One of the most common formats is one that mimics a typical format supported in previous OpenGL and Direct3D implementations, that of a simple untransformed vertex that contains color and normal data.

Once the structure is defined, you create an array of structures and fill them up with your data. Once you have your data in your array, completely filled out, you create a vertex buffer and copy the data from your array into the vertex buffer array. This can cause the video driver to optimize the data and store it in the graphics cards memory for faster processing (a lot depends on the hardware and the desires of the driver writers). Streams were designed to enable driver writers to perform a DMA (direct memory access) from multiple buffers in parallel while accessing the data in as linear a mode as possible.

A simple example of creating, filling, and loading a vertex buffer using the vertex elements types we defined can be seen in the following code:

 // DirectX 8! // First allocate memory for the data, 3000 elements const int ArraySize = 3000; MYCUSTOMVERTEX * pVertexArray =      new MYCUSTOMVERTEX[ArraySize]; if ( 0 == pVertexBuffer)      return E_FAIL; // some code here to fill in the data // now the data array is created and filled, // we need to create a vertex buffer of // the correct size // Create a vertex buffer handle IDirect3DVertexBuffer8* pVertexBuffer; if( FAILED( g_pD3DDevice- >CreateVertexBuffer(     ArraySize * sizeof( MYCUSTOMVERTEX ). // memory size     0.                      // Usage     D3DFVF_MYCUSTOMVERTEX,  // Our custom format define     D3DPOOL_DEFAULT,        // Let D3D decide where to allocate     &pVertexBuffer      // pointer to the memory     ) )                return E_FAIL; // we now have a vertex buffer, so we can now fill it // We need to tell D3D that we're touching the buffer // and when you're done with changing it. // lock it BYTE** plocalVBPointer; // create local pointer if( FAILED( g_ pVertexBuffer ->Lock(      0,                    // offset into the vertex data      0,                    // size to lock (0 means entire buffer)      &plocalVBPointer, // pass pointer to VB data pointer      0      )))      return E_FAIL; // modify local pointer with vertex data ::memcpy( plocalVBPointer, pVertexArray,      ArraySize * sizeof( MYCUSTOMVERTEX ) ); // unlock it pVertexBuffer->Unlock(); // can now delete pVertexArray if we don't need to // modify the data anymore since we've made a // copy into the video card's memory. 

Though it may seem to be a large chunk of code, the basics are pretty simple. The complicated part is understanding that you need to copy the data into device memory so that the device can process the data as quickly as possible. Thus you may need to keep two copies of your data around if you are going to need to modify it. On the other hand, once you've created your data and passed it off to the device, you can free up that system memory for your own use if you don't need to change it anymore.

If we were using a custom vertex format (one not covered by FVF flags), then we would make the FVF flag in the CreateVertexBuffer() call zero to indicate that we were using a nonstandard vertex format. In that case, we would be unable to use the FFP to render our primitive and would have to use a vertex shader.

Vertex Buffer Strategies for Static Data

When you've got static data—static meaning data that isn't changing every frame—you should optimize the vertex buffer usage to make things easy for the hardware to optimize. Basically, you need to create, lock, and fill the vertex buffer as infrequently as possible. You should also get into the habit of creating one largish vertex buffer and filling it with as many vertices as you can—even with vertices from different objects. You can use index primitives to select which objects to draw, but the idea is to keep the size of the vertex buffers as large as is optimal for the hardware (currently, the size is around 2000 or so vertices). Note that if you add in a few degenerate triangles (a triangle that has a repeated vertex), you can stitch together different objects that share the same rendering properties so that you can render them using the same rendering call. If the data is truly static, meaning once you enter it, it's never going to change, then you should use the D3DUSAGE_WRITEONLY flag. It's best to create one largish buffer, to hold all of your static objects if you can. If you need random access into the buffer, then try to use a vertex buffer size that's a multiple of 32 bits and use an index buffer to allow the driver to cache data.

Vertex Buffer Strategies for Dynamic Data

Dynamic data is data that you are creating or modifying just about every frame. In this case, you want to create as few vertex buffers as possible since it's generally faster to reuse vertex buffers than to create a bunch of them. Dynamic vertex buffers should be allocated with around 1000 vertices. When you render, you lock the buffer, fill it with your vertices, unlock, render, then start over. The first lock should use the D3DLOCK_DISCARD flag to tell the hardware that it can create a new vertex buffer memory location. If you are fetching the data piecewise and filling the vertex buffer with numerous locks, you should use the D3DLOCK_NOOVERWRITE until you are ready to start refilling the vertex buffer from the beginning. If you have any shared vertices, it's best to use an indexed vertex buffer, since this gives the driver the opportunity to cache the data.

Customized Vertex Buffer Formats

If you are creative, you can create your own complicated vertex formats that can optimize performance. For example, you can usually specify multiple texture coordinates (up to eight) in an FVF. Remember that texture coordinates can hold from one to four floating point values. Say you have an application where you need to create a vehicle that should be dented. You could encode an extra texture coordinate slot in the FVF to be an "indentation" value that you "dent" a vertex in by. You could have up to eight vehicles using the same vertex buffer rather than eight separate vertex buffers. On the other hand, you don't want to destroy the cache coherency by making the distance between the referenced parts of a vertex too large.



Real-Time Shader Programming(c) Covering Directx 9. 0
Real-Time Shader Programming (The Morgan Kaufmann Series in Computer Graphics)
ISBN: 1558608532
EAN: 2147483647
Year: 2005
Pages: 104
Authors: Ron Fosner

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net