编程一个或多个流 (Direct3D 9)
本部分介绍可用于可编程流模型的着色器。
DirectX 8 引入了将数据绑定到供着色器使用的输入寄存器的流的概念。 流是组件数据的统一数组,其中每个组件由一个或多个元素组成,这些元素表示单个实体,例如位置、普通、颜色等。 流使图形芯片能够并行执行来自多个顶点缓冲区的直接内存访问,并提供来自应用程序数据的更自然的映射。 它们还启用简单多文本与多通道。 想一想, 如下所示:
- 顶点由 n 个流组成。
- 流由 m 元素组成。
- 元素为 [position, color, normal, texture coordinate]。
IDirect3DDevice9::SetStreamSource 方法将顶点缓冲区绑定到设备数据流,从而在顶点数据与源基元处理函数的多个数据流端口之一之间创建关联。 调用绘图方法(如 IDirect3DDevice9::D rawPrimitive)之前,才会实际引用流数据。
输入顶点元素到可编程顶点着色器的顶点输入寄存器的映射在着色器声明中定义,但输入顶点元素没有有关它们的使用的特定语义。 输入顶点元素的解释是使用着色器指令编程的。 顶点着色器函数由应用于每个顶点的指令数组定义。 使用着色器函数中的指令显式写入顶点输出寄存器。
不过,对于此讨论,不太关心元素到寄存器的语义映射,更关心使用流的原因以及使用流解决的问题。 流的主要好处是,它们删除了以前与多文本关联的顶点数据成本。 在流之前,用户必须复制顶点数据集来处理没有未使用的数据元素的单文本和多文本事例,或者携带未使用的数据元素(多文本大小写除外)。
下面是使用两组顶点数据的示例,一组用于单个纹理,一组用于多文本。
struct CUSTOMVERTEX_TEX1
{
FLOAT x, y, z; // The untransformed position for the vertex
DWORD diffColor; // The vertex diffuse color
DWORD specColor; // The vertex specular color
float tu_1, tv_1; // Texture coordinates for a single texture
};
struct CUSTOMVERTEX_TEX2
{
FLOAT x, y, z; // The untransformed position for the vertex
DWORD diffColor; // The vertex diffuse color
DWORD specColor; // The vertex specular color
float tu_2, tv_2; // Texture coordinates for multitexturing
};
替代方法是有一个包含这两组纹理坐标的顶点元素。
struct CUSTOMVERTEX_TEX2
{
FLOAT x, y, z; // The untransformed position for the vertex
DWORD diffColor; // The vertex diffuse color
DWORD specColor; // The vertex specular color
float tu_1, tv_1; // Texture coordinates for a single texture
float tu_2, tv_2; // Texture coordinates for multitexturing
};
使用此顶点数据时,只有一个位置副本和颜色数据在内存中携带,代价是在单个纹理大小写中携带这两组纹理坐标来呈现。
现在权衡是明确的,流提供了一个优雅的修复这一困境。 下面是一组顶点定义以支持三个流:一个具有位置和颜色,一个具有第一组纹理坐标,另一个具有第二组纹理坐标。
// Multistream vertex
// Stream 0, pos, diffuse, specular
struct POSCOLORVERTEX
{
FLOAT x, y, z;
DWORD diffColor, specColor;
};
#define D3DFVF_POSCOLORVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_SPECULAR)
// Stream 1, tex coord 0
struct TEXC0VERTEX
{
FLOAT tu1, tv1;
};
#define D3DFVF_TEXC0VERTEX (D3DFVF_TEX1)
// Stream 2, tex coord 1
struct TEXC1VERTEX
{
FLOAT tu2, tv2;
};
#define D3DFVF_TEXC1VERTEX (D3DFVF_TEX0)
顶点声明如下所示:
// Multitexture - multistream
D3DVERTEXELEMENT9 dwDecl3[] =
{
{0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT,
D3DDECLUSAGE_POSITION, 0},
{0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT,
D3DDECLUSAGE_COLOR, 0},
{0, 16, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT,
D3DDECLUSAGE_COLOR, 1},
{1, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT,
D3DDECLUSAGE_TEXCOORD, 0},
{2, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT,
D3DDECLUSAGE_TEXCOORD, 1},
D3DDECL_END()
};
现在创建顶点声明对象并将其设置为如下所示:
LPDIRECT3DVERTEXDECLARATION9 m_pVertexDeclaration;
g_d3dDevice->CreateVertexDeclaration(dwDecl3, &m_pVertexDeclaration);
m_pd3dDevice->SetVertexDeclaration(m_pVertexDeclaration);
漫射颜色呈现的顶点声明和流设置如下所示:
D3DVERTEXELEMENT9 dwDecl3[] =
{
{0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT,
D3DDECLUSAGE_POSITION, 0},
{0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT,
D3DDECLUSAGE_COLOR, 0 ,
{0, 16, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT,
D3DDECLUSAGE_COLOR, 1},
D3DDECL_END()
};
m_pd3dDevice->SetStreamSource(0, m_pVBVertexShader0, 0,
sizeof(CUSTOMVERTEX));
m_pd3dDevice->SetStreamSource(1, NULL, 0, 0);
m_pd3dDevice->SetStreamSource(2, NULL, 0, 0);
单个纹理呈现的顶点声明和流设置如下所示:
D3DVERTEXELEMENT9 dwDecl3[] =
{
{0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT,
D3DDECLUSAGE_POSITION, 0},
{0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT,
D3DDECLUSAGE_COLOR, 0},
{0, 16, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT,
D3DDECLUSAGE_COLOR, 1},
{1, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT,
D3DDECLUSAGE_TEXCOORD, 0},
D3DDECL_END()
};
m_pd3dDevice->SetStreamSource(0, m_pVBPosColor, 0,
sizeof(POSCOLORVERTEX));
m_pd3dDevice->SetStreamSource(1, m_pVBTexC0, 0,
sizeof(TEXC0VERTEX));
m_pd3dDevice->SetStreamSource(2, NULL, 0, 0);
双纹理多纹理呈现的顶点声明和流设置如下所示:
D3DVERTEXELEMENT9 dwDecl3[] =
{
{0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT,
D3DDECLUSAGE_POSITION, 0},
{0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT,
D3DDECLUSAGE_COLOR, 0},
{0, 16, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT,
D3DDECLUSAGE_COLOR, 1},
{1, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT,
D3DDECLUSAGE_TEXCOORD, 0},
{2, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT,
D3DDECLUSAGE_TEXCOORD, 1},
D3DDECL_END()
};
m_pd3dDevice->SetStreamSource(0, m_pVBPosColor, 0,
sizeof(POSCOLORVERTEX));
m_pd3dDevice->SetStreamSource(1, m_pVBTexC0, 0,
sizeof(TEXC0VERTEX));
m_pd3dDevice->SetStreamSource(2, m_pVBTexC1, 0,
sizeof(TEXC1VERTEX));
在每种情况下,以下 IDirect3DDevice9::D rawPrimitive 调用就足够了。
m_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, NUM_TRIS);
这显示了流在解决跨总线的数据重复/冗余数据传输问题(即浪费带宽)方面的灵活性。