Chad Vernon
  • Home
  • Reel/Resume
  • Work
  • Resources
  • About
  • Contact
sideBar

Search

Categories

  • CG
  • cvxporter
  • Maya
    • API
    • Plug-ins
  • Personal

Archives

  • May 2012
  • March 2012
  • September 2011
  • June 2011
  • March 2011
  • December 2010
  • November 2010
  • September 2010
  • August 2010
  • July 2010
  • May 2010
  • April 2010
  • March 2010
  • December 2009
  • October 2009
  • September 2009
  • August 2009
  • July 2009
  • June 2009
  • May 2009
  • April 2009
  • March 2009
  • February 2009
  • January 2009
  • December 2008
  • November 2008
  • October 2008
  • September 2008
  • August 2008
  • July 2008
  • June 2008
  • May 2008
  • April 2008
  • March 2008
  • February 2008
  • January 2008
  • December 2007
  • November 2007
  • October 2007
  • September 2007
  • August 2007

Rss

  • Main Entries RSS
  • Comments RSS
Home » Resources » DirectX 9 » Rendering Primitives

Rendering Primitives

  CU_Rendering.zip (25.2 KiB, 2,744 hits)

3d objects are all comprised of vertices. Each vertex can hold certain information such as position, color, texture coordinates, normals, fog data, etc. To tell DirectX what information our vertices contain, we fill out an array of D3DVERTEXELEMENT9 structures (If your familiar with FVF’s, in DirectX 9, D3DVERTEXELEMENT9 structures have replaced the flexible vertex format, although FVF’s are still used in certain cases suchs as when creating vertex buffers). Why doesn’t DirectX just have its own vertex type with all of this data? Well, straight from MSDN: “By using only the needed vertex components, your application can conserve memory and minimize the processing bandwidth required to render models.” We begin by creating our own vertex definition and initializing our three vertices that will form a triangle.

struct CUSTOMVERTEX
{
float x, y, z; // Position in 3d space
DWORD color;   // Color

};

CUSTOMVERTEX g_triangle[] =
{

{-2.0f, -2.0f, 5.0f, D3DCOLOR_XRGB( 255,   0,   0 )}, // Bottom left vertex
{ 0.0f,  2.0f, 5.0f, D3DCOLOR_XRGB(   0,   0, 255 )}, // Top vertex
{ 2.0f, -2.0f, 5.0f, D3DCOLOR_XRGB(   0, 255,   0 )}  // Bottom right vertex

};

