HLSL(DX9)编程

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"签名表示着色器将返回一个单个颜色作为输出
{
//入口函数主要负责依据输入顶点计算输出顶点
… …
}

上面Main函数定义等价于以下代码:

struct INPUT
{
float2 base : TEXCOORD0;
float2 spot : TEXCOORD1;
float2 text : TEXCOORD2;
};

strcut OUTPUT
{
float4 c : COLOR;
};

OUTPUT Main( INPUT input)
{
//入口函数主要负责依据输入顶点计算输出顶点
… …
}

常量表:

获取常量的句柄:
每个着色器都用常量表来存储其变量,为了使程序能够访问着色器的常量表,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
);
  • pDevice 与常量表相关的设置指针
  • hConstant 我们想要设置的那个变量的句柄
  • value 指定了我们引用的那个着色器中的变量应被赋为何值,其中 XXX 应替换为该变量的类型名,对于某些类型(bool,int,float)我们传入的时该值的副本,而对于另外一些类型(向量,矩阵,结构体)我们传入的时指向该值的指针。
    如果要对数组进行设置,SetXXX 方法还应增加一个参数,以接收该数组的维数,例如,用于设置一个4D向量的数组的方法原型为:
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		//于常量表相关的设备指针
);

HLSL着色器程序的编译

我们可以用如下函数对保存在文本文件中的着色器程序进行编译

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 支持下列标量类型

  • bool 布尔值,注意,HLSL 提供了关键字 true 和 false
  • int 32位有符号整数
  • half 16位浮点数
  • float 32位浮点树
  • double 64位浮点数
  • 注意:不是所有平台都支出int,half 和 double,如果遇到这种情况,这些类型将用 float 来模拟。

向量类型:
HLSL具有下列内置向量类型

  • vector 一个 4D 向量,其中每个元素的类型都是 float
  • vector 一个n维向量,其中每个元素的类型位标量类型T。维数n必须介于1~4之间,下面时一个二维 double 型向量的例子。
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 表示一个 4 * 4 矩阵,该矩阵中每个元素的类型均为 float
  • matrix 表示一个 m * n 矩阵,其中的每个元素都为标量类型 T,该矩阵的维数 m 和 n 必须介于 1~4 之间,例如要表示一个 2 * 2 的整数矩阵可写作: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赋予另一个名称point:

typedef vector<float,3> point;
//这样就不必写出:
vector<float,3> myPoint;
//而是写成:
point myPoint;

下面的例子演示如何对常量类型和数组类型运用关键字typedef

typedef const float CFLOAT;
typedef float point2[2];

变量的前缀:

  • static 如果在全局变量的声明时使用了关键字 static,则该变量在着色器程序外不可见,这个全局变量时该着色器程序的局部变量。如果一个局部变量在声明时使用了关键字 static,则该变量与C++中的局部变量具有完全相同的行为,即该变量只初始化一次。
  • uniform 如果声明变量时使用了 uniform,表明该变量将在该着色器之外进行初始化,例如,在 C++程序中对该变量进行初始化,然后再作为输入传给该着色器。
  • extern 如果声明变量时使用了 extern,表面该变量可在着色器程序之外进行访问,例如可由C++程序对其进行访问,只有全局变量可以使用关键字 extern,非静态的全局变量再默认状态下都是 extern 类型的。
  • shared 使用关键字shared,则提示效果框架该变量可再多个效果之间共享,只有全局变量方可使用该声明。
  • volatile 使用该关键字提示效果框架,该变量将经常被修改。只有全局变量方可使用该关键字进行声明。
  • const HLSL 中的 const 关键字与 C++ 中的含义完全相同,即声明时使用了const 关键字表面该变量不可被修改

你可能感兴趣的:(HLSL(DX9)编程)