zoukankan      html  css  js  c++  java
  • 处理顶点——使用顶点缓冲和索引缓冲将顶点和索引保存在显存中

    问题

    每次当你调用device. DrawUserPrimitives方法时,顶点都会从系统内存传递到显卡中。通常,大部分数据没有变化,这意味着每帧重复传递了相同的数据。今天的显卡有容量很大而且很快的显存,所以你可以将顶点数据存储在显存中加速程序。从显存中将顶点数据传递到显卡速度要快得多,因为这些数据只在同一板卡的不同芯片间传输。同样索引数据也能获得加速。

    解决方案

    通过创建顶点数组的VertexBuffer,你可以将顶点数据复制到显存中。将顶点存储到显存后,就可以调用DrawPrimitives方法 (代替DrawUserPrimitives方法),这个方法从更快的显存中获取顶点。

    注意:如果顶点几乎无需更新,那么这个方法可以极大地提高性能。但当处理被称为动态顶点数据(dynamic vertex data)时,这意味着顶点数据更新频繁,你应使用DynamicVertexBuffer,这会在下一个教程介绍。

    工作原理

    你可以一次性地将数据传递到显卡并把它们存储在显存中,而不是在每次调用DrawUserPrimitives方法时将顶点数组传递到显卡。要实现这个方法,你可以在创建了顶点数组后将它加载到一个VertexBuffer(顶点缓冲)中。在项目中添加VertexBuffer变量:

    VertexBuffer vertBuffer; 

    然后使用下面的方法生成六个顶点显示两个带纹理的三角形(可见教程5-1)。在这个方法最后,这六个顶点会加载到VertexBuffer并传递到显存中:

    private void InitVertices()
    {
        myVertexDeclaration = new VertexDeclaration(device, VertexPositionTexture.VertexElements); 
        VertexPositionTexture[] vertices = new VertexPositionTexture[6]; 
        int i = 0; 
        
        vertices[i++] = new VertexPositionTexture(new Vector3(-5.0f, new Vector2(-0.5f,1.5f)); 
        vertices[i++] = new VertexPositionTexture(new Vector3(-2.5f, new Vector2(0.5f, -1.5f)); 
        vertices[i++] = new VertexPositionTexture(new Vector3(0, -3, new Vector2(1.5f, 1.5f)); 
        vertices[i++] = new VertexPositionTexture(new Vector3(0, -3, -1), new Vector2(-0.5f, 1.5f)); 
        vertices[i++] = new VertexPositionTexture(new Vector3(2.5f, 5, -1), new Vector2(0.5f, 1.5f));
        vertices[i++] = new VertexPositionTexture(new Vector3(5.0f, -3, -1), new Vector2(1.5f, 1.5f)); 
        vertBuffer = new VertexBuffer(device, VertexPositionTexture.SizeInBytes * vertices.Length, BufferUsage.WriteOnly);
        vertBuffer.SetData(vertices, 0, vertices.Length); 
    } 

    当定义了所有顶点后,你需创建一个VertexBuffer。这个VertexBuffer将传递到显卡的保留内存中,由于这个原因,你需要指定对设备的链接和顶点占据的字节数量,这个数量等于 (一个顶点的字节大小)乘以(顶点数量)。

    注意:你当然不想每帧都创建一个VertexBuffer,请确保只调用这行代码一次。如果你想覆盖VertexBuffer的数据,不要删除它并重建一个,而是应该使用SetData方法将新数据加载到当前已经存在的VertexBuffer中。只要当你需要增加顶点的数量时才有必要删除VertexBuffer 并重建一个。

    创建了VertexBuffer后,你就可以使用SetData方法将顶点数据传递到内存中。显然,SetData方法需要从顶点数组中复制数据。它的一个重载方法可以只写入VertexBuffer的部分数据,在这种情况下,你需指定从哪个顶点开始以及复制多少个顶点。

    注意:如果你要频繁地更新VertexBuffer的内容,你应该使用DynamicVertexBufferDynamicVertexBuffer的SetData方法更为强大。

    在显卡中存储了顶点数据后就可以绘制三角形了。你可以使用教程5-1中的代码,但有两处变化。

    你将使用DrawPrimitive方法表明你将从VertexBuffer中获取数据进行绘制。在这之前首先要激活VertexBuffer。

    device.RenderState.CullMode = CullMode.None; 
    basicEffect.World = Matrix.Identity; 
    basicEffect.View = fpsCam.ViewMatrix; 
    basicEffect.Projection = fpsCam.ProjectionMatrix; 
    basicEffect.Texture = myTexture; 
    basicEffect.TextureEnabled = true; 
    basicEffect.Begin(); 
    foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
    {
        pass.Begin(); 
        device.VertexDeclaration = myVertexDeclaration; 
        device.Vertices[0].SetSource(vertBuffer, 0, VertexPositionTexture.SizeInBytes); 
        device.DrawPrimitives(PrimitiveType.TriangleList, 0, 2); pass.End(); 
    }
    basicEffect.End(); 

    在pass中,与以前一样需要将VertexDeclaration传递给设备表明顶点存储的数据类型、下一行代码设置VertexBuffer。对每个顶点来说,你可以在不同的VertexBuffers中存储不同的信息。本例中,你只使用了一个VertexBuffer,因此指定索引为0。使用SetSource方法,表明你将激活vertBuffer作为顶点的源。你还要指定第一个顶点的开始位置和一个顶点占据多少个字节(这样显卡才能将字节流裁切成独立的顶点)。激活VertexBuffer后,从第一个顶点开始使用TriangleList进行绘制,这个VertexBuffer 包含两个三角形的数据。

    对性能的考虑:VertexBuffer构造函数

    VertexBuffer的构造函数可以在最后一个参数中指定一些有用的可选择的标志,显卡的驱动会使用这个标志决定哪种内存是最快的。下面是可以使用的BufferUsages:

    • BufferUsage. None:允许从VertexBuffer中读写。
    • BufferUsage. Points:表示VertexBuffer中的顶点数据是用来绘制点和精灵的。这个与性能无关。
    • BufferUsage. WriteOnly:不从VertexBuffer读取数据。当使用VertexBuffer时,可以将顶点数据放在快得多的显存中。这样,当你想处理顶点数据时,比起将顶点数据在系统内存中存储一个副本,调用VertexBuffers的GetData方法往往更好。

    你也可以组合这些标志,用 | 分隔表示OR操作(如果不矛盾,结果是指定选项的AND逻辑)。

    IndexBuffer(索引缓冲)

    如果你还想将索引存储在显卡中,你应在创建VertexBuffer之后再创建一个IndexBuffer ,所以在项目中添加一个IndexBuffer变量:

    IndexBuffer indexBuffer; 

    作为一个简单的例子,你将定义两个三角形,因为它们共享一个顶点,所以只需定义5个独立顶点:

    private void InitVertices() 
    {
        myVertexDeclaration = new VertexDeclaration(device, VertexPositionTexture.VertexElements);
        VertexPositionTexture[] vertices = new VertexPositionTexture[5]; 
        int i = 0; 
        
        vertices[i++] = new VertexPositionTexture(new Vector3(-5.0f, -3, -1), new Vector2(-0.5f, 1.5f)); 
        vertices[i++] = new VertexPositionTexture(new Vector3(-2.5f, 5, -1), new Vector2(0.5f, -1.5f)); 
        vertices[i++] = new VertexPositionTexture(new Vector3(0, -3, -1), new Vector2(1.5f, 1.5f)); 
        vertices[i++] = new VertexPositionTexture(new Vector3(2.5f, 5, -1), new Vector2(0.5f, -1.5f)); 
        vertices[i++] = new VertexPositionTexture(new Vector3(5.0f, -3, -1), new Vector2(-0.5f, 1.5f)); 
        vertBuffer = new VertexBuffer(device, VertexPositionTexture.SizeInBytes * vertices.Length, BufferUsage.WriteOnly); 
        vertBuffer.SetData(vertices, 0, vertices.Length); 
    } 

    接下来使用InitIndices方法创建索引数组并将它们复制到索引缓冲中:

    private void InitIndices() 
    {
        int[] indices = new int[6]; 
        int i = 0; 
        indices[i++] = 0; 
        indices[i++] = 1; 
        indices[i++] = 2; 
        
        indices[i++] = 2; 
        indices[i++] = 3; 
        indices[i++] = 4; 
        
        indexBuffer = new IndexBuffer(device, typeof(int), indices.Length, BufferUsage.WriteOnly); 
        indexBuffer.SetData<int>(indices); 
    } 

    创建IndexBuffer时,你需要指定索引的类型,类型可以是ints或shorts,还要指定索引的多少。

    当心:一些低端显卡只支持16-bit的索引,如果你使用32-bit的整数型索引会出错。要解决这个问题你应将索引存储在一个short数组中,指定创建一个short类型的索引缓冲。

    技巧:如果你的数组包含不超过32,768的索引,你应该使用shorts而不是ints。这样索引缓冲可以节省一半内存。

    别忘了在项目开始调用这个方法。

    当进行绘制时,你需要激活VertexBuffer和IndexBuffer,然后调用DrawIndexedPrimitives 方法绘制三角形:

    device.RenderState.CullMode = CullMode.None; 
    basicEffect.World = Matrix.Identity; 
    basicEffect.View = fpsCam.ViewMatrix;
    basicEffect.Projection = fpsCam.ProjectionMatrix; 
    basicEffect.Texture = myTexture; 
    basicEffect.TextureEnabled = true; 
    basicEffect.Begin(); 
    foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
    {
        pass.Begin(); 
        device.VertexDeclaration = myVertexDeclaration; 
        device.Vertices[0].SetSource(vertBuffer, 0, VertexPositionTexture.SizeInBytes); 
        device.Indices = indexBuffer; 
        device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 5, 0, 2); 
        pass.End(); 
    }
    basicEffect.End(); 
    代码

    InitVertices,InitIndices和Draw方法的代码前面已经写过了。

    image

  • 相关阅读:
    一文看懂Fluentd语法
    mongo 使用聚合合并字段
    加速开发流程的 Dockerfile 最佳实践
    nodejs之RSA加密/签名
    nodejs之https双向认证
    自签证书生成
    白话理解https
    一文看懂k8s Deployment yaml
    基于xtermjs实现的web terminal
    intelliJ 中文设置
  • 原文地址:https://www.cnblogs.com/AlexCheng/p/2120117.html
Copyright © 2011-2022 走看看