深入Managed DirectX
一、Device类详解(example1)
二、三维化三角形(example2)
三、拖放窗口时自动重置Device(example3)
四、选择正确的Device(example4)
五、渲染技术(example5)
六、1.高级渲染技术(example6_1)
2. Mesh详解(example6_2)
七、使用Managed DirectX编写游戏(example7)
八、理解资源(Understanding Resoruces)(example8)
九、使用其它Mesh类型(example9)
十、使用助手类(Using the Helper Class)(example10)
十一、可编程渲染管道以及高级着色语言入门(example11)
十二、使用HLSL的高级功能(example12)
十三、渲染骨骼动画(Rendering Skeletal Animation)(example13)
Device类详解
Device类是DirectX里的所有绘图操作所必须的。可以把这个类假想为真实的图形卡。场景里所有其他图形对象都依赖于device。你的计算机里可以有一个到几个device,在Managed DirctX3D里,你可以控制任意多个device。
Device共有三个构造函数,现在我们只讨论其中的一个,但我们会在后边的内容里讨论其他的。先来看看具有如下函数签名的构造函数
public Device(into adapter,DeviceType deviceType,Control renderWindow,CreateFlags behaviorFlags, PrentParameters[] prentationParameters);
(构造函数的第二种重载类似于上边这个,但它接受来自非托管(或者非windows form)的窗口句柄作为renderWindow。而只接受一个IntPtr参数的重载是非托管com组建指向Idirect3Ddevice9的接口。当你的代码需要和非托管的程序协作时则应用它)
好了,这些参数是什么意思,以及我们怎样来使用呢?呵呵,参数adapter表示我们将要使用哪个物理图形卡。计算机里的所有图形卡都有一个唯一的适配器标识符(通常是0到你的图形卡数量-1),默认的显卡总是表示为0 的图形卡。
下一个参数,DeviceType,告诉了DirectX3D你要创建那种类型的device。这里最常用的值就是DeviceType.Hardware,表示你将创建一个硬件设备。另一个选项是DeviceType.Reference,这种设备允许你使用―参考光栅器‖(reference rasterizer),所有的效果由DirectX3D运行时来实现,以很慢、很慢、很慢的速度运行^_^。应该仅在调试时或测试你的显卡所不支持的特性时使用这个选项。
(注意参考光栅器只包含在DirectX SDK里,so DirectX运行时是不能使用这个特性的。最后一个为DeviceType.Software的值允许使用用户自定义的软件光栅器(custom software rasterizer)在不确定是否有这样一个光栅器存在时,忽略这个选项吧^_^。)
rendrWindow表示把设备绑定到的窗口。因为windows form控件类都包含了一个窗口句柄(windows handle),所以很容易把一个确定的类作为渲染窗口。可以使用form、panel 或其他任意的控件作为
这个参数的值。但现在,我们只用form。
下一个参数用来描述设备创建之后的行为。大部分CreateFlags枚举的成员都能组合起来使用,使设备具有多种行为。但有一些flag是相互排斥的,我们稍后讨论。我们现在只使用SoftwareV ertexProcessing标志。这个标志适合于所有定点处理都用CPU计算的情况。应此,这自然比所有点都用GPU处理要慢,因为我们不确定你的显卡是否支持所有特性。So,安全第一,假设你的CPU能完成现在的任务。
最后一个参数,它表示你的设备把数据呈现到显示器的方式。Prentation Parameter类的外观都可以由这个类来控制。我们过后再来深入讨论它的构造函数,现在,我们只关心―Windowed‖成员和―SwapEffect‖成员。
Windowed成员是一个布尔类型的值,决定设备是全屏还是窗口模式。
SwapEffect成员用于控制缓存交换的行为。如果选择了SwapEffect.Flip,运行时会创建
额外的后备缓冲(back buffer),并且在显示时拷贝front buffer。SwapEffect.Copy与Flip相似,但要求你把后备缓冲设为1。我们将要选择的SwaoEffect.Discard,如果缓冲没有准备好被显示,则会丢弃缓冲中的内容(which simply discards the contents of the buffer if it isn’t ready to be prented)。
学了这么多,现在来创建一个设备吧。回到代码上来,首先为我们的程序将创建一个device 对象:
(代码略,参见DirectX sdk Tutorial 1: Create a Device)
现在让我们来重写Paint()函数:
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
device.Clear(ClearFlags.Target, System.Drawing.Color.Blue, 1.0f, 0);
device.Prent();
}
我们使用Clear()方法把窗口填充为实心的颜色。它的第一个参数指定了我们要填充的对象;在例子里,我们填充的即是目标窗口。稍后再来讨论ClearFlags枚举的其它成员。第二个参数是我们所要填充的颜色。其他的两个参数先暂时忽略。在device被填充之后,我们必须更新显示:Prent方法会为我们完成这个任务。这个方法也有几个重载的类型;上边使用的方法会显示device的整个区域。同样稍后再讨论。
看到有些枯燥了吗,好吧,现在我们来真正绘制一些图形
三维图形世界里最基本的图形就是三角形。使用足够的三角,我们可以呈现出任何东西,甚是是平滑的曲面。没有什么比画一个简单的三角和更好的了。为了使过程尽可能的简单,我们先避开―world space‖以及各种变换(当然,我们马上就会提到他们),使用屏幕坐标来绘制一个简单的三角。再绘制我们迷人的三角前,我们必须做2件事。1,我们需要一些数据结构来保存三角的信息。2,我们告诉device来绘制它。
很幸运,DirectX已经有这样的一个数据结构来保存三角了。Direct3D名称空间里叫做CustomV ertex的类可以用来储存大多数Direct3D中用到的―顶点格式‖数据结构(vertex format)。
一个顶点格式结构把数据保存为一种DirectX3D认识并且可以使用的格式。我们将讨论很多这种结构,但先让我们来看看即将用来创建三角的TransformedColored结构。这个结构告诉DirectX3D运行时我们的三角不需要进行左边变换(比如旋转或移动),因为我们已经指定了使用屏幕坐标系。它也包含了每一个点(顶点)的颜色的信息。回到重写的OnPaint 方法添加如下代码:
CustomV ertex.TransformedColored[] verts = new CustomV ertex.TransformedColored[3];
V erts[0].SetPosition(new V ector4(this.Width/2.0f,50.0f,0.5f,1.0f);
V erts[0].Color = System.Drawing.Color.Aqua.ToArgb();
V erts[1]`````````
V erts[2]`````````
(参见DirectX sdk Tutorial 2: Rendering V ertices)
数组里的每一个元素表示三角的一个顶点,所以我们创建了3个元素。然后我们使用新创建的V ector4结构为每一个成员调用SetPositin方法。变换过的顶点坐标包含了在屏幕上x 和y的坐标(相对于屏幕的(0,0)点而言),当然也包括z坐标和rhw成员(reciprocal of homogenous w三维齐次坐标)。先忽略后边两个参数.V ector4结构(注:V ector4其实就是(x,y,z,w)经过变换后成为(x/w,y/w,z/w))是保存这种信息最方便的方式。然后我们设置了点的颜色。注意,我们使用了标准颜色的ToArgb方法。DirectX3D假设所接受的颜色为32为的into。
既然我们已经有了数据就可以告诉DirectX我们需要绘制这个三角形,并且绘制它。在重写的OnPaint里添加如下代码
device.BeginScene();
device.V ertexFormat = CustomV ertex.TransformedColored.Format;
device. DrawUrPrimitives (PrimitiveType.TriangleList,1,verts); 注意和sdk中的示例有区别
device.EndScene();
好了,这几行代码是什么意思呢?其实很简单。BefinScene方法告诉DirectX3D我们即将绘制一些东西,为绘制做好准备。现在我们已经告诉了DirectX3D要绘制一些东西,接下来就必须告诉它画什么。这就是V ertexFormat属性的作用。它决定了DirectX3D运行时使用哪种―确定的功能管道‖(fixed function pipline)格式。在我们的例子里使用变换过的,着色过的顶点管道。
不用担心你现在不明白确定的功能管道是什么意思,我们会很快来讨论它。
DrawUrPrimitives函数是真正发生绘图的地方。So,他的参数是什么意思呢?第一个参数是我们要绘制的初等几何体的类型。有很多种可用的类型,but now,我们只是画一系列的三角形。所以选择了PrimitiveType.TriangleList类型。第二个参数是我们要绘制的三角形的数量。对于一个三角形的集合来说,这个值应该是你的顶点数量除以3。我们只画一个三角,所以设为1。最后一个参数则是DirectX3D用来绘图的数据。最后一个EndSence方法通知DirectX3D我们不再绘图了。你必须再每次调用BeginSence之后都调用这个方法。
如果现在编译运行程序的话,你会发现但移动或重置窗口大小之后,并不会更新显示。原因是当我们
需要重绘整个窗口时,Windows并不会每一次都计算窗口的收缩情况。因此,你只是移除了显示过的数据,当并没有删除已经显示的内容。很幸运,有个简单的方法解决这个问题,我们可以告诉Windows窗口总是需要被整个的重绘。在OnPaint的最后加上一下代码:
this.Invalidate();
呵呵,现在再来试试看,哦,看起来我们破坏了程序!现在只能显示一片空白了,并且我们的三角看起来还在不停的闪烁,尤其是当你调整窗口大小的时候。我们都些什么呢?原来―聪明‖的Windows总是尝试在Invalidate()方法后来绘制当前的窗口(即空白的这个窗口)。在我们的OnPaint方法之外还存在其他的绘制过程!能容易的通过改变窗口的―style‖属性来解决。在构造函数里加上如下代码
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ConstolStyles.Opaque, true);
哦~,好了,终于erying works as expected。我们所做的就是告诉Windows一切绘图过程都在OnPaint里完成。
三维化三角形
再来看看我们的程序,看起来并不是那么―三维‖。而且我们所做的都能用GDI+轻易完成。So,我们
应该怎样在3维空间里绘图,并且给人留下深刻的印象呢?实际上,简单的修改就能达到这样的效果。
如果你还记得,先前在我们创建第一个三角形的时候,我们使用了一个叫做―经过变换的‖(transformed)坐标系统。这种坐标是显示器的屏幕区所使用的坐标,也是最容易定义的。如果我们使用未变换过的坐标系统会怎样呢?实际上,未变换过的坐标系统被广泛的用于现代游戏场景。
与屏幕坐标(screem space)相比我们定义这些坐标时,还应在世界坐标(world space)里定义每一个顶点。你可以把世界坐标设想为一个无限大的三维笛卡儿坐标。你可以把你的对象放到这个―世界‖的任意位置。现在来修改我们的程序,绘制一个未经过世界坐标变换的三角形。
首先使用未变换顶点格式类型中的一种来改变三角形的数据。在这里我们只关心顶点的位置,以及颜色,因此使用CustomV ertex.PositionColored。
CustomV ertex.positionColored[] verts = new CustomV ertex. positionColored[3];
V erts[0].SetPosition(new V ector3(0.0f,1.0f,1.0f));
V erts[0].Color = System.Drawing.Color.Aqua.ToArgb();
V erts[1]`````````
V erts[2]`````````
(参见DirectX sdk Tutorial 3: Using Matrices)
同样改变V ertexFormat属性:
device.V ertexFormat = CustomV ertex.PositionColored.Format;
好了,现在运行程序:什么也没有发生,仅获得一个填充过的窗口。在讨论为什么之前,