Our vertex definition will hold a position in 3d space and a color. To define our triangle, we need to create 3 points. Here, we create an array of 3 vertices. For each vertex, we specify the x, y, and z coordinates along with a color using the D3DCOLOR_XRGB macro. If you draw these points out on paper, keeping in mind a left-handed coordinate system, you’ll notice the vertices are specified in clockwise order. This is known as the winding order of the polygon. The winding order is used to determine whether or not the vertex is facing the camera. If you imagine a triangle on the back side of my current triangle facing away from you and using the same vertex declaration, the winding order would be counter-clockwise with respect to the camera. This would prevent the back triangle from being rendered. The process of eliminating backfacing polygons is known as backface culling. If the triangle were to rotate, it would disappear once the back side faces the camera because the back triangle is never rendered. This reduces the processing load on the GPU.

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Summary:
This callback function will be called immediately after the Direct3D device has been created. This is
the best location to create D3DPOOL_DEFAULT resources. Resources created here should be released in
the OnLostDevice callback.
Parameters:
[in] pDevice – Pointer to a DIRECT3DDEVICE9 instance
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void CGameApp::OnResetDevice( LPDIRECT3DDEVICE9 pDevice )
{
D3DVERTEXELEMENT9 vertexDeclaration[] =
{
{0,  0, D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
{0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR,    0},
D3DDECL_END()

};

LPDIRECT3DVERTEXDECLARATION9 _vertexDeclaration = 0;
pDevice->CreateVertexDeclaration( vertexDeclaration, &_vertexDeclaration );
pDevice->SetVertexDeclaration( _vertexDeclaration );

// Set up the render states
pDevice->SetRenderState( D3DRS_FILLMODE, m_pFramework->GetFillMode() );      
pDevice->SetRenderState( D3DRS_SHADEMODE, D3DSHADE_GOURAUD );  
pDevice->SetRenderState( D3DRS_LIGHTING, FALSE );

// Set the world and view matrices
D3DXMATRIX identity;
D3DXMatrixIdentity( &identity );
pDevice->SetTransform( D3DTS_WORLD, &identity );
pDevice->SetTransform( D3DTS_VIEW, &identity );

// Set the projection matrix
D3DXMATRIX projection;
float aspect = (float)m_pFramework->GetWidth() / (float)m_pFramework->GetHeight();
D3DXMatrixPerspectiveFovLH( &projection, D3DX_PI / 3, aspect, 1.0f, 1000.0f );
pDevice->SetTransform( D3DTS_PROJECTION, &projection );

}

This is where we fill out the D3DVERTEXELEMENT9 array. Our vertices have a position (D3DDECLUSAGE_POSITION), which is comprised of 3 floats (D3DDECLTYPE_FLOAT3), and a color (D3DDECLTYPE_D3DCOLOR and D3DDECLUSAGE_COLOR). Notice at the end of the array there is a call to the macro D3DDECL_END, which tells DirectX that we’re done filling out the array. After we have defined the vertex element array, we create and set the vertex declaration with IDirect3DDevice9::CreateVertexDeclaration and IDirect3DDevice9::SetVertexDeclaration respectively.

You can see that I am also telling DirectX how the geometry should be displayed by setting some DirectX render states with IDirect3DDevice9::SetRenderState. You can see that I set the fill mode depending on whether wireframe is toggled (hit F2), a gouraud shade mode, which interpolates the vertex colors over the surface of the polygon, and I disable lighting.

The next step is to initialize all the transform matrices. These matrices are used in the calculations that determine where in 3d space the geometry is rendered (world transform), where the virtual camera is located (view transform), and how the geometry is projected into 2d space (projection transform). To initialize the world and view matrices, we’ll set a D3DXMATRIX to an identity matrix with D3DXMatrixIdentity and then we’ll tell DirectX to use these matrices with IDirect3DDevice9::SetTransform. Having identity matrices in the world and view transforms creates the effect of a camera sitting at the origin of the 3d world, looking straight down the positive Z-axis. To create the projection matrix, we need to calculate the aspect ratio. If you look at the coordinates of the triangle, you’ll notice that the triangle will fit perfectly in a 4×4 square. However, since monitors and windows may not be perfectly square, if we just copied the geometry straight to the screen, it would appear stretched or squashed because the image fills up the entire viewport and the entire viewport is not a perfect square. The aspect ratio fixes this problem by altering all the geometry prior to transforming the geometry into projection space. This makes all the geometry appear as we would expect it to be. Once we have the aspect ratio, we can create the actual matrix with D3DXMatrixPerspectiveFovLH. The field of view parameter is usually set to around 60 degrees (PI / 3).

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Summary: Renders the current frame.
Parameters:
[in] pDevice – Pointer to a DIRECT3DDEVICE9 instance
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void CGameApp::OnRenderFrame( LPDIRECT3DDEVICE9 pDevice )
{
pDevice->Clear( 0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB( 0, 0, 0 ), 1.0f, 0 );
pDevice->BeginScene();

pDevice->DrawPrimitiveUP( D3DPT_TRIANGLESTRIP, 1, g_triangle, sizeof( CUSTOMVERTEX ) );

pDevice->EndScene();
pDevice->Present( 0, 0, 0, 0 );

}

To render the triangle, all we need to do is call IDirect3DDevice9::DrawPrimitiveUP.
Notice that we are rendering using a triangle strip. Using triangle strips or triangle fans is often more efficient than using triangle lists because fewer vertices are duplicated. DirectX has a few other
primitive types
to choose from. The UP in DrawPrimitiveUP stands for “User Pointer,” which means that we are using vertex information straight from a specified pointer. You’ll normally never want to do this since using vertex buffers is a lot faster, however using a user pointer is an easy way to learn about vertex declarations and basic rendering.

If you look at the WM_SIZE event in the window procedure in CFramework, you’ll notice that we update the window size variables and reset the device. If we didn’t do this, then the geometry would be squashed and stretched when we resize the window as shown below.

Squished

Now we have a pretty triangle to stare at all day.

No Responses to “Rendering Primitives”

Subscribes to this topic Comment RSS or TrackBack URL

Leave A Reply

Allowed tag : <blockquote>, <p>, <code>, <em>, <small>, <ul>, <li>, <ol>, <a href=>..

 Username

 Email Address

 Website

Sticky: Always double check your comment before posting Please Note: Comment Moderation Maybe Active So There Is No Need To Resubmit Your Comments
Home » Resources » DirectX 9 » Rendering Primitives

© 2011 Chad Vernon