【技术美术图形部分】2.3 HLSL常用函数

——介绍HLSL常用函数,API的使用。

想要成为合格的技术美术,一定要具备Shader开发能力,满足性能的需求,无论是技术美术的哪一个方向,HLSL都是需要点满的技能点。

参考

微软官方HLSL库:HLSL 参考 - Win32 apps | Microsoft Docs

 《DirectX 3D HLSL高级实例精讲》我看的是实体书:GitHub - ljb1672/DirectX-3D-HLSL-Senior-instance-succinctly: code for DirectX 3D HLSL Senior instance succinctly


HLSL 语言

在渲染管线和介绍主流API的博客中,都提到了“可编程渲染管线”,其核心就是HLSL语言,正是因为HLSL才能对渲染管线可编程(针对微软的DX而言,当然还有Cg和OpenGL)。

HLSL语言和C++很相似,但是除了bool、int、float、double等标准型变量外,还有texture纹理变量、float、vec等向量类型等等。

本篇博客就根据技术美术百人计划的进度,主要介绍介绍HLSL的常用函数,秉持着不单纯copy老师PPT的原则,惯例会加入自己的理解。

HLSL的函数

HLSL的核心函数是由显卡驱动程序提供的函数库,用于向量及图形的计算,有了此函数库,程序的重点是将物理现象和光照模型转化成数学算法,算法则是由核心函数表达的代码,HLSL常用的函数大概有以下几种。

图形计算器

推荐了一个很方便的、图形相关数学函数实施验证的网站:Graphtoy

当然,其他的例如dmsmos也可以: Desmos | 图形计算器

把HLSL函数输进去,可以实时看到函数的数学表达形式,无需写Shader才能可视化。

1 基本数学运算

10个常用的数学运算

max(a,b) 返回较大的一个
min(a,b) 返回较小的一个
mul(a,b) 两变量相乘,常用于矩阵运算
abs(value a) 返回a的绝对值
round(x) 返回与x最近的整数
sqrt(x) 返回x的平方根
rsqrt(x) 返回x的平方根的倒数
degrees(x) 弧度转换成角度
redians(x) 角度转换成弧度
noise(x) 噪声函数,传入二维坐标返回[0,1]随机值

2 幂指对函数

常用的幂指对函数

pow(x,y) x的y次幂 x^{y}
exp(x) 返回以e为底的指数函数 e^{x}
exp2(value x) 返回以2为底,x为指数的幂 2^{x}
ldexp(x,exp) 返回x与2的exp次方乘积 x*2^{exp}
log(x) 返回以e为底的对数 ln x
log2(x) 返回以2为底的对数 log_{2}x
log10(x) 返回以10为底的对数 log_{10}x
frexp(x,out exp) 把浮点数x分解成尾数和指数,返回值是尾数ret,exp参数返回的是指数,如果x为0,则exp和ret均0 x=ret*2^{exp}

in/out/inout关键字

frexp(x,out exp)中出现了out,是个关键字,一起的还有in、inout:

  • in——默认的,可不加,表必须传递参数值
  • out——表该参数是函数的一个返回值,仅用作输出,不能用作输入
  • inout——可输入也可输出

关于out具体点的解释:指定实参应该在函数返回时被拷贝给型参,这样可以通过参数返回值。out关键字是必须的,因为HLSL不允许传递一个引用或一个指针。如果实参标记为out,在函数开始前,型参就不拷贝给实参了。换句话说,out实参仅可以被用于输出数据——它不能用于输入。

3  三角函数和双曲函数

三角函数

sin(x) 求正弦,下划线前x均为弧度
cons(x) 求余弦
tan(x) 求正切
sincos(x,out s,out c) 返回x的正弦值s和余弦值c
tan(y,x) 返回y/x的正切值
asin(x) 返回输入值的反正弦值
acos(x) 返回输入值的反余弦值
atan(x) 返回输入值的反正切值
atan2(y,x) 返回y/x的反正切值

