D3D绘制图元理论基础

在前面部分,我介绍了D3D的初始化和固定渲染流水线。这一章,将它们用于实践。
我们需要解决的事情是:
1、在D3D中如何存储顶点和索引数据;
2、怎样使用渲染状态来改变渲染结果;
3、学习怎样渲染场景;
4、学习怎样用D3DXCreate*函数创建更多的3D物体。

一、顶点缓冲区、索引缓冲区的概念

    顶点缓冲区是一块连续的存储了顶点数据的内存。
    索引缓冲区是一块连续的存储了索引数据的内存。

    我们使用顶点缓冲区和索引缓冲区是来保存我们的数据是 因为它们能被放置在显存中。渲染显存中的数据要比渲染系   统内存中的数据

快的多。

    在D3D中定义顶点缓冲区是通过IDirect3DVertexBuffer9   接口来定义的;索引缓冲区是通过IDirect3DIndexBuffer9   接口来定义的。

    在程序中创建顶点缓冲区和索引缓冲区给我们提供了两个 方法:
       1、CreateVertexBuffer();
       2、CreateIndexBuffer();

    以上这两个函数的详细说明,请参阅D3DAPI。

二、静态缓冲区、动态缓冲区的区别

    静态存储区存储的是不经常更改的数据,原因这块数据被 放置在显存中,读和写变的非常的低效,访问 显存很低效。   但是渲染显存中

的数据却是很快的。这里需要注意下这个双面性。

    将D3DUSAGE_DYNAMIC存储的是动态缓冲区。动态缓冲区通 常被放在AGP内存中,这种内存中的数据能够被很快的更新   。处理动态缓冲区的中的数据没有在显存中处理的快,因为   这些数据在渲染前,必须要从内存中转移到显存中,动态缓   冲区的目的,它们能够被稍微快一点的被刷新。对于动态缓   冲区,粒子系统肯能是最好的例子,并且它们每一帧都会被   更新(比CPU写快)。

    注意:在程序中读取显存和AGP内存都是非常慢的,因此 假如你在运行时需要读取你的几何物体,最好的解决方案是 指定一个系统内存,都在其中拷贝并且读取数据。

IDirect3DVertexBuffer9* vb;
g_pDevice->CreateVertexBuffer
(
    8*sizeof(Vertex),
    0,
    D3DFVF_XYZ,
   D3DPOOL_MANAGER,
   &vb,
   NULL
)

三、访问缓冲内存,实际上就是应用加锁解锁函数。

    为了访问一个顶点\索引缓冲区,我们需要得到一个指针     。那如何获得这个指针呢,很简单,你只要通过顶点缓冲   的对象来进行加

锁就可以了,里边有一个参数就可以得到   了!

    原型:
        HRESULT IDirect3DVertexBuffer::Lock
            {
   UINT offsetToLock,
                UINT SizeToLock,
                BYTE** pPvertex,
                DWORD Flags
            }
     对于索引缓冲区的Locked原型跟它参数一样,只不过调     用者不同罢了。
        具体参数意义,请参阅D3DAPI帮助文档。

     下边演示如何往顶点缓冲区域内写数据(也就是往里边读   写数据的基本样板操作。)

     Vertex *vertex;
     IDirect3DVertexBuffer9::Lock
     {
        0,        //偏移量
        0,        //指定锁定的内存数据大小,0,指全部
        &vertex, //锁定内存的起始地址
        0         //标记,这里为什么是0,我还不清楚
     }
     
     vertex[0] = vertex(..,..,..,..);
     vertex[0] = vertex(..,..,..,..);
     vertex[0] = vertex(..,..,..,..);
     vertex[0] = vertex(..,..,..,..);
     ...
     ...
     
     IDirect3DVertexBuffer9::Unlock();
     该函数通知D3D将要多顶点缓冲区进行内存操作,并获得 指向顶点缓冲区的内存指针(首地址),顶点数据复制完成后 ,顶点缓冲区通

知Unlock()通知D3D内存操作结束。示例如 下:
      void *ptr;
      IDirect3DVertexBuffer::Lock
      {
         0,
         sizeof(顶点格式结构体变量),
         (void **)&ptr,
         0
      }
      
      memcpy(ptr,顶点格式结构体变量,sizeof(顶点格式结              构体变量));//***核心代码***
      IDirect3DVertexBuffer9::UnLock();

