因为用汇编语言写着色器(shader)实在是太不方便了,所以产生了高级着色器语言(High-Level Shader Language —— HLSL)。
这是Microsoft DirectX9包含的一种,用于开发和调试着色器的类C语言,可以用于顶点着色器,也可以用于像素着色器,当然也可以用来做一些有意思的特效(Effect)。但是说白了,这些有意思的特效,也是大部分都是针对Vertex和Pixel的,一般来说,如果有人发挥了超乎寻常的创造力,将主要逻辑放在HLSL中处理,我就只能很无语的在一边看着,并报以复杂难明的目光……(附加申明,对于CUDA这种东西,我还是觉得挺有新意的,赞赏中欣喜学习中,不过请将其与HLSL分开)
有意思的是,很多招聘(或者说绝大部分)都写着对于3D引擎程序员的要求:精通vertex shader 、 pixel shader 、 HLSL。原来我觉得这个要求挺正常的,现在想起来还真的就觉得不是那么回事。简单的评价一下道理,你说我精通HLSL的话如果不是用来写vertex shader,pixel shader,难道是吃饱了饭没事干学这个类c语言类,用于逻辑兴趣的扩充?完善以及学习设计思想?真是让人感到哭笑不得……估计是某人编写人员招聘需求时,不大不小的一个小疏忽吧!但是一传十,十传百,变成大家都犯的小错误,错误了一千遍,这个对于3D引擎程序员的要求就变成真理了/(^o^)/~!!
顺便在此抨击一下微软这只大鳄,这是一种专门写Shader程序的编程语言,这是一种面向过程的高级语言。它除了读起来很不爽外(全是辅音字母,没法缩读),由于是微软开发和拥有的语言,所以只能供D3D使用,确切说是只能嵌套在D3D中跑,并且只有D3D能理解它(通过内置的编译器)。 微软这种垄断式的做法是为了对抗GLSL(GL Shader Language)。
但是同时,必须说,因为不用考虑所谓的兼容什么的,也去掉了不少让人心烦的东西。记得为了学习使用cg,摆弄莫名其妙,文档不全的编辑器就让我的耐心被消磨了十层楼那么高。
回到正题,HLSL支持类C函数开发着色器,支持函数(fuctions),表达式(expresstions),声明(statements),标准数据类型(standard date types),自定义数据类型(user-desighed date types),预处理指令(preprocessor directives)。
数值类型(scalar types)
bool true or false
int 32位整数
half 16位浮点数
float 32位浮点数
double 64位浮点数
因为不是所有的目标平台都支持整数型数据,整数型可能被浮点型硬件模拟。所以在这些平台上整数型操作超出整数位部分会被表达成浮点型,这种情况下DirectX不保证执行期望的功能。
不是所有的目标平台都支持half和double类型,如果不支持将用float类型模拟。这个问题倒是不算大,至少对于运行没有什么太大影响,可能稍微多站一些显存,要求用到double的无法支持那么大的数值(硬件限制情况下要达到相应效果也没有那么容易)。
下面开始说一些比较特别的东西,针对这种语言的数据类型。
这两个东西表面上看,是专门用于进行3D渲染变成而定的变量,但是,伟大的语言创制者们早就考虑到了扩展。
vector 由四个float类型组成的矢量
vector 由size个数值类型组成的矢量
矢量的各个组成部分可以通过标量存取,通过数组存取语法,如下:
vec[0] (same as vec.x)
vec[3] (same as vec.w)
matrix 4*4矩阵,由float类型组成
matrix row*col矩阵,由type类型组成
矩阵的各部分可以通过标量存取,通过数组存取语法如下:
mat[2] (same as mat.m20m21m22m23)
mat[2].w (same as mat.m23)
mat[2][3] (same as mat.m23)
矢量和矩阵也都可以通过名称存取,通过结构成员存取语法。
矢量默认设置成员名称 x y z w
当作为颜色是成员名称变为 r g b a
矩阵名称的设置也有两种
基于1的设置
_11 _12 _13 _14
_21 _22 _23 _24
_31 _32 _33 _34
_41 _42 _43 _44
基于0的设置
_m00 _m01 _m02 _m03
_m10 _m11 _m12 _m13
_m20 _m21 _m22 _m23
_m30 _m31 _m32 _m33
上面有种类似杂耍的表达方式,就是将
mat[2] 表达为 mat.m20m21m22m23
这种表达我是不太赞成的,虽然实际上内部数据排列就是这样,但是每次看到这种东西出现在HLSL的代码中我都会想,这是要体现对于语法的熟悉程度么?就不能好好把代码写清楚一点,让阅读者更多的了解程序的本意不好么!
一个pixelshader对象表示Direct3D像素着色器对象。当着色器函数里没有操作直接接受像素着色器时,像素着色器可以在technique内部设置给设备。下面的例子,可以被一个pixelshader对象质问,通过结构成员存取语法。
const string version;
字面上的pixelshader值可以被表示成汇编块:
pixelshader ps = asm { ps.2.0 mov oC0, c0 };
或者一个compile调用:
pixelshader ps = compile ps_2_0 psmain();
一个sampler对象表示Direct3D取样器层。其样器层被用来取样纹理。取样器被指定纹理和过滤类型。
一个texture对象表示Direct3D纹理对象。当着色器函数没有操作直接接受纹理时,纹理可以在technique中被设置给设备。下例可以被一个texture对象质问,通过结构成员存取语法。
const string type;
const string format;
const int width;
const int height;
const int depth;
一个vertexshader对象表示Direct3D顶点着色器对象。当着色器函数没有操作直接接受顶点着色器时,顶点着色器可以在technique中被设置给设备。下例可以被一个vertexshader对象质问,通过结构成员存取语法。
const string version;
字面上的vertexshader值可以被表示成汇编块:
vertexshader vs = asm { vs.2.0 mov oPos, c0 };
或者一个compile调用:
vertexshader vs = compile vs_2_0 psmain();
需要关注的还有technique 和 pass,这两个东西非常的关键。实际上应该写在这个文章的最开头,但是我没有想好怎么写,另外一篇杂谈完成之后我再写好了。然后会找时间重编一个规律的学习过程,整理自己的思路。当做补上以前的学习笔记好了。