鍙?/font>
Xv
杞村悜涓鴻瀵熸柟鍚?/font>PREF錛屽叾鍗曚綅鐭㈤噺u=PREF/|PREF|=(ux,uy,uz)
鍙?font face="Times New Roman">Yv杞村悜鐨勫崟浣嶇煝閲?/font>v=u脳n=錛?/font>vx錛?/font>vy錛?/font>vz錛?/font>
鍥犳涓栫晫鍧愭爣鍒拌瀵熷潗鏍囧埌鍙樻崲鐭╅樀涓猴細
聽姝e鉤琛屾姇褰憋紙涓夎鍥撅級
聽聽聽 鎶曞獎鏂瑰悜鍨傜洿浜庢姇褰卞鉤闈㈢殑鎶曞獎縐頒負姝e鉤琛屾姇褰?/a>錛屾垜浠氬父鎵璇寸殑涓夎鍥懼潎灞炰簬姝e鉤琛屾姇褰便備笁瑙嗗浘鐨勭敓鎴愬氨鏄妸
x
銆?/font>
y
銆?/font>
z
鍧愭爣緋葷殑褰綋鎶曞獎鍒?/font>
z
=0鐨勫鉤闈紝鍙樻崲鍒?/font>
u
銆?/font>
v
銆?/font>
w
鍧愭爣緋匯備竴鑸繕闇灝嗕笁涓鍥懼湪涓涓鉤闈笂鐢誨嚭錛岃繖鏃跺氨寰楀埌涓嬮潰鐨勫彉鎹㈠叕寮忥紝鍏朵腑(
a
錛?/font>
b
)涓?/font>
u
銆?/font>
v
鍧愭爣緋諱笅鐨勫鹼紝
tx
銆?/font>
ty
銆?/font>
tz
鍧囧鍥句腑鎵紺恒?br />聽聽 1錛変富瑙嗗浘
聽聽聽 鎶曞獎鏂瑰悜涓嶅瀭鐩翠簬鎶曞獎騫抽潰鐨勫鉤琛屾姇褰辮縐頒負鏂滃鉤琛屾姇褰?/font>錛岀幇鍦ㄨ鎴戜滑鏉ユ帹瀵兼枩騫寵鎶曞獎鐨勫彉鎹㈢煩闃點備笅鍥句腑鐨刏=0鐨勫潗鏍囧鉤闈負瑙傚療騫抽潰錛岀偣錛?/font>
x
錛?/font>
y
錛変負鐐癸紙
x
錛?/font>
y
錛?/font>
z
錛夊湪瑙傚療騫抽潰涓婄殑姝e鉤琛屾姇褰卞潗鏍囷紝鐐癸紙
x麓
錛?/font>
y麓
錛変負鏂滄姇褰卞潗鏍囥傦紙
x
錛?/font>
y
錛変笌錛?/font>
x麓
錛?/font>
y麓
錛夌殑璺濈涓?/font>
L
銆?
聽聽 4錛夌粫浠繪剰杞寸殑鏃嬭漿鍙樻崲
聽聽 璁炬棆杞醬
AB
鐢變換鎰忎竴鐐?/font>
A
錛?/font>
xa
錛?/font>
ya
錛?/font>
za
錛夊強鍏舵柟鍚戞暟(
a
錛?/font>
b
錛?/font>
c
)瀹氫箟錛?img height="29" src="http://necweb.neu.edu.cn/ncourse//tuxingxue/Chapter6/CG_Gif_6_288.gif" width="400" border="0" />
聽聽聽聽聽
聽聽 鍙互閫氳繃涓嬪垪姝ラ鏉ュ疄鐜?/font>
P
鐐圭殑鏃嬭漿錛?/font>
聽聽聽聽 A. 灝?/font>
A
鐐圭Щ鍒板潗鏍囧師鐐廣?/font>
聽聽聽聽 B. 浣?/p>
AB
鍒嗗埆緇?/font>
X
杞淬?/font>
Y
杞存棆杞傚綋瑙掑害涓?/font>
Z
杞撮噸鍚堛?/font>
聽聽聽聽
聽聽聽聽 D.浣滀笂榪板彉鎹㈢殑閫嗘搷浣滐紝浣?/font>
AB
鍥炲埌鍘熸潵浣嶇疆銆?/font>
鏄?/font>
AB
鍦?/font>
YOZ
騫抽潰涓?/font>
XOZ
騫抽潰鐨勬姇褰變笌
Z
杞寸殑澶硅銆?br />
]]>managed directx瀹炵幇璇誨啓buffer (Read and Write VertexBuffer and IndexBuffer Data)http://www.shnenglu.com/mzty/archive/2006/06/09/8343.html姊﹀湪澶╂動姊﹀湪澶╂動Fri, 09 Jun 2006 08:39:00 GMThttp://www.shnenglu.com/mzty/archive/2006/06/09/8343.htmlhttp://www.shnenglu.com/mzty/comments/8343.htmlhttp://www.shnenglu.com/mzty/archive/2006/06/09/8343.html#Feedback1http://www.shnenglu.com/mzty/comments/commentRss/8343.htmlhttp://www.shnenglu.com/mzty/services/trackbacks/8343.htmlRead and Write VertexBuffer and IndexBuffer Data With GraphicsStreams
using System; using Microsoft.DirectX; using Microsoft.DirectX.Direct3D; publicstruct PositionNormalTexVertex { public Vector3 Position; public Vector3 Normal; publicfloat Tu0, Tv0; publicstaticreadonly VertexFormats FVF = VertexFormats.Position | VertexFormats.Texture1; } publicclass Example { publicunsafevoid GraphicsStreamReadWrite() { //Create a vertex buffer in the managed pool VertexBuffer vb =new VertexBuffer(typeof(PositionNormalTexVertex), 100, device, Usage.None, PositionNormalTexVertex.FVF, Pool.Managed); //First, fill an array of PositionNormalTexVertex elements with data. PositionNormalTexVertex[] vertices =new PositionNormalTexVertex[50]; for(int i=0; i<50; i++) { //fill the vertices with some data vertices[i].Position =new Vector3(3f,4f,5f); } //The size of the verticies are 32-bytes each (float3 (12) + float3 (12) + float(4) + float(4)) //To lock 50 verticies, the size of the lock would be 1600 (32 * 50) GraphicsStream vbData = vb.Lock(0,1600, LockFlags.None); //copy the vertex data into the vertex buffer vbData.Write(vertices); //Unlock the VB vb.Unlock(); //This time, lock the entire VertexBuffer vbData = vb.Lock(0, 3200, LockFlags.None); //Cast the InternalDataPointer (a void pointer) to an array of verticies PositionNormalTexVertex* vbArray = (PositionNormalTexVertex*) vbData.InternalDataPointer; for(int i=0; i<100; i++) { //perform some operations on the data vbArray[i].Tu0 = i; vbArray[i].Tv0 = vbArray[i].Tu0 *2; Console.WriteLine(vbArray[i].Tv0.ToString()); } //Unlock the buffer vb.Unlock(); vb.Dispose(); } }
Read and Write VertexBuffer Data With Arrays
using System; using Microsoft.DirectX; using Microsoft.DirectX.Direct3D; publicstruct PositionNormalTexVertex { public Vector3 Position; public Vector3 Normal; publicfloat Tu0, Tv0; publicstaticreadonly VertexFormats FVF = VertexFormats.Position | VertexFormats.Texture1; } publicclass Example { publicvoid ArrayBasedReadWrite() { //Create a vertex buffer in the managed pool VertexBuffer vb =new VertexBuffer(typeof(PositionNormalTexVertex), 100, device, Usage.None, PositionNormalTex1Vertex.FVF, Pool.Managed); //Fill an array of the appropriate type with the VB data using Lock() PositionNormalTexVertex[] vbData = (PositionNormalTexVertex[]) vb.Lock(0, typeof(PositionNormalTexVertex), LockFlags.None, 50); for(int i=0; i<50; i++) { //set your vertices to something vbData[i].Position =new Vector3(2f,2f,2f); vbData[i].Normal =new Vector3(1f,0f,0f); vbData[i].Tu0 = i; vbData[i].Tv0 = i; } //Unlock the vb before you can use it elsewhere vb.Unlock(); //This lock overload simply locks the entire VB -- setting ReadOnly //can improve perf when reading a vertexbuffer vbData = (PositionNormalTexVertex[]) vb.Lock(0, LockFlags.ReadOnly); for(int i=0; i<100; i++) { //read some vertex data Console.WriteLine("Vertex "+ i +"Tu: "+ vbData[i].Tu0 +" , Tv: "+ vbData[i].Tv0); } //Unlock the buffer vb.Unlock(); vb.Dispose(); } }
]]>Managed directx -----the different of transformed vertex and untransformed vertexhttp://www.shnenglu.com/mzty/archive/2006/06/09/8341.html姊﹀湪澶╂動姊﹀湪澶╂動Fri, 09 Jun 2006 08:33:00 GMThttp://www.shnenglu.com/mzty/archive/2006/06/09/8341.htmlhttp://www.shnenglu.com/mzty/comments/8341.htmlhttp://www.shnenglu.com/mzty/archive/2006/06/09/8341.html#Feedback1http://www.shnenglu.com/mzty/comments/commentRss/8341.htmlhttp://www.shnenglu.com/mzty/services/trackbacks/8341.html
]]>Managed Directx ---world matrix , projection matrix , view matrixhttp://www.shnenglu.com/mzty/archive/2006/06/09/8338.html姊﹀湪澶╂動姊﹀湪澶╂動Fri, 09 Jun 2006 07:07:00 GMThttp://www.shnenglu.com/mzty/archive/2006/06/09/8338.htmlhttp://www.shnenglu.com/mzty/comments/8338.htmlhttp://www.shnenglu.com/mzty/archive/2006/06/09/8338.html#Feedback0http://www.shnenglu.com/mzty/comments/commentRss/8338.htmlhttp://www.shnenglu.com/mzty/services/trackbacks/8338.html
Set Up a View Matrix
(^_^,鍙兘鐪嬪埌projection浠ュ悗媯遍敟涓婁笅鎴潰闂寸殑閮ㄥ垎) The three input vectors represent the following, respectively:
The eye point: [0, 3, -5].聽聽聽聽 (鐪肩潧)
The camera look-at target: the origin [0, 0, 0]. (鐪肩潧瑕佺湅鐨勪笢涓?
The current world's up-direction: usually [0, 1, 0].聽聽 (鎸囧嚭閭h竟鏄笂闈?
This example demonstrates how to set up the projection transformation matrix, which transforms 3-D camera or view space coordinates into 2-D screen coordinates.
See the following C# code example, the Projection transformation matrix is set to be equal to the left-handed (LH) PerspectiveFovLH matrix. Input arguments to PerspectiveFovLH are as follows.
Field of view in radians: pi/4. (涓鑸兘涓?/4 pi)
Aspect ratio, or view-space height divided by width: 1, for a square window. (闀垮鐨勬瘮)
Near clipping plane distance: 1 unit.聽聽聽聽 (紱葷溂鐫涜繎鐨勭偣,鍗蟲1閿ョ殑涓婇潰)
Far clipping plane distance: 100 units.聽 (紱葷溂鐫涜繙鐨勭偣,鍗蟲1閿ョ殑涓嬮潰)
A world transformation changes coordinates from model space, where vertices are defined relative to a model's local origin, to world space, where vertices are defined relative to an origin common to all of the objects in a scene. In essence, the world transformation places a model into the world; hence its name.
Setting Up a World Matrix
As with any other transformation, you create the world transformation by concatenating a series of transformation matrices into a single matrix that contains the sum total of their effects. In the simplest case, when a model is at the world origin and its local coordinate axes are oriented the same as world space, the world matrix is the identity matrix. More commonly, the world matrix is a combination of a translation into world space and possibly one or more rotations to turn the model as needed.
The following C# code example, from a fictitious 3-D model class written in C#, creates a world matrix that includes three rotations to orient a model and a translation to relocate it relative to its position in world space.
Note:聽Direct3D uses the world and view matrices that you set to configure several internal data structures. Each time you set a new world or view matrix, the system recalculates the associated internal structures. Setting these matrices frequently鈥攆or example, thousands of times per frame鈥攊s computationally time-consuming. You can minimize the number of required calculations by concatenating your world and view matrices into a world-view matrix that you set as the world matrix, and then setting the view matrix to the identity. Keep cached copies of individual world and view matrices so that you can modify, concatenate, and reset the world matrix as needed. For clarity, Direct3D samples in this documentation rarely employ this optimization.
]]>Managed DirectX ---- Using Matriceshttp://www.shnenglu.com/mzty/archive/2006/05/17/7294.html姊﹀湪澶╂動姊﹀湪澶╂動Wed, 17 May 2006 02:16:00 GMThttp://www.shnenglu.com/mzty/archive/2006/05/17/7294.htmlhttp://www.shnenglu.com/mzty/comments/7294.htmlhttp://www.shnenglu.com/mzty/archive/2006/05/17/7294.html#Feedback0http://www.shnenglu.com/mzty/comments/commentRss/7294.htmlhttp://www.shnenglu.com/mzty/services/trackbacks/7294.html闃呰鍏ㄦ枃
]]>Managed DirectX --- Matrix&Transform(Translation and Scaling and Rotation)http://www.shnenglu.com/mzty/archive/2006/05/09/6823.html姊﹀湪澶╂動姊﹀湪澶╂動Tue, 09 May 2006 08:10:00 GMThttp://www.shnenglu.com/mzty/archive/2006/05/09/6823.htmlhttp://www.shnenglu.com/mzty/comments/6823.htmlhttp://www.shnenglu.com/mzty/archive/2006/05/09/6823.html#Feedback0http://www.shnenglu.com/mzty/comments/commentRss/6823.htmlhttp://www.shnenglu.com/mzty/services/trackbacks/6823.html
涓 Translation
The following transformation translates the point (x, y, z) to a new point (x', y', z').
You can manually create a translation matrix in managed code. The following C# code example shows the source code for a function that creates a matrix to translate vertices.
For convenience, managed the Microsoft Direct3D supplies the Translation method.
浜?Scaling
The following transformation scales the point (x, y, z) by arbitrary values in the x-, y-, and z-directions to a new point (x', y', z').
聽
涓壜犅?Rotation
聽
The transformations described here are for left-handed coordinate systems, and so might be different from transformation matrices that you have seen elsewhere. For more information, see 3-D Coordinate Systems.
The following transformation rotates the point (x, y, z) around the x-axis, producing a new point (x', y', z').
The following transformation rotates the point around the y-axis.
The following transformation rotates the point around the z-axis.
In these example matrices, the Greek letter theta (?) stands for the angle of rotation, in radians. Angles are measured clockwise when looking along the rotation axis toward the origin.
One advantage of using matrices is that you can combine the effects of two or more matrices by multiplying them. This means that, to rotate a model and then translate it to some location, you do not need to apply two matrices. Instead, you multiply the rotation and translation matrices to produce a composite matrix that contains all of their effects. This process, called matrix concatenation, can be written with the following formula.
In this formula, C is the composite matrix being created, and M1 through Mn are the individual transformations that matrix C contains. In most cases, only two or three matrices are concatenated, but there is no limit.
Use the Matrix.Multiply method to perform matrix multiplication.
The order in which the matrix multiplication is performed is crucial. The preceding formula reflects the left-to-right rule of matrix concatenation. That is, the visible effects of the matrices that you use to create a composite matrix occur in left-to-right order. A typical world transformation matrix is shown in the following example. Imagine that you are creating the world transformation matrix for a stereotypical flying saucer. You would probably want to spin the flying saucer around its center - the y-axis of model space - and translate it to some other location in your scene. To accomplish this effect, you first create a rotation matrix, and then multiply it by a translation matrix, as shown in the following formula.
In this formula, Ry is a matrix for rotation about the y-axis, and Tw is a translation to some position in world coordinates.
The order in which you multiply the matrices is important because, unlike multiplying two scalar values, matrix multiplication is not commutative. Multiplying the matrices in the opposite order has the visual effect of translating the flying saucer to its world space position, and then rotating it around the world origin.
No matter what type of matrix you are creating, remember the left-to-right rule to ensure that you achieve the expected effects.
鍒板簳浠涔堟椂鍊欏湪宸﹁竟浠涔堟椂鍊欏湪鍙寵竟?
鎬葷粨:
浜?3-D Transformations
In applications that work with 3-D graphics, geometrical transformations can be used to do the following.
Express the location of an object relative to another object.
Rotate and size objects.
Change viewing positions, directions, and perspectives.
You can transform any point (x,y,z) into another point (x', y', z') using a 4 x 4 matrix.
Perform the following operations on (x, y, z) and the matrix to produce the point (x', y', z').
The most common transformations are translation, rotation, and scaling. You can combine the matrices that produce these effects into a single matrix to calculate several transformations at once.
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鈥檚 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鈥檛 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鈥檛 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鈥檛 a requirement. The tutorial assumes you鈥檝e 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鈥檙e competent enough in it鈥檚 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鈥檛 be too hard to fix if you鈥檙e using a previous version. I suggest you get the update though if you don鈥檛 already have it. Note that I won鈥檛 be using the DirectX wizards or any template files in my project. I鈥檒l be showing you how to do everything yourself so that we can gain a deeper understanding of what鈥檚 actually going on and what we鈥檙e 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 鈥淲indows Application鈥?or 鈥淐onsole 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鈥檛 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 鈥淎dd Reference鈥? or right click on the 鈥淩eferences鈥?section in the project explorer, and click 鈥淎dd Reference鈥? Now you should be presented with the 鈥淎dd Reference鈥?dialog. Make sure the 鈥?Net鈥?tab is selected, and scroll down to 鈥淢icrosoft.DirectX鈥? You will see that there are several entries. At the very least, add 鈥淢icrosoft.DirectX鈥? The other assembly names reflect quite clearly what they contain. For example 鈥淢icrosoft.DirectX.Direct3D鈥?is the 3D graphics API, 鈥淢icrosoft.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.
Adding the DirectX references in Microsoft Visual Studio .Net 2003.
Section 2.3 鈥?The 鈥渦sing鈥?Directives
You may wish to add some 鈥渦sing鈥?directives for the namespaces in DirectX. You should know what the 鈥渦sing鈥?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鈥檚 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 鈥淣VIDIA 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鈥檛 know what that is). To actually enumerate an adapter, we use the Manager class located inside the Microsoft.DirectX.Direct3D namespace. You don鈥檛 create a new instance of this class as all of it鈥檚 methods are static. The Manager class contains a static property called 鈥淎dapters鈥?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鈥檚 an example program showing the main things we鈥檝e 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鈥檚 extremely simple, I think you鈥檒l agree. The output isn鈥檛 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鈥檚 available display modes to another combo box, selecting the current display mode by default. That鈥檚 how I usually do it, and that鈥檚 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:
I鈥檓 not quite sure what you could do with that handle, but I鈥檓 sure it鈥檚 useful somewhere, such as with some Win32 API functions that return information about the monitor or something like that. Note that the 鈥淎dapter鈥?property of the AdapterInformation? class is the 鈥渁dapter 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鈥檛 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鈥檚 nothing hard there, is there? The only thing that might confuse you is the DeviceType?. The 2 main types are 鈥淒eviceType.Hardware鈥?and 鈥淒eviceType.Reference鈥? There is also another device type called a Software Device, but this is very rarely used. I think it鈥檚 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鈥檚 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):
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 鈥渁lpha鈥?component. This is to do with something called alphablending, which is a type of transparency. You don鈥檛 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 鈥淴鈥? For example, there is a 32 bit mode called 鈥淴8R8G8B8鈥?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 鈥淎8R8G8B8鈥?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鈥檛 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 鈥渁rtifacts鈥?where the screen encounters a vertical resync (a refresh) while you鈥檙e 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鈥檛 see each individual part being drawn. This area of memory where you do your drawing is called a 鈥渂ackbuffer鈥?and the process of drawing the backbuffer onto the screen is called 鈥渇lipping鈥?or 鈥減resenting鈥? Hopefully the term 鈥渂ackbuffer format鈥?should be quite clear now that I鈥檝e 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鈥檛 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鈥檛 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鈥檝e 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鈥檚 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鈥檓 talking about a top of the range card 鈥?the best you can get. The problem with software is that it鈥檚 painfully slow compared to using hardware. So if you just want to test out some features in your game that your graphics card doesn鈥檛 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鈥檙e willing to slow down everything to a crawl to get it to work then you can use software emulation for that. You probably wouldn鈥檛 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鈥檛 care if it takes an extra hour to render (These things take hours anyway).
Section 3.5 鈥?Using Manager.CheckDeviceType
At this point I鈥檝e 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鈥檒l explain later. Lets just say for now that it鈥檚 incredibly important). So now you鈥檝e chosen all of these things. Unless you know what you鈥檙e doing or you got pretty lucky, they鈥檙e probably all incompatible with each other and wouldn鈥檛 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鈥檝e 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鈥檝e 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鈥檚 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鈥檒l 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鈥檚 important. This is the main object we use for all our rendering) is a vertex processing type. I assume you鈥檝e 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:
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 鈥減ure 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鈥檚 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鈥檚 compatible with everything else you鈥檝e 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鈥檛 worry about what these actually are, you will learn later. I鈥檓 just putting this in the enumeration section because that鈥檚 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鈥檚 compatible with the backbuffer format. If so, then we can use it (You鈥檒l see what it鈥檚 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鈥檝e 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鈥檒l 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鈥檝e mentioned so much in the enumeration section. What is a device? The device is the object we use in almost everything. It鈥檚 a representation of our actual adapter (well, in reality it鈥檚 driver). You will use the device to do all of the rendering (drawing, if I haven鈥檛 mentioned that yet) and lighting and so on. You can鈥檛 do very much without a device. So I think I鈥檝e stressed just how important it is. Now to actually create a device is a little bit trickier than anything we鈥檝e seen yet, but it鈥檚 still fairly trivial. Especially with all our enumeration done above.
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鈥檝e 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, 鈥渢his鈥? must be a windows forms control! So this won鈥檛 work in console mode obviously. You must write the above code in a windows forms application. Using 鈥渢his鈥?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鈥檛 get too excited yet though, we鈥檙e 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 鈥渢he 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鈥檒l learn 2 new methods of the Device object: Clear and Present. The 鈥淐lear鈥?method simply clears the entire drawing area with your chosen color. The 鈥淧resent鈥?method performs the 鈥渇lip鈥? drawing the backbuffer to the screen. That鈥檚 all we鈥檒l use right now. So here鈥檚 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.