Managed DirectX 第四章

2024-02-17 10:58
文章标签 第四章 directx managed

本文主要是介绍Managed DirectX 第四章,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 

更多渲染技术

  翻译:clayman  

   在讨论过了基础渲染方法之后,我们应该把注意力放到一些能提高性能,并且让场景看起来更好的渲染技术上来:

 

 

 

渲染各种图元类型

     至今为止,我们只渲染过一种类型的图元,称为三角形集合。实际上,我们可以绘制很多种不同类型的图元,下边的列表描述了这些图原类型:

     PointList――这是一个自我描述的图元类型,它把数据作为一系列离散的点来绘制。不能使用这种类型绘制indexed primitives

     LineList——把每一对点作为单独的直线来绘制。使用时至少需要有两个顶点。

     LineStrip——把顶点绘制为一条折线。至少需要两个顶点。

     TrangleList——这就是我们一直在使用的类型。每三个顶点被绘制为一个单独的三角形。通过当前的剔除模式来决定如何进行背面剔除。

     TrangleStrip——三角形带是一系列相连的三角形,每两个相邻的三角形共享两个顶点。剔除模式会自动翻转所有偶数个三角形(flipped on all even-numbered triangles),因为相邻的三角形共享两个顶点,他们会被翻到反方向。这也是复杂的3D对象使用的最多的图元类型。

     TrangleFan——与三角形带相似,不过所有的三角形都共享一个顶点。

     可以使用同样的数据来绘制任意类型,任意数量的图元。Direct3D会根据给定的图元类型来绘图。写一点来嘛来绘制一下这几种图元吧。    

     修改我们创建顶点缓冲时的代码。因为不需要移动顶点,可以把SetupCamera里的world transform删除了,同样所有引用到angle成员的代码也可以删除了。添加一下代码:

     private const int NumberItems 12

     12虽然是随便挑选的数字,但也有一定的原因。太多的顶点会让屏幕太拥挤,同时,顶点的数量要同时能被23整除。这样无论那种图元都能都被正确的渲染。接下来修改创建顶点缓冲的代码:

     vb=new VertexBuffer(typeof(CustomVertex.PositionColored), NumberItems, device, Usage.Dynamic | Usage.WriteOnly, CustomVertex.PositionColored.Format,Pool.Default);

     CustomVertex.PositionColored[] verts = new CustomVertex.PositionColored[NumberItems];

for(int i=0;i<NumberItems;i++)

     {

         float xPos = (float)(Rnd.NextDouble()* 5.0f) - (float)(Rnd.NextDouble()* 5.0f);

         ······(详见源码)

         verts[i].SetPosition(new Vector3(xPos,yPos,zPos));

         verts[i].Color = RandomColor.ToArgb();

     }

     这里没有什么特别的地方,我们修改了顶点缓冲大小来保存足够多的顶点。接下来,修改了创建顶点的方法,用一种随机的方式来填充顶点。你可以在源码中找到关于RndRandomColor的声明。

     现在需要修改绘图方法了。不停的滚动显示几种类型的图原,可以简单的展示出他们之间的联系。我们每两秒钟显示一种类型。可以根据开机时到现在为止的相对时间(in ticks)来计时。添加一下两个成员变量的声明:

     private bool needRecreate = false;

    private static readonly int ImitialTickCount  = System.Environment.TickCount;

 

 

 

     第一个布尔变量控制着在每个“周期”开始的时候重新创建顶点缓冲。这样,就不必每次都显示同样的顶点。用一下代码代替简单的DrawPrimitives方法:

     (见源码中带有switch的部分)

     这基本上是一段可以自我解释的代码。根据一个周期中的不同时刻,调用DrawPrimitives来绘制相应的图原。注意,由于图原类型的不同,相同数量的顶点能绘制的图原数也是不同的。运行程序,将按照PointListLinelistLineStripTragleList,TangleStrip的顺序显示图原。如果你觉得显示PointList时“点”太小看不清楚,可以通过调整render state把它稍稍放大一点:

     device.RenderStare.PointSize = 3.0f;

 

 

使用索引缓冲(Index Buffer

     还记得我们创建盒子时的带码吗,我们一共创建了36个顶点。实际上,我们只使用了8个不同的顶点而已,即正方形的8个顶点。在这样的小程序里把相同的顶点储存许多次并不会出什么大问题。但在需要储存大量数据的大得多的程序里,减少数据的重复来节约空间就显得很重要了。很幸运,Direct3D里一种成为索引缓冲的机制能让同一个图原共享他的顶点数据。

     就像他的名字暗示的那样,索引缓冲就是一块保存了顶点数据索引的缓冲。缓冲中的索引为32位或16位的整数。比如,你使用索引016来绘制一个三角形时,会通过索引映射到相应的顶点来渲染图像。使用索引来修改一下绘制盒子的代码吧,首先修改创建顶点的方法:

     vb=new VertexBuffer(typeof(CustomVertex.PositionColored), 8, device, Usage.Dynamic | Usage.WriteOnly, CustomVertex.PositionColored.Format,Pool.Default);

    CustomVertex.PositionColored[] verts = new CustomVertex.PositionColored[8];

    verts[0] = new CustomVertex.PositionColored( -1.0f, 1.0f, 1.0f, Color.Red.ToArgb());

    ·····(见源码OnVertexBufferCreate方法)

 

 

 

    如你所见,我们戏剧性的减少了顶点的数量,仅储存正方形的8个顶点。既然已经有了顶点,那36个绘制盒子的索引应该是什么样子呢?看一下先前的程序,依照36个顶点的顺序,列出适当的索引:

     private static readonly short[] indices =

    {

        0,1,2,  //front face

        1,3,2,  //front face

·····

    }

    为了便于阅读,索引分为3个一行,表示一个特点的三角形。第一个三角形使用顶点012第二个使用132;以此类推。仅仅有索引列表是不够的,还需要创建索引缓冲:

     private IndexBuffer ib null

这个对象就是储存并且让Direct3D访问索引的地方。它与创建顶点缓冲的方法也很相似。接下来初始化对象,填充数据:

ib = new VertexBuffer(typeof(short),indices.Length,device,Usage.WriteOnly,Pool.Default);

    ib.Created += new EventHandler(ib_Created);

OnIndexBufferCreate(ib,null);

 

 

 

private void ib_Created(object sender, EventArgs e)

    {

        IndexBuffer buffer = (IndexBuffer)sender;

        buffer.SetData(indices,0,LockFlags.None);

}

 

 

 

除了参数的约束条件以外,索引缓冲的构造器简直就是一个模子里出来的。与前面提到的一样,只能使用16位或32位的整数作为索引。我们订阅了事件处理程序,并且在程序第一次运行时手动调用他。最后为索引缓冲填充了数据。

现在,需要修改渲染图像的代码来使用这个数据了。如果你还记得,我们以前使用了一个叫“SetStreamSource”的方法来告诉DirectX渲染的时候使用哪一快顶点缓冲。同样,对于索引缓冲来说也有这样一种机制,不过它仅仅只是一个属性而已,因为同一时间只可能使用一种类型的索引缓冲。在SetStreamSource之后,设置如下属性:

device.Indices = ib;

 

 

 

这下Direct3D知道顶点缓冲的存在了,接下来修改绘图代码。目前,我们的绘图方法尝试从顶点缓冲绘制12个图原,可是这必然不会成功,因为现在顶点缓冲里只有8个顶点了。添加DrawBox方法:

private void DrawBox(float yaw,float pitch,float roll,float x,float y,float z)

    {

        angle += 0.01f;

        device.Transform.World = Matrix.RotationYawPitchRoll(yaw,pitch,roll) * Matrix.Translation(x,y,z);

        device.DrawIndexedPrimitives(PrimitiveType.TriangleList,0,0,8,0,indices.Length /3);

}

 

 

 

这里,我们把DrawPrimitives改为了DrawIndexedPrimitives。来看看这个方法的原型吧:

public void DrawIndexedPrimitives(PrimitiveType primitiveType,int baseVertex ,int minVertexIndex,int numVertices, int startIndex, int primCount);

 

 

 

第一个参数和上一个方法的一样,表示要绘制的图原类型。参数baseVertex表示从索引缓冲起点到要使用的第一个顶点索引的偏移量。MinVertexIndex是这几个顶点中最小的顶点索引值。很显然,numVertices指的就是所要使用的顶点数量。startIndex表示从数组中的哪一个位置开始读取顶点。最后一个参数则是要绘制的图原数量。

现在通过索引缓冲中的8个顶点,就可以绘制出了构成立方体的12个图原了。接下来用DrawBox方法代替原来的DrawPrimitives方法。

DrawBox(angle / (float)Math.PI, angle / (float)Math.PI * 2.0f, angle / (float)Math.PI / 4.0f, 0.0f, 0.0f, 0.0f);

(略,详见源码)

 

 

 

再次运行程序,可以看到颜色非常鲜艳的盒子在旋转。我们的每一个顶点都有不同的颜色,因此,真实的反映了使用索引缓冲共享顶点的缺点。当多个图原共享顶点的时候,所有的顶点数据都是共享的,包括颜色,法线数据等等。当决定是否共享顶点时,必须确定共享数据不会带来灯光或颜色上的错误(因为灯光的计算依赖于法线)。可以看到立方体每个面的颜色都是由顶点颜色插值计算出来的。

 

 

 

使用深度缓冲(Using Depth Buffer

     深度缓冲(depth buffer)(也就是通常所说的z-bufferw-buffer)是Direct3D在渲染时储存“深度”(“depth一般指方向为从屏幕指向观察者的z轴的窗口坐标)。深度信息用于在光栅化时决定象素之间的替代关系(注:度通常用视点到物体的距离来度量,这样带有较大深度值的象素就会被带有较小深度值的象素替代,即远处的物体被近处的物体遮挡住了)。至今为止,我们的程序都没有使用过深度缓冲,所以光栅化时没有象素被遮挡住。除此之外,我们甚至还没有会相互重叠的象素,那么,现在来绘制一些会与已有的立方体重叠的的立方体吧。

在已有的DrawBox方法调用后添加如下代码:

DrawBox(angle / (float)Math.PI,angle / (float)Math.PI* 2.0f, angle / (float)Math.PI / 4.0f, 0.0f,(float)Math.Cos(angle),(float)Math.Sin(angle));

···(略)

 

 

 

     我们在添加了三个旋转的立方体到原来中间一排的立方体上。运行程序,可以看到重叠的立方体,却不能分清两个立方体重叠部分的边界,看起来不过是一块普通的斑点而已。这就需要通过深度缓冲来处理了。

     添加深度缓冲实在是一个简单的任务。记得我们传递给device构造函数的presentation parameters参数吗?well,这将是我们添加深度缓冲的地方。创建一个包含深度缓冲的device,需要用到两个新的参数:

     public Mircosoft.DirectX.Direct3D.DepthFormat AutoDepthStencilFormat [ get, set ]

     public bool EnableAutoDepthStencil [get,set]

    

     EnableAutoDepthStencil设置为true就可以为device打开深度缓冲,使用DepthFormat来指定AutoDepthStencilFormat成员。DepthFormat枚举中,可使用的值列在下表中:

 

 

 

D 16           A 16-bit z-buffer bit depth.

D32           A 32-bit z-buffer bit depth.

D16Lockable   A 16-bit z-buffer bit depth that is lockable.

D32Flockable  A lockable format where depth value is represented by a standard IEEE floating point number.

D15S1         A 16-bit z-buffer bit depth using 15 bits for depth channel, with the last bit used for the stencil channel (stencil channels will be discussed later).

D24S8         A 32-bit z-buffer bit depth using 24 bits for depth channel, with the remaining 8 bits used for the stencil channel.

D24X8         A 32-bit z-buffer bit depth using 24 bits for depth channel, with the remaining 8 bits ignored.

D24X4S4       A 32-bit z-buffer bit depth using 24 bits for depth channel, with 4 bits used for the stencil channel, and the remaining 4 bits ignored.

D24FS8        A non-lockable format that contains 24 points of depth (as a floating point) and 8 bits for the stencil channel.

 

 

 

     深度缓冲越大,能储存的深度数据也越多,但这是以牺牲性能为代价的。除非你确定需要使用很大的深度缓冲,否则使用最小的值就可以了。大部分现代的图形卡都支持最小16bit的深度缓冲,so,添加代码:

     presentParams.AutoDepthStencilFormat = DepthFormat.D16;

    presentParams.SwapEffect = SwapEffect.Discard;

 

 

 

Perfect,现在device获得了深度缓冲。来看看有什么不同吧,运行程序。哇,结果并不是我们期盼的那样,程序被破坏了。这些立方体发生了什么?为什么加入了深度缓冲之后导致渲染被破坏了呢。呵呵,原因是深度缓冲从来没有被“cleared”,所以它一直处于一种不正确的状态。应该在clear device的同时clear深度缓冲,修改代码如下

device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.CornflowerBlue, 1.0f, 0);

 

 

 

Ok,一切正常了,休息一下来欣赏我们的作品吧^_^

需要源码可以到这个地方去下http://bbs.gameres.com/showthread.asp?threadid=24673

这篇关于Managed DirectX 第四章的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/717559

相关文章

【CSS in Depth 2 精译_023】第四章概述 + 4.1 Flexbox 布局的基本原理

当前内容所在位置(可进入专栏查看其他译好的章节内容) 第一章 层叠、优先级与继承(已完结) 1.1 层叠1.2 继承1.3 特殊值1.4 简写属性1.5 CSS 渐进式增强技术1.6 本章小结 第二章 相对单位(已完结) 2.1 相对单位的威力2.2 em 与 rem2.3 告别像素思维2.4 视口的相对单位2.5 无单位的数值与行高2.6 自定义属性2.7 本章小结 第三章 文档流与盒模型(已

pip install pyaudio sounddevice error: externally-managed-environment

shgbitai@shgbitai-C9X299-PGF:~/pythonworkspace/ai-accompany$ pip install pyaudio sounddeviceerror: externally-managed-environment× This environment is externally managed╰─> To install Python package

第四章 类和对象(2)

4.2 类         类是封装对象的属性和行为的载体,Java中定义类使用class关键字,其语法如下: class 类名称{// 成员变量// 成员方法()}         在Java语言中对象的属性以成员变量的形式存在,对象的方法以成员方法的形式存在。本节将对类与对象进行详细讲解。          4.2.1 成员变量         在Java中对象的属性也称为成员变量,

第四章 类和对象(1)

4.1面向对象概述         在程序开发初期,人们使用结构化开发语言,但随着软件的规模越来越庞大,结构化语言的弊端也逐渐暴露出来,开发周期被无休止地拖延,产品的质量也不尽如人意,结构化语言已经不再适合当前的软件开发。这时,人们开始将另一种开发思想引入程序中,即面向对象的开发思想。面向对象思想是人类最自然的一种思考方式,它将所有预处理的问题抽象为对象,同时了解这些对象具有哪些相应的属性以及行

深入理解计算机系统阅读笔记-第四章

第四章 处理器体系结构 一个处理器支持的指令和指令的字节级编码称为它的ISA(instruction-set architecture,指令集体系结构)。不同家族处理器有不同的ISA。ISA在编译器编写者和处理器设计人员之间提供了一个概念抽象层,编译器编写者只需要知道允许哪些指令,以及他们是如何编码的;而处理器设计者,必须建造出执行这些指令的处理器。 ISA模型看上去是顺序执行的,实际上同时处

React第四章(babel)

Babel 什么是Babel? Babel 是一个 JavaScript 编译器,提供了JavaScript的编译过程,能够将源代码转换为目标代码。 AST -> Transform -> Generate 官网 https://babeljs.io/ 查看AST https://astexplorer.net/ Babel所有的包 https://babeljs.io/docs/

嵌入式开发高频面试题——第四章 常见算法(下)

目录 4.2.1 Vector和List的异同4.2.2 Vector的内存增长与底层实现4.2.3 Vector和Deque的比较4.2.4 STL里有sort函数,为什么list还要定义sort?4.2.5 STL底层数据结构实现4.2.6 利用迭代器删除元素会发生什么?4.2.7 Map的实现与查找效率4.2.8 几种模板插入的时间复杂度 4.2.1 Vector和Lis

初次用用Spring 和mybatis整合的报出Manual close is not allowed over a Spring managed SqlSession错误

一般这种错误是由于没有删dao实现类中的close,因为框架已经帮你写好了

第四章 结构化分析简记

第四章  结构化分析 结构化方法:结构化分析+结构化设计+结构化程序设计 结构化分析的基本思想:自顶向下,逐步求精;基本原则:抽象与分解。

第四章 感受Mac之美-效率提高从操作快捷键开始

了解和掌握快捷键,可以提高工作效率,作用和意义是不言而喻的。节省不必要的操作时间,让你专注做自己真正重要的事。 比如最简单的复制,粘贴之类的入门快捷键,如果每个操作都需要右键执行去操作的话,那么在电脑上做什么操作都会慢一拍。 我是在入手俩周后,自己摸索之后,基本对系统里面基本的快捷键都有个印象了。 看了这么多的快捷键,一时半会也记不住啊,是不是有点崩溃啊,不过大家也不用担心快捷键的记不住,今