HLSL概述:编写DX着色器代码的高级编程语言,DX9可以使用HLSL编写顶点/像素着色器.
输入和输出结构: 这些结构分别定义了该着色器的输入和输出的顶点数据(即输入输出顶点的结构)
struct VS_INPUT
{ //:冒号表示一种语义,用来指定变量的用途
vector position : POSITION; //位置分量 POSITION 表示向量 position 用于描述输入顶点的位置信息
};
struct VS_OUTPUT
{
vector position : POSITION; //位置分量 POSITION 表示向量 position 用于描述输出顶点的位置信息
vector diffuse : COLOR; //颜色分量 COLOR 表示向量 diffuse 用于描述输出顶点的颜色信息
};
入口函数:和C/C++一样,每个 HLSL 程序必须有一个入口点,例如 Main,但名称并非强制性的,遵循命名规则即可。
入口函数形参:必须有一个可接收输入结构的参数,用于把输入顶点传给着色器
入口函数返回:必须返回一个输出结构的实例,用来将经过处理的顶点从着色器输出
入口函数定义方法之一
VS_OUTPUT Main(VS_INPUT input)
{
//入口函数主要负责依据输入顶点计算输出顶点
… …
VS_OUTPUT output = (VS_OUTPUT)0; //定义一个结构变量并初始化为0
output.position = mul( input.position, ViewProjMatrix); //mul()返回矩阵乘积
output.diffuse = Blue; //将顶点颜色设为蓝色
return output; //必须返回一个输出结构实例
}
入口函数定义方法二
float4 Main(in float2 base : TEXCOORD0,
in float2 spot : TEXCOORD1,
in float2 text : TEXCOORD2) : COLOR //"COLOR"签名表示着色器将返回一个单个颜色作为输出
{
//入口函数主要负责依据输入顶点计算输出顶点
… …
}
struct INPUT
{
float2 base : TEXCOORD0;
float2 spot : TEXCOORD1;
float2 text : TEXCOORD2;
};
strcut OUTPUT
{
float4 c : COLOR;
};
获取常量的句柄:
每个着色器都用常量表来存储其变量,为了使程序能够访问着色器的常量表,D3DX库提供了接口 ID3DXConstantTable
借助该接口可以在程序源代码中对着色器变量进行访问
获取着色器常量的句柄 :
D3DXHANDLE ID3DXConstantTable::GetConstantByName(
D3DXHANDLE hConstant, //常数范围
LPCSTR pName //常数名称
);
hConstant 一个 D3DXHANDLE 类型的句柄,标识了那个包含了我们希望获取其句柄的变量的父结构。例如希望获得
某个特定结构实例的一个单个数据成员的句柄,可为该参数传入该结构实例的句柄。如果想要获取指向顶级变量
的句柄,应将该参数指定为0.
pName 我们希望获取其句柄的那个着色器源代码中的变量的名称
例如:如果期望应用的着色器中的变量名称为 ViewProjMatrix,且该变量是顶级参数,我们可这样实现引用:
D3DXHANDLE h0;
h0 = ConstTable->GetConstantByName(0,“ViewProjMatrix”);
常量的设置:
一旦应用程序获得了着色器代码中希望引用的那个变量的 D3DXHANDLE 类型的句柄,我们就可以在应用程序中使用方法 ID3DXConstantTable::SetXXX对该变量进行设置,其中XXX表示被设置变量的类型名称,实际调用时只要用类型名将其替换即可。例如我们希望设置的变量为一个 vector 类型的数组,该方法将对应 SetVectorArray。
//方法 ID2DXConstanTable::SetXXX 的通用签名如下:
HRESULT ID3DXConstantTable::SetXXX(
LPDIRECT3DDEVICE9 pDevice,
D3DXHANDLE hConstant,
XXX value
);
HRESULT ID3DXConstantTable::SetVectorArray(
LPDIRECT3DDEVICE9 pDevice, //关联设备
D3DXHANDLE hConstant, //着色变量句柄
CONST D3DXVECTOR4* pVector, //对于数组指针
UINT Count //数组中的元素数
);
下面的列表描述了那些可用结构 ID3DXConstantTable 进行设置的类型,假定我们已经拥有了一个合法的设备指针(Device)以及索要设置的变量的句柄。
- setBool 用于设置一个布尔值
- SetBoolArray 用于设置布尔数组
- SetFloat 用于设置一个浮点数
- SetFloatArray 用于设置一个浮点数数组
- SetInt 用于设置一个整数
- SetIntArray 用于设置一个整数数组
- SetMaitrix 用于设置一个4*4的矩阵
- SetMatrixArray 用于设置一个4*4的矩阵数组
- SetMatrixPointerArray 用于设置一个4*4矩阵的指针数组
- SetMatrixTranspose 用于设置一个4*4的转置矩阵
- SetMatrixTransposeArray 用于设置一个4*4的转置矩阵数组
- SetVector 用于设置一个 D3DXVECTOR4 类型的变量
- SetVectorArray 用于设置向量数组类型的变量
- SetValue 用于设置大小任意的类型,例如结构体
设置常量的默认值:
下面的方法仅是将常量设为其默认值,即那些在变量声明时被赋予的初值,该方法在应用程序的设置过程中,应调用一次。
HRESULT ID3DXConstantTable::SetDefaults(
LPDIRECT3DDEVICE9 pDevice //于常量表相关的设备指针
);
我们可以用如下函数对保存在文本文件中的着色器程序进行编译
HRESULT D3DXCompileShaderFromFile(
LPCSTR pSrcFile, //要编译的目标文件名称
CONST D3DXMACRO* pDefines, //可选参数,或NULL
LPD3DXINCLUDE pInclude, //指向ID3DXInterface接口的指针,指定NULL而将其忽略
LPCSTR pFunctionName, //目标着色器内入口函数名称的字符串
LPCSTR pTarget, //指定目标生成的着色器版本,一个字符串,具体版本见下方
DWORD Flags, //编译调试选项,NULL或其他,具体见下
LPD3DXBUFFER* ppShader, //返回 ID3DXBuffer 指针,是编译后的代码,可用作其他函数的参数来创建实际的顶点/像素着色器
LPD3DXBUFFER* ppErrorMsgs, //返回 ID3DXConstantTable 指针,包含了错误消息
LPD3DXCONSTANTTABLE* ppConstantTable //返回 ID3DXConstantTable 指针,包含该着色器内常量数据表
);
5: pTarget //指定生成的着色器版本,该参数为一个字符串。
合法的顶点着色器版本有:vs_1_1, vs_2_0, vs_2_sw.
合法的像素着色器版本有:ps_1_1, ps_1_2, ps_1_3, ps_1_4, ps_2_0, ps_2_sw.
例如想将顶点着色器编译为2.0版本,则将该参数指定为 "vs_2_0" .
有了版本的选择,可以在不同的目标平台选择不同的版本而不用更改着色器源代码
6: Flags //编译调试选项,有一下一个选择
- D3DXSHADER_DEBUG 指示编译器写入调试信息
- D3DXSHADER_SKIPVALIDATION 指示编译器不进行任何代码验证,仅当您正在使用一个已确定可用着色器时才使用该参数
- D3DXSHADER_SKIPOPTIMIZATION 指示编译器不对代码做任何优化,实际上仅当调试时该选项有用,因为调试时您不希望编译器对代码做任何改动
一个调用实例:
ID3DXConstantTable* TransformConstantTable =0; //用于接收着色器的常量数据表
ID3DXBuffer* shader = 0; //用于接收着色器编译后代码
ID3DXBuffer* errorBuffer = 0; //用于接收着色器编译的错误信息
hr = D3DXCompileShaderFromFile(
"transform.txt",
0,
0,
"Main",
"vs_2_0",
D3DXSHADER_DEBUG,
&shader,
&errorBuffer,
&TransformConstantTable
);
if(errorBuffer)
{
//编译的着色器程序有问题,在这进行处理。。。
}
if(FAILER(hr))
{
//D3DXCompileShaderFromFile()函数执行失败,在这里进行处理。。。
return false;
}
标量类型:
HLSL 支持下列标量类型
向量类型:
HLSL具有下列内置向量类型
vector<double, 2> vec2;
//我们可以通过数组下标语法来访问向量的每个元素。例如,要想对向量 vec 中的第 i 个元素进行设置,可以这样做:
vec[i] = 2.0f;
此外,借助一些已定义的分量名x,y,z,w,r,g,b,a,我们还可像访问结构体中成员那样访问向量 vec 中的分量
vec.x = vec.r = 1.0f;
vec.y = vec.g = 2.0f;
vec.z = vec.b = 3.0f;
vec.w = vec.a = 4.0f;
这些名称r,g,b,a,与 x,y,z,w一样,分别精确的指向了同一分量,当用向量表示颜色时,我们更倾向于使用RGBA表示法,因为这将有助于强调该向量代表了某种颜色的事实
我们还可使用其他预定义类型来分别表示 2D,3D和4D向量:
float2 vec2;
float3 vec3;
float4 vec4;
向量复制操作:替换调配
vector u = {1.0f,2.0f,3.0f,4.0f}
vector v = {0.0f,0.0f,5.0f,6.0f}
v = u.xyyw; //v = {1.0f,2.0f,2.0f,4.0f}
对向量进行复制操作时,我们不一定非要对每个分量进行复制,可有选择的复制,例如仅复制x和y分量
vector u = {1.0f,2.0f,3.0f,4.0f}
vector v = {0.0f,0.0f,5.0f,6.0f}
v.xy = u; //v = {1.0f,2.0f,5.0f,6.0f}
矩阵类型:
HLSL 具有下列内置矩阵类型
matrix m2x2;
我们还可以用如下语法来定义一个 m * n 矩阵,其中 m 和 n 必须介于 1~4 之间:
float2x2 mat2x2;
float3x3 mat3x3;
float4x4 mat4x4;
float2x4 mat2x4;
注意:矩阵类型可以不时 float 类型,也可以使用其他类型,例如想定义整形矩阵可以写作:
int2x2 i2x2;
int3x3 i3x3;
int2x4 i2x4;
我们可以使用数组的双下标语法来访问矩阵的各项元素,例如要访问矩阵 M 中第 i 行,j 列的元素可以这样做:M[i][j] = value;
此外,还可访问结构体中的成员那样访问矩阵 M 中的项,HLSL定义了下列项名称
//下标从 1 开始的情形:
M._11 = M._12 = M._13 = M._14 = 0.0f;
M._21 = M._22 = M._23 = M._24 = 0.0f;
M._31 = M._32 = M._33 = M._34 = 0.0f;
M._41 = M._42 = M._43 = M._44 = 0.0f;
//下标从 0 开始的情形:
M._m00 = m._m01 = M._m02 = M._m03 = 0.0f;
M._m10 = m._m11 = M._m12 = M._m13 = 0.0f;
M._m20 = m._m21 = M._m22 = M._m23 = 0.0f;
M._m30 = m._m31 = M._m32 = M._m33 = 0.0f;
//引用矩阵 M 的第 i 行:
vector ithRow = M[i]; //得到M中的第i行向量
HLSL中变量的初始化:
vector u = {0.6f,0.3f,1.0f,1.0f};
vector v = {1.0f,5.0f,0.2f,1.0f};
//也可采用等价的构造函数语法:
vector u = vector(0.6f,0.3f,1.0f,1.0f);
vector v = vector(1.0f,5.0f,0.2f,1.0f);
//一些其他的例子:
float2x2 f2x2 = float2x2(1.0f,2.0f,3.0f,4.0f);
int2x2 i2x2 = {1,2,3,4};
int n = int(5);
int a = {5};
float3 x = float(0,0,0);
//一句话总结:HLSL中初始化必须使用初始化列表{}或构造函数语法
数组类型:
我们可用与C/C++类似的语法声明一个特定类型的数组。例如:
float M[4][4];
half p[4];
vector v[12];
结构体类型:
HLSL 中的结构体定义方法与 C/C++完全相同,但是 HLSL 中的结构体不允许有成员函数,下面时一个 HLSL 中结构体的例子:
struct MyStruct
{
Matrix T;
vector n;
float f;
int x;
bool b;
};
MyStruct s; //实例化
s.f = 5.0f; //成员访问
关键字 typedef:
HLSL 的 typedef 关键字与 C++中的功能完全一样。例如我们用如下语法为类型 vector
typedef vector<float,3> point;
//这样就不必写出:
vector<float,3> myPoint;
//而是写成:
point myPoint;
下面的例子演示如何对常量类型和数组类型运用关键字typedef
typedef const float CFLOAT;
typedef float point2[2];
变量的前缀: