欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Direct3D中的绘制

程序员文章站 2022-07-13 10:10:33
...

一、顶点缓存与索引缓存

         
         一个顶点缓存是一个包含顶点数据的连续内存空间,类似点,一个索引缓存是一个包含索引数据的连续内存空间。我们之所以用顶点缓存和索引缓存而非数组来存储数据,是因为顶点缓存和索引缓存可以被放置在显存(video memory)中。运行绘制时,使用显存中的数据将获得比使用系统内存中的数据快得多的绘制速度。

1、创建顶点缓存和索引缓存


      创建方法如下:

    HRESULT IDirect3DDevice::CreateVertexBuffer(
             UINT               Length,
             DWORD         Usage,
             DWORD         FVF,
             D3DPOOL      Pool,
             IDirect3DVertexBuffer9**  ppVertexBuffer,
             HANDLE*       pSharedHandle
       );



      HRESULT IDirect3DDevice::CreateIndexBuffer(
             UINT                 Length,
             DWORD           Usage,
             D3DFORMAT   Fromat,
             D3DPOOL        Pool,
             IDirect3DIndexBuffer9**  ppIndexBuffer,
             HANDLE*         pSharedHandle
       );


     ●  Length表示为缓存分配的字节数
     ●  Usage指定关于如何使用缓存的一些附加属性。该值可以为0(表明无需附加属性)或是以下标记中的某一个或某种组合:
         D3DUSAGE_DYNAMIC   将缓存设为动态缓存。
         D3DUSAGE_POINTS      该标记规定缓存将用于存储点图元,该标记仅用于顶点缓存。
         D3DUSAGE_SOFTWAREPROCESSING   指定软件顶点运算方式。
         D3DUSAGE_WRITEONLY    规定应用程序对缓存的操作模式为“只写”。
     ●  FVF 存储在顶点缓存中顶点的灵活顶点格式。
     ●  Pool  容纳缓存的内存池。
     ●  ppVertexBuffer   用于接收所创建的顶点缓存的指针。
     ●  pSharedHandle   不使用,该值设为0。
     ●  Format   指定索引的大小。设为D3DFMT_INDEX16表示16位索引,设为D3DFMT_INDEX32表示32位索引。
     ●  ppIndexBuffer   用于接收所创建的索引缓存的指针。
     

2、访问缓存内容


       为了访问顶点缓存或索引缓存中的数据,我们需要获得指向缓存内部的存储区的指针。可以借助方法Lock来获取指向缓存内容的指针,对缓存访问完毕后,必须借助方法Unlock对缓存进行解锁。
 
      
      HRESULT IDirect3DVertexBuffer9::Lock(
             UINT               OffsetToLock,
             UINT               SizeToLock,
             BYTE**           ppbData,
             DWORD         Flags
          );

     HRESULT IDirect3DIndexBuffer9::Lock(
             UINT               OffsetToLock,
             UINT               SizeToLock,
             BYTE**           ppbData,
             DWORD         Flags
          );


     Direct3D中的绘制

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

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

      
      获取信息时需要用到以下两个结构体:

      typedef struct _D3DVERTEXBUFFER_DESC
      {
         D3DFORMAT       Format,
         D3DRESOURCETYPE   Type,
         DWORD               Usage,
         D3DPOOL            Pool,
         UINT                     Size,
         DWORD               FVF;
       }D3DVERTEXBUFFER_DESC; 


      typedef struct _D3DINDEXBUFFER_DESC
      {
         D3DFORMAT       Format,
         D3DRESOURCETYPE   Type,
         DWORD               Usage,
         D3DPOOL            Pool,
         UINT                     Size
       }D3DINDEXBUFFER_DESC; 

      
      使用方法如下:

       D3DVERTEXBUFFER_DESC   vbDescription;
       vertexbuffer->GetDesc(&vbDescription);


       D3DINDEXBUFFER_DESC   ibDescription;
       indexbuffer->GetDesc(&ibDescription);

二、绘制状态

         
         Direct3D封装了多种绘制状态,这些绘制状态将影响几何体的绘制方式。各种绘制状态均有默认值,所以仅当程序需要使用一种不同于默认值的绘制状态时,才需要对其进行修改。自指定某种绘制状态起,直至该状态被修改,该状态值始终有效。

         设置绘制状态的方法如下:
   
        HRESULT  IDirect3DDevice::SetRenderState(D3DRENDERSTATETYPE  State,  DWORD  Value);


