每日一语:
呆在这样的公司,的确没有什么积累,但我却应该庆幸这一段经历,不管什么东西都是有其两面性的,有得就有失,有成就有败。如果,之前没有进入这样的公司,没有这段经历,我就不会接触到手机,而且对Windows驱动有了更加深刻的认识,提高了动手实践能力。对Windows底层有了更深层次的认识。给自己的产品以及行业经验增加很重的砝码。
正文:
高级着色语言:HLSL (High-Level Shading Language).
接下来,我们来介绍使用HLSL来编写顶点着色器和像素着色器程序。简单来说,顶点着色器和像素着色器就是我们自行编写的一些小规模的定制程序,这些定制程序可取代固定功能流水线中某一功能模块,并可在图形卡的GPU中执行。通过这种功能替换,我们便在实行图形效果时获得了巨大的灵活性。HLSL是在DirectX 9中出现的。
有一点需要注意,如果你的图形卡不支持顶点着色器和像素着色器,在运行程序时,务必切换到REF设备。
注意:顶点着色器可用软件顶点运算方式来模拟,即在创建设备时,将设备行为标记设定为
D3DCREATE_SOFTWARE_VERTEXPROCESSING.
HLSL着色器程序的编制:
HLSL着色器程序可以一个长字符串的形式出现在应用程序的源文件中,但更方便也更模块化的做法是将着色器代码与应用程序代码分离。基于上述考虑,我们可在记事本中编写着色器代码,并将其保存为常规的ASCII文本文件。接下来就可以使用函数D3DXComplieShaderFromFile对着色器文本进行编译了。
我们下面看一下,简单的的用HLSL编写的顶点着色器程序,这段程序对顶点实施了取景变换和投影变换,并将顶点的漫反射颜色分量设置为蓝色。我们主要是熟悉HLSL的语法和格式。
matrix ViewProjMatrix;
vector Blue = {0.0f,0.0f,1.0f,1.0f};
struct VS_INPUT
{
vector position : POSITION;
};
struct VS_OUTPUT
{
vector position : POSITION;
vector diffuse : COLOR;
};
VS_OUTPUT Main(VS_INPUT input)
{
VS_OUTPUT output = (VS_OUTPUT)0;
output.position = mul(input.position,ViewProjMatrix);
output.diffuse = Blue;
return output;
}
全局变量:
首先我们来实例化两个全局变量。
matrix ViewProjMatrix;
vector Blue = {0.0f,0.0f,1.0f,1.0f};
第一个变量ViewProjMatrix,是一个matrix类型的实例。该类型是HLSL中的内置类型,表示维数为4x4的矩阵。该变量存储了取景变换矩阵和投影变换矩阵的乘积,这样它就同时描述了两种交换。所以,我们只需进行一次向量矩阵乘法就可实现上述两种变换。注意,我们在着色器源代码中是找不到这些变量的初始化代码的。这是因为这些变量的初始化应在应用程序源代码中进行而非着色器程序代码中。应用程序与着色器程序之间经常需要进行数据通信的。
第二个变量Blue,是HLSL内置类型vector的一个实例,该类型表示一个4D向量。
输入和输出结构:
声明了全局变量以后,我们定义了两种特殊的结构,称为输入输出结构。对于顶点着色器,这些结构分别定义了该着色器的输入和输出的顶点数据(即输入输出顶点的结构)
struct VS_INPUT
{
vector position : POSITION;
};
struct VS_OUTPUT
{
vector position : POSITION;
vector diffuse : COLOR;
};
注意:像素着色器的输入和输出结构定义了像素的数据格式。
在本例中,顶点着色器的输入顶点包含了一个位置分量。而顶点着色器的输出顶点中不但包含了位置分量,还包含了颜色分量。
上面出现的和特别的冒号语法表达了一种语意,用来指定变量的用途。这与顶点结构中的灵活顶点格式(FVF)非常类似。
从底层的观点看,语义语法建立了着色器中的变量与硬件寄存器之间的联系,即输入变量总是与输入寄存器项联系的,而输出变量总是与输出寄存器相联系的。例如,VS_INPUT结构的程序position将被连接到一个特定的顶点输入位置寄存器,类似地,VS_OUTPUT结构中的diffuse成员将被连接到一个特定的顶点输出颜色寄存器中。
入口函数:
像C++程序一样,每个HLSL程序也应该有一个入口点。在上面的着色器例程中,入口函数为Main。但是,该名称并非强制性的。在遵循函数命名规则的前提下,着色器入口函数的命名可以自由选择。入口函数必须有一个输入结构的参数,该参数将用于把输入顶点传给着色器,而且入口函数必须返回一个输出结构的实例,用来将经过处理的顶点自着色器输出。
实际上,输入和输出结构的使用并非是强制性的。例如,有时会遇到类似这样的语法,该用法在像素着色器程序中尤为常见:
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)
{
....
}
入口函数主要是负责依据输入顶点计算输出顶点。本例中,着色器要完成的任务仅仅是将顶点变换到观察坐标系继而变换至投影坐标系中,将顶点漫反射颜色设置为蓝色,最后将经过上述运算的顶点返回。首先,我们对一个VS_OUTPUT结构进行了实例化,并将该实例的各成员设为0.
VS_OUTPUT output = (VS_OUTPUT)0;
然后;该着色器借助函数mul将输入