D3D中多流的用法

D3D中多流的用法

 

(本文为了方便起见,把DirectX Graphics称为D3D)



  在D3D中,要渲染的顶点数据是放在Vertex Buffer中的,一般的做法是把顶点坐标、颜色等按照自定义的顶点格式放在同一个Vertex Buffer中,代表一个流,然后通过FVF来告诉D3D要渲染的顶点的格式。但是在某些情况下,放在一起并不是最佳选择。比如在动画中,每一帧的顶点坐标都在变化,但是纹理坐标却都不变(md3格式就是如此)。如果只用一个流,每画一帧就得把所有数据都拷贝到一个临时Vertex Buffer中再渲染,开销巨大。如果能把不同的数据放在不同的流中,就可以只更新顶点坐标的流,而纹理坐标的流就可以保持不变了。而且,只用一个Vertex Buffer时要管理FVF和数据结构体之间的关系,万一弄错了,是很难发现的。

  如果使用多流,麻烦就能少些。下面就让我们看看如何在D3D中实现多流。这个例子很简单,显示一个三角形,三个顶点的颜色分别是红、绿、蓝。因为本例的数据只有顶点坐标和颜色两种数据,所以只有两个Vertex Buffer。同时,由于D3D8在使用多流时必须使用Shader来声明顶点格式,所以声明一个g_Shader变量。整个程序是这样的:

#include <d3d8.h>
#include <d3dx8.h>

#pragma comment(lib, "d3d8.lib")
#pragma comment(lib, "d3dx8.lib")

template <typename T>
inline void SAFE_RELEASE(T& p)
{
    if (p != 0)
   
{
   
    p->Release();
   
    p = 0;
   
}
}

IDirect3D8* g_pD3D = NULL;
IDirect3DDevice8* g_pd3dDevice = NULL;
IDirect3DVertexBuffer8* g_pVBPos = NULL;
IDirect3DVertexBuffer8* g_pVBCol = NULL;
DWORD g_Shader = 0xFFFFFFFF;

void InitD3D(HWND hWnd)
{
   
g_pD3D = Direct3DCreate8(D3D_SDK_VERSION);

   
D3DDISPLAYMODE d3ddm;
   
g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);

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

   
g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pd3dDevice);

   
g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);

   
D3DXMATRIX matWorld;
   
D3DXMatrixIdentity(&matWorld);
   
g_pd3dDevice->SetTransform(D3DTS_WORLD, &matWorld);

   
D3DXMATRIX matView;
   
D3DXMatrixLookAtLH(&matView, &D3DXVECTOR3(0, 0, -3), &D3DXVECTOR3(0, 0, 0), &D3DXVECTOR3(0, 1, 0));
   
g_pd3dDevice->SetTransform(D3DTS_VIEW, &matView);

   
D3DXMATRIX matProj;
   
D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI / 4, 1, 0.1f, 10);
   
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj);
}

void InitVB()
{
   
// 多个流的格式
   
DWORD shaderDecl[] =
   
{
   
    D3DVSD_STREAM(0),
   
    D3DVSD_REG(D3DVSDE_POSITION, D3DVSDT_FLOAT3),

   
    D3DVSD_STREAM(1),
   
    D3DVSD_REG(D3DVSDE_DIFFUSE, D3DVSDT_D3DCOLOR),

   
    D3DVSD_END(),
   
};

   
// 位置数据
   
D3DVECTOR Positions[] =
   
{
   
    { -1, -1, 1 },
   
    { 0, 1, 1 },
   
    { 1, -1, 1 },
   
};

   
// 颜色数据
   
D3DCOLOR Colors[] =
   
{
   
    D3DCOLOR_XRGB(0xFF, 0, 0),
   
    D3DCOLOR_XRGB(0, 0xFF, 0),
   
    D3DCOLOR_XRGB(0, 0, 0xFF),
   
};

   
// 建立两个VB
   
g_pd3dDevice->CreateVertexBuffer(3 * sizeof(D3DVECTOR), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &g_pVBPos);
   
g_pd3dDevice->CreateVertexBuffer(3 * sizeof(D3DCOLOR), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &g_pVBCol);

   
// 建立VS
   
g_pd3dDevice->CreateVertexShader(shaderDecl, NULL, &g_Shader, 0);

   
// 填充数据
   
void* pPositions;
   
g_pVBPos->Lock(0, 0, reinterpret_cast<BYTE**>(&pPositions), 0);
   
memcpy(pPositions, Positions, sizeof(Positions));
   
g_pVBPos->Unlock();

   
void* pColor;
   
g_pVBCol->Lock(0, 0, reinterpret_cast<BYTE**>(&pColor), 0);
   
memcpy(pColor, Colors, sizeof(Colors));
   
g_pVBCol->Unlock();
}

void Cleanup()
{
   
// 释放两个流
   
g_pd3dDevice->SetStreamSource(0, NULL, sizeof(D3DVECTOR));
   
g_pd3dDevice->SetStreamSource(1, NULL, sizeof(D3DCOLOR));

   
// 删除VS
   
g_pd3dDevice->DeleteVertexShader(g_Shader);

   
SAFE_RELEASE(g_pVBPos);
   
SAFE_RELEASE(g_pVBCol);

   
SAFE_RELEASE(g_pd3dDevice);
   
SAFE_RELEASE(g_pD3D);
}

