Managed DirectX First Steps: Direct 3D Basics and DirectX vs. GDI+ - Put That in Your Pipeline and Shade It
(Page 4 of 14 )
As we’ve touched on the concept of vertex processing, it’s probably a good idea to describe some of the basics about DirectX and, more specifically, the DirectX pipeline. Because we won’t be covering the details of the pipeline, nor how to manipulate the programmable parts of it, you won’t miss anything if you skip over this part. However, if you’re curious (like most programmers are), this section will give you a very generalized notion of the details of DirectX.
Modern graphics cards have several different stages of processing. In the past, those stages generally consisted of three parts. The first part transformed the vertices of 3-D models, which were generated with their own notion of a coordinate system, into a coordinate system that mapped into the “world” (scene) that the model existed in, and then ultimately into a specific viewpoint into that world. During those transformations, the graphics pipeline also performed techniques to alter the color and intensity of the vertices depending on the light sources in the scene. This stage was generally referred to as the transform and lighting stage (T&L).
Once the T&L was complete, the scene was trimmed down to throw out the parts that weren’t going to be in the final image the viewer would see. This was generally called the clipping stage.
The final part was where all the “magic” happened and the 3-D world got converted into a 2-D image that you could display on a screen. This was generally called rasterization.
Modern graphics processors have much more control over the pipeline, and DirectX gives you many different ways to control these stages creatively. For instance, during the T&L stage, you have more abilities to manipulate the vertices in different ways, including the ability to perform high-speed manipulations on sets of vertices. In effect, you bypass the “fixed” T&L stage and can do interesting things like simulating cloth, face morphing, or fancy underwater effects. This technique is generally called vertex shading.
But DirectX doesn’t stop there—it also lets you “romp and stomp” inside the rasterization stage, allowing you to directly manipulate different parts of it. Pixel shading, for instance, allows you to apply special lighting effects on a per-pixel basis. In addition, you can create fog effects and blend separate frame buffers, basically tweaking the output right up until it’s sent to the monitor.
Figure 3-3 shows an example of a pixel-shading technique in which a normal 3-D scene is given a “hand-drawn” effect, all in real time. This demo can be found on ATI’s Web site
http://www.ati.com/ developer/demos/r9700.html), under Non Photorealistic Rendering. Note that you must have a graphics card that supports pixel-shading, like the Radeon 9700.
Figure 3-3. Non-photorealistic rendering using pixel shaders
All these concepts are well beyond the scope of this book, but you can find plenty of information on them in modern graphics programming books and on the Internet. Look in Appendix A for some recommendations.
The preceding flags are mutually exclusive, but they can be combined with the following flags to pass additional information to DirectX when creating a device:
- FPU_Preserve: This flag informs DirectX to perform all the calculations using double-precision floating points, which can lead to slower performance.
- MultiThreaded: Use this flag to inform DirectX that you need a multi-thread-safe environment.
- PureDevice: This flag is used only in combination with the HardwareVertexProcessing flag, and specifies that the hardware can do rasterization, matrix transformations, and lighting and shading calculations. It’s the best choice for any application, and most modern graphics cards offer this feature.
The last set of parameters for creating a device, the presentation parameters flags, is a complex structure whereby the programmer can define many low-level details about the device being created. We’ll present here the most commonly used attributes. For a full list, refer to the DirectX SDK help feature.
- EnableAutoDepthStencil and AutoDepthStencilFormat: These structure members tell DirectX that you want to use a depth buffer and which format to be used in such buffer (according to the Format enumeration), respectively. The depth buffer helps with defining the relative distance of the object in relation to the screen, which is used to draw nearby objects in front of far ones. Although this seems to be a concept exclusive to the 3-D gaming world, that’s not entirely true: Even some very basic 2-D games have so-called layers—usually the background and any objects that must appear behind the player (such as trees or bushes) stay in a back layer, and the player and other objects stay in the front layers.
- BackBufferCount, BackBufferFormat, BackBufferWidth, and BackBufferHeight: These members define the number of back buffers (from 1 to 3), the format of such buffers (defined by the Format enumeration), and their width and height. The back buffer format (as with the depth stencil buffer) must be valid, that is, one that can be checked by the CheckDeviceType method of the Direct3D object. If the buffer can’t be created, the creation of the device will fail. The back buffers are used to render the scene being drawn in the background thread automatically, in order to allow a smooth transition between frames drawn (no partial drawing is shown to the player). This parameter is closely related to the SwapEffect attribute, which will tell DirectX how to swap the back buffers to the screen, and to the Windowed attribute, which will force some limitations to the possible values.
- SwapEffect.Discard: The back buffers content isn’t preserved in the swap operation, allowing the application to choose the best performing technique, sometimes leading to big performance gains in the swapping operation. However, the scene must be completely redrawn for each frame.
- SwapEffect.Flip: Creates a circular list of buffers to be swapped to screen (called a swap chain), allowing synchronization with the video refresh rate in a smooth way when running full screen. The flip term means that you have no copy of the memory block—DirectX just repositions the video memory start pointer to the next buffer. When running in windowed mode, there’s no real flip; the video memory gets copied to the window, which is an operation with slower performance. In this operation, the front buffer becomes one of the back buffers, so the game can rely on this to redraw only part of the scene.
- SwapEffect.Copy: This setting preserves the contents of the back buffer, just copying it over the front buffer (the screen). This setting forces BackBufferCount to be set to 1, because there’s no need to have more buffers. This is the simplest of the buffer swap operations, although it’s the one with the worst performance. The most important gain for the programmer is that the application isn’t forced to perform complex control operations over multiple back buffers.
- Windowed: When set to true, indicates that the application will run in a window; a setting of false indicates the application will run full screen. When running in windowed mode, BackBufferFormat must match the current display resolution, and BackBufferWidth and BackBufferHeight may not be specified, as they are assumed to be the window client area dimensions. When running in full screen, the width and height of the back buffer must match one of the possible display modes (explained in the next section) for the device.
- DeviceWindowHandle: The handle of the window to be used by DirectX. If it’s set to null, DirectX will use the active window.
Understanding Display Modes Although the term adapter refers to the hardware and its driver, and the term device refers to the main object used to access a specific window and draw over it, we use the term display modes to define the objects (the DisplayMode class) that store basic information about the screen status, including width, height, refresh rate, and a format flag that returns extra information about how colors are controlled by the display. The formats for rendering displays are as follows:
- A8R8G8B8: Color format in which each pixel on screen is defined using a 32-bit ARGB value—255 possible values for each red, green, and blue (RGB) color component, and an extra alpha (A) value that defines the transparency of each pixel (255 is fully opaque and is 0 is totally transparent).
- X8R8G8B8: Color format with 32-bit RGB values, and an extra byte (indicated by the “X”) for color definition, not used.
- R5G6B5: Color format using 16 bits, where each RGB color component can assume 32 different values; an extra bit for green makes this show 64 possible values, reaching a total of about 64,000 colors.
- X1R5G5B5: 16-bit color format in which each color component takes 5 bits (32 possible values), making a total of a little more than 32,000 colors.
When choosing the display mode for games, it’s important to balance the number of desired colors against the memory used to display them. The 32-bit format spends almost twice as much time to display the same number of pixels when using the copy swap modes than do the 16-bit formats. However, the 32-bit format enables a huge number of colors, which may be needed with games that have more sophisticated artwork. The rule of thumb is always use 16-bit format, unless you need more colors, so you’ll get the best performance.
NOTE When running in windowed mode, you must use the computer’s current resolution and color depth, so this discussion applies only to full-screen modes.
Creating a Simple Direct3D Program Now that you understand the basic concepts involved in creating a DirectX device to render your graphics, let’s look at the basic structure for Managed DirectX programs. This basic structure will always be the same, even for the most sophisticated programs.
All the drawing operations on Direct3D are made with the use of a Device object and must occur between the calls of the BeginScene and EndScene methods. These methods internally lock the back buffer you use while rendering and unlock it when you finish. Calling the Present method of the Device object, after ending the scene, will display the contents of the back buffer to the screen (front buffer), according to the behavior parameters set when creating the device.
The basic structure for a Direct3D program is shown in the following pseudo-code:
Set the presentation parameters for the device to be
created
Create the Device object
Repeat in a loop, until Game Over
Clear the Device
Begin the Scene
Draw the Scene (render)
End the Scene
Present the Scene to the user
Dispose the Device object
This will map to the following code (this is a simplified version of the code you can find in Microsoft’s DirectX Sample Browser, which is installed when you install the DirectX SDK):
public class SimpleDxApp : Form {
private Device device;
public void InitializeGraphics() {
PresentParameters presentParams = new Present
PresentParameters();
presentParams.Windowed=true;
presentParams.SwapEffect = SwapEffect.Discard;
device = new Device(0, DeviceType.Hardware, this,
CreateFlags.SoftwareVertexProcessing,
presentParams);
}
protected override void OnPaint(System.Windows.Forms
PaintEventArgs e) {
this.Render(); // Render on painting.
}
private void Render() {
device.Clear(ClearFlags.Target,
System.Drawing.Color.Blue, 1.0f, 0);
device.BeginScene();
// Rendering of scene objects can happen here.
device.EndScene();
device.Present();
}
static void Main() {
using (SimpleDxApp frm =new SimpleDxApp()) {//dispose
frm object when done
frm.InitializeGraphics();
frm.Show();
while(frm.Created) {
Application.DoEvents();
}
}
}
}
That’s it. Of course, some details aren’t presented here, the most important one being the error trapping. For instance, in the scene-drawing sequence, you have three related methods—Begin, End, and Present—that must be executed as a whole; if one of them fails, the others will fail, too. But you’ll see the details in the section “The Coding Phase.”
If you run this code (see details about setting the correct reference to the Managed DirectX type library in the section “The Coding Phase”), all you get is a blue window, because you don’t know yet what you can use in the Render procedure to draw something. But DirectX will already be up and running, ready for you!
To complete your first program, let’s see some basic concepts regarding Direct3D drawing in the next sections.
This chapter is from Beginning .NET Game Programming in C# by David Weller et. al.(Apress, 2004, ISBN: 1590593197). Check it out at your favorite bookstore today. Buy this book now.
|
Next: 3-D Coordinate Systems and Projections >>
More ASP.NET Articles
More By Apress Publishing