Greg Dolley’s Weblog

A Blog about Graphics Programming, Game Programming, Tips and Tricks

DirectX 9 C++ Graphics Tutorial 2: Drawing a Triangle

Posted by gregd1024 on February 28, 2008

In this tutorial we’re going to look at how to draw a stationary triangle using DirectX 9 and C++. We’ll be building off of the concepts taught in the first tutorial (DirectX 9 C++ Graphics Tutorial 1: Getting Started). Most of the code will be the same since it involves setting up a form and initializing DirectX.

Requirements

The tutorial sample code was built and tested with Visual Studio Express 2008. However, using the code “as-is” in Visual Studio 2005 should work too.

You’ll need the DirectX 9 SDK for compiling the sample code. Use this link for downloading: click here.

Tutorial Source and Project Files

Download the project files, binaries, and source with this link:

Getting Started

I’m not going to cover the Win32 initialization code or how to initialize DirectX in this tutorial. For that, see the previous tutorial: DirectX 9 C++ Graphics Tutorial 1: Getting Started.

The only difference between this tutorial’s code versus the last version is how we handle WM_PAINT messages. Previously, we simply cleared the color buffer and displayed it on the screen. This time we’re going to define three triangle vertices, create a vertex buffer, send that vertex buffer to DirectX, and finally tell DirectX to render the triangle.

Defining the Vertices

In DirectX there is no pre-defined vertex type or object. You have to make your own struct (or class) and then tell DirectX about its format via FVF (Flexible Vertex Format) codes. FVF codes are a set of constants that describe the contents and size of a vertex structure. For example, the constant D3DFVF_XYZ describes your structure as having three float variables representing an untransformed vertex; the constant D3DFVF_DIFFUSE describes a single DWORD value representing a diffuse color component in ARGB order. You can (and often will) combine a set of FVF code together. For instance, “D3DFVF_XYZ|D3DFVF_DIFFUSE” means your structure has three float variables followed by one DWORD variable. The correlation between constant and vertex layout is clearly defined in the DirectX SDK documentation.

In our sample program we used the following structure:

struct D3DVERTEX
{
   float x, y, z, rhw;
   DWORD color;
};

The (x, y, z, rhw) combination describe the transformed position of a vertex. The “color” member describes its diffuse color in the format of ARGB (Alpha, Red, Green, Blue). To describe this structure we use the following FVF code:

  • “D3DFVF_XYZRHW|D3DFVF_DIFFUSE” – transformed position with color info.

Let’s look at the code for handling the WM_PAINT event where it actually creates the vertices:

case WM_PAINT:

  // setup vertex information

  struct D3DVERTEX {float x, y, z, rhw; DWORD color;} vertices[3];

 

  vertices[0].x = 50;
  vertices[0].y = 50;
  vertices[0].z = 0;
  vertices[0].rhw = 1.0f;
  vertices[0].color = 0x00ff00;

 

  vertices[1].x = 250;
  vertices[1].y = 50;
  vertices[1].z = 0;
  vertices[1].rhw = 1.0f;
  vertices[1].color = 0x0000ff;

 

  vertices[2].x = 50;
  vertices[2].y = 250;
  vertices[2].z = 0;
  vertices[2].rhw = 1.0f;
  vertices[2].color = 0xff0000;

In this code we really just define a vertex array (“vertices[3]”) and fill in the values for a triangle.

Creating the Vertex Buffer

Next, we tell DirectX about our vertex data by creating a vertex buffer object. Here’s the code to do it (this code comes directly after the code in the last section):

LPDIRECT3DVERTEXBUFFER9 pVertexObject = NULL;
void *pVertexBuffer = NULL;

 

if(FAILED(g_pDirect3D_Device->CreateVertexBuffer(3*sizeof(D3DVERTEX), 0,
          D3DFVF_XYZRHW|D3DFVF_DIFFUSE, D3DPOOL_DEFAULT, &pVertexObject, NULL)))
   return(0);

if(FAILED(pVertexObject->Lock(0, 3*sizeof(D3DVERTEX), &pVertexBuffer, 0)))
   return(0);

