1.1编写HLSL着色器
阅读此文表明您已同意文末的声明
我们可以直接把HLSL着色器代码作为一长串字符串编写进我们的应用程序源文件中。但是,更加方便和模块化的方法是把着色器的代码从应用程序代码中分离出来。因此,我们可以在记事本里编写我们的着色器代码,并将之保存进一般的ASCII文本文件中。然后再使用D3DXCompileShaderFromFile函数(1.2.2节)编译我们的着色器。
作为介绍,下面给出一个简单的用HLSL编写的顶点着色器,它的源文件是从文本文件Transform.txt生成而来。完整的项目可以在Transform标题下找到。顶点着色器通过一个组合视图和投影矩阵变换(transform)顶点,并且设置顶点颜色的漫反射成分(the diffuse color component of the vertex)为蓝色。
注意:本例使用顶点着色器作为示例,但是别急管顶点着色器做了什么,它们会在下一章中讲述。至于现在,你要做的就是熟悉HLSL程序的语法和格式。
/////////////////////////////////////////////////////////////////////
//
// File: transform.txt
//
// Author: Frank D. Luna (C) All Rights Reserved
//
// System: AMD Athlon 1800+ XP, 512 DDR, Geforce 3, Windows XP,
// MSVC++ 7.0
//
// Desc: Vertex shader that transforms a vertex by the view and
// projection transformation, and sets the vertex color to blue.
//
/////////////////////////////////////////////////////////////////////
//
// Globals
//
// Global variable to store a combined view and projection
// transformation matrix. We initialize this variable
// from the application.
matrix ViewProjMatrix;
// Initialize a global blue color vector.
vector Blue = {0.0f, 0.0f, 1.0f, 1.0f};
//
// Structures
//
// Input structure describes the vertex that is input
// into the shader. Here the input vertex contains
// a position component only.
struct VS_INPUT
{
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 VS_OUTPUT
{
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.
//
VS_OUTPUT Main(VS_INPUT input)
{
// zero out members of output
VS_OUTPUT output = (VS_OUTPUT)0;
// transform to view space and project
output.position = mul(input.position, ViewProjMatrix);
// set vertex diffuse color to blue
output.diffuse = Blue;
//Output the projected and colored vertex.
return output;
}
1.1.1全局变量
首先,我们初始化了两个全局变量:
matrix ViewProjMatrix;
vector Blue = {0.0f, 0.0f, 1.0f, 1.0f};
第一个变量,ViewProjMatrix,是matrix类型,它是一个HLSL内建的4×4矩阵类型。这个变量存储视图和投影矩阵的组合,这样它就同事描述了这两个变换。这种方式使我们仅需使用一个向量矩阵乘法(vector-matrix multiplicatio),而不是两个。要注意的是,着色器源代码的任何地方都没有初始化这个变量。那是因为我们它是我们在应用程序的源代码中进行设置的——而不是在着色器中。应用程序和着色器直接的通信是常用操作,详见1.2.1节。
第二个变量,Blue,是内建的vector类型,它是一个4D数组。我们简单的将其成分(译者注:这里的成分指的是顶点颜色的漫反射成分)初始化为蓝,将其视为一个RGBA颜色数组。
1.1.2输入和输出结构
在全局变量声明之后,我们定义了两个特殊的结构,我们叫它们“输入”(input)和“输出”(output)结构。对于顶点着色器而言,这些结构分别定义了我们的着色器要输入和输出的顶点数据(vertex data)。
struct VS_INPUT
{
vector position : POSITION;
};
struct VS_OUTPUT
{
vector position : POSITION;
vector diffuse : COLOR;
};
注意:像素数据(pixel data)由像素着色器的输入和输出结构定义。
本例中,我们输入进顶点着色器的顶点仅包括一个位置成分(position component)。我们输出的着色器包含了一个位置成分和一个颜色成分。
特殊的冒号代表了一种语义,它用于指定变量的用途。这类似于自由顶点格式(flexible vertex format ,简称FVF)的顶点结构。例如,在VS_INPUT中,有一个成员:
vector position : POSITION;
" : POSITION"的语法表示向量position是用于描述输入的顶点的位置的。又例如,在VS_OUTPUT中,我们有:
vector diffuse : COLOR;
这里,": COLOR"表示向量diffuse是用于描述输出顶点的颜色的的。我们将在下两章中详细讨论可用的用途标识符(usage identifier)。
注意:从底层的观点来看,与变量语法的语义相关联的是硬件寄存器。也就是说,输入变量与输入寄存器关联,输出变量与输出寄存器关联。例如,VS_INPUT的position成员与顶点输入位置寄存器(the vertex input position register)相连。类似的,diffuse与一个特定的顶点颜色输出寄存器(vertex output color register)相连。
1.1.3入口点函数
和一个C++程序一样,每个HLSL程序都有一个入口点。在我们的示例着色器中,入口点函数是Main;但是,这个名称并不是强制性的。着色器的入口点函数名可以是任何有效的函数名。入口点函数必须有输入结构参数,用于传递输入顶点到着色器。这个入口点还必须返回一个输出结构的实例(instance),它用于从着色器输出经过处理的顶点。
VS_OUTPUT Main(VS_INPUT input)
{
注意:实际上,输入和输出结构的使用并不是强制性的。例如,你有时可能会看见类似于像下面这样的语法,特别是在像素着色器中:
float4 Main(in float2 base : TEXCOORD0,
in float2 spot : TEXCOORD1,
in float2 text : TEXCOORD2) : COLOR
{
...
}
三个参数输入到着色器中,本例中是输入了三个纹理坐标。而着色器返回的却只有一个单独的颜色,它在函数签名(function signature)后用: COLOR语法进行指示。其定义等价于:
struct INPUT
{
float2 base : TEXCOORD0;
float2 spot : TEXCOORD1;
float2 text : TEXCOORD2;
};
struct OUTPUT
{
float4 c : COLOR;
};
OUTPUT Main(INPUT input)
{
...
}
入口点函数的函数体有责任计算给定的输入顶点的输出顶点。本例中的着色器简单的变换输入顶点到视图空间和投影空间,设置顶点颜色为蓝,并且返回作为结果的顶点。首先我们初始化了一个VS_OUTPUT实例并将其所有成员置零。
VS_OUTPUT output = (VS_OUTPUT)0; // zero out all members
然后我们的着色器使用mul函数并通过ViewProjMatrix变量变换输入顶点的位置,mul函数是一个内建函数,它既可以执行向量的矩阵乘法(vector-matrix multiplication),又可以执行矩阵的矩阵乘法(matrix-matrix multiplication)。我们在output变量的position成员中保存已变换的顶点向量:
// transform and project
output.position = mul(input.position, ViewProjMatrix);
Next we set the diffuse color member of output to Blue:
// set vertex diffuse color to blue
output.diffuse = Blue;
Finally, we return our resulting vertex:
return output;
}
[声明]:本文译自Frank Luna的《Introduction to 3D Game Programming with DirectX 9.0》,限于译者水平,文中难免错漏之处,欢迎各位网友批评指正;本文仅用于学习交流与参考用途,不得用于任何形式的商业用途;如需转载需事先征得作者本人和译者的同意,保持文章的完整性,并注明作者、译者和出处,对于违反以上条款造成的后果,译者对此不负任何责任。我的邮箱地址是Raymond_King123@hotmail.com,欢迎热爱3D图形和游戏,并有一定图形编程经验的朋友来信交流。