http://www.bullock.cn/blogs/geeker/archives/68700.aspx
高级着色语言HLSL入门(1)
在我们写的程序里顶点和像素是很小的对象,它们由GPU来执行,是固定功能管线的一部分。用我们自己写的着色器程序替换一部分固定功能管线,在绘制效果上我们获得很大的灵活性。我们不再局限于预定义的"固定"操作。
为了编写着色器程序,我们需要一种高级着色器语言(High-Level Shading Language ,简称HLSL)。 在DirectX 8中,着色器是用低级着色器汇编语言编写的。幸运的是,我们不必再用汇编语言来写着色器了,DirectX 9支持一种高级着色器语言来写。用HLSL写着色器程序与使用高级语言有同样的优势,像C++,它超越了汇编语言,即:
增加生产力—用高级语言比用低级语言写程序更快、更容易。 我们可以花费更多的时间关注于算法而不是代码。
增加可读性—用高级语言写的程序更易读,这意味着用高级语言编程更易于调试和维护。
大多数情况下,编译器产生的汇编代码比手写有效率。
使用HLSL 编译器,我们可以编译我们的代码到任何可用shader版本,使用汇编语言我们将不得不为每个需要的版本移植代码。
HLSL 同C和C++语法很类似,所以缩短了学习曲线。
最后,如果你的显卡不支持顶点和像素着色器的话,为了执行着色器的例子程序你将需要转换REF设备。使用REF设备意味着着色器例子运行的会很慢,但它至少能显示结果,让我们去检查是否代码可以被执行。
提示:顶点shaders可以用软件来模拟 ―― D3DCREATE_SOFTWARE_VERTEX-PROCESSING。
16.1编写HLSL 着色器
我们可以在程序源文件中用长字符串直接编写HLSL着色器代码,然而更方便、更模块化的方法是把它与程序代码分离出来。因此,我们在记事本中编写着色器并保存成一般的ASCII文本文件,然后可以用D3DXCompileShaderFromFile函数(section 16.2.2)来编译它们。
作为介绍,下面是用HLSL编写的一个简单的顶点着色器,用记事本生成并保存成文本文件“VertexShader.cxx”。顶点着色器用组合视图和投影矩阵转换顶点,并设置顶点漫射光为红色。
注意:这是一个顶点着色器的例子,不必关心顶点着色器做了什么,现在的目标是熟悉HLSL编程的语法和格式。
Vertex shader that transforms a vertex by the view and projection transformation,
and sets the vertex color to red.
************************************************************************************/
// Global variable to store a combined view and projection transformation matrix,
// we initialize this variable from the application.
matrix g_view_proj_matrix;
// initialize a global blue color vector
const vector RED = {1.0f, 0.0f, 0.0f, 1.0f};
// Input structure describes the vertex that is input into the shader.
// Here the input vertex contains a position component only.
struct sVertexInput
{
vector position : POSITION;
};
// Output structure describes the vertex that is output from the shader.
// Here the output vertex contains a position and color component.
struct sVertexOutput
{
vector position : POSITION;
vector diffuse : COLOR;
};
// Main Entry point, observe the main function receives a copy of the input vertex through
// its parameter and returns a copy of the output vertex it computes.
sVertexOutput main(sVertexInput input)
{
// zero out members of output
sVertexOutput output = (sVertexOutput)0;
// transform to view space and project
output.position = mul(input.position, g_view_proj_matrix);
// set vertex diffuse color to blue
output.diffuse = RED;
return output;
}
16.1.1 全局变量
首先是2个全局变量:
// Global variable to store a combined view and projection transformation matrix.
// We initialize this variable from the application.
matrix g_view_proj_matrix;
// Initialize a global blue color vector.
const vector BLUE = {0.0f, 0.0f, 1.0f, 1.0f};
第1个变量g_view_proj_matrix是矩阵类型,它是一个在HLSL内创建的4×4的矩阵类型。这个变量保存视图与投影的组合矩阵,它描述两者的变换。使用这种方法我们只要做一个向量和矩阵的乘法(而不是二个)。注意,在着色器源代码的任何地方都没有初始化这个变量,因为它是我们在应用程序的源代码里设置的,而不是在着色器中。从应用程序向着色器程序通讯是常用的操作。
第二个变量BLUE是built-in(内建)类型的4D向量,我们简单的将它初始化成蓝色,它是个RGBA的颜色向量。
16.1.2 输入和输出结构
在全局变量定义之后,定义2个特殊的结构,我们调用输入和输出结构。对于顶点着色器而言,这些结构定义了顶点的数据,分别是:
// Input structure describes the vertex that is input into the shader.
// Here the input vertex contains a position component only.
struct sVertexInput
{
vector position : POSITION;
};
// Output structure describes the vertex that is output from the shader.
// Here the output vertex contains a position and color component.
struct sVertexOutput
{
vector position : POSITION;
vector diffuse : COLOR;
};
注意:给像素着色器的结构定义输入和输出像素数据。
在例子中,INPUT 顶点着色器只包含位置成员(POSITION),OUTPUT顶点着色器包含位置和颜色成员(POSITION and COLOR)。
特殊的冒号是一种语义,用于是声明变量。这与vertex结构中的自由顶点格式(FVF)相似。例如,在sVertexInput中有成员:vector position : POSITION;
": COLOR"是说顶点的漫射光是用sVertexOutput结构的COLOR成员来说明的。
注意:从底层来说,着色器变量的语义和语法同硬件寄存器是相关联的。即,input变量与input寄存器关联,output变量与output寄存器关联。例如,sVertexInput中的position成员与顶点input的position寄存器相关联。同样,diffuse与顶点的output的color寄存器关联。
16.1.3 函数的入口点
在C++程序中,每个HLSL程序有一个入口点。在我们的着色器例子中,我们调用入口点函数main。然而名字不是强制的。入口点函数名可以是任何有效的函数名,入口点函数必须有一个input结构参数,它通过input顶点进入着色器。入口点函数必须返回一个output结构实例,在着色器中使用output操作顶点。
sVertexOutput main(sVertexInput input)
{
注意:实际上,使用input、output结构不是强制的。例如,有时你将会看到使用类似下面的语法,特别是在像素着色器中:
float4 Main(in float2 base : TEXCOORD0,
in float2 spot : TEXCOORD1,
in float2 text : TEXCOORD2) : COLOR
{
...
}
例子中,输入到着色器中的参数是3个纹理坐标。着色器输出(返回)一个颜色,COLOR语句在函数的声明以后。这种定义是类似于:
struct INPUT
{
float2 base : TEXCOORD0;
float2 spot : TEXCOORD1;
float2 text : TEXCOORD2;
};
struct OUTPUT
{
float4 c : COLOR;
};
OUTPUT Main(INPUT input)
{
...
}
输入点函数负责根据给定的input顶点计算output顶点。例子中的着色器简单的变换input顶点到视图空间和投影空间,设置顶点颜色为红色,并返回结果顶点。首先我们定义sVertexOutput的实例并初始化所有成员为0。
// zero out members of output
sVertexOutput output = (sVertexOutput)0;
然后着色器变换input顶点位置用g_view_proj_matrix变量,使用mul函数。它是一个built-in(内建)函数,实现向量与矩阵相乘,或矩阵与矩阵相乘。我们保存结果变换的向量(在output实例的position成员中)。
// transform to view space and project
output.position = mul(input.position, g_view_proj_matrix);
然后设置output的成员diffuse的颜色为红色:
// set vertex diffuse color to red
output.diffuse = RED;
最后返回结果向量:
return output;
}
高级着色语言HLSL入门(2)
16.2 编译HLSL 着色器
16.2.1 常量表
每个着色器有一个常量表,用来保存它的变量。D3DX库通过ID3DXConstantTable接口,提供给应用程序访问着色器的常量表。通过这个接口我们能够在应用程序中设置着色器源代码中的变量。
我们现在描述ID3DXConstantTable接口的方法列表的实现,全部的列表请查阅Direct3D文档。
16.2.1.1 取得常量句柄
为了在应用程序中设置着色器中的一个特定变量,需要有一种方法去引用它,我们能够在应用程序中用D3DXHANDLE引用一个在着色器中的变量,下面的方法返回一个着色器中的变量的D3DXHANDLE,使用时,需要传递一个变量的名字作为参数:
D3DXHANDLE ID3DXConstantTable::GetConstantByName( D3DXHANDLE hConstant, // scope of constant LPCSTR pName // name of constant ); |
Hconstant——我们要取得的父结构中变量句柄的D3DXHANDLE标识。例如,如果我们想获得一个特定数据结构中单一数据成员的句柄,我们可以传递结构实例的句柄。如果我们获得一个顶级变量的句柄,给这个参数设为NULL。
PName——我们想获得的句柄的着色器代码中的变量的名字。
Gets a constant by looking up its name.
D3DXHANDLE GetConstantByName(
D3DXHANDLE hConstant,
LPCSTR pName
);
Parameters
-
hConstant
- [in] Unique identifier to the parent data structure. If the constant is a top-level parameter (there is no parent data structure), use NULL. pName
- [in] Name of the constant.
Return Values
Returns a unique identifier to the constant.
例如,如果在着色器中变量的名字为ViewProjMatrix,并且这是顶级变量,我们这么写:
// 取得着色器中ViewProjMatrix变量的句柄 D3DXHANDLE h0; h0 = ConstTable->GetConstantByName(0, "ViewProjMatrix"); |
16.2.1.2 设置常量
一旦应用程序有了一个D3DXHANDLE,要引用着色器代码中的具体变量,我们可以在应用程序中使用ID3DXConstantTable::SetXXX方法设置变量。如果我们想设置一个向量数组类型的变量,方法名是SetVectorArray。
ID3DXConstantTable::SetXXX的一般语法是:
HRESULT ID3DXConstantTable::SetXXX( LPDIRECT3DDEVICE9 pDevice, D3DXHANDLE hConstant, XXX value ); |
PDevice:常量表所关联的设备的指针。
HConstant:我们正在设置的变量句柄的引用。
Value:我们要把变量设置成的值,XXX是我们设置的要替换的变量类型名,对于有些类型(bool, int, float),传递变量值的COPY,另外一些类型(vectors, matrices, structures),传递值的指针。
下面的列表描述了我们能用ID3DXConstantTable接口设置的类型列表。这里假定我们有一个有效的设备,和一个有效句柄。
SetBool—Used to set a Boolean value. Sample call:
bool b = true; ConstTable->SetBool(Device, handle, b); |
Sets a Boolean value.
HRESULT SetBool(
LPDIRECT3DDEVICE9 pDevice,
D3DXHANDLE hConstant,
BOOL b
);
Parameters
-
pDevice
- [in] Pointer to an IDirect3DDevice9 interface, representing the device associated with the constant table. hConstant
- [in] Unique identifier to the constant. See D3DXHANDLE. b
- [in] Boolean value.
Return Values
If the method succeeds, the return value is D3D_OK. If the method fails, the return value can be D3DERR_INVALIDCALL.
SetBoolArray—Used to set a Boolean array. Sample call:
bool b[3] = {true, false, true}; ConstTable->SetBoolArray(Device, handle, b, 3); |
Sets an array of Boolean values.
HRESULT SetBoolArray(
LPDIRECT3DDEVICE9 pDevice,
D3DXHANDLE hConstant,
CONST BOOL* pB,
UINT Count
);
Parameters
-
pDevice
- [in] Pointer to an IDirect3DDevice9 interface, representing the device associated with the constant table. hConstant
- [in] Unique identifier to the array of constants. See D3DXHANDLE. pB
- [in] Array of Boolean values. Count
- [in] Number of Boolean values in the array.
Return Values
If the method succeeds, the return value is D3D_OK. If the method fails, the return value can be D3DERR_INVALIDCALL.
SetFloat—Used to set a float. Sample call:
float f = 3.14f; ConstTable->SetFloat(Device, handle, f); |
Sets a floating-point number.
HRESULT SetFloat(
LPDIRECT3DDEVICE9 pDevice,
D3DXHANDLE hConstant,
FLOAT f
);
Parameters
-
pDevice
- [in] Pointer to an IDirect3DDevice9 interface, representing the device associated with the constant table. hConstant
- [in] Unique identifier to the constant. See D3DXHANDLE. f
- [in] Floating-point number.
Return Values
If the method succeeds, the return value is D3D_OK. If the method fails, the return value can be D3DERR_INVALIDCALL.
SetFloatArray—Used to set a float array. Sample call:
float f[2] = {1.0f, 2.0f}; ConstTable->SetFloatArray(Device, handle, f, 2); |
Sets an array of floating-point numbers.
HRESULT SetFloatArray(
LPDIRECT3DDEVICE9 pDevice,
D3DXHANDLE hConstant,
CONST FLOAT* pf,
UINT Count
);