• Direct3D中的绘制


    顶点缓存与索引缓存

    一个顶点缓存是一个包含顶点数据的连续内存空间,一个索引缓存是一个包含索引数据的连续内存空间,之所以使用顶点缓存和索引缓存而非数组来存储数据,是因为顶点缓存和索引缓存可以被放置在显存中,进行绘制时,使用显存中的数据将获得比使用系统内存中的数据快的多的绘制速度。代码中顶点缓存使用接口IDirect3DVertexBuffer9表示,索引缓存使用接口IDirect3DIndexBuffer9表示。

    1. HRESULT IDirect3DDevice9::CreateVertexBuffer(
    2. UINT Length,
    3. DWORD Usage,
    4. DWORD FVF, //存储在顶点缓存中顶点的灵活顶点格式
    5. D3DPOOL Pool, //容纳缓存的内存池
    6. IDirect3DVertexBuffer9** ppVertexBuffer, //用于接收所创建的顶点缓存的指针
    7. HANDLE* pSharedHandle //不使用,设置为0
    8. );
    9. HRESULT IDirect3DDevice9::CreateIndexBuffer(
    10. UINT Length,
    11. DWORD Usage,
    12. D3DFORMAT Format,
    13. D3DPOOL Pool,
    14. IDirect3DIndexBuffer9** ppIndexBuffer, //用于接收所创建的索引缓存的指针
    15. HANDLE* pSharedHandle
    16. );

    Length:为缓存分配的字节数,如果想让顶点缓存足够存储8个顶点,改参数需设为8*sizeof(Vertex),Vertex是定义顶点的结构

    Usage:指定关于如何使用缓存的一些附加属性,该值为可为0(表示无需附加属性)或是以下标记中的某一个或某种组合
    D3DUSAGE_DYNAMIC:将缓存设为动态缓存
    D3DUSAGE_POINTS:规定缓存将用于存储点图元
    D3DUSAGE_SOFTWAREPROCESSING:指定软件顶点运算方式
    D3DUSAGE_WRITEONLY:规定应用程序对缓存的操作模式为"只写",这样驱动程序就可以将缓存放在最适合写操作的内存地址中,对使用该标记创建的缓存进行读操作将会出错。

    Format:指定索引大小,设置为D3DFMT_INDEX16表示16位索引,设为D3DFMT_INDEX32表示32位索引,并非所有图形设备都支持32位索引,如需使用请检查硬件性能

    静态缓存:创建缓存时未使用标记D3DUSAGE_DYNAMIC,则为静态缓存,静态缓存一般被放置在显存中,以保证存储于其中的数据得到最高效的处理,静态缓存是以牺牲对静态缓存读写操作的速度为代价的,这是因为访问显存的速度本身就很慢,所以我们用静态缓存来存储静态数据(那些不需要经常修改或访问的数据)。例如地形和城市建筑数据就很适合存储在静态缓存中,这类数据在程序运行时通常不需要进行修改,静态缓存必须在应用程序初始化时用几何体的数据进行填充。

    动态缓存:创建缓存时使用标记D3DUSAGE_DYNAMIC,则为动态缓存,动态缓存一般放置在AGP存储区中,其内容可被迅速更新,动态缓存中数据的处理速度不像静态缓存那样快,这是因为在绘制前数据必须传输到显存中,但动态缓存的突出优点是其更新速度相当快(快速的CPU写操作)。所以如果需要频繁更新缓存中的内容,该缓存应设置为动态的。粒子系统就是使用动态缓存的一个很好例子,因为粒子是活动的,所以需要对每帧图像中粒子的几何布局进行更新。另外,对显存和AGP存储区进行读操作非常慢,所以需要在程序运行时读取几何数据,最好在系统内存中保留一份副本,然后在需要时对其进行读操作

    1. //创建一个可容纳8个Vertex类型的顶点静态顶点缓存
    2. IDirect3DVertexBuffer9* vb;
    3. Device->CreateVertexBuffer(
    4. 8 * sizeof(Vertex),
    5. 0,
    6. D3DFVF_XYZ,
    7. D3DPOOL_MANAGED,
    8. &vb,
    9. 0);
    10. //创建可容纳36个16位索引的动态索引缓存
    11. IDirect3DIndexBuffer9* ib;
    12. Device->CreateIndexBuffer(
    13. 36 * sizeof(WORD),
    14. D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY,
    15. D3DFMT_INDEX16,
    16. D3DPOOL_MANAGED,
    17. &ib,
    18. 0);

    访问缓存内容

    为了访问缓存中的数据,需要获得指向缓存内部存储区的指针,可以借助方法Lock来获取指向缓存内容的指针,对缓存访问完毕后,必须对缓存进行解锁unlock,只要获取了指向缓存内容的指针,就可以对其内容进行读写操作(如果使用了D3DUSAGE_WRITEONLY标记,则无法对该缓存进行读操作,否则会导致读取失败)。

    1. HRESULT IDirect3DVertexBuffer9::Lock(
    2. UINT OffsetToLock,
    3. UINT SizeToLock,
    4. void** ppbData,
    5. DWORD Flags
    6. );
    7. HRESULT IDirect3DIndexBuffer9::Lock(
    8. UINT OffsetToLock,
    9. UINT SizeToLock,
    10. void** ppbData,
    11. DWORD Flags
    12. );

    OffsetToLock:自缓存的起始点开始锁定的位置的偏移量,单位为字节
    SizeToLock:所要锁定的字节数
    ppbData:指向被锁定的存储区起始位置的指针
    Flags:该标记描述了锁定的方式,可以是0,也可是下列标记之一或某种组合
    D3DLOCK_DISCARD:该标记仅用于动态缓存它指示硬件将缓存内容丢弃,并返回一个指向重新分配的缓存的指针,该标记十分有用,因为这允许在我们访问新分配的内存时,硬件能够继续使用被丢弃的缓存中的数据进行绘制,这样硬件的绘制就不会中止。
    D3DLOCK_NOOVERWRITE:该标记仅用于动态缓存,使用该标记后,数据只能以追加方式写入缓存,即不能覆盖当前用于绘制的存储区中任何内容,这十分有用,因为它可保证在往缓存中增加数据时,硬件仍可持续进行绘制。
    D3DLOCK_READONLY:该标记表示对于锁定的缓存只可读而不可写,利用这一点可以作一些内部优化。

    标记D3DLOCK_DISCARDD3DLOCK_NOOVERWRITE表明缓存区的某一部分在锁定之后可以使用(用于绘制),如果环境(硬件配置)允许使用这些标记,在对缓存进行锁定时,其他操作就不会中断

    获取顶点缓存和索引缓存的信息

    1. D3DVERTEXBUFFER_DESC vbDescription;
    2. vertexBuffer->GetDesc(&vbDescription); //get vb info
    3. D3DINDEXBUFFER_DESC ibDescription;
    4. indexBuffer->GetDesc(&ibDescription); //get ib info
    1. /* Vertex Buffer Description */
    2. typedef struct _D3DVERTEXBUFFER_DESC
    3. {
    4. D3DFORMAT Format;
    5. D3DRESOURCETYPE Type;
    6. DWORD Usage;
    7. D3DPOOL Pool;
    8. UINT Size;
    9. DWORD FVF;
    10. } D3DVERTEXBUFFER_DESC;
    11. /* Index Buffer Description */
    12. typedef struct _D3DINDEXBUFFER_DESC
    13. {
    14. D3DFORMAT Format;
    15. D3DRESOURCETYPE Type;
    16. DWORD Usage;
    17. D3DPOOL Pool;
    18. UINT Size;
    19. } D3DINDEXBUFFER_DESC;

    绘制状态

    Direct3D封装了多种绘制状态,这些绘制状态将影响几何体的绘制方式,各种绘制状态都有默认值,自指定某种绘制状态起,直至该状态被修改,该状态始终有效。

    1. HRESULT IDirect3DDevice9::SetRenderState(
    2. D3DRENDERSTATETYPE State, //绘制状态枚举
    3. DWORD Value //状态对应的具体枚举值
    4. );

    绘制的准备工作

    一旦我们创建了顶点缓存以及索引缓存(可选),基本上已经可以准备对其所存储的内容进行绘制了,在绘制前还有3个步骤需要完成。

    1.指定数据流输入源:将顶点缓存和数据流进行链接,实质上是将几何体的信息传输到绘制流水线中。

    1. HRESULT IDirect3DDevice9::SetStreamSource(
    2. UINT StreamNumber,
    3. IDirect3DVertexBuffer9* pStreamData,
    4. UINT OffsetInBytes,
    5. UINT Stride
    6. );

    StreamNumber:标识与顶点缓存建立链接的数据流,如果不使用多个流,则设置为0即可
    pStreamData:指向我们希望与给定数据流建立链接的顶点缓存的指针
    OffsetInBytes:自数据流的起始点算起的一个偏移量,单位为字节,指定了将被传输至绘制流水线的顶点数据的起始位置,如果想将该参数设为某一个非0值,请务必检查D3DCAPS9结构中的D3DDEVCAPS2_STREAMOFFSET标记,已判断设备是否支持该功能(顶点运算功能校验)。
    Stride:将要链接到数据流的顶点缓存中每个元素的大小,单位为字节

    Device->SetStreamSource(0, vb, 0, sizeof(Vertex));

    2.设置顶点格式:在这里指定后续绘制调用中使用的顶点格式

    Device->SetFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1);

    3.设置索引缓存:如果我们想使用索引缓存,必须对后续绘制操作中所要使用的索引缓存进行设置,任意时刻只允许使用一个索引缓存,如果要使用一个不同的索引缓存绘制物体时,必须进行切换

    Device->SetIndices(_ib); //参数为IDirect3DIndexBuffer9指针

    使用顶点缓存和索引缓存进行绘制

    缓存创建完毕且准备工作完成后,便可对几何体进行绘制,也就是使用DrawPrimitiveDrawIndexedPrimitive方法将待绘制几何体的信息通过绘制流水线传输,这两个方法从顶点数据流中获取顶点信息,并从当前设定的索引缓存中提取索引信息。

    顶点缓存绘制

    1. HRESULT IDirect3DDevice9::DrawPrimitive(
    2. D3DPRIMITIVETYPE PrimitiveType,
    3. UINT StartVertex,
    4. UINT PrimitiveCount
    5. );
    6. Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 4);

    PrimitiveType:所要绘制的图元类型,绘制三角形则使用D3DPT_TRIANGLELIST,除此之外还可以绘制点和线。
    StartVertex:顶点数据流中标识顶点数据读取起点的元素索引,该参数赋予了我们一定自由度,使得我们可以只对顶点缓存中的某一部分进行绘制
    PrimitiveCount:所要绘制的图元数量

    索引缓存绘制

    1. HRESULT IDirect3DDevice9::DrawIndexedPrimitive(
    2. D3DPRIMITIVETYPE Type,
    3. INT BaseVertexIndex,
    4. UINT MinIndex,
    5. UINT NumVertices,
    6. UINT StartIndex,
    7. UINT PrimitiveCount
    8. );
    9. Device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 8, 0, 12);

    Type:所要绘制的图元类型
    BaseVertexIndex:为索引增加一个基数
    MinIndex:允许引用的最小索引值
    NumVertices:本次调用中将引用的顶点总数
    StartIndex:顶点缓存中标识索引的读取起始点的元素的索引
    PrimitiveCount:所要绘制的图元总数

    参数BaseVertexIndex值得一提,局部索引缓存的内容应与局部顶点缓存中的顶点一致,假定我们想将球、盒以及圆柱体的顶点合并到同一个全局缓存中,对于每个物体我们必须重新计算索引,确保这些索引能正确的指向全局顶点缓存中对应的顶点,新索引的计算方法是为每个索引增加一个指定了物体顶点在全局缓存中存储的起始位置的偏移量,偏移量用顶点个数来度量。

    Begin/End Scene

    所有的绘制方法必须在IDirect3DDevice9::BeginScene和IDirect3DDevice9::EndScene构成的方法之间进行调用

    1. Device->BeginScene();
    2. Device->DrawPrimitive(...)
    3. Device->EndScene();
  • 相关阅读:
    Java 字符串的数字校验:isNumeric,isNumericSpace和正则表达式,对比分析
    python如何进行内存管理
    【lombok原理】无聊的周末一个人手写一个lombok
    LeetCode in Python 48. Rotate Image/Matrix (旋转图像/矩阵)
    web常见页面错误
    【游戏建模全流程】使用ZBrush制作龙模型
    云服务器中mongodb配置账号密码 图文讲解(全)
    MySQL高级十一:其它索引详解
    [交互]AJAX
    Reactor 之 手把手教你 Spring Boot 整合 Reactor
  • 原文地址:https://blog.csdn.net/SwordArcher/article/details/127283187