1.3怎么写HLSL着色器
我们可以直接把HLSL着色器代码作为一长串字符串编写进我们的应用程序源文件中,但是,更加方便和模块化的方法是把着色器的代码从应用程序代码中分离出来。因此,我们将着色器代码单独保存为文本格式,然后在应用程序中使用特定函数将其加载进来。
下面是一个完整的HLSL着色器程序代码,我们把它保存在BasicHLSL.txt中。该着色器完成顶点的世界变换、观察变换和投影变幻,并将顶点颜色设定为指定的颜色。
//
// BasicHLSL.txt
//
//
// Global variable
//
matrix WVPMatrix;
vector color;
//
// Structures
//
struct VS_INPUT
{
vector position : POSITION;
};
struct VS_OUTPUT
{
vector position : POSITION;
vector color : COLOR;
};
//
// Functions
//
VS_OUTPUT SetColor(VS_INPUT input)
{
VS_OUTPUT output = (VS_OUTPUT)0;
output.position = mul(input.position, WVPMatrix);
output.color = color;
return output;
}
下面就针对上述代码讲解一下HLSL着色器程序的编写:
代码中声明了两个全局变量:
matrix WVPMatrix;
vector color;
变量WVPMatrix是一个矩阵类型,它包含了世界、观察、投影的合矩阵,用于对顶点进行坐标变换;
变量color是一个向量类型,它用于设定顶点颜色;
代码中并没有对全局变量进行初始化,这是因为我们对全局变量的初始化过程将在应用程序中进行,全局变量在应用程序中赋值而在着色器程序中使用,这是应用程序和着色器通信的关键所在。具体赋值过程将在后续部分讲述。
² 输入输出结构
程序中定义了两个输入输出结构VS_INPUT和VS_OUTPUT
struct VS_INPUT
{
vector position : POSITION;
};
struct VS_OUTPUT
{
vector position : POSITION;
vector color : COLOR;
};
自定义的结构可以采用任意名称,结构不过是一种组织数据的方式,并不是强制的,你也可以不使用,而将本程序的输入改为:
vector position : POSITION;
² 标志符
用于输入输出的变量采用用一种特殊的声明方式:
Type VariableName : Semantic
这个特殊的冒号语法表示一个语义,冒号后面的标志符用来指定变量的用途,如
vector position : POSITION;
其中,POSITION标志符表明该变量表示顶点位置,另外还有诸如COLOR、NORMAL等很多表示其他意义的标志符。
本节所说的输入输出其实是指着色器代码和编译器、GPU之间的通信,和应用程序是无关的,所以这些变量不需要在应用程序中进行赋值,标志符告诉编译器各个输入输出变量的用途(顶点位置、法线、颜色等),这是着色器代码和编译器、GPU之间通信的关键。
程序中还定义了一个函数SetColor:
OUTPUT SetColor(INPUT input)
{
VS_OUTPUT output = (VS_OUTPUT)0;
output.position = mul(input.position, WVPMatrix);
output.color = color;
return output;
}
1. 该函数以input和output类型作为输入输出;
2. 使全局变量WVPMatrix和input.position相乘,以完成顶点的世界、观察、投影变换,并把结果赋值到output.position;
output.position = mul(input.position, WVPMatrix);
3. 将全局变量color的值赋给output.color;
output.color = color;
4. 在同一个着色器代码文件中,可以有多个用户自定义函数,因此在应用程序中需要指定一个入口函数,相当于windows程序的WinMain函数,本程序只包含SetColor一个函数而且它将被做为入口函数使用。
至此,一个HLSL着色器编写完毕,渲染过程中,当一个顶点被送到着色器时:
1. 全局变量WVPMatrix、color将在应用程序中被赋值;
2. 入口函数SetColor被调用编译器根据标志符将顶点信息填充到VS_INPUT中的各个字段;
3. SetColor函数中,首先定义一个VS_OUTPUT信息,之后根据WVPMatrix和color变量完成顶点的坐标变换和颜色设定操作,最后函数返回VS_OUTPUT结构;
4. 编译器将会再次根据标志符把返回的VS_OUTPUT结构中的各字段映射为顶点相应的信息。
5. 顶点被送往下一个流程接受进一步处理。
上述过程中,全局变量在应用程序中赋值而在着色器程序中使用,这是应用程序和着色器通信的关键所在;标志符告诉编译器各个输入输出变量的用途(顶点位置、法线、颜色等),这是着色器代码和编译器、GPU之间通信的关键。个人认为这是着色器中最为精义的地方:)