void Render()
{
   
g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0x33, 0x66, 0x99), 1.0f, 0);

   
g_pd3dDevice->BeginScene();

   
g_pd3dDevice->SetVertexShader(g_Shader);
   
g_pd3dDevice->SetStreamSource(0, g_pVBPos, sizeof(D3DVECTOR));
   
g_pd3dDevice->SetStreamSource(1, g_pVBCol, sizeof(D3DCOLOR));
   
g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);

   
g_pd3dDevice->EndScene();

   
g_pd3dDevice->Present(NULL, NULL, NULL, NULL);
}

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

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

LPCTSTR wcName(TEXT("Multistream"));

INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR, INT)
{
   
WNDCLASS wc;
   
wc.style = CS_HREDRAW | CS_VREDRAW;
   
wc.lpfnWndProc = MsgProc;
   
wc.cbClsExtra = 0;
   
wc.cbWndExtra = 0;
   
wc.hInstance = hInst;
   
wc.hIcon = NULL;
   
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
   
wc.hbrBackground = static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
   
wc.lpszMenuName = NULL;
   
wc.lpszClassName = wcName;
   
RegisterClass(&wc);

   
HWND hWnd(CreateWindow(wcName, wcName, WS_OVERLAPPEDWINDOW, 100, 100, 300, 300, GetDesktopWindow(), NULL, wc.hInstance, NULL));

   
InitD3D(hWnd);
   
InitVB();

   
ShowWindow(hWnd, SW_SHOWDEFAULT);
   
UpdateWindow(hWnd);

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

   
Cleanup();
   
UnregisterClass(wcName, wc.hInstance);

   
return 0;
}


  这里把顶点坐标放在一个VB中,而把颜色放在另一个VB中,也终于看到了SetStreamSource的第一个参数不是0的情况:-)。由于使用了两个流,所以设置了0号和1号流,分别为前面填充的顶点坐标流和颜色流。然后建立Vertex Shaer。由于这里只使用固定的pipeline,所以CreateVertexShader的第二参数为NULL。要注意的是,SetVertexShader的参数是Verter Shader的句柄,而不是FVF。如果没有CreateVertexShader,而想当然地直接把FVF的值作为参数传给SetVertexShader,则会出现结果未定义的情况。


DX9的情况

D3D9对流的部分作了不少改进,所以,我们要写的代码也得做一些更改。全局变量改为:

IDirect3D9* g_pD3D = NULL;
IDirect3DDevice9* g_pd3dDevice = NULL;
IDirect3DVertexBuffer9* g_pVBPos = NULL;
IDirect3DVertexBuffer9* g_pVBCol = NULL;
IDirect3DVertexDeclaration9* g_pVertexDeclaration = NULL;


前四个变量不用说了,只是把8变成9。而IDirect3DVertexDeclaration9是D3D9新增的,专门用于声明流的格式,相当于一个功能更强大的FVF。这回不用声明Vertex Shader了。

InitD3D中只需把

g_pD3D = Direct3DCreate8(D3D_SDK_VERSION);
改为

g_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
就行了。

InitVB改为:

void InitVB()
{
    D3DVERTEXELEMENT9 shaderDecl[] =
    {
        { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
        { 1, 0, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0 },
        { 0xFF, 0, D3DDECLTYPE_UNUSED, 0, 0, 0 },
    };

    D3DVECTOR Positions[] =
    {
        { -1, -1, 1 },
        { 0, 1, 1 },
        { 1, -1, 1 },
    };

    D3DCOLOR Colors[] =
    {
        D3DCOLOR_XRGB(0xFF, 0, 0),
        D3DCOLOR_XRGB(0, 0xFF, 0),
        D3DCOLOR_XRGB(0, 0, 0xFF),
    };

    g_pd3dDevice->CreateVertexBuffer(3 * sizeof(D3DVECTOR), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &g_pVBPos, NULL);
    g_pd3dDevice->CreateVertexBuffer(3 * sizeof(D3DCOLOR), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &g_pVBCol, NULL);

    g_pd3dDevice->CreateVertexDeclaration(shaderDecl, &g_pVertexDeclaration);

    void* pPositions;
    g_pVBPos->Lock(0, 0, &pPositions, 0);
    memcpy(pPositions, Positions, sizeof(Positions));
    g_pVBPos->Unlock();

    void* pColor;
    g_pVBCol->Lock(0, 0, &pColor, 0);
    memcpy(pColor, Colors, sizeof(Colors));
    g_pVBCol->Unlock();
}

在DX9中应该通过D3DVERTEXELEMENT9来声明数据格式,所以shaderDecl部分做了变化,并用CreateVertexDeclaration建立了一个VertexDeclaration。

最后在Render中把

g_pd3dDevice->SetVertexShader(g_Shader);
换成

g_pd3dDevice->SetVertexDeclaration(g_pVertexDeclaration);
这就是多流的DX9版本。


总结

灵活运用多流,能提高渲染效率,减少出错的可能性。如果要做跨API的渲染系统,应该优先考虑使用多流。


参考
DX SDK
OGRE

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值