memcpy(pVertexBuffer, vertices, 3*sizeof(D3DVERTEX));
pVertexObject->Unlock();

The first two lines just declare the pointers we’re going to use. The next line calls a DirectX function called CreateVertexBuffer(). This function allocates a vertex buffer object which we’ll use for all buffer operations.

CreateVertexBuffer() takes six parameters. The first parameter tells DirectX the required size of the vertex buffer (in bytes). The second parameter specifies how the vertex buffer will be used – “0” being the default. The third parameter tells DirectX about the memory layout of each vertex (the FVF format). The fourth parameter says that you don’t care where memory is allocated. The fifth parameter is the address of a pointer to be filled with the vertex buffer object location. Lastly, the sixth parameter specifies a shared handle (don’t worry about this).

Now we use our newly created vertex buffer object and call its Lock() method. This call gives us a memory buffer (pointed to by pVertexBuffer) that we must copy our vertex data into. I know this seems strange – we already created our own vertex array, filled it with data, now we have to copy it somewhere else? Don’t ask why, this is just how DirectX works. The next line, with the call to memcpy(), does this copying process.

Finally, when we’re done with the copy, we have to tell DirectX that the data is ready to go. This is done via the Unlock() method of the vertex buffer object.

Rendering the Vertex Buffer

Now we’re ready to actually draw the scene! Check out the following code (again, this code comes directly after the last line of the previous section):

// clear background to black
g_pDirect3D_Device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

 

// render the actual scene
if(SUCCEEDED(g_pDirect3D_Device->BeginScene()))
{
   g_pDirect3D_Device->SetStreamSource(0, pVertexObject, 0, sizeof(D3DVERTEX));
   g_pDirect3D_Device->SetFVF(D3DFVF_XYZRHW|D3DFVF_DIFFUSE);
   g_pDirect3D_Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);
   g_pDirect3D_Device->EndScene();
}

 

g_pDirect3D_Device->Present(NULL, NULL, NULL, NULL);
pVertexObject->Release();

 

ValidateRect(hwnd, NULL);

First, we clear the background to black by calling Clear(). You might recognize this from the previous tutorial (it’s the exact same call, but the color is different).

Next we call BeginScene() – every 3D frame in DirectX begins with this call.

The next two lines set the stream source and vertex format. SetStreamSource() tells DirectX to take the vertex information from our vertex buffer object. The first parameter specifies the stream number and the second is a pointer to the vertex buffer object itself. The third parameter says there is no offset from the beginning of the stream to the vertex data (if there was, this value would be the number of bytes in between). Finally, the fourth parameter is the size, in bytes, of each vertex. SetFVF() sets the FVF code that describes our vertex format (see “D3DFVF” in the DirectX SDK documentation for all the possible code combinations and their corresponding formats).

Now we’re at DrawPrimitive(). This tells DirectX to actually draw something. The first parameter specifies what to draw – in our case, a list of triangles (actually, just one triangle since our list only contains one element). The second parameter says start from vertex zero (in some cases you may want to start from somewhere other than the beginning). And the last parameter tells DirectX how many primitives to draw (for us, just one triangle).

Once all the drawing code is executed, we must call EndScene(). All 3D scenes in DirectX end with this call.

Finally we call Present() to display everything on the screen and call Release() on our vertex buffer object (since it’s no longer needed).

ValidateRect() is from the first tutorial. It tells Windows that we’ve handled all of the drawing in this window and Win32 doesn’t need to do any further processing.

Program Output

All this code generates the following output:

directx_tutorial_2_cpp_output

Notice how the colors of each vertex are “smoothed” over the face of the triangle. This is the default behavior of DirectX (and even OpenGL). Since colors are only defined for the vertices and not the face of the polygon, DirectX interpolates the color in between. This interpolation gives you the gradient effect.

Conclusion

