游戏开发论坛

 找回密码
 立即注册
搜索
查看: 4694|回复: 6

深入Managed DirectX9(三)

[复制链接]

59

主题

984

帖子

1200

积分

金牌会员

Rank: 6Rank: 6

积分
1200
发表于 2005-1-23 16:59:00 | 显示全部楼层 |阅读模式
转载请注明作者

calyman    clayman_joe@yahoo.com.cn

拖放窗口时自动重置Device

        任何曾经使用C++或VB开发DirectX3D的人都知道,在改变窗口大小时,需要重新设置devicd,否则,DirectX3D会按原来的分辨率继续渲染场景,并且把结果拷贝到(通过拉伸)新的窗口。当通过Windows Form控件创建device时,聪明的Mamaged DirectX能发现你改变了窗口的大小,并且重置device。毫无疑问,程序总是能在正常的行为下运行,同时,你也能方便的自己重置device。在自动重置device之前,会引发一个叫做DeviceResizing的事件。捕获这个事件,把EventArgs类的Cancel成员设置为true,就能回到默认的行为,在创建device之后加上如下代码
        private void CancelResize(object sender, CancelEventArgs e)
        {
                e.Cancel = true;
}

如你所见,这个方法只是简单的say yes,我们确实想要取消这个操作。现在订阅事件处理程序,让device知道不进行这种操作 :
device.DeviceResizing += new CancelEventHandler(this.CancelResize);(注:CancelEventHandle委托在System.ComponentModel名称空间)
运行程序,最大化窗口。三角的位置还和原来一样,不过这次看起来可怕极了。边缘都是锯齿,看起来糟糕透了。可以删除我们刚添加的代码了。Managed DirectX默认操作已经帮我们完成了这个任务,可以直接利用它。

我说:“要有光”,于是场景就有了光
我们绘制了三角形并且让他转起来了,怎样才能让他更好呢?当然是灯光。在前面曾简要的提到过它,事实上,那个时候我们完全关闭了灯光。首先要做的就是先回到那个黑暗的场景:
deviceRenderState.Lighting = true;

