上一章里,我们学习了HLSL主要的语法元素。现在,唯一没有讲解的只剩下函数了,包括如何声明与定义函数,如何在shader中使用函数。函数是高级语言中的重要组成部分,它在shader中也同样扮演了重要角色。HLSL语法允许使用两种类型的函数。内置(或固有)函数为shader提供了一个预定义函数库,同时也为特定着色构架提供了某些特殊指令。
当然,你可以创建自定义函数。自定义函数可以用来把shader组织为一个整体,也可以用来打包部分希望重用的功能。
接下来的几节里,我将会讨论两种类型的函数,在最后还会讲解如何用函数定义shader。先来看看HLSL提供的丰富内置函数库吧。
内置函数
HLSL着色语言包含了一系列广泛的,内置,或固有函数。这些函数在开发shader时相当有用。它们提供了从数学计算到纹理采样等广泛的功能。先依次浏览一下这些函数。
表 3-1 HLSL内置函数
abs 计算输入值的绝对值。
acos 返回输入值反余弦值。
all 测试非0值。
any 测试输入值中的任何非零值。
asin 返回输入值的反正弦值。
atan 返回输入值的反正切值。
atan2 返回y/x的反正切值。
ceil 返回大于或等于输入值的最小整数。
clamp 把输入值限制在[min, max]范围内。
clip 如果输入向量中的任何元素小于0,则丢弃当前像素。
cos 返回输入值的余弦。
cosh 返回输入值的双曲余弦。
cross 返回两个3D向量的叉积。
ddx 返回关于屏幕坐标x轴的偏导数。
ddy 返回关于屏幕坐标y轴的偏导数。
degrees 弧度到角度的转换
determinant 返回输入矩阵的值。
distance 返回两个输入点间的距离。
dot 返回两个向量的点积。
exp 返回以e为底数,输入值为指数的指数函数值。
exp2 返回以2为底数,输入值为指数的指数函数值。
faceforward 检测多边形是否位于正面。
floor 返回小于等于x的最大整数。
fmod 返回a / b的浮点余数。
frac 返回输入值的小数部分。
frexp 返回输入值的尾数和指数
fwidth 返回 abs ( ddx (x) + abs ( ddy(x))。
isfinite 如果输入值为有限值则返回true,否则返回false。
isinf 如何输入值为无限的则返回true。
isnan 如果输入值为NAN或QNAN则返回true。
ldexp frexp的逆运算,返回 x * 2 ^ exp。
len / lenth 返回输入向量的长度。
lerp 对输入值进行插值计算。
lit 返回光照向量(环境光,漫反射光,镜面高光,1)。
log 返回以e为底的对数。
log10 返回以10为底的对数。
log2 返回以2为底的对数。
max 返回两个输入值中较大的一个。
min 返回两个输入值中较小的一个。
modf 把输入值分解为整数和小数部分。
mul 返回输入矩阵相乘的积。
normalize 返回规范化的向量,定义为 x / length(x)。
pow 返回输入值的指定次幂。
radians 角度到弧度的转换。
reflect 返回入射光线i对表面法线n的反射光线。
refract 返回在入射光线i,表面法线n,折射率为eta下的折射光线v。
round 返回最接近于输入值的整数。
rsqrt 返回输入值平方根的倒数。
saturate 把输入值限制到[0, 1]之间。
sign 计算输入值的符号。
sin 计算输入值的正弦值。
sincos 返回输入值的正弦和余弦值。
sinh 返回x的双曲正弦。
smoothstep 返回一个在输入值之间平稳变化的插值。
sqrt 返回输入值的平方根。
step 返回(x >= a)? 1 : 0。
tan 返回输入值的正切值。
fanh 返回输入值的双曲线切线。
transpose 返回输入矩阵的转置。
tex1D* 1D纹理查询。
tex2D* 2D纹理查询。
tex3D* 3D纹理查询。
为了贴近实际,举个例子来展示如何使用这些函数吧。假设你需要把纹理映射到一个像素上,并且使用方向光来照亮这个像素。要完成这个任务,必须先计算光源对像素颜色的贡献,然后查找纹理颜色,最后把这两个颜色混合起来。首先,为了计算方向光的贡献,需要计算像素法线和光源方向的点积。使用dot函数可以很方便的完成这一步计算:
LightIntensity = dot ( LightDirection , PixelNormal);
这里可能会出现一点小小的问题,如果像素法线背对着光源方向,那么得到的亮度将为负值。必须保证亮度在0和1之间,以避免这种效果。怎么做呢?很幸运,saturate函数能完成这个任务,修改上面的代码:
LightIntensity = saturate ( dot ( LightDirection , PixelNormal ) );
在把灯光颜色添加到物体上之前,还需要从纹理中获得像素的颜色。假设像素已经包含了适当的纹理坐标并且有一张简单的2D纹理,那么纹理采样的代码如下:
PixelColor = tex2D ( objectTexture , TextureCoord ) ;
最后一步,就是对灯光和像素颜色进行混合。这里,我们需要灯光颜色,灯光亮度以及像素颜色作为参数。计算很简单,但你应该注意shader构架的矢量天性是如何对颜色中的所有分量同时起作用的。
FinalColor = ( LightColor * LightIntensity) * PixelColor ;
这个例子虽然简单,但是它展示了HLSL的强大威力,仅仅使用三行代码就能完成简单的光照。在进入下一个主题之前,需要指出根据完成功能的不同,内建函数可以接收不同的参数。另外,由于硬件性能的不同,部分内建函数并不是在所有顶点和像素着色器版本上都可用。