Now that’s definitely better than the last tutorial of simply drawing a solid color on a form. However, the triangle is technically still 2D. If it were a 3D triangle we could adjust the z coordinate at each vertex, giving them different depths, and watch the triangle get drawn in perspective. Currently that’s not possible with this code. In order to turn it into a “real” 3D triangle, we need a camera position, a transformation matrix, and a couple other components. We’ll be discussing all this in the next DirectX C++ tutorial.

However, for the very next post, I’ll go over the Managed DirectX version of this same stuff using C#.

-Greg Dolley

*Get new posts automatically! Grab the RSS feed here. Want email updates instead? Click here.

Advertisements

35 Responses to “DirectX 9 C++ Graphics Tutorial 2: Drawing a Triangle”

  1. Matt said

    I am having a problem with this, when I link it says unicode identifiers are not supported.

    Any thoughts?
    Matt

    • Islam Al-Rayan said

      I believe you’re using the Unicode Character Set
      Try to use the Multi-Byte Character Set
      since I think it’s the default for this tutorial πŸ™‚

      Property Page-> Configuration properties -> General -> Character Set “change it to Multi-Byte”

  2. gregd1024 said

    Matt,

    Hmmm… I need to see your project. Please email me through my Contact page, and I’ll reply with an email address to send it. Thanks.

    -Greg

  3. Pat said

    Man, first DirectX Tutorial I really understood… Great work, IΒ΄m keen on the next one!

  4. Pury said

    >>I am having a problem with this, when I link it says unicode identifiers are not supported.

    >>Any thoughts?
    The reason is “Γ—”<-this character and some quotes in previous example. Your should change “Γ—” to “x” and quotes to “.

    And of course Great Thanks to -Greg Dolley.

    P.S. Can i translate it into russian and make a reference to these pages?

  5. Pury said

    P.P.S. Greg, can you show, how to act with 2 triangles? I’ve changed 3*sizeof(D3DVERTEX) to 6*sizeof(D3DVERTEX),
    DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1) to DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2) and D3DVERTEX vertices[3]; to
    D3DVERTEX vertices[2][3]; but i can still see only 1 triangle…

  6. Pury said

    P.P.P.S. Ok, now! I just didn’t know that points should go clockwise.

  7. gregd1024 said

    Pury,

    Thanks for the info and, yes, translating it is fine by me. πŸ™‚

    Yeah, the points should go clockwise by default. Counter-clockwise makes the polygon face away from the camera and therefore nothing is drawn.

    -Greg

  8. Pury said

    Greg, could you say some words about dynamic scenes in directX? May be not in this article, but in private talk?

  9. gregd1024 said

    Pury,

    Please send me an email using the Contact link on the top of the page so we can discuss. Thanks.

    -Greg

  10. Roland said

    Greg these are great tutorials thanks πŸ™‚ i can’t wait till you do the next one! its great how we dont have to use any weird headers like other site tutorials have.

  11. gregd1024 said

    Roland,

    Glad you like ’em! πŸ™‚ The next one is coming soon. I know it’s been a while since my last update, but the next tutorial is HUGE.

    -Greg

  12. locksley said

    When I download and try to run this sample, I get the following error:

    “Access violation reading location 0x000000”

    At this code:

    if(FAILED(g_pDirect3D_Device->CreateVertexBuffer(3*sizeof(D3DVERTEX), 0,
    D3DFVF_XYZRHW|D3DFVF_DIFFUSE, D3DPOOL_DEFAULT, &pVertexObject, NULL)))
    return(0);

    Any suggestions to what I am doing wrong?

    Thanks.

  13. gregd1024 said

    Hi Locksley,

    Sounds to me like DirectX couldn’t create the D3D Device object at the start of the program. I didn’t have any NULL checking there, so you wouldn’t have got an error. Check the g_pDirect3D_Device variable in the debugger right before this call is made (on the CreateVertexBuffer line). Is it NULL?

    -Greg

  14. JohnBello said

    Hi Greg

    I was wondering if and how it would be possible to change the colors of the vertices while running (with WM_KEYDOWN maybe?)

    Hope you’re still looking at these comments
    Thanks.

  15. gregd1024 said

    Hi John,

    You could use WM_KEYDOWN, but that method causes a small delay to occur each time a key is pressed. I’d use DirectInput so you can get immediate notification when key states change. Check out the code in my DirectX tutorial #3 – the DirectInput sections are marked in the comments.

    As far as making the vertices change color – just store the colors in some global variables, read them in the rendering section (under WM_PAINT), and change these vars in response to key presses. Also, once a key is pressed and you’ve updated the global vars to go to the next color, you’ll need to call Invalidate() on the main window handle so another WM_PAINT message is fired.

    Hope this helps. πŸ™‚

    -Greg Dolley

  16. JohnBello said

    Thanks I’ll try it out.

  17. Jared said

    Hi Greg,

    First off, thanks for these great tutorials.

    To my question, then – I was wondering if you could recommend the best way to import a 3D model into a C++ Windows Forms application for use with DirectX (specifically, Direct3D). I haven’t made the 3D model yet, but it would likely be made in Blender or 3DMax.

    Thanks in advance.

    • gregd1024 said

      Hi Jared,

      I would imagine there’s some Open Source C++ class to read 3d Studio Max file formats. Try doing a google search. Or, I don’t know if this feature exists in the current version, but see if 3d Studio Max has an export to C++ file format option. GIMP has this ultra handy feature (but GIMP only does bitmaps, of course).

      -Greg Dolley

  18. Jared said

    Thanks a ton, I’ll look into that.

  19. spy84 said

    Greetings Greg,

    I must say this tutorial is a “piece of art”… well done!
    I’m working on my project (finite-element method issue) atm and I was wondering about drawing a multiply triangles as a result. I’ve used tips mentioned by Pury but I’m still having a problem with this. Could you show how to act with more than one triangle?
    Thanks in advance.

    Regards

    • gregd1024 said

      spy84 – Thank you! πŸ˜‰ You can draw more than one triangle just by increasing the size of the “vertices” array by multiples of three and putting your new triangle vertices in there. The first parameter to CreateVertexBuffer() would need to change to reflect the added vertices – so “3*sizeof(D3DVERTEX)” would turn into “*sizeof(D3DVERTEX).” Sames goes for the Lock() function (parameter two) and memcpy() (parameter three).

      -Greg

  20. P_86 said

    Hi,

    First of all, thanks for these great tutorials.

    To my questions, I was wondering if you could recommend the best way on how to detect the on-screen drawings from the user. Can i change the triangle to the square so that a 3D cube will be generated after compiling?? Currently, I am doing some projects regarding to 3D object reconstruction from 2D sketch. So your kindness to reply to my questions is fully appreciated.. It seems like “http://vimmi.inesc-id.pt/publication.php?publication_id=77”

    Thanks…

    • gregd1024 said

      P_86 – you’ll first have to get all the (x,y) pixel coordinates of each polygon vertex that DirectX projected onto the screen. This is a bit tough because I don’t think an API call exists where you can query this kind of information. You’ll just have to look at the math that DirectX uses to do the projection transform and perform the same operations in your code. Here’s the projection math details: http://msdn.microsoft.com/en-us/library/bb147302(VS.85).aspx and for a higher level overview: http://msdn.microsoft.com/en-us/library/bb206260(VS.85).aspx. This information is also in the DirectX 9 help files, BTW.

      While you’re doing the manual math in your code, save all the (x,y) pixel coords with the corresponding (x,y,z) 3D vertices. Now you’ll have an array that you can check against whenever the user clicks somewhere on the screen. When a click occurs, find the corresponding 3D vertex (if any) at that pixel (or nearby). When the mouse drags, dynamically change the 3D vertex at that point and refresh the screen. Obviously farther away points will need to be moved at greater x/y deltas than nearby points, so you’ll need to come up with some fudge factor that looks consistent given your field-of-view. This would be something like: “delta_x_or_y*z/w” where “w” is another factor you’ll have to play with that prevents the x/y movements from being too large (or else the points would move too far for small mouse movements).

      Hope this helps!

      -Greg Dolley

  21. JB said

    hi,
    its a very good tutorial for drawing a triangle. What should i do to draw different triangles in two different places??
    can i do that without using Index buffer??

    Thanks

  22. JB said

    Hi Greg,

    i could draw two triangles now. No need to reply my prev post. Thank you.

  23. TyPR said

    Hey, nice tutorial. Thanks πŸ™‚

    I just have one question. You create pVertexObject and pVertexBuffer, and then lock pVertexObject, which gives you a pointer to where to write to the buffer. My question is: are pObject and pBuffer not the same? (I would guess not if it requires both of them). I’m guessing pObject points to some type of struct/class/other data that DirectX uses (basically a header for the actual vertex buffer data), and pBuffer points to where the actual data is?

    Also, once pBuffer has a value (and as long as you don’t resize pObject), will that value always be the same?

    Lol I know I’m probly a pain but just wondering. Thanks for the tutorials again! πŸ™‚

    • gregd1024 said

      TyPR – pVertexObject is not the same as pVertexBuffer. pVertexObject is actually an interface that has functions for working with vertices (such as Lock(), which allocates vertex buffers). pVertexBuffer is just a pointer to your vertex data. And, yes, the value of pVertexBuffer will stay the same unless you acquire a new lock on it.

      -Greg Dolley

  24. TyPR said

    Thanks.

    Also, through playing around a lot, I have some more questions.

    First, I am currently having a problem with one of either two things. I’m guessing the problem is that the window never redraws itself (unless I resize it). Is there a way to change this? Also, will this work for getting the r/g/b/a values of a color?

    union
    {
    DWORD color;
    struct { BYTE r, g, b, a; }; //Does alpha come first or last?
    };

    I’m guessing my first question should solve my problem. If it doesn’t then I’m guessing either the union is wrong or theres something messing me up with my multithreading (I have one thread for drawing and one for the game loop… though at this point I doubt its the problem)

  25. TyPR said

    Ok, sorry about the double post, but I did figure out how to force a redraw

    For anyone that wants to know:
    InvalidateRect(NULL, hWindowHandle, FALSE)

    Anyway, now I’m looking for an accurate timing system to force redraws every set number of milliseconds. Anything in ctime.h goes by seconds, and clock() uses a preset (unaccurate) measure that basically makes it a really fast frame counter. I’ll probly be able to find one myself, but just wanted to let you know I solved my original problem πŸ™‚

    BTW thanks again for the tutorial

  26. Peter said

    If you’re looking for a timer the easiest way is just to create a form in your project and set visible to false, and set the location waaay of screen, like 3000,3000. Then you add a timer to your form and voila! a very accurate timer that takes 30 seconds to set up:P

    Regards, Peter

  27. jayson said

    I was able to create 2 triangles to make a box ( FINALLY! thanks alot), but what if i wanted to create a full checkerboard what would be the best way to do that? I know that there is a better way then creating that many triangles. Maybe create 1 black/white pair and then “copy” it across the board. Thanks alot.

    • jayson said

      I am thinking that I should be using Sprites? Am I Correct?

    • Benjamin said

      Your vertex buffer can have vertices that aren’t connected. Go ahead and put your 64 sets of squares in there using a couple nested loops.

      You can use the triangle strip method of drawing in a 64x loop to draw them all:

      for (int i=0; iDrawPrimitive( D3DPT_TRIANGLESTRIP, i*4, 4 );
      }

  28. Etni said

    Hi and thanks for another great tutorial!

    A tip from the perspective of a complete newbie; you could try to avoid structures that are not required for the program to run (and if you feel these should be included, get to it at later tutorials when the reader has a better idea of what he is doing).

    For example, I found:

    pVertexObject->Lock(0, 3*sizeof(D3DVERTEX), &pVertexBuffer, 0)

    easier to understand than:

    if(FAILED(pVertexObject->Lock(0, 3*sizeof(D3DVERTEX), &pVertexBuffer, 0)))
    return(0);

    because the code doesn’t fail anyway, whatever that means. And the above can be used in void functions etc.

    Cheers πŸ™‚

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: