×

Managed DirectX +C# 开发(入门篇)4

Kalet Kalet 发表于2009-03-20 12:00:14 浏览186 评论0

抢沙发发表评论

第七章 顶点缓冲

一、索引缓冲

3D物体中的三角形经常会有许多共用顶点。如果用两个三角形组合为一个长方形,有两个点被重复使用,当要表现一个更精细更复杂的模型的时候,重复的顶点数将会变得很大。下图的立方体,仅有八个顶点,但是当用三角形列表示它的时候,所有的点都被重复使用。
Managed DirectX +C# 开发(入门篇)4

索引缓冲就是一块保存了顶点数据索引的缓冲。缓冲中的索引为32位或16位的整数。比如,当使用索引0,1,6来绘制一个三角形时,会通过索引映射到相应的顶点来渲染图像。

以下为创建一个索引缓冲的代码示例:

int numberVerts = 8;

short[] indices = {

    0,1,2, // Front Face

    1,3,2, // Front Face

    4,5,6, // Back Face

    6,5,7, // Back Face

    0,5,4, // Top Face

    0,2,5, // Top Face

    1,6,7, // Bottom Face

    1,7,3, // Bottom Face

   0,6,1, // Left Face

    4,6,0, // Left Face

    2,3,7, // Right Face

    5,2,7 // Right Face

};


Mesh mesh = new Mesh(numberVerts * 3, numberVerts, MeshFlags.Managed,

                     CustomVertex.PositionColored.Format, device);


using(VertexBuffer vb = mesh.VertexBuffer)

{

    GraphicsStream data = vb.Lock(0, 0, LockFlags.None);


    data.Write(new CustomVertex.PositionColored(-1.0f, 1.0f, 1.0f, 0x00ff00ff));

    data.Write(new CustomVertex.PositionColored(-1.0f, -1.0f, 1.0f, 0x00ff00ff));

    data.Write(new CustomVertex.PositionColored(1.0f, 1.0f, 1.0f, 0x00ff00ff));

    data.Write(new CustomVertex.PositionColored(1.0f, -1.0f, 1.0f, 0x00ff00ff));

    data.Write(new CustomVertex.PositionColored(-1.0f, 1.0f, -1.0f, 0x00ff00ff));

    data.Write(new CustomVertex.PositionColored(1.0f, 1.0f, -1.0f, 0x00ff00ff));

    data.Write(new CustomVertex.PositionColored(-1.0f, -1.0f, -1.0f, 0x00ff00ff));

    data.Write(new CustomVertex.PositionColored(1.0f, -1.0f, -1.0f, 0x00ff00ff));


    vb.Unlock();

}


using (IndexBuffer ib = mesh.IndexBuffer)

{

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

}


顶点和索引缓存有相似的地方,一个顶点缓存是一块连续的存储了顶点数据的内存。同样的,一个索引缓存是一块连续的存储了索引数据的内存。使用顶点和索引缓存保存数据是因为它们能被放置在显存中。渲染显存中的数据要比渲染系统内存中的数据快的多。

二、索引缓冲实例

下面为一使用索引缓冲的例子,显示一个旋转的正方体;



相关代码如下:

首先,定义对象变量;

private VertexBuffer vb = null;

         private IndexBuffer ib = null;

在图形的初始化函数中实例化对象:

public void InitializeGraphics()

         {

……

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

              vb.Created += new EventHandler(this.OnVertexBufferCreate);

              OnVertexBufferCreate(vb, null);


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

              ib.Created += new EventHandler(OnIndexBufferCreated);

              this.OnIndexBufferCreated(ib,null);

}

对于IndexBuffer()构造函数,相关参数说明如下:

    indices.Length —— 分配给缓存的字节大小。假如想得到一个能存储8个顶点的顶点缓存,那么就要在顶点结构中设置这个参数为 8 * sizeof ( Vertex ) 。

    Usage指定关于怎样使用缓存的额外信息。这个值可以是0,没有标记,或者是下面标记的组合: ——

Dynamic:设置这个参数可以使缓存是动态的。

Points:这个参数指定缓存存储原始点。

Softwareprocessing:使用软件顶点处理

Writeonly:指定应用程序只能写缓存。它允许驱动程序分配最适合的内存地址作为写缓存。

Pool —— 缓存放置在哪一个内存池中