其实你甚至可以把整行都删了,device的默认行为是打开灯光的;只是为了让代码更清楚才保留它。现在获得了一个黑色的旋转三角。或许我们应该先定义一盏灯,再来打开它。你可能已经注意到有一个灯光数组连接到了device类上,并且这个数组的每一个元素都保存了有关灯光的大量属性。我们希望定义场景里的第一盏灯并且打开它,So,在OnPaint方法定义了三角形之后的地方(注:与sdk中有区别,不过都是一样的效果^_^)添加如下代码:
device.Lights[0].Type = LightType.Point;
device.Lights[0].Positon = new Vector3();
                        device.Lights[0].Diffuse = System.Drawing.Color.White;
                        device.Lights[0].Attenuation = 0.2f;
                        device.Lights[0].Range = 1000.0f;
                        device.Lights[0].Commit();
                        device.Lights[0].Enabled = true;
        这些代码什么意思呢?首先申明了要创建的灯光类型,我们选择了一个在所有方向上辐射强度都一样的point light,创造了一个灯泡般的世界。当然,也有灯光沿着指定方向传播的direction light。direction light只会产生方向和颜色上的效果,忽略其他的灯光要素(比如光线的削弱(attenuation)和范围(range)),因此它也是计算量最小的灯光。最后一种能用的就是spot light了,类似于剧场里用来照亮舞台上人物的灯光。有许多的要素来描述spot light(位置,方向,角度,等等),所以它是系统里所需计算量最大的灯光。
        在对灯光类型简单的讨论之后,我们继续。接下来设置灯光的位置。因为三角形的中心在(0,0,0),所以我们把灯光也放到那个位置。Vector3无参数的构造函数完成了这个任务。把灯光的漫射颜色设置为白色,这样可以正常的照亮表面。接下来设置控制灯光强度在空间改变的削弱属性。范围是灯光能产生效果的最远距离。例子里的范围已经远远超过了我们所需要的。请查阅sdk寻找有关灯光的更多内容。
        最后我们把灯光提交给了device,并使它可用。如果你浏览灯光的属性,会注意到一个叫做“Deferred”的布尔值。默认情况下,这个值是false,所以你需要在准备使用灯光之前调用Commit函数。把这个值设为true,可以取消对Commit的调用,但会带来一定的性能损失。在观看灯光的效果前一定要确定它是enable和committed的。
        回到程序,你发现即使我们为场景定义了灯光,三角也还是黑色的!打开了灯,却看不到光,Direct3D一定没有照亮我们的三角形,事实上,它确实没有。只有在几何体的每一个面都有一条法线(normal)时,才会进行灯光的计算。知道了这点,我们来为三角添加法线吧,这样就能在场景里看到它了。最简单的方法就是把顶点格式改为一种包含了法线的格式。碰巧我们也有这样一个结构了,改变创建三角形的代码:
        CustomVertex.PositionNormalColored[] verts = new CustomVertex.PositionNormalColored[3];
        verts[0].SetPositon(new Vector3(0.0f,1.0f,1.0f));
        verts[0].SetNormal(new Vector3(0.0f,0.0f,-1.0f));
        verts[0].Color = Ststem.Drawing.Color.White.ToArgb();
        verts[1]``````
        `````````
        更新顶点格式来适应新的数据:
        device.VertexFormat = CustomVertex.PositionNormalColored.Format;

        这次最大的改变就是使用了一组包含法线的数据,并且把三角形的颜色改为白色。可以看到,我们把垂直于顶点指向外的方向定义为法矢量。因为点只是在Z平面内移动,所以沿着Z轴的负方向即是法线矢量的方向。现在程序就一切正常了。可以试着改变一下灯光的漫射颜色,看看会有怎样的变化。
        还有一件应该记住的事:灯光是按照每一个顶点来计算,所以在low polygon模型(就像我们简单的三角形)的情况下,灯光可能会不太真实。我们会在后边的章节里讨论一些高级灯光技术,比如per pixel linghting。这些灯光能创造一个真实的世界。

        Device State and Transforms
        至今为止,示例代码里还有两项没有讨论过:设备状态(device state)以及变换(transform)。对一个设备来说,有三种不同方式的设备状态:the render state,The sampler states,和 the texture state。我们仅仅使用过the render state中的几种类型;后边的两种类型是用来处理纹理的。不要担心我们很快就会谈到纹理。The render state类规定了DirectX3D怎样来对场景进行光栅化。可以使用这个类来改变很多属性,包括我们已经使用过的灯光以及剔除。其他render state可用的选项有填充模式(fill mode) (比如wire frame mode)和各种雾化参数。我们也会来接下来的几章深入讨论。
                前面提到过,变换就是用来把几何体位置从一个坐标系转到另一个坐标系的一系列矩阵。用于device上的三个主要变换就是world,view以及projection变换,但是也有一些其他的变换。比如用来控制texture stages的变换,就依赖于一个255的世界矩阵(There are transforms thst are used to modify texture stages,as well as up to 255 world matrices??).
               

                Swapchains and RenderTargets
                Device到底作了些什么工作来绘制这些三角形呢?device有一些固定的方法来处理在哪绘制并且如何绘制对象。每一个device都有一个交换链(swap chain)以及一个渲染目标(render target)。
                一条交换链实际上就是一系列被控制着用来渲染的缓冲区。所有绘图过程都是在交换链中的后备缓冲区发生。当使用SwapEffect.Flip来创建一条交换链时,后备缓冲区翻转(flipped)为真正被图形卡用于读取数据的前缓冲(front buffer)。同时,三号缓冲区变为新的后备缓冲,而先前的前缓冲变为未使用过的三号缓冲区。
                真正的翻转操作是通过改变图形卡当前所读的数据区、刚读过的数据区以及后备缓冲区之间的地址来实现。只有在全屏模式下,才会发生真正的翻转操作。而在窗口模式,翻转实际上只是数据的拷贝而已,因为device并没有控制着整个显示器,仅仅是一小部分而已。虽然两种模式下结果都一样。全屏模式下,有一些驱动程序也会使用翻转操作来实现SwapEffect.Discard 或者 SwapEffect.Copy。
                如果使用SwapEffect.Copy或SwapEffect.Flip来创建交换链,可以确保presen()之后不会影响后备缓冲中的内容。运行时会在需要时强制创建额外的隐藏缓冲。建议使用SwapEffect.Discard来避免这种潜在的损失。这种模式允许驱动程序选择最高效的方法分配后备缓冲。使用SwapEffect.Discard时,不值得(???)在绘制新的图形前检查你是否清除了整个后备缓冲。调试模式下的运行时将会使用随机的数据来填充(刚刚使用过的)后备缓冲,让开发者检查是否忘了调用clear()。(it is worth nothing that when usuing SwapEffect.Discardyou will want to ensure that you clear the entire back buffer before starting new drawing operations. the runtime will fill the the back buffer with random data in the debug runtime so developers can see if they forget to call clear)(注:这一段内容看的不是太明白,所以把原文也给出来。Sdk中对SwapEffect枚举的解释也不是太清除。参考sdk:交换效果明确定义了调用present()之后,后备缓冲的状态。Flip交换链是一个循环的队列,可以有0~(n-1)块后备缓冲, discard交换链是一个队列, copy交换链只有一块后备缓冲。Flip中的后备缓冲在present()之后内容不会改变,所以系统需要额外内存作为后备缓冲,带来性能损失。既然后备缓冲中的内容不改变,如何构成循环队列来使用?? Discard后备缓冲中队列的长度以及怎样变化也没有明确说明,只有“The swap chain is essentially a queue where 0 always indexes the back buffer that will be displayed by the next Present operation and from which buffers are discarded once they have been displayed. An application that uses this swap effect should update an entire back buffer before invoking a Present operation that displays it.The debug version of the runtime overwrites the contents of discarded back buffers with random data, to enable developers to verify that their applications are updating the entire back buffer surface correctly.” 随机数据能帮助检查是否更新了整个后备缓冲区??既然会丢弃数据还需要调用clear??)
                交换链的后备缓冲区也同样能作为渲染目标。毫无疑问,当创建了device,创建了交换链之后,渲染目标就被设置为链的后备缓冲。一个渲染目标就是能保存所执行的绘制任务的输出的表面(a surface that will hold the output of the drawing operations that you perform)。如果你创建了多个交换链的话,就必须确定预先更新了device的渲染目标。后边我们会稍后讨论这点。


~~~~~~~~~~~~~~~~~第三部分完~~~~~~~~~~~~~~~~~~~~~


第一章的内容到这里就结束了,第二章我们将会学习如何选择正确的device,coming soon。
关于SwapEffect的内容欢迎高手来一起讨论^_^

1

主题

12

帖子

12

积分

新手上路

Rank: 1

积分
12
发表于 2005-1-23 19:02:00 | 显示全部楼层

Re:深入Managed DirectX9(三)

ding

1367

主题

1993

帖子

2118

积分

金牌会员

Rank: 6Rank: 6

积分
2118
发表于 2005-1-23 19:13:00 | 显示全部楼层

Re:深入Managed DirectX9(三)

ok good

2

主题

11

帖子

13

积分

新手上路

Rank: 1

积分
13
发表于 2005-1-24 11:46:00 | 显示全部楼层

Re: 深入Managed DirectX9(三)

兄弟,可不可以把Manged Directx 9 kick start的原代码发上来,多谢多谢!

2

主题

59

帖子

59

积分

注册会员

Rank: 2

积分
59
发表于 2005-2-15 22:09:00 | 显示全部楼层

Re:深入Managed DirectX9(三)

粗略地翻译了一下Swap Chain的部分:

Swap chains and Render Targets

那么Device为了绘制这些三角形,究竟要做哪些事情呢?嗯,事实上有一些信息告诉Device在哪里以及如何进行绘制工作。每一个Device都有一个隐含的交换链(Swap Chain)以及一个渲染对象(Render Target)。

交换链实际上是用来控制渲染的一系列缓冲区。其中一个被称为后台缓冲区(Back Buffer),所有在这条交换链上进行的绘制都发生在它身上。当一个设置为SwapEffect.Flip的交换链被呈现时(presented),后台缓冲区中的数据被“翻转(Flip)”到前台缓冲区(Front Buffer)中。前台缓冲区是显卡真正读取数据的地方。与此同时,三号缓冲区 (Third Buffer) 成为了新的后台缓冲区而原先的前台缓冲区成为了未被使用的三号缓冲区。详细情况请见附图

真正的“翻转”操作是通过将显卡读取数据的位置由旧的缓冲区设置为当前的后台缓冲区而实现的。在DirectX 9中,使用这个术语来描述后台缓冲区被显示出来的情况。在窗口模式(Windowed Mode)下,这些翻转操作实际上只是进行了数据拷贝,因为我们的Device并不能控制整个屏幕的显示,而只负责其中的一小部分。当然两种方式最终的呈现结果都是一样的。在全屏模式(Full Screen Mode)下,使用SwapEffect.Flip属性,就能产生真正的翻转。在全屏模式下,一些驱动程序(Driver)也使用翻转来实现SwapEffect.Discard和SwapEffect.Copy。

如果你使用SwapEffect.Copy或者SwapEffect.Flip来创建交换链,那么呈现(Present)操作将不会对交换链中的后台缓冲区产生任何影响。运行时(Runtime)为了保证这一点,会在需要的时候创建一个隐藏缓冲区。建议你使用SwapEffect.Discard来避免这种可能的性能损耗。在该模式下,驱动程序(Driver)能自由使用最有效率的方式来呈现后台缓冲区。值得一提的是,当使用SwapEffect.Discard时,你必须确保在开始新的绘制前清空(Clear)整个后台缓冲区。在调试执行(Debug Runtime)时,运行时会向后台缓冲区填充随机内容,这样如果程序员忘记调用清空(Clear)操作,就可以及时发现。

交换链的后台缓冲区也被用作渲染对象(Render Target)。当你创建Device时,也隐式创建了一条交换链,并且渲染对象被设置为该条交换链的后台缓冲区。渲染对象其实就是一个表面(Surface),接收所有绘制动作的输出。如果你创建了多条交换链来处理不同的渲染操作,你应该在绘制前确认已经更新了你的Device的渲染对象。在之后的章节我们会进一步讨论这个问题。

2

主题

59

帖子

59

积分

注册会员

Rank: 2

积分
59
发表于 2005-2-15 22:16:00 | 显示全部楼层

Re: 深入Managed DirectX9(三)

附图
sf_2005215221622.jpg

0

主题

26

帖子

32

积分

注册会员

Rank: 2

积分
32
发表于 2005-9-25 21:36:00 | 显示全部楼层

Re:深入Managed DirectX9(三)

谢谢
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-12-27 22:01

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表