C++ Programmer's Cookbook

{C++ 基础} {C++ 高级} {C#界面,C++核心算法} {设计模式} {C#基础}

Managed DirectX start !

DirectX 首次出现在 1995 年,当时称为“GameSDK”。在其原始形式中,针对的目标是使用 C 和 C++ 的开发人员。只有在 2002 年 12 月该 API 的第一个托管版本 (9.0) 发布以来,才可以使用 C# 或 VB.NET 开发 DirectX(实际上,如果您希望,那么可以使用任何符合 CLR 的语言)。

虽然与非托管版本相比,关于托管 DirectX 的性能争论很多,但商业游戏已使用托管 DirectX 进行创建的事实应该能够从根本上平息这样的争论。虽然某些需要极高性能的游戏可能需要使用非托管代码,但大多数游戏可以使用托管代码或者结合使用托管和非托管代码进行创建。编写托管代码使开发人员的效率更高,从而编写出更多的代码,生成更安全的代码。

安装 DirectX SDK 之后,在 C:\WINDOWS\Microsoft.NET\Managed DirectX 应该有一个目录,在机器上安装的每个版本的 SDK 都有一个子目录。我机器上使用的已经是第四版了,因此我有四个子目录。在每个子目录中应该有九个 DLL 和九个 XML 文件。由于托管环境中的 .NET 允许同一台机器上的同一个 DLL 文件有多个版本,而不会引起以前被称为 DLL Hell 的问题,所以我们可以使用多版本的托管 DirectX 库。这就允许您在安装新版本之后轻松地回滚到以前的版本。

如果您以前有在 Windows 下处理 DLL 文件的经验,您可能会担心在同一台计算机上安装同一个文件的多个版本会产生问题。自从 .NET 引入并行版本控制,这些版本控制问题就不复存在了。这意味着当新版本的 SDK 发布时,您可以使用多个版本来检查兼容性问题,而不必强迫自己进行升级。

九个 DLL 文件大致对应于 DirectX 中的十个命名空间。在创建游戏时,我们使用其中的大量命名空间来提供对输入设备、声音、网络播放(当然还有 3D 图形)的支持。

命名空间 描述

Microsoft.DirectX

公共类和数学结构

Microsoft.DirectX.Direct3D

3D 图形和助手库

Microsoft.DirectX.DirectDraw

Direct Draw 图形 API。这是旧式命名空间,您不需要使用它。

Microsoft.DirectX.DirectPlay

用于多玩家游戏的网络 API

Microsoft.DirectX.DirectSound

声音支持

Microsoft.DirectX.DirectInput

输入设备支持(例如,鼠标和游戏杆)

Microsoft.DirectX.AudioVideoPlayback

播放视频和音频(例如,在 PC 上播放 DVD)

Microsoft.DirectX.Diagnostics

疑难解答

Microsoft.DirectX.Security

访问安全性

Microsoft.DirectX.Security.Permissions

访问安全权限



HEL和HAL

  在图5.2中,可以看到在DirectX下有两个层面,分别是HEL(硬件模拟层)和HAL(硬件抽象层)。这是一个约定:DirectX是一种非常有远见的设计思路,它假定可以通过硬件来实现高级性能。但是,如果硬件并不支持某些性能,那怎么办呢?这就是HAL和HEL双重模式设计思路的基础。
  HAL(硬件抽象层)是接近硬件的底层。它直接和硬件交流。该层通常有设备生产商提供的设备驱动程序。可以通过常规DirectX调用直接和硬件进行联系。使用HAL要求硬件能够直接支持所要求的性能,这时使用HAL,就能够提高性能。例如,当绘制一个位图时,使用硬件要比软件更胜任该项工作。
  当硬件不支持所要求的性能时,使用HEL(硬件仿真层)。我们以使用视频卡旋转一个位图为例来说明。如果硬件不支持旋转动作,则使用HEL,软件算法取代硬件。很明显这样做速度要慢一些,但关键是这样做至少不会中断应用程序,应用程序仍然在运行,仅仅是速度慢一些而已。另外,HAL和HEL之间的切转是透明的。如果要求DirectX做某工作,而HAL直接处理该工作,也就是硬件直接处理该项工作。否则,HEL将调用软件仿真来处理该工作。



Section 1 – Introduction

Section 1.1 – What is DirectX?

DirectX is the name given to a series of API for running and displaying multimedia rich applications. Using it, you can have graphics, video, 3D animation, surround sound and so on. The different API’s available in DirectX are Direct Graphics, Direct Input, Direct Sound, Direct Play and Direct Show.

Direct Graphics deals with everything related to graphics. With it you can load 3D meshes, render textures, animate 3D models, light your scene, add particle effects and much more. It is concentrated more towards 3D making it ideal for rendering 3D graphics, although it will also work just as well doing 2D graphics too.
Direct Input is the API that allows you to use many different kinds of input devices such as keyboards, mice, joysticks, joy pads and so on. It gives you much more control than the Win32 API does, and is ideal for games.
Direct Sound does everything you could ever want to do with audio. You can play midi files and wav files and other types of music. You can add special effects such as an echo or flanging. You can also add 3D surround sound effects by telling Direct Sound to apply special 3D algorithms and so on. Again, this is ideal for games.
Direct Play is the networking API used mainly in games. If you wish to make a game run over a network then you can use DirectPlay?. There isn’t much more to say about that really.
Direct Show can be used for all kinds of multimedia tasks such as video playback. It is ideal for audio or video recording, editing and manipulation.


Section 1.2 - Assumptions

Readers should have a basic understanding of the C# language and should have at least enough experience using it to understand the examples. The code examples are fairly simple and don’t use anything overly complicated, but you should still have a firm grasp of the language nevertheless. If you have any experience with using previous versions of DirectX in other languages then this would certainly help, but isn’t a requirement. The tutorial assumes you’ve never used DirectX before.
I will be using the .Net framework version 1.1. Readers using the new .Net 2.0 Framework that comes with Microsoft Visual C# 2005 Express Edition should be able to convert the code without too much trouble. Most of it should be compatibl. I will be using the Microsoft Visual Studio .Net 2003 IDE so any screenshots or descriptions will explain how to perform the task in that IDE. If you are using a different IDE such as Sharp Develop, Borland C# Builder or Microsoft Visual C# 2005 Express Edition, then I assume that you’re competent enough in it’s use that you can understand how to do the tasks in your own IDE. You should at least know how to create a new project, add references to your project, add files and code to your project, save it, and build it. Obviously knowing how to do other important things such as use the debugger would be a strong advantage.
I will use the DirectX Summer Update 2004 SDK. That is: version 1.0.1901.0. There may be some slight incompatibilities with previous versions, but they shouldn’t be too hard to fix if you’re using a previous version. I suggest you get the update though if you don’t already have it.
Note that I won’t be using the DirectX wizards or any template files in my project. I’ll be showing you how to do everything yourself so that we can gain a deeper understanding of what’s actually going on and what we’re actually writing.



Section 2 – Creating a New DirectX Project


Section 2.1 – Creating a New Project

Start up your IDE. As I mentioned above, I use Microsoft Visual Studio .Net 2003. Start a new project, and save it. Choose either “Windows Application” or “Console Application” depending on which type of project you wish to make. If you installed DirectX9? properly then you should be able to create a new DirectX project, which has a wizard to guide you through creating a simple application. As I mentioned, I won’t be using this. I prefer to write the code myself to gain a deeper understanding of what is actually going on in my project.


Section 2.2 – Adding the DirectX References

In order to use DirectX in your applications, you must add a reference to the assemblies. To do this in Microsoft Visual Studio .Net 2003 you can click the project menu and click “Add Reference”, or right click on the “References” section in the project explorer, and click “Add Reference”.
Now you should be presented with the “Add Reference” dialog. Make sure the “.Net” tab is selected, and scroll down to “Microsoft.DirectX”. You will see that there are several entries. At the very least, add “Microsoft.DirectX”. The other assembly names reflect quite clearly what they contain. For example “Microsoft.DirectX.Direct3D” is the 3D graphics API, “Microsoft.DirectX.DirectSound” is the Direct Sound API and so on. Go ahead and add whichever ones you require.
Some of you may notice that you have 2 versions of each assembly with different versions. You will have this if you installed the Summer 2004 Update. I have one set using version 1.0.1901.0 and another set using version 1.0.900.0. I will be adding the assemblies with the newer version: 1.0.1901.0.

Image


Adding the DirectX references in Microsoft Visual Studio .Net 2003.


Section 2.3 – The “using” Directives

You may wish to add some “using” directives for the namespaces in DirectX. You should know what the “using” directive does. Here are some simple ones for using Direct3D:

using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;


Section 3 – Direct Graphics Enumeration


Section 3.1 – Enumerating Adapters and Display Modes

An adapter is basically a graphics card. Most systems will have just 1 adapter. Systems with multiple monitors may have more than 1 adapter. If you wish to support systems with multiple adapters then you will have to enumerate them, otherwise you can just use the default one. The code to enumerate and choose an adapter is incredibly simple though, so it’s worth the little bit of extra effort.
An adapter is described by an AdapterInformation? class located inside the Microsoft.DirectX.Direct3D namespace. This class has some useful properties such as the CurrentDisplayMode? property, which funnily enough, returns the current display mode; the AvailableDisplayModes? property that returns a list of supported display modes, which can be enumerated using a foreach loop; and an Information property, which returns an AdapterDetails? class. This class contains several useful properties which you can use to identify individual cards and so on. One of the most useful properties of this class is the Description property. This gives you a human readable description of the graphics card such as “NVIDIA GeForce2? MX/MX 400” (My old crappy card). There are many other properties and methods inside the AdapterInformation? class too. Explore the properties and methods using the tooltips and the intellisense (Intellisense is the name given to the lists that pop up in the code editor containing all the accessible members of an object for those of you who don’t know what that is).
To actually enumerate an adapter, we use the Manager class located inside the Microsoft.DirectX.Direct3D namespace. You don’t create a new instance of this class as all of it’s methods are static. The Manager class contains a static property called “Adapters” which returns a list of AdapterInformation? classes described above, that you can enumerate using a foreach loop. It really is that simple. If you wish to use the default adapter, or just want to know what it is at least, then you can get that simply by using Manager.Adapters.Default.
Here’s an example program showing the main things we’ve learned so far:

using System;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;


class AdapterEnumeration
{
static void Main(string[] args)
{
	foreach(AdapterInformation adapter in Manager.Adapters)
	{
	// Check whether or not this is the default adapter
		if(adapter.Equals(Manager.Adapters.Default))
		{
			Console.WriteLine("This is the default adapter\n");
		}
		
		// Write out some information about the adapter
		Console.WriteLine("" + adapter.Information.ToString() + 
					"\n\nDisplay Modes:\n");
			
		// Write out each display mode
		foreach(DisplayMode display in adapter.SupportedDisplayModes)
		{
			Console.WriteLine("{0}x{1} : {2} @ {3} Hz", 
					display.Width,
					display.Height,
					display.Format,
					display.RefreshRate);
		}
			
		// Write out the current display mode
		Console.WriteLine ("\nCurrent Display Mode: {0}x{1} : {2} @ {3} Hz", 
					adapter.CurrentDisplayMode.Width,
					adapter.CurrentDisplayMode.Height,
					adapter.CurrentDisplayMode.Format,
					adapter.CurrentDisplayMode.RefreshRate);
			
		Console.WriteLine("\n\n");
	}

	Console.ReadLine();
}
}




That’s extremely simple, I think you’ll agree. The output isn’t the nicest looking output in the world, but it shows you how to do it. You can make it look pretty quite easily. In a windows application you can add each adapter description to a combo box or some other suitable control. When an adapter in the combo box is selected, you could add information about that adapter to a listview, and add it’s available display modes to another combo box, selecting the current display mode by default. That’s how I usually do it, and that’s an example of the kind of things you can do with the very simple code above.
As a by the way, you can get a handle to the monitor associated with each adapter using the following code:

IntPtr MonitorHandle = Manager.GetAdapterMonitor(adapter.Adapter);



I’m not quite sure what you could do with that handle, but I’m sure it’s useful somewhere, such as with some Win32 API functions that return information about the monitor or something like that. Note that the “Adapter” property of the AdapterInformation? class is the “adapter ordinal” which is just a fancy name for the adapter number. The first one has ordinal 0, the second ordinal 1 and so on. Usually there will be just one default adapter with an adapter ordinal of 0.


Section 3.2 – Device Capabilities (Caps)

Device Caps is one of those terms you will hear a lot with DirectX programming. Caps is simply short for Capabilities. The Device Caps are therefore just what the device is and isn’t capable of doing. There are literally hundreds of things you can check. Retrieving device capabilities is very simple. Again, you use the Manager class to do this. Here is a short snippet of code showing you how to do it:

AdapterInformation ai = Manager.Adapters.Default;
Caps caps = Manager.GetDeviceCaps(ai.Adapter, DeviceType.Hardware);



There’s nothing hard there, is there? The only thing that might confuse you is the DeviceType?. The 2 main types are “DeviceType.Hardware” and “DeviceType.Reference”. There is also another device type called a Software Device, but this is very rarely used. I think it’s something to do with plugging in your own software renderer or something. Using the hardware device type will query the capabilities of the graphics adapter. Using the reference device type will query the capabilities of software emulation, basically. A lot of drivers will be able to make up for lack of hardware features by providing software emulation. A common difference for example, is that the hardware will only usually be able to do a few active lights such as 8 or 16 whereas the reference device may be able to do an unlimited amount. The software device will be able to do pretty much everything, but it’s much slower than the hardware.
Anyway, back to the point. You can probably figure out how to use the Caps class by looking at the intellisense again. This is a very powerful way to learn about classes. Most of it should be fairly self explanatory. You can test for all kinds of things. The tooltips will explain what all of the properties mean. For example, if you want to see whether or not textures must be square, you can test that using the following code:

if(caps.TextureCaps.SupportsSquareOnly)
{
	Console.WriteLine("Square textures only are supported");
}



As I mentioned, there are literally hundreds of things you can check for. They are all organised into sensible properties. For example, all texture capabilities are stored inside the TextureCaps? property, vertex processing capabilities are inside the VertexProcessingCaps? property and so on. You can explore it using the object browser as well as the intellisense. (Press F2 in Visual Studio .Net 2003 to open the object browser):

Image
Exploring the Caps class inside the object browser.

Section 3.3 – Adapter Formats and BackBuffer? Formats

There are 3 main questions I will answer in this section: What is a format? What is a backbuffer (format), and what is an adapter format?
In answer to the first question, the format is just the way that pixel data is encoded. For example, you may have 5 bits to store the amount of red, 6 bits to store the amount of green and another 5 bits to store the amount of blue. This adds up to 16 bits, or 2 bytes per pixel. As a by the way, we give green more bits for 2 reasons. Firstly, to make the pixel data up to 2 bytes without wasting a pixel, and secondly, we chose the green component because our eyes are more sensitive to green and can therefore distinguish between more shades of green than they can red or blue, so it makes sense to give the green component the extra bit. The 2 most common formats are 16 bit and 32 bit. There is a 24 bit format and there are 8 bit formats, but these are rarely used. Some formats have some bits set aside for an “alpha” component. This is to do with something called alphablending, which is a type of transparency. You don’t need to know what that is exactly right now, but just bear in mind that some formats have an extra alpha component. Yet other formats have unused bits, denoted by “X”. For example, there is a 32 bit mode called “X8R8G8B8” which means that the red, green and blue components are all 8 bits each, and there is an unused 8 bits to round the format up to 4 bytes per pixel. An example of a format with an alpha component is one called “A8R8G8B8” which has the same as the other format, but instead of having an unused 8 bits, it uses the extra 8 bits for the alpha component.
Now in order to explain what the backbuffer format is, I suppose I better mention briefly what a backbuffer is. When you draw things on the screen, they aren’t all drawn at the same time obviously. So you may draw a tree, then draw a bush, then draw another tree, then draw a character, then draw a house for example. They are all drawn one after the other. There is a phenomena called “artifacts” where the screen encounters a vertical resync (a refresh) while you’re in the middle of drawing your scene on to the screen. This results in flickering graphics where you can briefly see things being drawn on the screen. The way this is fixed is we draw everything offscreen to an area of memory, and only once the scene is finished, we draw this entire area of memory onto the screen at once. This way you don’t see each individual part being drawn. This area of memory where you do your drawing is called a “backbuffer” and the process of drawing the backbuffer onto the screen is called “flipping” or “presenting”. Hopefully the term “backbuffer format” should be quite clear now that I’ve explained what a backbuffer is, and what a format is.
Now for the adapter format, this is basically just your display mode. The format of the pixels on the main screen. One question may arise from this – why aren’t the backbuffer format and the adapter format the exact same? Well the answer to that is in most cases they are. Although there is one difference. In a backbuffer you can have an alpha component, but you can’t have an alpha component in the adapter format. This is why they may differ.


Section 3.4 – Device Types

This is quite a short section. I’ve already mentioned device types before, but here it is again. There are 3 device types: Hardware, Software and Reference. Hardware means use the graphics card where possible, reference means use software emulation. And the software device is rarely used. It’s something to do with writing your own software renderer I think. Now you may be wondering why you would ever want to use software instead of hardware. Well there is actually a good reason. Because the reference device supports absolutely everything. You would need to fork out a huge amount of money to get a graphics card that can come anywhere near close to being able to do everything. I’m talking about a top of the range card – the best you can get. The problem with software is that it’s painfully slow compared to using hardware. So if you just want to test out some features in your game that your graphics card doesn’t support, you can switch to software emulated mode and check that it works. Or if you just really want to use some feature not supported by the hardware so much that you’re willing to slow down everything to a crawl to get it to work then you can use software emulation for that. You probably wouldn’t want to do that in a game unless you want a frame rate of about 0.5 frames per second. That would be for an application such as a 3D rendering application, where you really want to have some special effect in your scene, and don’t care if it takes an extra hour to render (These things take hours anyway).


Section 3.5 – Using Manager.CheckDeviceType

At this point I’ve explained the following things to you: how to enumerate adapters, how to enumerate display modes, device types and backbuffer and adapter formats. Now you need to begin choosing them. So choose an adapter, a device type, an adapter format (from your list of display modes), a backbuffer format, and a device type. Now you need to choose one more thing: whether or not you want to run this application in a window, or in full screen mode. You will need all of these to create a device (which I’ll explain later. Lets just say for now that it’s incredibly important). So now you’ve chosen all of these things. Unless you know what you’re doing or you got pretty lucky, they’re probably all incompatible with each other and wouldn’t work. Ideally you want to present a list of adapters to the user. Once they choose an adapter, you would then want to present a list of device types compatible with that adapter to them, and have them choose one of those. Once they’ve chosen an adapter and a device type, you would like them to choose a display mode (the important part being the format, for the adapter format). Once they’ve chosen that, you would then ideally like to present them with a list of backbuffer formats which are compatible with the adapter format to choose from. And finally, you want to present them with the option of fullscreen or windowed mode, if that’s still there.
The best way to do this is to check every possibly combination of the following: adapters, device types, available adapter formats, backbuffer formats, whether or not to use windowed or full screen mode. By checking all the combinations of the above, you’ll get a list of all the compatible combinations which you can present to the user whatever way you see fit. The method used to check these is Manager.CheckDeviceType. The following code snippet shows you how you can check all possible combinations. When you get a valid combination, you can add that to your combo boxes or list boxes or your arrays or whatever method of storing the combinations you choose.


DeviceType[] deviceTypes = new DeviceType[] 
{DeviceType.Hardware, DeviceType.Software, DeviceType.Reference};
		
Format[] backBufferFormats = new Format[] 
{Format.A8R8G8B8, Format.X8R8G8B8, Format.A2R10G10B10,
Format.R5G6B5, Format.A1R5G5B5, Format.X1R5G5B5};
		
bool[] windowModes = new bool[] {true, false};
		
// For each adapter
foreach (AdapterInformation adapter in Manager.Adapters)
{
	ArrayList adapterFormats = new ArrayList();
			
	// Build the list of adapter formats
	foreach (DisplayMode dispMode in adapter.SupportedDisplayModes) {
		if (!adapterFormats.Contains (dispMode.Format))
			adapterFormats.Add (dispMode.Format);
	}
	
	foreach (DeviceType deviceType in deviceTypes) {
		foreach (Format adapterFormat in adapterFormats) {
			foreach (Format backBufferFormat in backBufferFormats) {
				foreach (bool windowMode in windowModes) {
					if (Manager.CheckDeviceType (
						adapter.Adapter,
						deviceType,
						adapterFormat,
						backBufferFormat,
						windowMode)) {
						// *** This combination is valid!
						}
				} // windowMode	
			} // backBufferFormat
		} // adapterFormat
	} // deviceType
} // adapter




Section 3.6 – Vertex Processing

The last thing we need before we can create a device (As I said earlier, this will be explained. Just know that it’s important. This is the main object we use for all our rendering) is a vertex processing type. I assume you’ve used something similar to the above code to enumerate all the possible combinations of adapters, device types, adapter/backbuffer formats and windowed or not and chosen one of each. Now before we can build a device, we need this vertex processing method. Basically the vertex processing method is just telling Direct3D where to do all of the vertex processing. How much should the hardware be involved, and how much should the software be involved, basically. Most cards these days should support hardware vertex processing. Lower end ones will support mixed vertex processing, where the processing is done by both hardware and software. Software only vertex processing will always work, but is slower. We can go through some methods to determine which type of processing is available like this:


Caps caps = Manager.GetDeviceCaps(Manager.Adapters.Default.Adapter, 
						DeviceType.Hardware);

	if (caps.DeviceCaps.SupportsHardwareTransformAndLight)
	{
		if (caps.DeviceCaps.SupportsPureDevice)
		{
			Console.WriteLine ("Pure Device");
		}
			
		Console.WriteLine ("Hardware Vertex Processing");
		Console.WriteLine ("Mixed Vertex Processing");
	}
		
	Console.WriteLine ("Software Vertex Processing");




You can see that first we get the device capabilities, as we use them to check the vertex processing available. Then we check if the device supports hardware processing. If it does, we know that at least it supports hardware and mixed processing. If it supports hardware processing, then we check if it supports the “pure device” which means it also supports resterization and shading as well as just lighting and transformation. We know that software vertex processing will always be available so that’s a given.
Now you have everything you need to create a device. You can enumerate and choose an adapter, and a display mode, and a backbuffer format and a device type and finally you can choose whether or not to use windowed mode or full screen mode if it’s compatible with everything else you’ve chosen.


Section 3.7 – Enumerating Depth Stencil Formats

There is one other main thing you will need to be able to enumerate – depth stencil formats. Don’t worry about what these actually are, you will learn later. I’m just putting this in the enumeration section because that’s where it belongs – it is afterall enumeration. Just know how to do it. You already know what a format is. This is done quite easily. We first just check the format is valid for the device, then check if it’s compatible with the backbuffer format. If so, then we can use it (You’ll see what it’s for later).


DepthFormat[] depthStencilFormats = new DepthFormat[]
{
DepthFormat.D16,
		DepthFormat.D15S1,
		DepthFormat.D24X8,
		DepthFormat.D24S8,
		DepthFormat.D24X4S4,
		DepthFormat.D32,
};

foreach (DepthFormat depthStencilFormat in depthStencilFormats)
{
if(Manager.CheckDeviceFormat(adapter, deviceType, adapterFormat, 
		Usage.DepthStencil, ResourceType.Surface, depthStencilFormat))
	{
if (Manager.CheckDepthStencilMatch(adapter, deviceType,
			adapterFormat, backbufferFormat, depthStencilFormat))
		{
			// This depth stencil format is compatible
		}
	}
}




Section 3.8 – Final Word on Enumeration

Note that I’ve covered more than enough on enumeration for you to get started. There is plenty more that you can figure out on your own though. Also remember all the stuff in the device capabilities (device caps) classes that can be checked. You check that stuff as you need it though. Unfortunately, enumeration is quite boring. I should have probably told you that at the start. We’ll start doing some more interesting stuff in the next section.


Section 4 – Rendering Something


Section 4.1 – Creating the Device

Finally we get on to this device I’ve mentioned so much in the enumeration section. What is a device? The device is the object we use in almost everything. It’s a representation of our actual adapter (well, in reality it’s driver). You will use the device to do all of the rendering (drawing, if I haven’t mentioned that yet) and lighting and so on. You can’t do very much without a device. So I think I’ve stressed just how important it is. Now to actually create a device is a little bit trickier than anything we’ve seen yet, but it’s still fairly trivial. Especially with all our enumeration done above.

// Assumes you have:
	// 
	// AdapterInformation	adapter;
	// DeviceType		deviceType;
	// CreateFlags		createFlags;
	// DisplayMode		displayMode;
	// Format		backbufferFormat;
	// bool			windowed;
		
	PresentParameters presentation = new PresentParameters();
		
	presentation.BackBufferFormat	= backbufferFormat;
	presentation.BackBufferWidth	= displayMode.Width;
	presentation.BackBufferHeight	= displayMode.Height;
	presentation.SwapEffect		= SwapEffect.Flip;
	presentation.Windowed		= windowed;
		
	Device device = new Device(
			adapter.Adapter, 
			deviceType, 
			this,
			createFlags,
			presentation);



So first you need to choose an adapter, a device type, a vertex processing type (this is the createFlags variable) a display mode, a backbuffer format, and whether or not to use windowed mode. All of these are stored in the variables mentioned above. One you’ve chosen these, you then create a new PresentParameters? object. This stores information about the backbuffer, and the display mode, and other important presentation information. You can change things like the number of back buffers to use and so on. We just change the backbuffer format, width and height as appropriate. Then we change the swap effect. The swap effect is just how the backbuffer is presented to the screen. We just tell it to flip the backbuffer and the screen around so that the backbuffer now becomes drawn on the screen, and what was previously on the screen now becomes the backbuffer. Lastly, we set whether or not we want to use windowed mode or not. Now to finally create the device, you pass first the adapter ordinal, then the device type. The next parameter, “this”, must be a windows forms control! So this won’t work in console mode obviously. You must write the above code in a windows forms application. Using “this” will just draw onto the form. You could also draw onto a picturebox or something else if you pleased. The next parameter is the create flags, or the vertex processing type that I talked about a lot above in the enumeration section. And lastly, you pass the presentation parameters. This should hopefully give us a working device.


Section 4.2 – Rendering Something

Now we get to the fun part – actually rendering! Don’t get too excited yet though, we’re only going to clear the window with a color. First we need to know where to actually render though. You could override the OnPaint? event and render everything in there. For a game or something, you will need a loop though. This is usually called “the game loop”. This game loop usually goes after all the initialization code. Just remember to show your form before entering the loop. So now we know roughly where to put our rendering code, how do we actually write it? Well, we’ll learn 2 new methods of the Device object: Clear and Present. The “Clear” method simply clears the entire drawing area with your chosen color. The “Present” method performs the “flip”, drawing the backbuffer to the screen. That’s all we’ll use right now. So here’s the code for a typical rendering loop:

while (Running)
{
	device.Clear(ClearFlags.Target, System.Drawing.Color.Blue, 1.0f, 0);
	device.Present();
	Application.DoEvents();
}



Notice that we have a variable, Running. This would be a boolean that you would set to true if your initialization was successful. Then when you want to end the loop, for example when the escape key is pressed, or the form is closing, then you set the Running variable to false. Notice that we also use Application.DoEvents to continue processing messages.


Section 4.3 – A Complete Sample Application


using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

public class MainForm : Form
{
	Device device = null;

	/// <summary>
	///	When the form is clicked, we close it and stop
	///	the application
	/// </summary>
	protected override void OnClick(EventArgs e)
	{
		this.Close();
		base.OnClick (e);
	}

	
	/// <summary>
	///	In the constructor we initialize the device using
	///	the default adapter and the current display mode
	/// </summary>
	public MainForm()
	{
		AdapterInformation adapter = Manager.Adapters.Default;
	
		PresentParameters presentation = new PresentParameters ();
		presentation.BackBufferFormat = adapter.CurrentDisplayMode.Format;
		presentation.BackBufferWidth = adapter.CurrentDisplayMode.Width;
		presentation.BackBufferHeight = adapter.CurrentDisplayMode.Height;		
		presentation.SwapEffect	= SwapEffect.Flip;
		presentation.Windowed = false;
		
		// Place it in a try catch block just 
		//in case it goes wrong for
		// some reason.
		try
		{
			// To make things simple, we'll just 
			//use the reference device, and
			// software vertex processing. 
			//Although this will be extrelemly slow
			// it doesn't matter in this case.
			// It will definetly work on 
			// all machines, so it saves us doing 
			//enumeration for now.
			device = new Device (adapter.Adapter, 
						DeviceType.Reference,
						this,
						CreateFlags.SoftwareVertexProcessing,
						presentation);
		}
		catch (DirectXException e)
		{
			MessageBox.Show (e.Message);
			return;
		}
	}


	/// <summary>
	///	Clear the screen with a blue color
	/// </summary>
	public void Render()
	{
		device.Clear(ClearFlags.Target, Color.Blue, 1.0f, 0);
		device.Present();
	}


	/// <summary>
	///	The entry point where the main render loop goes
	/// </summary>
	static void Main() 
	{
		using (MainForm form = new MainForm())
		{
			form.Show();
			
			while (form.Created)
			{
				form.Render();
				Application.DoEvents();
			}
		}
	}
}




As a simple exercise you could try changing the application so that it exits when the escape key is pressed instead of clicked. Also, try changing the color to red. And if you like you can try making the program run in windowed mode by commenting all the backbuffer parameters in the presentation parameters and just change the Windowed property to true.

posted on 2006-04-19 13:56 梦在天涯 阅读(2504) 评论(6)  编辑 收藏 引用 所属分类: DirectX

评论

# re: DirectX start ! 2006-04-19 16:23 小银子

继续深入...  回复  更多评论   

# re: Managed DirectX start ! 2006-06-14 18:12 梦在天涯

托管的 D3DX 库
托管的 D3DX 实用工具库具有用来处理网格、纹理、Shader、效果、文本和动画的类。但是,与非托管的 D3DX 库不同的是,它没有数学类,如矩阵、矢量和四元组类。在托管 DirectX 中,数学类是 DirectX 库的一部分。

用来处理网格的类包括 Mesh、SimplificationMesh、ProgressiveMesh、SkinMesh 和 PatchMesh。Mesh 和 ProgressiveMesh 从 BaseMesh 类继承。BaseMesh 管理内存(包括顶点和索引缓冲区)中的网格表示形式,并提供用来呈现网格的 DrawSubset 方法。其他方法包括用来复制网格的 Clone 方法,以及用来计算顶点法线并获取相邻项信息的方法。

Mesh 类使用可将网格保存到 .X 文件(标准的 DirectX 文件类型)或二进制流中并从中加载网格的方法来扩展 BaseMesh。还提供了用来生成表示以下图形的网格的方法:n 边形、圆柱、球形、圆环、给定字体的 3D 文本或者甚至是茶壶。用户可以网格化网格以获得更光滑的对象(通过将每个三角形解释为 N-patch),简化网格(通过减少三角形的数量)、优化网格(通过对三角形重新排序,以便利用硬件中的顶点缓存并提高性能)或者执行与网格的光交叉。

SimplificationMesh 对象是从 Mesh 构造的,它具有可减少表面(三角形)或顶点数量的方法,从而可产生比初始网格简单但是质量更低的网格形式。ProgressiveMesh 对象是从 Mesh 构造的。它允许实时简化网格,以便在网格对象和照相机之间的距离增大时,最小化所呈现三角形的数量。SkinMesh 类(由主干制作动画的网格)提供的方法可用来从 .X 文件数据对象加载外观网格。包含了用来公开基础网格和外观的信息(如每根骨头对结构的影响)的属性。

PatchMesh 类表示更高阶的表面,如 N-Patch、Triangle Patch 或 Rectangle Patch 表面。PatchMesh 包括用来执行如下操作的方法:从 Mesh 对象创建 N-Patch 网格、从 .X 文件加载 PatchMesh、网格化 PatchMesh 等。用来处理纹理的类主要包括:前面介绍的 TextureLoader 类、RenderToEnvironmentMap 类和 RenderToSurface 类。RenderToEnvironmentMap 和 RenderToSurface 类用来在运行时创建立方体环境贴图和纹理。如果在运行时呈现到纹理中,则允许您获得特殊效果,如让类似于镜面的物体反射布景中的其他物体,或者制造双窗格呈现效果。用来处理文本的类主要包括 Font 类,Font 类可以从 System.Drawing.Font 对象实例化,并且具有可以在支持 Direct3D 的设备上呈现文本的方法。用来处理动画的类由 KeyFrameInterpolator 类组成,KeyFrameInterpolator 类具有用来以内插值替换对象的缩放、旋转和/或平移的方法。内插值替换是通过使用一组 3D 矢量(用来缩放和平移)和四元组项(用于旋转)来执行的。

  回复  更多评论   

# re: Managed DirectX start ! 2006-06-14 18:13 梦在天涯

与几何形状相关的类
Direct3D 中的几何形状被定义为顶点定义的数组。不同的图形算法需要存储在每个顶点的不同信息,了解这一点很重要。这些顶点组分需要由应用程序进行声明,具体情况取决于程序员希望实现的效果。例如,如果程序员希望将纹理映射到 Direct3D 将呈现的几何形状,则程序员必须进行如下声明:在每个顶点,都将有 tu 和 tv 值(纹理坐标)。托管的 Direct3D 提供了两种顶点定义机制:VertexFormats 和 VertexDeclarations。使用这些机制,可以在每个顶点定义其他数据,如颜色、混合权重、纹理坐标或应用程序定义的任何用法。作为一种方便的方法,为经常使用的顶点格式定义提供了 CustomVertex helper 类。CustomVertex 类提供 11 种不同的顶点定义,这些定义经常用于 Direct3D 应用程序中。

在托管的 Direct3D 中,应用程序用来定义其顶点布局的顶点格式由 Device 类的读和写 VertexFormat 属性来访问。VertexFormat 属性访问 VertexFormats 枚举的元素,并且只需通过以下方法来实现:针对 IDirect3DDevice9 接口使用 GetFVF 和 SetFVF 方法,调用内部的非托管 Direct3D Device COM 对象。托管的 VertexFormats 枚举映射到在 d3dtypes.h 中定义的 VertexFormat 常量位,如下所示:

public struct Vertex
{
public Vector3 position;
public float tu, tv;
public static readonly VertexFormats Format =
VertexFormats.Position | VertexFormats.Texture1;
};
// From a typical managed Direct3D applications Render() method.
device.VertexFormat = Vertex.Format;

在使用可编程的管线时,需要自定义的顶点声明。与使用灵活的顶点格式定义的顶点缓冲区不同,自定义顶点声明可以通过使用多个流来定义,从而可节省带宽:

VertexElement[] declaration = new VertexElement[]
{
new VertexElement(
0, // short stream
0, // short offset
DeclarationType.Float2, // DeclarationType declType
DeclarationMethod.Default, // DeclarationMethod declMethod
DeclarationUsage.Position, // DeclarationUsage declUsage
0 // byte usageIndex
),
VertexElement.VertexDeclarationEnd
};
VertexDeclaration declaration = new VertexDeclaration(device,
declaration);

托管 Direct3D 中的几何形状通常是通过处理 VertexBuffer.Created 事件来创建的。图 5 显示了从 SDK 中的 Tutorial2 示例提取的简单三角形形状,并说明了预定义的自定义顶点 (CustomVertex.TransformedColored) 的用法。

  回复  更多评论   

# re: Managed DirectX start ! 2006-06-14 18:13 梦在天涯

图形状态类
如前所述,非托管代码的图形状态是通过 IDirect3DDevice9 接口的方法来配置的。例如,在非托管代码中,变换状态可通过 GetTransform 和 SetTransform 方法直接访问,这两种方法都使用 D3DTRANSFORMTYPE 类型的枚举值。

// From unmanaged IDirect3DDevice9 interface:
HRESULT GetTransform(D3DTRANSFORMSTATETYPE State, D3DMATRIX *pMatrix);
HRESULT SetTransform(D3DTRANSFORMSTATETYPE State,
CONST D3DMATRIX *pMatrix);

托管的 Direct3D 为非托管层提供一些抽象,这会使客户端代码变得更简洁。下面显示的 Device 类说明了一组只读属性:

public class Device : MarshalByRefObject, IDisposable
{
// Additional properties, methods and events.
public Transforms Transform { get; }
public RenderStates RenderState { get; }
public SamplerStates SamplerState { get; }
public TextureStates TextureState { get; }
}

这些属性返回 Transforms、RenderStates、SamplerStates 和 TextureStates 类的实例。这些实例随后用于配置托管 Direct3D 的图形状态。例如,Device 类不直接公开使用枚举值的 Transform 属性。Device 托管类提供可返回 Transforms 对象的属性。Transforms 对象随后公开一组可用来访问当前世界、视图和投影矩阵的属性,这些属性本身是 TransformType 托管枚举的元素。

RenderStates 类提供了大量的属性来配置为光栅化阶段选择的特定算法。下面的客户端代码显示了如何设置 Direct3D 托管管线的状态。

// cull, spec, dither states.
device.RenderState.CullMode = Cull.None;
device.RenderState.SpecularEnable = false;
device.RenderState.DitherEnable = false;
// Filter states.
device.SamplerState[0].MagFilter = TextureFilter.Linear;
device.SamplerState[0].MinFilter = TextureFilter.Linear;

请注意,使用索引器来访问取样器状态的多纹理层叠所在的阶段。

取样器状态指定用于特定阶段的筛选、平铺和纹理寻址模式,这些模式可通过 Sampler 类的属性来公开。筛选功能用于指定如何将图像映射到特定的 Direct3D 基元。根据图像(纹理)的大小以及它需要映射到的屏上基元的大小,可能需要放大或缩小图像。Direct3D 提供了三种筛选方法:MipFilter、MinFilter 和 MagFilter,每种方法都具有可由 TextureFilter 枚举配置的模式。平铺功能用于将一组单个的纹理汇集成单个纹理并减少切换纹理的需要。平铺功能是通过 Sampler 类的一组属性获得支持的。最后,纹理寻址模式通过 TextureAddress 枚举的元素为 [0,1] 边界外部的纹理坐标定义操作:

public class SamplerStates
{
private Sampler[] m_lpTex;
private bool pureDevice;
public Sampler get_SamplerState(int index);
internal SamplerStates(Device dev, bool pure);
public Sampler this[int index] { get; }
}

TextureStates 和 SamplerStates 类提供只读索引器,该索引器可返回特定阶段的 TextureState 实例。TextureState 类定义的属性可获得或指定特定纹理阶段状态的元素。诸如 ColorArgumentn 或 AlphaArgumentn(其中 n 可以是 0、1 或 2)属性可以是 TextureArgument 枚举的元素,而 ColorOperation 或 AlphaOperation 可以属于 TextureOperation 枚举,如下所示:

// color = tex mod diffuse.
device.TextureState[0].ColorArgument1 = TextureArgument.TextureColor;
device.TextureState[0].ColorOperation = TextureOperation.Modulate;
device.TextureState[0].ColorArgument2 = TextureArgument.Diffuse;
// alpha = select texture alpha.
device.TextureState[0].AlphaArgument1 = TextureArgument.TextureColor;
device.TextureState[0].AlphaOperation = TextureOperation.SelectArg1;
device.TextureState[0].AlphaArgument1 = TextureArgument.Diffuse;

  回复  更多评论   

# re: Managed DirectX start ! 2006-06-14 18:13 梦在天涯

设备类
托管的 Direct3D 提供了基于类的设备抽象,该抽象是通过使用非托管的 IDirect3DDevice9 接口实现的。编写 Direct3D 托管代码的程序员使用 Device 类的方法来访问其他 Direct3D 对象,以便设置各种图形状态并呈现几何形状。为了执行这些函数,Device 类提供了许多属性、方法和事件。图 2 列出了 Device 类最重要的方法,这些方法由 3D 应用程序调用。

谈到对象激活,一种观点认为,非托管的 Direct3D 之所以使用工厂方法,只是因为 COM 不支持新运算符。COM 的标准激活模式是基于工厂的构造。CoCreateInstance 函数模仿 COM 的新运算符,但它只是一个在内部使用 COM 工厂的包装。使用工厂(相对于新运算符)的另一个原理就是,在使用新运算符时,需要将对象的特定实现绑定到客户端的代码中;因此,如果大量使用新运算符,可能会导致版本控制问题。工厂方法通常用于将代码的客户端或用户与代码的创建者去耦。非托管的 COM IDirect3DDevice9 接口提供 14 种显式工厂方法,以便创建在使用 Direct3D 时需要的对象(如像素和顶点 Shader 以及顶点和索引缓冲区)。

目前,托管的 Direct3D 在很大程度上依赖于使用新运算符来构造类。尽管 Device 托管类只提供三种工厂方法,但是,一个值得注意的有趣现象是,非托管的 IDirect3DDevice9 工厂方法和由 Device 类公开的公共属性之间存在一对一关联。以下代码行说明了由 Device 托管类公开的三种工厂方法,每种方法都使用多个参数:

public Surface CreateRenderTarget(...);
public Surface CreateDepthStencilSurface(...);
public Surface CreateOffscreenPlainSurface(...);

在托管的 Direct3D 中大量使用方法重载。下面的代码示例说明了非托管的 Device.ColorFill 方法及其托管的重载包装:

typedef DWORD D3DCOLOR;

// Methods from the unmanaged IDirect3DDevice9 interface.
HRESULT ColorFill(IDirect3DSurface9 *pSurface, CONST RECT *pRect,
D3DCOLOR color);

// Methods from the managed Device class.
public void ColorFill(Surface surface, Rectangle rect, Color color);
public void ColorFill(Surface surface, Rectangle rect, int color);

与传统的图形系统一样,非托管的 Direct3D 需要将颜色值作为 32 位整数值在内部传递。另一方面,Microsoft .NET Framework 显式提供用来处理 ARGB 颜色值的 System.Drawing.Color 类,其中包含几种用来在不同表示形式之间进行转换的方法。为方便那些已习惯于使用整数值来表示颜色的程序员,托管的 Direct3D 在需要颜色参数时使用这两种表示形式来提供方法重载。

非托管的 IDirect3DDevice9 接口既公开只读属性,又公开读写属性,这两种属性都可以带参数,也可以不带参数。在最新版本的 .NET Framework 中,属性不带参数。因此,在将非托管的 Direct3D 映射到托管的 Direct3D 时,不使用参数的属性可以很好地映射到 C# 属性,而使用参数的 Direct3D 非托管属性只作为 Get 和 Set 方法公开。图 3 列出了 Device 类的一些最重要的属性。

最后,Direct3D Device 托管类提供了五个用来与设备进行交互的显式事件。基于 Direct3D 的应用程序可以通过以下方法来将事件作为 Device 的来源:启动应用程序、调整窗口的大小、从窗口模式更改到全屏模式或者退出应用程序。一个有趣的发现是由 Direct3D 使用的基于 .NET 委托的事件模式。对于 Device 类的每个事件声明,提供了添加、删除和引发方法,以及用来存储事件处理程序的私有实例变量。在 C# 中,客户端事件注册和注销的语法形式分别是通过 += 和 -= 运算符来提供的(请参阅图 4)。
  回复  更多评论   

# re: Managed DirectX start ! 2006-06-14 18:15 梦在天涯

纹理类
在托管的 Direct3D 中,与纹理相关的类从抽象类 Resource 继承,并且在极大程度上在与纹理相关的非托管接口上建立层。托管的 Direct3D 有三个纹理类:Texture、CubeTexture 和 VolumeTexture。Texture 类表示可映射到基元上的图像,用于呈现或用于凹凸贴图、法线贴图或其他效果。纹理的基本函数用于将一对纹理坐标映射到某个值(通常是颜色值),但是可以通过其他方式进行解释。纹理坐标通常是顶点数据的一部分。纹理坐标到颜色或其他值的映射是在呈现管线的像素处理阶段完成的。Direct3D 允许一次最多使用八个纹理。CubeTexture 类是用于立方体环境贴图的特殊种类的纹理,立方体环境贴图这种技术允许环境(如室内或外景)由反光物体反射出来,这是通过以下方法来实现的:使用纹理来为立方体周围的六个面各定义一个图像,并由所呈现的物体来反射。VolumeTexture 类表示存在于三维矩形空间中的颜色或其他值。同样,它将纹理坐标的三元组映射到颜色或其他值。这些类最重要的方面是,它们都支持一组用于锁定和取消锁定纹理的重载方法。

经常用于加载和保存纹理的类是 TextureLoader。TextureLoader 类包含用来从文件或流加载纹理、立方体纹理或体积纹理的方法。TextureLoader 类的其他用法包括填充和筛选纹理以及计算法线贴图。用来创建纹理的有用的 helper 方法由 GraphicsUtility 类提供:文件的 GraphicsUtility.CreateTexture 方法只是查找指定纹理的路径,然后调用 TextureLoader.FromFile 方法以加载它。以下代码说明了 GraphicsUtility 类的用法:

Texture texture = GraphicsUtility.CreateTexture(device,"banana.bmp");

  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


公告

EMail:itech001#126.com

导航

统计

  • 随笔 - 461
  • 文章 - 4
  • 评论 - 746
  • 引用 - 0

常用链接

随笔分类

随笔档案

收藏夹

Blogs

c#(csharp)

C++(cpp)

Enlish

Forums(bbs)

My self

Often go

Useful Webs

Xml/Uml/html

搜索

  •  

积分与排名

  • 积分 - 1784739
  • 排名 - 5

最新评论

阅读排行榜