双曲函数

sinh(x) 返回x的双曲正弦值 (e^{x}-e^{-x})/2
cosh(x) 返回x的双曲余弦值 (e^{x}+e^{-x})/2
tanh(x) 返回x的双曲正切值 (e^{x}-e^{-x})/(e^{x}+e^{-x})

双曲函数——悬链线

这部分参考了:可能是最好的讲解双曲函数的文章 - 知乎 (zhihu.com)

双曲函数的起源是悬链线。双曲函数是一条有点像二次抛物线又不完全是的曲线,这条曲线与两端固定的绳子(铁链)在均匀引力作用下下垂的弧度相似,因此这条曲线也直接被叫做悬链线。具体的直接看上面的参考文章把,讲得非常不错!

一图展示三角函数和双曲函数的关系: 

4  数据范围类

接下来是10个求数据范围的常用函数。

数据范围相关常用函数

ceil(x) 返回≥x的最小整数
floor(x) 返回≤x的最大整数
step(x,y) x≤y为1,否则为0
saturate(x) 钳制x在0到1之间,返回
clamp(x,min,max) 把x限制在[min,max]内,小于返回min大于返回max,是saturate()的升级版
fmod(x,y) 返回x/y的浮点部分(取余的余数部分)
frac(x) 返回输入x的小数部分
modf(x,out ip) 将x分为小数和整数部分,ip返回整数整体返回小数
lerp(x,y,s) 按照s在x到y之间插值,可以看成按照s分段,即x*(1-s)+y*s
smoothstep(min,max,x) 如果x在[min,max]范围内,则返回介于0和1之间的平滑Hermite插值,使用smoothstep在两个值之间创建平滑过渡,例如平滑的混合两种颜色值

smoothstep函数应用之-产生距离场

这部分参考了:GLSL函数smoothstep讲解 - 简书 (jianshu.com)

Shader实验室: smoothstep函数 - 知乎 (zhihu.com)

min --> edge0下界,max --> edge1上界,x插值输入,三者不仅可以是float,还可以是vec2/vec3/vec4变量,其用法:

float smoothstep(float edge0, float edge1, float x)  
vec2 smoothstep(vec2 edge0, vec2 edge1, vec2 x)  
vec3 smoothstep(vec3 edge0, vec3 edge1, vec3 x)  
vec4 smoothstep(vec4 edge0, vec4 edge1, vec4 x)
vec2 smoothstep(float edge0, float edge1, vec2 x)  
vec3 smoothstep(float edge0, float edge1, vec3 x)  
vec4 smoothstep(float edge0, float edge1, vec4 x)

如果想要创建一个能够输出平滑过渡的函数,就可以用smoothstep,它其实又等同于:

genType t;
t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
return (3.0 - 2.0 * t) * t * t;

 可以用来干什么?——产生距离场(Distance Field)效果,下图是参考文章Shader实验室: smoothstep函数 - 知乎 (zhihu.com)中实现的效果:

5 类型判断类

给出了6个与数据类型判断相关的函数:

类型判断函数

all(x) 可以传入矢量,所有分量均为非0返回true,否则false
any(x) 只要传入的x有任何一个分量非0返回true,否则false
clip(x)

如果输入值小于0,丢弃该像素值,不进行渲染

(像素值为负->没有颜色)

sign(x) 返回x的正负,x<0返回-1,x=0返回0,x>0返回1
isinf(x)

如果x参数为+INF或-INF,返回true,否则false

isinite(x) 判断x参数是否有限(有界),与isinf(x) 相反
isnan(x)

x为NAN(非数字),返回true

x为数字,则返回false

clip函数应用之-透明度测试

这部分参考自《入门精要》

在片元着色器中使用clip函数进行透明度测试,如果给定参数的任何一个分量是负数,就会舍弃当前像素的输出颜色,它相当于:

