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


    第七章 顶点缓冲
    一、索引缓冲
    3D物体中的三角形经常会有许多共用顶点。如果用两个三角形组合为一个长方形,有两个点被重复使用,当要表现一个更精细更复杂的模型的时候,重复的顶点数将会变得很大。下图的立方体,仅有八个顶点,但是当用三角形列表示它的时候,所有的点都被重复使用。
    索引缓冲就是一块保存了顶点数据索引的缓冲。缓冲中的索引为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参数创建的缓存被叫做静态缓存。静态缓存通常被放置在显存中,在其中的数据能被很好的处理。然而,对于静态缓存,从中读取和写入数据是很慢的,因为访问显存是很慢的。所以一般用静态缓存存储不需要被经常改变的数据。比如,可以存储地形和建筑物。静态缓存应该在应用程序初始化的时候就被填充好,而不是在运行时才做。
    动态缓存通常被放在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);
    现在,再执行程序,结果如下:
    现在,三个立方体有明显的层次感了。
    以上八篇文章均来自‍http://blog.csdn.net/dandanCool/archive/2007/06/26/1666635.aspx,转帖请注明
  • 相关阅读:
    简单的运动框架——分享给初学者
    Python数据分析学习日志(1. 书单)
    mysql恢复数据参考
    window cmd 杀掉 java.exe 进程
    转载: Ajax关于readyState和status的讨论
    开发问题bug记录
    vue基础part10
    vue基础part9
    vue基础part(7-8)
    vue基础part(4-6)
  • 原文地址:https://www.cnblogs.com/lcxu2/p/2004033.html
Copyright © 2020-2023  润新知