三、绘制的准备工作

       
        创建顶点缓存和索引缓存后,在绘制之前还需要完成以下3个步骤:

1、指定数据流输入源


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

         HRESULT IDirect3DDevice9::SetStreamSource(
              UINT    StreamNumber,
              IDirect3DVertexBuffer9*    pStreamData,
              UINT    OffsetInBytes,
              UINT    Stride
          );

       ●  StreamNumber   标识与顶点缓存建立链接的数据流。
       ●  pStreamData      指向我们希望与给定数据流建立链接的顶点缓存的指针。
       ●  OffsetInBytes      自数据流的起始点算起的一个偏移量,单位为字节,指定了将被传输至绘制流水线的顶点数据的起始位置。
       ●   Stride                  将要链接到数据流的顶点缓存中的每个元素的大小,单位为字节。

      例如:
        g_pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof( CUSTOMVERTEX ) );
 

2、设置顶点格式


      指定后续绘制中调用中使用的顶点格式。
      例如:
          g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
       

3、设置索引缓存


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

       例如:
         g_pd3dDevice->SetIndices(g_pIB);

四、绘制

      
        对几何体进行绘制,也就是使用DrawPrimitive或DrawIndexedPrimitive方法将待绘制几何体的信息通过绘制流水线传输。这两个方法从顶点数据流中获取顶点信息,并从当前设定的索引缓存中提取索引信息。

1、IDirect3DDevice9::DrawPrimitive

      
      该方法可用于绘制未使用索引信息的图元。

      HRESULT   IDirect3DDevice9::DrawPrimitive(
          D3DPRIMITIVETYPE  PrimitiveType,
          UINT            StartVertex,
          UINT            PrimitiveCount
         );

       ● PrimitiveType  所要绘制的图元类型。Direct3D定义了6中图元类型,分别是:D3DPT_POINTLIST, D3DPT_LINELIST, D3DPT_LINESTRIP, D3DPT_TRIANGLELIST, D3DPT_TRIANGLESTRIP, D3DPT_TRIANGLEFAN。
       ●  StartVertex   顶点数据流中标识顶点数据读取起点的元素的索引。这使得我们可以只对顶点缓存中的某一部分进行绘制。
       ●  PrimitiveCount  所要绘制的图元数量。

      例如:
          g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 3, 1 );

2、IDirect3DDevice9::DrawIndexedPrimitive

   
      HRESULT   IDirect3DDevice9::DrawPrimitive(
          D3DPRIMITIVETYPE  PrimitiveType,
          INT               BaseVertexIndex,
          UINT            MinIndex,
          UINT            NumVertices,
          UINT            StartIndex,
          UINT            PrimitiveCount
          );

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

        参数BaseVertexIndex的使用方法见此文(DrawIndexedPrimitive的使用方法

        例如:
           g_pd3dDevice->D(D3DPT_TRIANGLELIST,0,0,4,0,2 );


3、Begin/End Scene


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


五、示例代码


              以下代码运用顶点缓存和索引缓存绘制一个彩色矩形。


#include <d3d9.h>
#pragma warning( disable : 4996 ) // disable deprecated warning 
#include <strsafe.h>
#pragma warning( default : 4996 )
#include <d3dx9math.h>

LPDIRECT3D9             g_pD3D = NULL; 
LPDIRECT3DDEVICE9       g_pd3dDevice = NULL; 
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; 
LPDIRECT3DINDEXBUFFER9   g_pIB = NULL;

struct CUSTOMVERTEX
{
    FLOAT x, y, z; 
    DWORD color; 
};

#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE)


HRESULT InitD3D( HWND hWnd )
{
    if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
        return E_FAIL;

    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory( &d3dpp, sizeof( d3dpp ) );
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;

    if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
                                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                      &d3dpp, &g_pd3dDevice ) ) )
    {
        return E_FAIL;
    }

    return S_OK;
}