void clip(float4 x){
    if(any(x<0))
        //一个discard指令以剔除片元
        discard;
}

通常透明度测试会给定一个材质参数_Cutoff,当前像素颜色为texColor.a,通过clip函数配合discard指令来实现:

//Alpha Test
clip(texColor.a - _Cutoff);

 当然除了透明度测试外,还可以用来模拟间切平面、颜色渐渐消失的效果等等。

6 向量与矩阵类

举出了7个与向量、矩阵计算有关的函数。

向量与矩阵计算函数

length(v) 返回向量的长度
normalize(v) 向量归一化,x/length(x)
distance(a,b) 向量各分量之差的平方和
dot(a,b) a在b上的投影长,|a||b|cosθ,返回值是标量
cross(a,b)

a,b向量的叉积,例如计算TBN矩阵等等,返回值是个向量,与a,b均垂直

determinant(m) 返回矩阵m按行列式方式计算的值
transpose(m)

返回m的转置矩阵

7 光线运算类

介绍4个与光照计算相关的函数。

光照计算相关函数

reflect(i,n) i为入射向量,n为法向量,返回反射向量
refract(i,n,ri) i为入射向量,n为法向量,ri为折射率,返回折射向量
lit(n\cdot l,n\cdot h,m) n为法向量,l为光向量,h为半角向量,m为镜面反射系数,返回光照向量(环境光,漫反射光,镜面高光反射,1)
faceforward(n,i,ng) n得到面向视图方向的曲面法向量,返回值-n*sign(dot(i,ng))

lit函数到底返回了什么

官方文档参考:lit - Win32 apps | Microsoft Docs

 懂了吧!传出的其实就是Blinn-Phong反射模型计算环境光项La、漫反射项Ld、高光项Ls需要用到的一些值,简单的补充一下

  • 环境光—— L_{a}=k_{a}(I/r^{2})
  • 漫反射——L_{d}=k_{d}(I/r^{2}){\color{Red} max(0,\vec{n}\cdot \vec{l})}
  • 高光项——L_{s}=k_{s}(I/r^{2}){\color{Red} max(0,\vec{n}\cdot \vec{h})^{p}}

(其中高光项的p相当于lit函数里输入的m——越大代表着高光点越小,到这里一脸懵的话快去101补课!!!)

标红的部分就分别对应着lit函数返回的vector->(环境光,漫反射光,镜面高光反射,1)

faceforward函数

仍旧放上官方:faceforward - Win32 apps | Microsoft Docs

注意,返回的n的方向肯定会朝着入射向量i的相反方向。

8 纹理映射函数(纹理查找)

须知,纹理从一维、二维到三维都可进行查询。低维(一维或二维)的纹理例如法线纹理、渐变纹理、遮罩纹理等等,一维类比一维数组,二维的类比二维数组,很好理解。三维的相当于二维的叠加,还有立方体纹理查找。

所有的纹理查找函数都可以分为普通的、微分的和投影的三类方式。关于每一个查找函数细节一点的解释,我都放在了2D纹理查找函数介绍中进行说明。

8.1 1D纹理查找(几乎不用)

tex1D(s,t) 普通1D纹理查找,s是纹理采样器,t是位置,返回颜色值
tex1D(s,t,ddx,ddy) 带微分的1D纹理查找值,即使用导数查找纹理
tex1Dbias(s,t) 使用t.w决定某个MIP层的1D纹理查找
tex1Dgrad(s,t,ddx,ddy) 使用微分(梯度)并指定MIP层的1D纹理查找
tex1Dlod(s,t)

使用LOD方式的1D纹理查找

tex1Dproj(s,t) 使用投影方式的1D纹理查找,把纹理当作一张图片投影到场景中,先使用投影纹理技术计算出投影纹理坐标t(做了透视除法的t.w),然后使用投影纹理坐标进行查询

8.2 2D纹理查找

