深入Managed DirectX9(五)

来源:互联网 发布:考试软件代理 编辑:程序博客网 时间:2024/05/17 06:21
使用简单的渲染技术
  至今为止,我们的渲染工作效率都很低。每次渲染场景时,都要分配新的顶点列表,并且所有东西存储在系统内存里。现代显卡集成了足够的显存,把顶点数据存放在显存可以获得大幅的新能提升:存放在系统内存里的数据,渲染每一帧时都要拷贝到显卡,这会带来极大的损失。只有移除每帧时的这种分配才能帮助我们提高性能。


使用顶点缓冲(Using Vertex Buffers)
  Direct3D已经包含了这种机制:顶点缓冲(vertex buffer)。顶点缓冲,就像他名字的意思一样:一块储存顶点的内存。顶点缓冲的机动性能完美实现共享场景里变经过变换的几何体。如何让我们在第一章编写的三角形程序使用顶点缓冲呢?

创建顶点缓冲同样简单,有三个构造函数能完成这个任务,我们依次来看看:
    public VertexBuffer( Device device, int sizeOfBufferInBytes, Usage usage, VertexFormats vertexFormat, Pool pool);
    public VertexBuffer( Type typeVertexType, int numVerts, Device device, Usage usage,VertexFormats vertexFormat, Pool pool);

以下是各参数的意义:
device——用来创建顶点缓冲的device,创建的顶点缓冲只能被这个device使用;
sizeOfBufferInBytes——所创建的顶点缓冲大小,以字节为单位。使用带有这个参数的构造函数创建的顶点缓冲可以存放任何类型的顶点;
typeVertexType——如果去要创建的顶点缓冲只储存一种类型的顶点,则使用这个参数。它的值可以是CustomVertex类中的顶点结构类型,也可以是自定义的顶点类型。且这个值不能为null;
numVert——指定了顶点缓冲的储存类型之后,也必须指定缓冲储存的顶点数量最大值。这个值必须大于0;
usage——定义如何使用顶点缓冲。并不会是所有Usage类型的成员都能使用,只有一下几个是正确的参数:
  DoNotClip,Dynamic, Npatches, Points, PTPatches, SoftwareProcessing, WriteOnly;
vertexFormat—— 定义储存在顶点缓冲中的顶点格式。,如果创建的为通用缓冲的话,则使用VertexFormat.None;
pool——定位顶点缓冲使用的内存池位置,可以指定一下几个内存池位置:
  Default, Managed, SystemMemory, Scratch。

观察第一章中的程序,把三角形的数据移动到顶点缓冲里应该很容易。首先,申明顶点缓冲变量:
    private Device device = null;
    private VertexBuffer vb = null;

接着添加创建三角形的代码:
    device = new (0,DeviceType.Hardware, this.CreatFlags.softwreVertexProccessing, presentParams);
    CustomVertex.positionColored[] verts = new CustomVertex. positionColored[3];
    Verts[0].SetPosition(new Vector3(0.0f,1.0f,1.0f));
    Verts[0].Color = System.Drawing.Color.Aqua.ToArgb();
    Verts[1]`````````
    Verts[2]`````````
    vb = new VertexBuffer(typeof(VustomVertex.PositionColored),2,device,Usage.Dynamic| Usage.WriteOnly, CustomVertex.PositionColored.Format, Pllo.Default);
    vb.SetData(vets,0,LockFlags.None);

  唯一的改变就是定义了三角形之后的两行代码。首先,创建用来保存三个顶点的顶点缓冲。出于性能上的考虑,创建的缓冲是动态、只读的并且位于默认的内存池。接下来,我们把三角形的顶点放到缓冲内,使用简单的SetData方法。这个方法接收任何类型的对象作为第一个参数,第二个参数是顶点缓冲中所要放置数据地址的便宜量。我们打算填充所有的顶点缓冲,所以设置为0。最后一个参数描述了当写入数据时,如何锁定缓冲。我们将稍后讨论锁存机制;现在,不用关心他是怎样锁定的。

  现在编译程序,很自然,得到了一个编译错误:因为OnPaint方法里的DrawUserPrimitives需要获得verts变量。需要有一个方法告诉Direct3D,我们要绘制顶点缓冲里的内容,而不是先前所申明的数组。调用device的SetStreamSource让Direct3D绘图的时候读取顶点缓冲。这个方法有以下两种重载:

    public void SetStreamSource(int streamNumber, VertexBuffer streamData, int offsetInBytes, int stride);
    public void SetStreamSource( int streamNumber, VertexBuffer streamData, int offsetInBytes);

  两个函数的不同之处在于其中一个多了表示(数据)流步幅大小(stride size of the stream)的参数。第一个参数是这段数据所使用流的数量。现在,把它设置为0即可;我们会在下一章讨论使用多个流。第二个参数是作为数据源的顶点缓冲,第三个则是顶点缓冲里需要DirectX绘制的数据的偏移量(以字节为单位)。stride则是缓冲里每一个顶点的大小。如果是用特定类型创建的顶点缓冲,则不需要这个参数。

