词汇约定
虽然语法定义了如何把所有语言元素组合到一起,比如,如何定义函数和代码片段,但它只定义了如何把表达式和操作
符以及标识符一起使用。这意味着语言的语法并没有定义文法(例如: 什么是标识符)。接下来的几段详细解释了HLSL编译器中的词汇约定。
空白字符
HLSL语言中,下面这些字符都被认为是空白字符: ? 空格 ? Tab字符 ? 换行符
? C风格的注解(/* */)。 ? C++风格的注解( // )。
? asm代码块中,汇编风格的注解(;)。
数字
HLSL中的数字可以为浮点类型,也可以为整型。浮点数通常有以下呈现形式:
Float:
( fractional-constant [ exponent-part ] [ float-suffix ] )| (digit-sequence exponent-part [ float-suffix ] )
( [digit – sequence] . digit – sequence ) | (digit – squence . )
Fractnal-const: Sign:
+ | -
Dight – sequence: digit | (digit – sequence digit )
floating – suffix: h | H | f | F
整型的语法与此类似:
Integer: integer – constant [ interger – suffix ] Integer – constant:
digit – sequence | ( 0 digit – sequence ) | ( 0 × digit – sequence )
Digit – sequence: digit | ( digit – sequence digit ) Integer – suffix: u | U | l | L
字符
HLSL允许定义字符和字符串。字符串都是由字符组成。下面是字符的定义:
? ‘ c ‘ (字符)
? ‘ \\t ‘ , ‘ \\n ‘,…..( 转义字符 ) ? ‘ \\### ‘ (八进制换码顺序 ) ? ‘ \\x## ‘ (十六进制换码顺序)
注意
预处理指令中不能包含转义字符。
字符串包含在一对引号中,可以包含前面所述的任意有效字符组合。
标识符
标识符用来表示函数名或变量名之类的语言元素。除了前面所列的关键字以外,标识符可以是字母和数字的任意组合,
但必须保证第一个字符为字母。
操作符
HLSL定义了一组操作符,以便在表达式中使用。表1-5 列出了所有标准操作符,以及他们的含义。如果你熟悉
C或C++,那么这些操作符对你来说应该是一目了然的。
表 1-5 HLSL 操作符
操作符 描述
++ -- && || == :: << <<= >> >>= … <= >= != *= /= += -= %= &= |= ^= ->
一元加法。 一元减法。 逻辑与。 逻辑或。 等号。
成员标识符(用于结构和类)。 二进制左移。
自赋值(self assigning)二进制左移,a<<= b等于a = a<
自赋值二进制右移,a>>=b等于a = a>>b。 省略符(用于可变参数函数)。 小于等于。 大于等于。 不等于。
自赋值乘法, a *= b 等于 a = a * b。 自赋值除法, a /= b 等于 a = a / b。 自赋值加法, a += b 等于 a = a + b。 自赋值减法, a -= b 等于 a = a – b。 自赋值求余, a %= b 等于 a = a % b。 自赋值逻辑与, a &= b 等于a = a & b。 自赋值逻辑或, a |= b 等于 a = a | b。 自赋值求幂, a ^= b 等于 a = a ^ b。 重定向操作符,用来访问结构成员。
语言语法
HLSL语言的语法相当简单。初看可能有些复杂,但只要使用它写几个程序,你马上就能掌握要领。目前为止,你不应
该对语法太过担心,后面的章节我们将逐步了解语言的每个部分。由于实际的语法表相当长,我决定单独把他放到附录D中。另外你也可以参考DirectX SDK获取更多信息。
(译注:请参考DirectX SDK中DirectX Graphics--Reference--HLSL Shader Reference--Appendix中的Language Syntax部分)
观察表的第一行,可以看到HLSL程序被定义为一个program。每个program要么为空,要么包含一系列decl(声明)。
最初的两行表示每个decls可以由多条其它decl组成。你可能已经注意到,声明可以用来定义空白语句,类型声明,变量声明,结构声明,函数声明或technique声明。语义定义了不同声明类型等等。
小结以及接下来的内容
在这一章里,我们简要概括了DirectX和shader技术在过去几年间的发展和历史。随着shader model 2.0和3.0复杂度的增加,开发者不但需要利用语言的所有新能力,同时,还需要高效的完成任务。由于新着色管道的指令和通用处理器上的指令越来越类似,因此,开发一门高级语言,让开发者把注意力集中在shader所要实现的功能上,而不是把精力放在如何使用寄存器,或对某种硬件如何组合指令才能最优化之类的琐碎问题上是很有意义的。
在需求的驱动下,微软开发并通过DirectX SDK发布了HLSL着色语言,帮助开发者使用最新的图形技术,创建更加真实的图形。本章,我们学习了很多语法背后的基础知识。虽然这章看起来有些枯燥,不要担心,随后的几个章节我们就会讨论一些比较有趣的内容。
1.HLSL入门
1.1什么是着色器
DirectX使用管道技术(pipeline)进行图形渲染,其构架如下:
图1.1 Direct3D Graphics Pipeline
之前我们使用管道的步骤如下:
1. 设定顶点、图元、纹理等数据信息; 2. 设定管道状态信息;
? 渲染状态
通过SetRenderState方法设定渲染状态; 另外,使用以下方法设置变换、材质和光照:
SetTransform SetMaterial SetLight
LightEnable ? 取样器状态
通过SetSamplerState方法设定取样器状态; ? 纹理层状态
通过SetTextureStageState设定纹理层状态;
3. 渲染;
这部分交由D3D管道按照之前的设定自行完成,这部分操作是D3D预先固定的,所以这种管道技术被称为固定功能管道(fixed function pipeline);
固定功能管道给我们编程提供了一定的灵活性,但是仍有很多效果难以通过这种方式实现,比如:
1. 在渲染过程中,我们要求y坐标值大于10的顶点要被绘制到坐标值(0,0,0)的地方,在之前的固定功能管
道中,顶点被绘制的位置是在第1步即被设定好的,不可能在渲染过程中进行改变,所以是不可行的;
2. 某顶点在纹理贴图1上映射为点A,在纹理贴图2上映射为点B,我们要求该顶点颜色由A、B共同决定,即:
定点颜色 = A点色彩值*0.7 + B点色彩值*0.3
这在固定管道编程中也是不可行的。
以上两个问题都可以由可编程管道(pragrammable pipeline)来解决。
可编程管线允许用户自定义一段可以在GPU上执行的程序,代替固定管道技术中的Vertex Processing和Pixel Processing阶段(参照图1.1),从而在使我们在编程中达到更大的灵活性。其中替换Vertex Processing的部分叫做Vertex Shader(顶点着色器),替换Pixel Proccessing的部分叫做Pixel Shader(像素着色器),这就是我们所说的着色器Shader。
1.2什么是HLSL
Direct8.x中,着色器是通过低级着色汇编语言来编写的,这样的程序更像是汇编式的指令集合,由于其效率低、可读性差、版本限制等缺点,迫切要求出现一门更高级的着色语言。到了Direct3D9,HLSL(High Level Shading Language,高级渲染语言)应运而生了。
HLSL的语法非常类似于C和C++,学习起来是很方便的。
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;