1.2 编译HLSL着色器
阅读此文表明您已同意文末的声明
1.2.1 常量表
每个着色器都有一个常量表(constant table),它用于存储着色器变量。D3DX库为我们提供了应用程序访问着色器常量表的接口ID3DXConstantTable。通过这个接口,我们可以在应用程序的代码中设置(set)着色器源代码中的变量。(译者注:这里的“设置”可以理解为“为…赋值”,比如上面一句也可以理解为“我们可以在应用程序的代码中为着色器源代码中的变量赋值”,下同)
我们现在就描述一个删减了的ID3DXConstantTable的方法实现的列表。完整列表请参见Direct3D文档。
1.2.1.1 获得常量句柄
为了在应用程序代码中设置着色器中的一个特定的变量,我们需要一种方法引用它。我们可以在应用程序中通过D3DXHANDLE引用着色器的一个变量。下列方法返回一个着色器中变量的D3DXHANDLE,只要给定其(译者注:“其”指着色器中的变量)名字:
D3DXHANDLE ID3DXConstantTable::GetConstantByName(
D3DXHANDLE hConstant, // scope of constant
LPCSTR pName // name of constant
);
hConstant——需要句柄承载的变量的父结构的D3DXHANDLE标识。例如,如果我们要得到一个特定结构实例的单一数据成员的句柄,我们可以传递这个结构实例的句柄进去。如果我们要获得一个顶层变量(top-level variable)的句柄,给这个参数传递0就好了。
pName——需要句柄返回的着色器源代码中的变量的名字。
例如,如果着色器中变量的名字是ViewProjMatrix,并且它是一个顶层参数,我们可以这样写:
// Get a handle to the ViewProjMatrix variable in the shader.
D3DXHANDLE h0;
h0 = ConstTable->GetConstantByName(0, "ViewProjMatrix");
1.2.1.2 设置常量
一旦应用程序获得了着色器代码中引用的特定变量的D3DXHANDLE句柄,我们就可以用ID3DXConstantTable::SetXXX方法在应用程序中设置这个变量,其中XXX代表要设置的变量的类型。例如,如果我们要设置的变量是一个vector数组,方法名就应是SetVectorArray。
ID3DXConstantTable::SetXXX的一般语法形式如下:
HRESULT ID3DXConstantTable::SetXXX(
LPDIRECT3DDEVICE9 pDevice,
D3DXHANDLE hConstant,
XXX value
);
n pDevice——与常量表关联的设备的指针
n hConstant——一个句柄,它引用我们正在设置的变量
n value——我们要把变量设置成的值,其中XXX替换为我们要设置的变量的类型的名字。对于有些类型(bool,int,float)我们传递一个值的拷贝(a copy of the value),但是对于其它类型(vectors, matrices, structures),我们传递一个值的指针(a pointer to the value)。
当我们设置一个数组的时候,SetXXX方法需要一个额外的第四个参数,它指定数组中元素的个数。例如,对于设置一个4D数组的方法,原型如下:
HRESULT ID3DXConstantTable::SetVectorArray(
LPDIRECT3DDEVICE9 pDevice, // associated device
D3DXHANDLE hConstant, // handle to shader variable
CONST D3DXVECTOR4* pVector, // pointer to array
UINT Count // number of elements in array
);
以下列表描述了我们可以用ID3DXConstantTable接口设置的类型的列表。这里假定我们有一个设定用的有效设备,和一个有效句柄。
SetBool—Used to set a Boolean value. Sample call:
bool b = true;
ConstTable->SetBool(Device, handle, b);
SetBoolArray—Used to set a Boolean array. Sample call:
bool b[3] = {true, false, true};
ConstTable->SetBoolArray(Device, handle, b, 3);
SetFloat—Used to set a float. Sample call:
float f = 3.14f;
ConstTable->SetFloat(Device, handle, f);
SetFloatArray—Used to set a float array. Sample call:
float f[2] = {1.0f, 2.0f};
ConstTable->SetFloatArray(Device, handle, f, 2);
SetInt—Used to set an integer. Sample call:
int x = 4;
ConstTable->SetInt(Device, handle, x);
SetIntArray—Used to set an integer array. Sample call:
int x[4] = {1, 2, 3, 4};
ConstTable->SetIntArray(Device, handle, x, 4);
SetMatrix—Used to set a 4 × 4 matrix. Sample call:
D3DXMATRIX M(…);
ConstTable->SetMatrix(Device, handle, &M);
SetMatrixArray—Used to set a 4 × 4 matrix array. Sample call:
D3DXMATRIX M[4];
// ...Initialize matrices
ConstTable->SetMatrixArray(Device, handle, M, 4);
SetMatrixPointerArray—Used to set an array of 4 × 4 matrix pointers. Sample call:
D3DXMATRIX* M[4];
// ...Allocate and initialize matrix pointers
ConstTable->SetMatrixPointerArray(Device, handle, M, 4);
SetMatrixTranspose—Used to set a transposed 4 × 4 matrix. Sample call:
D3DXMATRIX M(…);
D3DXMatrixTranspose(&M, &M);
ConstTable->SetMatrixTranspose(Device, handle, &M);
SetMatrixTransposeArray—Used to set an array of 4 × 4 transposed matrices. Sample call:
D3DXMATRIX M[4];
// ...Initialize matrices and transpose them.
ConstTable->SetMatrixTransposeArray(Device, handle, M, 4);
SetMatrixTransposePointerArray—Used to set an array of pointers to 4 × 4 transposed matrices. Sample call:
D3DXMATRIX* M[4];
// ...Allocate,initialize matrix pointers and transpose them.
ConstTable->SetMatrixTransposePointerArray(Device, handle, M, 4);
SetVector—Used to set a variable of type D3DXVECTOR4. Sample call:
D3DXVECTOR4 v(1.0f, 2.0f, 3.0f, 4.0f);
ConstTable->SetVector(Device, handle, &v);
SetVectorArray—Used to set a variable that is a vector array. Sample call:
D3DXVECTOR4 v[3];
// ...Initialize vectors
ConstTable->SetVectorArray(Device, handle, v, 3);
SetValue—Used to set an arbitrarily sized type, such as a structure. In the sample call, we use SetValue to set a D3DXMATRIX:
D3DXMATRIX M(…);
ConstTable->SetValue(Device, handle, (void*)&M, sizeof(M));
1.2.1.3 设置常量默认值
下一个方法就是设置常量为它们的默认值,这些默认值在声明时初始化。这个方法应该在应用程序建立(setup)期间被一次性调用(called once)。
HRESULT ID3DXConstantTable::SetDefaults(
LPDIRECT3DDEVICE9 pDevice
);
n pDevice——关联到常量表的设备的指针。
1.2.2 编译HLSL着色器
我们可以编译一个着色器——用我们已保存的着色器的文本文件——使用下列函数:
HRESULT D3DXCompileShaderFromFile(
LPCSTR pSrcFile,
CONST D3DXMACRO* pDefines,
LPD3DXINCLUDE pInclude,
LPCSTR pFunctionName,
LPCSTR pTarget,
DWORD Flags,
LPD3DXBUFFER* ppShader,
LPD3DXBUFFER* ppErrorMsgs,
LPD3DXCONSTANTTABLE* ppConstantTable
);
n pSrcFile——要编译的包含着色器源代码的文本文件的文件名
n pDefines——参数可选,本书中指定为空。
n pInclude——ID3DXInclude接口指针。这个接口被设计成由应用程序实现,所以我们可以重载默认include的行为。通常,默认行为是良好,而且我们可以通过将其指定为空忽略此参数。
n pFunctionName——指定入口点函数名的字符串。例如,如果着色器的入口点函数叫做Main,我们可以给此参数传递“Main”。
n pTarget——指定要编译成的HLSL着色器源文件的版本的字符串。有效的顶点着色器版本是:vs_1_1, vs_2_0, vs_2_sw。有效的像素着色器版本是2.0,我们可以给此参数传递vs_2_0。
备注:有能力编译为不同着色器版本是HLSL超越汇编语言的一个主要好处。用HLSL我们只需简单的重新编译为需要的target,便可快速移植着色器到不同版本。使用汇编,则我们可能需要手动移植代码。
n Flags——可选的编译标记,指定为0标识没有标记。有效的选项是:
l D3DXSHADER_DEBUG——通知编译器写入调试信息
l D3DXSHADER_SKIPVALIDATION——通知编译器不要做任何代码检查。此项仅用于你已知着色器能够工作时
l D3DXSHADER_SKIPOPTIMIZATION——通知编译器不要执行任何代码优化。实践中,这个选项应该仅用于调试,因为这种情况下你不希望编译器以任何方式修改代码。
n ppShader——返回已编译的着色器代码的ID3DXBuffer指针。这个已编译着色器代码将作为另一个实际创建顶点/像素着色器的函数的参数
n ppErrorMsgs——返回包含错误码和错误消息字符串的ID3DXBuffer指针
n ppConstantTable——返回包含此着色器常量表数据的ID3DXConstantTable指针
这里是一个调用D3DXCompileShaderFromFile的例子:
//
// Compile shader
//
ID3DXConstantTable* TransformConstantTable = 0;
ID3DXBuffer* shader = 0;
ID3DXBuffer* errorBuffer = 0;
hr = D3DXCompileShaderFromFile(
"transform.txt", // shader filename
0,
0,
"Main", // entry point function name
"vs 2 0", // shader version to compile to
D3DXSHADER_DEBUG, // debug compile
&shader,
&errorBuffer,
&TransformConstantTable);
// output any error messages
if( errorBuffer )
{
::MessageBox(0, (char*)errorBuffer->GetBufferPointer(), 0, 0);
d3d::Release<ID3DXBuffer*>(errorBuffer);
}
if (FAILED (hr))
{
::MessageBox(0, "D3DXCreateEffectFromFile() - FAILED", 0, 0);
return false;
}
[声明]:本文译自Frank Luna的《Introduction to 3D Game Programming with DirectX 9.0》,限于译者水平,文中难免错漏之处,欢迎各位网友批评指正;本文仅用于学习交流与参考用途,不得用于任何形式的商业用途;如需转载需事先征得作者本人和译者的同意,保持文章的完整性,并注明作者、译者和出处,对于违反以上条款造成的后果,译者对此不负任何责任。我的邮箱地址是Raymond_King123@hotmail.com,欢迎热爱3D图形和游戏,并有一定图形编程经验的朋友来信交流。