现在修改绘图的方法:
    device.SetStreamSource(0, vb, 0);
    device.DrawPrimitives(PrimitiveType.TriangleLise, 0, 1);

  正如刚才所描述的,我们把顶点缓冲作为数据流0,同时把偏移量设置为0,使用所有数据。值得注意的是,我们同时也改变了真正绘图的函数。既然所有数据都在顶点缓冲里了,就不需要调用DrawUserPrimitives方法。因为DrawUserPrimitives只是用于绘制直接传递给它的用户定义数据。更加通用的DrawPrimitives将会绘制来自数据流源里的几何体。DrawPrimitives有三个参数,第一个我们已经讨论过了。第二个表示流里的起始顶点,最后一个表示所要绘制的几何体个数。

  就连这个仅绘制一个三角形的小样在使用了顶点缓冲之后都带来了10%的性能提升(基于画面更新率,即帧频frame rate)。我们会在稍后几张来讨论有关性能及帧频。不幸的是,当你尝试改变窗口大小的时候,三角形会立即消失。(注:偶在实际测试时三角形并米有消失,只是当窗口缩放为一定比例时,三角形会消失)

  有几种情况会导致这种行为,其中的两种我们先前已经讨论过了。回想一下上一章,我们知道在改变窗口大小的时候,设备会自动重置。但当所创建的资源位于默认的内存池时(比如顶点缓冲),重置设备会释缓冲。所以当改变窗口大小的时候,重置了device,释放了顶点缓冲。Managed DirectX有一个极好的特新就是在重置device之后会自动的重建顶点缓冲。但是,这是顶点缓冲里已经没有了数据,所以没有任何东西被绘制出来。

  我们可以捕获顶点缓冲一个叫做“created”的事件,它会在重建顶点缓冲,准备好填充数据的时候发生。现在是使用这个事件更新我们程序的时候了,修改代码如下:

private void OnVertexBufferCreate(object sender, EventArgs e)
{
    VertexBuffer buffer = (VertexBuffer)sender;
    CustomVertex.positionColored[] verts = new CustomVertex. positionColored[3];
    Verts[0].SetPosition(new Vector3(0.0f,1.0f,1.0f));
    Verts[0].Color = System.Drawing.Color.Aqua.ToArgb();
    Verts[1]`````````
    Verts[2]`````````
    buffer.SetData(verts,0,LockFlags.None);
}

订阅事件处理程序:

   
vb.Created += new EventHandleer(this.OnVertexBufferCreate);
    OnVertexBufferCreate(vb,null);

  这段代码为顶点缓冲订阅了事件处理程序,并且保证无论在什么情况下创建顶点缓冲,都会调用OnVertexBufferCreate方法。因为第一次创建顶点缓冲的时候,还没有订阅过处理程序,所以需要手动调用一次。

  好了,通过使用video memory和顶点缓冲,我们已经把原来缓慢的小样改变为了一个高效的程序。当然,它还是相当的枯燥。那么,接下来让我们创造一个盒子吧。