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
);
● 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 指向我们希望与给定数据流建立链接的顶点缓存的指针。
● 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;
}
运行效果如下:
上一篇: hutool工具中的雪花算法