四、如何找回顶点和索引缓冲区的信息(选看)
     有时我们需要得到顶点或者索引缓冲区信息。那如何从     新获取呢:
      D3DVERTEXBUFFER_DESC VbDesc;
      IDirect3DVertexBuffer9::GetDesc(VbDesc);

      D3DINDEXBUFFER_DESC IbDesc;
      IDirect3DVertexBuffer9::GetDesc(IbDesc);

      D3DVERTEXBUFFER_DESC\D3DINDEXBUFER_DESC结构属性 
      请参阅D3DAPI帮助文档。

五、渲染状态
     渲染状态影响集合体的渲染,这个你不用管,D3D为我们提供了好多渲染模式,你直接调用就OK,简单吧,那继续看下边吧。
     D3D有默认的渲染状态,当你想自己指定渲染状态的时候,你尽可以改变它的渲染状态。通过D3D提供的函数SetRendState。
     这里需要注意的是,当你指定一个渲染状态的时候,这个渲染状态会一直起作用直至你又重新设定了D3D的渲染状态。
     在开始正式渲染前,我们要做3个准备工作,(1)资源流的设定(2)设置顶点的灵活顶点格式(3)设置索引缓冲区
     使用顶点缓冲区和索引缓冲区配合工作,来进行我们的渲染之行,亢奋吧,呵呵!
     IDirect3DDevice9::DrawPrimitive
            {
              绘制图元的类型,
              待绘制图元的顶点编号,<相当给予了编程人员的控制权限,可以部分或者从开始处指定顶点编号>
              绘制图元的数目
            }

     IDiect3DDevice9::DrawIndexPrimitive
            {
              绘制图元的类型,
              。。。
            }

      以上结构体的具体参数,请参阅D3DAPI,很简单!

     还有一点需要注意的事情是,所有的绘制方法,必须填写在:
      IDirect3DDevice->BeginScene();
        IDirect3DDevice->DrawPirmitive(...);   中间渲染代码
      IDirect3DDevice->EndScene();

                                                                                                       2010年8月18日      星期三    晚

                                                                                                               阿舰          于日照


    上一篇主要一块学习了D3D绘制图元的一些个理论知识,再一起回顾一下:讲述了两个缓冲区的概念,存储顶点数据的静态与动态缓冲区的区别,如何访问内存,如何得知顶点索引缓冲区的信息,在一个就是绘制图元的方法,以及在哪绘制的问题。

        这一篇我将跟大家伙一起学习一下,各个概念之间的关联,下一篇,我将贴出源码,好了拭目以待吧!闲话少说,开干:

         一、昨晚描述遗留的模糊问题:

          D3DUSAGE_WRITEONLY与D3DUSAGE_DYNAMIC间的关联,到底是怎么样的呢?为了描述清 楚这个问题,以及解决我思想上的包袱,今天中午我特意查了一下,望能给你解疑: 网上资料称,D3DUSAGE_DYNAMIC当被指定的时候,D3DUSAGE_WRITEONLY才具有存在的价值。

         (1)D3DUSAGE_DYNAMIC+D3DUSAGE_WRITEONLY -->指定顶点数据被存储在显存中,读写速度并不快,但是有一个显著的特点就是渲染速 度很快!

         (2)D3DUSAGE_DYNAMIC 不附加D3DUSAGE_WRITEONLY的时候,其指定的是AGP内存,该内存的优点是数据能被很快的刷新!常用于粒子系 统那块!

          (3)D3DUSAGE_WRITEONLY 本身指定该内存为只写属性,任何试图从其中读数据的操作,均视为违法操作。

           (4)不用D3DUSAGE_WRITEONLY标记的时候,指定的就是静态缓冲区中存储的数据,也就是存储在显存中,但是数据不宜频繁变动!

      二、绘制图元的各个流程到底是在做些什么呢?

           昨晚可能描述的不够准确或者是这些个知识点没有串联,因此你可能对我说的各个理论之间的关联并不是十分明朗,在这我简要的再 说一下这个流程:

            D3D的基本绘制图元是以三角形绘制的。众所周知点成线,线成面的概念。所以,我们可以假想任何复杂的物体都是一系列相关联的点 组成的线连接起来的。要想绘制它,需要经过这些个步骤:

           1、指定各个顶点的属性,你比如说它的位置、法线、纹理我们借助C++中的结构体来实现它;        

           2、根据你所定义的顶点的各个属性,来定制属于你自己的灵活顶点格式;

           3、到了这个阶段,你就得需要考虑,你到底需要绘制哪种类型的图元呢?点?线段?三角形?正方形?还是多面体?完成这些,你需 要多少个你所指定类型的点组成呢?(这里暂时不提索引缓冲的概念)。这时呢,你就需要根据你想创建的图元的类型,来填充你的 这个顶点结构体

           4、当你自定义了那么多的顶点数据,你总得找个地方保存一下吧!这时,就是需要为其申请一段可以保存这些个顶点数据的内存段了 !通过D3D为我们提供的创建顶点缓冲区的函数CreateVertexBuffer来实现

            5、当你申请了足够容纳你所定义的顶点数据结构体数组之后,你就需要访问这块内存了,并且将你的顶点数据填入到你所申请的内存 段中。这就需要在加锁解锁函数中来实现了。

           6、设置渲染状态,指定渲染的模式,你比如说是否开启拣选模式等等。具体的参看D3DSETRENDERSTATETYPE。

           7、你以上搞的这一套,还只是设计阶段,我们的最终目的是看到效果,看不到都是白扯,所以我们需要将我们以上的想法或者设计, 通过某种介质来呈献给绘制设备。于是就有了资源流这么个概念。就是与你的顶点缓冲区与渲染数据流连接用的媒介。

           8、设置你的灵活顶点格式

            9、最后就可以绘制你的D3D图元了

       三、顶点缓冲区与索引缓冲区的优劣比较

        索引缓冲区诞生的意义是减少系统内存的开销,提高程序的性能。不相信的话,我们一块来算笔账,事实为证。当我们假设绘制一个 正方形图元的时候,我们需要定义几个点吧,傻子都知道我们需要4个顶点:如下图A、B、C、D。

        A--------B

        |          / |

         |        / |

         |   /       |

     C | /--------| D

           1、节省系统内存

       当我们仅仅用顶点缓冲直接绘制这个长方形图元的时候,我们拿出一种组织方式来分析一下:ABC,CBD。这时需要6个点来记录这些 组织方式我们才可以绘制出来这个图元:我们的图元坐标是int类型的,int占用4个字节,所以,需要的字节数:6*(3*4) = 72个字节 。如果我们用索引缓冲来实现的话,我们就只需剔除重复的点,这时我们只需要保存4个点就可以完成我们的绘制工作,这里呢,你要 借住索引来操作,索引也是占据空间的,但是它是16位的索引就可以搞定了就是WORD,一个WORD就是16位,即两个字节,所以依靠索 引来完成咱们的绘制的话,就需要:4*(3*4)+6*2 = 60个字节需要。 这里体现的节省系统内存开销,并不是很明显,如果你搞个复杂的图元来搞搞,那性价比就体现的淋漓尽致了!  

            2、提高程序的执行性能 使用顶点缓冲区来渲染的时候,D3D会对每个顶点进行计算,这样6个顶点需要计算;而如果你使用索引缓冲区来渲染的时候,你仅 仅需要让D3D处理4个顶点就可以了,这样可以让系统避免了不必要的性能开销,在一定程度上提高了程序的性能!

       四、顶点数组如何与索引数组搭配呢?

       前期在以上我所说的创建顶点缓冲区之前的那些操作,该怎么写还是怎么写,但是呢,在你填充顶点数据结构体后,需要一个16 位的索引数组,用于你用索引下标来组织你的三角形顶点的组织方式。

       五、顶点缓冲区如何与索引缓冲区搭配来完成图元绘制呢?

           1、依旧创建顶点缓冲区,需要注意的是,创建顶点缓冲区的大小那个参数,是你真正用到的点的个 数所需的内存大小。

          2、新创建索引缓冲区,这里需要注意的是,那个索引数组的元素格式是D3DFMT_INDEX16。

           3、操作内存,这里用索引数组来替换之前的顶点数组。

          4、资源流的设定,你要借住顶点来与渲染流接轨。

          5、依旧设置你的灵活顶点格式

          6、通过SetIndices(IDirect3DIndexBuffer*),这个函数将你的索引指针与其代表的索引数组链接起来了。

          7、绘制图元调用DrawIndexPrimitive(...)来正式渲染基于索引缓冲区模式的渲染。

                                                                                                      2010年8月19日   星期四晚

                                                                                                               阿舰 于日照

你可能感兴趣的:(数据结构,工作,manager,存储,文档,vb)