不使用Dynamic参数创建的缓存被叫做静态缓存。静态缓存通常被放置在显存中,在其中的数据能被很好的处理。然而,对于静态缓存,从中读取和写入数据是很慢的,因为访问显存是很慢的。所以一般用静态缓存存储不需要被经常改变的数据。比如,可以存储地形和建筑物。静态缓存应该在应用程序初始化的时候就被填充好,而不是在运行时才做。
Managed DirectX +C# 开发(入门篇)4
动态缓存通常被放在AGP内存中,这种内存中的数据能被很快的更新。处理动态缓存中的数据不会比处理静态缓存中的数据快,因为这些数据必须在渲染前被转移到显存中,动态缓存的优点是能够被更迅速地更新。因此,假如需要经常更新缓存中的数据,那么就应该使用动态缓存。比如,对于粒子系统来说,一般应用动态缓存。


下面看看灌入数据的代码:

private void OnIndexBufferCreated(object sender, EventArgs e)

         {

              IndexBuffer buffer = (IndexBuffer)sender;

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

         }


另外在渲染函数中:

protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)

         {

         ……

              device.SetStreamSource(0, vb, 0);

              device.Indices = ib;

              angle += 0.05f;

              device.Transform.World = Matrix.RotationYawPitchRoll(angle / (float)Math.PI,angle / (float)Math.PI * 2.0f,angle / (float)Math.PI / 4.0f) ;

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

              …….

         }


注意,对于顶点缓冲来说,使用的是SetStreamSource()方法,而对于索引缓冲来说,则是设置Indices属性。因为在同一时间内只可能使用同一种类型的索引缓冲。

另外,注意把以前的DrawPrimitives改为了DrawIndexedPrimitives。来看看这个方法的参数如下:

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


PrimitiveType:表示要绘制的图元类型。

baseVertex:表示从索引缓冲起点到要使用的第一个顶点索引的偏移量。

MinVertexIndex:这几个顶点中最小的顶点索引值。

numVertices:是所要使用的顶点数量。

startIndex:从数组中的哪一个位置开始读取顶点。

primCount:是要绘制的图元数量。


当创建完索引缓冲后,如果以后需要获得相关信息,可以使用IndexBuffer.Description属性来得到;


三、深度缓冲


深度缓冲不是用来存储图像数据,而是用来记录像素的深度信息。以便确定哪一个像素最后被绘制出来。因为在3D场景中,经常会发生一个物体把将另一个物体的一部分遮住了。为了使Direct3D能确定物体的前后关系并正确的绘制出来,需要使用深度缓冲。

它有时也称z-buffer或w-buffer,为每一个像素计算深度值并进行深度测试。通过深度测试可以比较得出哪个像素离摄相机更近并将它画出来。这样就可以只绘制最靠近摄相机的像素,被遮住的像素就不会被制。

深度缓冲的格式决定着深度测试的精确程度。一个24位的深度缓冲比16位的深度缓冲更精确。通常,应用程序在24位深度缓冲下就能工作的很好,但是Direct3D也同时支持32位的深度缓冲。

四、深度缓冲示例

打开前面绘制的立方体,在场景中再加入两个立方体,同时,为了观察方便,对加入的立方体进行了平移;代码如下:

     device.Transform.World = Matrix.RotationYawPitchRoll(angle / (float)Math.PI,angle / (float)Math.PI * 2.0f,angle / (float)Math.PI / 4.0f) ;

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


              device.Transform.World = Matrix.RotationYawPitchRoll(angle / (float)Math.PI,angle / (float)Math.PI * 2.0f,angle / (float)Math.PI / 4.0f)

              * Matrix.Translation(0,1,5);

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

             

              device.Transform.World = Matrix.RotationYawPitchRoll(angle / (float)Math.PI,angle / (float)Math.PI * 2.0f,angle / (float)Math.PI / 4.0f)

                   * Matrix.Translation(0,-1,-5);

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


执行程序,注意这是没有加入深入缓冲的效果:


下面加入深度缓冲,加入很简单;只需在创建设备时,将改变一下参数的属性:

PresentParameters presentParams = new PresentParameters();

presentParams.EnableAutoDepthStencil = true;

presentParams.AutoDepthStencilFormat = DepthFormat.D16;

presentParams.SwapEffect = SwapEffect.Discard;

Format current = Manager.Adapters[0].CurrentDisplayMode.Format;


其中EnableAutoDepthStencil表示是否使用深度缓冲。AutoDepthStencilFormat定义精度,常用枚举格式及含义如下:

D32——表示32位深度缓冲

D24S8——表示24位深度缓冲并保留8位模版缓冲(stencil buffer)

    D24X8——表示24位深度缓冲

    D24X4S4——表示24位深度缓冲并保留4位模版缓冲

    D16——表示16位深度缓冲


现在如果执行程序,还将无法得到正确的结果,在渲染函数中将代码:

device.Clear(ClearFlags.Target , Color.Black , 1.0f, 0);

更改为:

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

现在,再执行程序,结果如下:
Managed DirectX +C# 开发(入门篇)4

现在,三个立方体有明显的层次感了。


群贤毕至

访客