HRESULT InitVB()
{
	 CUSTOMVERTEX vertices[4] = { 
		 -10, -10, 0, D3DCOLOR_XRGB(255, 0, 0),
	     -10,  10, 0, D3DCOLOR_XRGB(0, 255, 0),
	      10,  10, 0, D3DCOLOR_XRGB(0, 0, 255),
	      10,  -10, 0, D3DCOLOR_XRGB(255, 255, 0)
	 };

    if( FAILED( g_pd3dDevice->CreateVertexBuffer( sizeof( vertices ),
                                                  0, D3DFVF_CUSTOMVERTEX,
                                                  D3DPOOL_DEFAULT, &g_pVB, NULL ) ) )
    {
        return E_FAIL;
    }

	VOID* pVertices;
    if( FAILED( g_pVB->Lock( 0, sizeof( vertices ), ( void** )&pVertices, 0 ) ) )
        return E_FAIL;
    memcpy( pVertices, vertices, sizeof( vertices ) );
    g_pVB->Unlock();

	

	if( FAILED( g_pd3dDevice->CreateIndexBuffer( 6 * sizeof( WORD ),
                                                  0, D3DFMT_INDEX16,
                                                  D3DPOOL_DEFAULT, &g_pIB, NULL ) ) )
	{
		return E_FAIL;
	}

	WORD* pIndices = NULL;
	g_pIB->Lock(0, 0, (void**)&pIndices,0);
	pIndices[0] = 0, pIndices[1] = 3, pIndices[2] = 1, pIndices[3] = 1, pIndices[4] = 3, pIndices[5] = 2;
	g_pIB->Unlock();
    return S_OK;
}


VOID Cleanup()
{
    if( g_pVB != NULL )
        g_pVB->Release();

	if( g_pIB != NULL )
        g_pIB->Release();

    if( g_pd3dDevice != NULL )
        g_pd3dDevice->Release();

    if( g_pD3D != NULL )
        g_pD3D->Release();
}

VOID SetupMatrices()
{

	D3DXVECTOR3 vEyePt(0.0f, 0.0f, -20);
	D3DXVECTOR3 vLookatPt(0.0f, 0.0f, 0.0f);
	D3DXVECTOR3 vUpVec(0.0f, 1.0f, 0.0f);
	D3DXMATRIXA16 matView;
	D3DXMatrixLookAtLH(&matView, &vEyePt, &vLookatPt, &vUpVec);
	g_pd3dDevice->SetTransform(D3DTS_VIEW, &matView);

	//D3DXMatrixPerspectiveFovLH()函数中的最远、最近距离为相对于视点的距离(即vEyePt中的距离)
	D3DXMATRIXA16 matProj;
	D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI / 2, 1.0f, 1.0f, 200.0f);
	g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj);
}


VOID Render()
{
    g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(255, 255, 255 ), 1.0f, 0 );

    if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
    { 
		SetupMatrices();

        g_pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof( CUSTOMVERTEX ) );
        g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
		g_pd3dDevice->SetIndices(g_pIB);

		g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );
		g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
		g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,0,4,0,2 );
        g_pd3dDevice->EndScene();
    }

    // Present the backbuffer contents to the display	
    g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}


LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
        case WM_DESTROY:
            Cleanup();
            PostQuitMessage( 0 );
            return 0;
    }

    return DefWindowProc( hWnd, msg, wParam, lParam );
}


INT WINAPI wWinMain( HINSTANCE hInst, HINSTANCE, LPWSTR, INT )
{
    
    // Register the window class
    WNDCLASSEX wc =
    {
        sizeof( WNDCLASSEX ), CS_CLASSDC, MsgProc, 0L, 0L,
        GetModuleHandle( NULL ), NULL, NULL, NULL, NULL,
        "D3D Tutorial", NULL
    };
    RegisterClassEx( &wc );

    // Create the application's window
    HWND hWnd = CreateWindow( "D3D Tutorial", "D3D Tutorial: ColorTriangle",
                              WS_OVERLAPPEDWINDOW, 100, 100, 600, 600,
                              NULL, NULL, wc.hInstance, NULL );

    if( SUCCEEDED( InitD3D( hWnd ) ) )
    {
        // Create the vertex buffer and index buffer
        if( SUCCEEDED( InitVB() ) )
        {
            // Show the window
            ShowWindow( hWnd, SW_SHOWDEFAULT );
            UpdateWindow( hWnd );

            // Enter the message loop
            MSG msg;
            ZeroMemory( &msg, sizeof( msg ) );
            while( msg.message != WM_QUIT )
            {
                if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
                {
                    TranslateMessage( &msg );
                    DispatchMessage( &msg );
                }
                else
                    Render();
            }
        }
    }

    UnregisterClass( "D3D Tutorial", wc.hInstance );
    return 0;
}


     运行效果如下:

      Direct3D中的绘制

相关标签: DirectX9 3D