tex2D(s,t) 普通2D纹理查找,s是纹理采样器,t是vector,返回颜色
tex2D(s,t,ddx,ddy) 带微分的2D纹理查找值,t和ddxy都是vector,ddx和ddy的作用是取屏幕上相邻采样点的差值(本身偏导的作用就是表示方向上变化的快慢),ddxddy越大差值越大,采样像素越少,结果越模糊
tex2Dbias(s,t)

使用t.w决定某个MIP层的偏移量,把计算出来的纹理层全部偏移某个指定的值

例如tex2Dbias(_Texture, float4(uv,0,1));则是将MIPMAP的level提高一个等级,可用于对图像采样结果进行模糊or锐化

tex2Dgrad(s,t,ddx,ddy) 使用微分(梯度)并指定MIP层的2D纹理查找
tex2Dlod(s,t)

使用LOD方式的2D纹理查找,查找t.w纹理、t.uv位置处的color4

tex2Dproj(s,t) 使用投影方式的2D纹理查找,把纹理当作一张图片投影到场景中,先使用投影纹理技术计算出投影纹理坐标t(做了透视除法的t.w),然后使用投影纹理坐标进行查询

8.3 3D纹理查找

3D纹理可以理解成多个2D纹理叠加的结果,因此在查找时除了指定uv还需要指定3D纹理的层数。

但我其实不是很分得开三维纹理和MIPMAP的区别?或许三维纹理对每一层的纹理可控性更高吗?因为MIPMAP层次是有规律的,而3D纹理我可以按照喜好存放每一层纹理的大小。

tex3D(s,t) 普通3D纹理查找
tex3D(s,t,ddx,ddy) 带微分的3D纹理查找值
tex3Dbias(s,t) 使用t.w决定某个MIP层的3D纹理查找
tex3Dgrad(s,t,ddx,ddy) 使用微分(梯度)并指定MIP层的3D纹理查找
tex3Dlod(s,t)

使用LOD方式的3D纹理查找

tex3Dproj(s,t) 使用投影方式的3D纹理查找,把纹理当作一张图片投影到场景中,先使用投影纹理技术计算出投影纹理坐标t(做了透视除法的t.w),然后使用投影纹理坐标进行查询

8.4 立方体纹理查找

除了低维的纹理查找,和3维的纹理(2维纹理的叠加)查找,还有更复杂一点的立方体纹理(Cubemap),立方体纹理一般用以实现环境映射来模拟物体周围的环境

立方体纹理一共包含了6张图像,每张图像对应立方体的六个面,那么如何进行查询(采样)呢?三维纹理坐标——从立方体中心出发,向外部延伸并和六个面之一相交,采样得到的结果就是由这个交点计算而来的。

texCUBE(s,t) 立方体环境贴图
texCUBE(s,t,ddx,ddy) 带微分的立方体纹理查找值
texCUBEbias(s,t) 使用t.w决定某个MIP层的立方体纹理查找
texCUBEgrad(s,t,ddx,ddy) 使用微分(梯度)并指定MIP层的立方体纹理查找
texCUBElod(s,t)

使用LOD方式的立方体纹理查找

texCUBEproj(s,t) 使用投影方式的立方体纹理查找,把纹理当作一张图片投影到场景中,先使用投影纹理技术计算出投影纹理坐标t(做了透视除法的t.w),然后使用投影纹理坐标进行查询

作业

1.写出你觉得最常用的5个函数

常用的话,感觉是涉及到向量计算的一些函数还有随机取值的一些:

  • mul(a,b)——变量相乘
  • dot(a,b)——向量点乘,光照计算里再普遍不过了
  • cross(a,b)——叉积,求垂直向量的时候经常用到,比如TBN矩阵那里
  • normalize(v)——向量归一化
  • clamp(x,min,max)——限制数的范围

2.ddx和ddy的实际使用测试

后续再填坑。

猜你喜欢

转载自blog.csdn.net/qq_41835314/article/details/126814172