顶点着色器(vertex shader)是-一段运行在图形卡GPU中的程序,它可取代固定功能流水线中的变换和光照环节(当然,这也不是绝对的,因为在硬件不支持顶点着色器的情况下,Dict3D运行时就会用软件运算方式来模拟顶点着色器)
可以看出,顶点是以局部坐标(局部坐标系中的坐标)输入顶点着色器的,而且顶点着色器必须将照亮的(上色的)顶点输出到齐次裁剪空间中。由于顶点着色器其实就是我们用HLSL语言编写的一段定制程序,这样我们在可实现的图形效果上就获得了很大的灵活性。例如借助顶点着色器,我们就可使用任何可在顶点着色器中实现的光照算法。这样,我们就不再受制于Direct3D的固定功能流水线了。而且这种对顶点位置进行操作的能力具有广泛的应用场合,例织物模拟、粒子系统的点尺寸处理、顶点融合/变形技术(morphing)等。
此外,可用的顶点数据结构也更加灵活,而且可编程流水线中的顶点结构可以包含比固定功能流水线更加丰富的数据。顶点着色器仍然是一项相对比较新的特性,目前许多图形卡尚不支持该特性,尤其是伴随DirectX9发布的那些比较新的顶点着色器版本。您可通过检查D3DCAPS9结构的成员VertexShaderVersion并与宏D3DVS_VERSION进行比较,来检测您的图形卡是否支持某个顶点着色器版本。D3DVS_VERSION宏中的俩个参数分别表示主版本号和次版本号,目前D3DXCompileShaderFromFile函数支持的顶点着色器版本为1.1、2.0和3.0。
//If the device's supported version is less than version 2.0
if(caps.VertexShaderversion < D3DVS_VERSION(2,0))
//Then vertex shader version 2.0 is not supported on this device
顶点声明
到月前为止,我们一直都在用灵活顶点格式(FVF)来描述顶点结构的分量。但是在可编程流水线中,顶点结构甚至可以包含那些超出FVF描述能力的数据。因此,我们通常使用描述能力更强、功能更丰富的顶点声明(vertex declaration)。
在可编程流水线中,如果顶点结构可用FVF来描述,我们仍可使用它。但是这仅仅是为了表示方便,实际上在可编程流水线内部,FVF最终将被转换为顶点声明。
顶点声明的描述
我们将顶点声明描述为一个D3DVERTEXELEMENT9类型的结构数组。该结构数组中的每个元素都描述了顶点结构的一个分量。所以,如果您的顶点结构具有3个分量(比如位置、法向量、颜色),则相应的顶点声明就可用一个维数为3的D3DVERTEXELEMENT9类型的结构数组来描述。
typedef struct _D3DVERTEXELEMENT9
{
WORD Stream; // Stream index
WORD Offset; // Offset in the stream in bytes
BYTE Type; // Data type
BYTE Method; // Processing method
BYTE Usage; // Semantics
BYTE UsageIndex; // Semantic index
} D3DVERTEXELEMENT9, *LPD3DVERTEXELEMENT9;
Stream:指定与顶点分量关联的数据流
Offset:自顶点数据起始点到与特定数据类型相关的数据的字节偏移量,例如顶点的结构为
struct Vertex
{
D3DXVECTOR3 pos;
D3DXVECTOR3 normal;
};
由于分量pos为该结构的第一个分量,所以其相对偏移量为0。而由于sizeof(pos)=12,所以分量normal的相对偏移量为12,即分量normal的位置始于Vertex结构的第12个字节。
Type:指定数据类型,为枚举D3DDECLTYPE的任何一个成员,常用类型为:
- D3DDECLTYPE_FLOAT1 浮点类型标量(value,0,0,1)
- D3DDECLTYPE_FLOAT2 浮点类型的2D向量(value,value,0,1)
- D3DDECLTYPE_FLOAT3 浮点类型的3D向量(value,value,value,1)
- D3DDECLTYPE_FLOAT4 浮点类型的4D向量
- D3DDECLTYPE_D3DCOLOR 一个被扩展为RGBA浮点类型颜色向量(r,g,b,a)的D3DCOLOR类型,其中颜色向量的每个分量都被规范化至区间[0,1]内
Method:指定了网格化(tesselation)方法。由于参数涉及到一些高级主题,本书中我们仪使
用默认方法,即用标识符D3DDECLMETHOD_DEFAULT来指定。
Usage:指定了顶点分量的用途。例如某一分量是作为位置向量、法向量还是纹理向量等。合法的用法标识符都取自枚举类型D3DDECLUSAGE,D3DDECLUSAGE_PSIZE类型用于指定顶点的点尺寸。该类型主要用于点精灵(point sprite),这样我们就可对每个顶点的尺寸进行控制。如果一个顶点声明中具有D3DDECLUSAGE_POSITION,则表明该顶点已经过了变换,并指示图形卡不要将该顶点输送到顶点处理环节中(变换和光照处理)。
UsageIndex:用于标识具有同用法的多个顶点分量。用法索引(usage index)是一个位于区间[0,15]内的整数。例如现在假定我们有3个顶点分量的用法都为D3DDECLUSAGE NORMAL。则我们可按序将这3个顶点分量的用法索引分别指定为0、1、2。按照这种方式,我们就可通过用法索引表示每个特定的法向量。
顶点声明示例:假定我们所要描述的顶点格式包含了一个位置向量和3个法向量,则相应的顶点声明可指定为:
D3DVERTEXELEMENT9 decl[] =
{
{0,0,D3DDECLTYPE_FLOAT3,D3DDECLMETHOD_DEFAULT,D3DDECLUSAGE_POSITION,0},
{0,12,D3DDECLTYPE_FLOAT3,D3DDECLMETHOD_DEFAULT,D3DDECLUSAGE_NORMAL,0},
{0,24,D3DDECLTYPE_FLOAT3,D3DDECLMETHOD_DEFAULT,D3DDECLUSAGE_NORMAL,1},
{0,36,D3DDECLTYPE_FLOAT3,D3DDECLMETHOD_DEFAULT,D3DDECLUSAGE_NORMAL,2},
D3DDECL_END()
}:
其中,D3DDECL_END宏用于初始化D3 DVERTEXELEMENT9数组中的最后一个顶点元素。同时也请您注意一下不同法向量的用法索引。
顶点声明的创建
一旦将顶点声明描述为一个D3DVERTEXELEMENT9类型的数组,我们就可用CreateVertexDeclaration方法获得指向接IDirect3DVertexDeclaration9的指针:
HERSULT CreateVertexDeclaration(
CONST D3DVerTEXELEMENT9* pVertexElements,
IDirect3DVertexDeclaration9** ppDecl
);
//decl是一个D3DVerTEXELEMENT9类型的结构数组
IDirect3DVertexDeclaration9* _decl = 0;
hr = _device->CreateVertexDeclaration(decl,&_decl);
pVertexElements:指向-个D3DVERTEXELEMENT9类型的结构数组,该数组描述了我们想要创建的顶点声明。
ppDecl:用于返回一个指向所创建的IDirect3DVertexDeclaration9接口的指针。
顶点声明的启用
Device->SetFVF(fvf);
//可用下面的代替上面的代码 _decl为IDirect3DVertexDeclaration指针
Device->SetVertexDeclaration(_decl);
顶点数据的使用
我们需要一种方式来定义从顶点声明中的元素到顶点着色器的输入结构的数据成员的映射。我们在输入结构中通过为每个数据成员指定一种语义(:usage-type[usage-index])来定义这种映射。该语义通过用法类型和用法索引来标识顶点声明中的每个元素。由数据成员的语义所标识的那个顶点元素就是被映射到该数据成员的那个元素。例如,与前面提到的那个顶点声明对应的输入结构为:
struct VS_INPUT
{
vector position : POSITION;
vector normal : NORMAL0;
vector faceNormal1 : NORMAL1;
vector faceNormal2 : NORMAL2;
};
其中,decl数组中的由用法POSITION和用法索引0所标识的元素0被映射为输入结构VS_INPUT中的数据成员position。decl数组中的由用法NORMAL和用法索引0标识的元素1被映射为输入结构VS_ INPUT中的数据成员normal。.decl数组中的由用法NORMAL和用法索引1所标识的元素2被映射为输入结构VS_INPUT中的数据成员faceNormall。decl数组中的由用法NORMAL和用法索引2所标识的元素3被映射为输入结构VS_INPUT中的数据成员faceNormal2。如果我们略去了用法索引,就意味着该索引为0。例如,POSITION的含义与POSITION0完全相同。
顶点着色器支持的输入用法包括:n为可选整数,区间[0,15]
- POSITION[n] 位置
- BLENDWEIGHTS[n] 融合权值(blend weights)
- BLENDINDICES[n] 融合索引(blend indices)
- NORMAL[n] 法向量
- PSIZE[n] 顶点的点尺寸
- DIFFUSE[n] 漫反射颜色
- SPECULAR[n] 高光颜色(specular color)
- TEXCOORD[n] 纹理坐标
- TANGENT[n] 切向量(tangent vector)
- BINORMAL[n] 副法向量(binormal vector)
- TESSFACTOR[n] 网格化因子(tessellation factor)
对于输出结构,我们必须指定每个成员的用途。例如,该数据成员应看作位置向量、颜色向量还是纹理坐标等。对于各数据成员的用途,图形卡无丛知晓,除非您显式指定。这种指定也是在语义层次实现的,例如:
struct VS_OUTPUT
{
vector position : POSITION;
vector diffuse : COLOR0;
vector specular : COLOR1;
};
顶点着色器支持的输出用法包括:n为可选整数,区间[0,15]
- POSITION[n] 位置
- PSIZE[n] 顶点的点尺寸
- FOG[n] 雾融合值
- COLOR[n] 顶点颜色,可输出多个顶点颜色,这些颜色混合在一起生成最终颜色
- TEXCOORD[n] 顶点纹理坐标 可能输出多个顶点纹理坐标