求知若饥,虚心若愚
前言
常用的着色语言有以下几种:
- Cg(C for Graphics),由NVIDIA公司开发,目前已不怎么维护。
- GLSL(OpenGL Shading Language),由 OpenGL ARB 所建立。
- HLSL(High Level Shader Language),是由微软拥有及开发的一种着色器语言,维护积极。
本节主要以HLSL语言为例,但其实也大差不差,原理搞懂即可。
官方文档:https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-intrinsic-functions
函数介绍
IQ大佬的可以查看函数图像的网站:https://graphtoy.com
mac os 自带的Grapher也挺好用的
基本数学运算
函数 | 描述 |
---|---|
max(a, b) | 返回较大的那个 |
min(a, b) | 返回较小的那个 |
mul(a, b) | 两数相乘,常用于矩阵运算 |
abs(a) | 返回输入值的绝对值 |
round(x) | 返回与x最近的整数 |
sqrt(x) | 返回指定值的平方根 |
rsqrt(x) | 返回指定值的平方根的倒数 |
degrees(x) | 转换成弧度 |
radians(x) | 将角度转为弧度制 |
noise(x) | 噪声函数,将uv的坐标作为x传入,返回一个随机的[-1,1]的值,默认应该是柏林噪声(有多种噪声实现方案) |
幂指对于偏导数
函数 | 描述 |
---|---|
pow(x, y) | x的y次幂(x和y均可为自变量或具体的数),即 |
ldexp(x, exp) | 返回x与2的exp次方的乘积,即 |
exp(x) | 返回以e为底的指数函数,即 |
exp2(value x) | 返回以2为底,x为幂的指数,即 |
log(x) | 返回指定值的以e为底的对数,即 |
log10(x) | 求以10为底的对数,即 |
log2(x) | 求以2为底的对数,即 |
frexp(x, out exp) | 把浮点数 x 分解成尾数和指数返回值是尾数,exp参数返回的值是指数 (如果x参数为0,则此函数的尾数和指数均返回0), 公式为 是按照计算机存储浮点数的方式分解的,例如1.2这个数,是分为两部分的,尾数为12,指数为0.1 |
三角函数与双曲线函数
双曲线的解释:https://zhuanlan.zhihu.com/p/20042215
具体在图形学的用途等后面了解更多再补充~
函数 | 描述 |
---|---|
asin(x) | 返回输入值的反正弦值 |
acos(x) | 返回输入值反余弦值 |
atan(x) | 返回输入值的反正切值 |
atan2(y,x) | 返回y/x的反正切值 |
sin(x)、cos(x)、tan(x)、tan(y, x) | 常用三角函数 |
sincos(x, out s, out c) | 返回x的正弦值和余弦值 |
sinh(x) | 返回x的双曲正弦值,即 |
cosh(x) | 返回x的双曲余弦值,即 |
tanh(x) | 返回x双曲正切值,即 |
数据范围类
函数 | 描述 |
---|---|
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 |
frac(x) | 返回x部分的小数 |
fmod(a, b) | 返回x对y取余的余数 |
modf(x, out ip) | 将值x分为小数和整数部分(各部分符号与x相同) ip写入整数部分,函数返回小数部分 |
lerp(a, b, s) | 按照s在a到b之间插值,返回 |
smoothstep(min, max, x) | 如果x在[min,max]范围内,则返回介于0和1之间的平滑Hermite插值 使用smoothstep 在两个值之间创建平滑过渡。 例如,使用此功能平滑地混合两种颜色 |
类型判断类
函数 | 描述 |
---|---|
all(x) | 确定指定量的所有分量是否均为非零,均非零则返回true,否则返回false (处理由浮点型、整型、布尔型数据定义的标量、向量或者矩阵) |
clip(x) | 如果输入值小于零,则丢弃当前像素 常用于判定范围(不仅仅针对0, 返回值为void) 常用于测试alpha,如果每个分量代表到平面的距离,还可以用来模拟剪切平面 |
sign(x) | 返回x的正负性,如果x小于零返回-1,如果x等于零返回0,如果x大于零返回1 |
isinf(x) | 如果x参数为+INF或-INF(无穷+无穷=无穷,0x3f3f3f3f),返回true,否则返回False |
isfinite(x) | 判断x参数是有限,即有界的,与isinf(x)相反 |
isnan(x) | 如果x参数为NAN(非数字),返回true,否则返回false |
向量与矩阵类
函数 | 描述 |
---|---|
length(v) | 返回向量的长度 |
normalize(v) | 向量归一化, |
distance(a, b) | 返回两个点的距离 |
dot(a, b) | 返回a和b这两个向量的标积/内积/数量积/点积 a在b上的投影长, |
cross(a, b) | 返回a和b这两个向量的矢积/外积/向量积/叉积 返回值是个向量,而且与a、b都垂直,大小上 |
determinant(m) | 返回指定浮点矩阵的按行列式方式计算的值,几何意义可看3BLUE1BROWN的线代课 |
transpose(m) | 返回矩阵m的转置矩阵 |
光线运算类
函数 | 描述 |
---|---|
reflect(i, n) | 以i为入射向量,n为法线方向的反射光 |
refract(i, n, ri) | 以i为入射向量,n为法线方向,ri为折射率的折射光 |
lit(n_dot_l, n_dot_h,m) | 输入标量(法线点乘光照方向, 法线点乘半程向量,镜面反射系数m) 返回光照向量(环境光,漫反射光,镜面高光反射,1) |
faceforward(n, i, ng) | -n * sign(dot(i, ng)) 函数接受一个输入向量和两个面的法线 如果数据向量与第二个法线的点积是负数,那么它会返回第一个法线 否则返回第一个法线取反的结果向量。 你可能已经猜到了,这个函数可以去判断一个面在某个视角下 是正面还是反面 |
纹理查找
《GPU 编程与CG语言之阳春白雪下里巴人》: https://blog.csdn.net/liu_lin_xm/article/details/4810266
https://en.wikipedia.org/wiki/Mipmap
https://developer.download.nvidia.com/cg/tex2Dbias.html
https://blog.csdn.net/u013467442/article/details/46444673
https://blog.csdn.net/u010019717/article/details/91352180
https://www.gamedev.net/forums/topic/612268-information-on-how-to-do-texture-lod/
https://docs.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-textures-intro#3d-textures
https://docs.microsoft.com/en-us/windows/win32/direct3d9/texture-coordinates
http://www.3dtexture.eu/
https://docs.microsoft.com/en-us/windows/win32/direct3d9/cubic-environment-mapping
前置知识
-
纹理查找:GPU在片元着色阶段在屏幕空间XY坐标系中对每一个像素去对应的纹理中查找对应的纹素来确定像素的颜色
-
纹理查找函数都可以分为三类:普通的、微分的、投影的
-
微分类介绍,主要使用了偏导函数ddx、ddy
- 如果函数 ddx 的参数为 myVar,该参数对应的像素点记为p(i,j),则 ddx(myVar)的值为 “像素点 p(i+1,j)的值减去myVar” (ddy同理)
- 如果函数 ddx 和 ddy 的输入参数为常数,则函数返回值永远为 0。
- 函数 ddx 和 ddy 用于求取相邻像素间某属性的差值
- 函数 ddx 和 ddy 的输入参数通常是纹理坐标
- 函数 ddx 和 ddy 返回相邻像素键的属性差值;偏导数的物理含义是:在某一个方向上的变化快慢。
- ddx 求的是 X 方向上,相邻两个像素的某属性值的变化量
- ddy 求的是 Y方向上,
- 由于 ddx 和 ddy 指令是作用于像素级的,所以 ddx 和 ddy 函数只被片段程序所支持
- mipmap在选择到底用哪一层mipmap的level时,有一种查找方法就是偏导数。
-
投影纹理是指:将纹理当做一张幻灯片投影到场景中,使用投影纹理技术需要计算投影纹理坐标,然后使用投影纹理坐标进行查询。
- float existingDepth01 = tex2D(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPosition.xy / i.screenPosition.w)).r;
- float existingDepth01 = tex2D(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPosition.xy / i.screenPosition.w)).r;
-
Mipmap使用相关
- lod,采样mipmap指定层数
- bias,偏置后再采样(由t.w决定)
- grad,使用微分来选择mip的层,进行采样
1D纹理查找
- tex1D(s, t),普通一维纹理查找,返回纹理采样器s在标量t位置的color4
- tex1D(s, t, ddx, ddy) 使用微分查询一维纹理,t和ddx、ddy均为vector
- tex1Dlod(s, t),使用LOD查找纹理s在t.w位置的color4
- tex1Dbias(s, t),将t.w决定的某个Mipmap层偏置后的一维纹理查找
- tex1Dgrad(s, t, ddx, ddy),使用微分并指Mipmap层的一维纹理查找
- tex1Dproj(s, t),把纹理当做一张幻灯片投影到场景中,先使用投影纹理技术需要计算出投影纹理坐标t(坐标t.w除以透视值),然后使用投影纹理坐标进行查询
2D纹理查找
- tex2D(s, t),普通二维纹理查找,返回纹理采样器s在vector t位置的颜色
- tex2D(s, t, ddx, ddy),使用微分查询二维纹理 t和ddx、ddy均为vector
- tex2Dlod(s, t),使用LOD查找纹理s在t.w位置的color4
- tex2Dbias(s, t),将t.w决定的某个Mipmap层偏置后的二维纹理查找
- tex2Dgrad(s, t, ddx, ddy),使用微分并指定Mipmap层的二维纹理查找
- tex2Dproj(s, t),把纹理当做一张幻灯片投影到场景中,先使用投影纹理技术需要计算出投影纹理坐标t(坐标t.w除以透视值),然后使用投影纹理坐标进行查询
3D纹理查找
- tex3D(s, t),普通三维纹理查找,返回纹理采样器s在vector t位置的颜色
- tex3D(s, t, ddx, ddy),使用微分查询三维纹理 t和ddx、ddy均为vector
- tex3Dlod(s, t),使用LOD查找纹理s在t.w位置的color4
- tex3Dbias(s, t),将t.w决定的某个Mipmap层偏置后的三维纹理查找
- tex3Dgrad(s, t, ddx, ddy),使用微分并指定Mipmap层的三维纹理查找
- tex3Dproj(s, t),把纹理当做一张幻灯片投影到场景中,先使用投影纹理技术需要计算出投影纹理坐标t(坐标t.w除以透视值),然后使用投影纹理坐标进行查询
立体纹理查找
- texCUBE(s,t),返回纹理采样器s在vector t位置的颜色
- texCUBE(s, t, ddx, ddy),使用微分查询立方体维纹理 t和ddx、ddy均为vector
- texCUBEDload(s, t),使用LOD查找纹理s在t.w位置的color4
- texCUBEbias(s, t),将t.w决定的某个Mipmap层偏置后的立方体纹理查找
- texCUBEgrad(s, t, ddx, ddy),使用微分并指定Mipmap层的立方体纹理查找
- texCUBEproj(s, t),使用投影方式的立方体纹理查找
Task:ddx ddy的实际使用测试
纹理Mipmap层级采样
由于纹理采样时,可以传入ddx、ddy来计算mipmap的层级,差值越大,层级越高。
1 | // mipmap采样测试 |
边缘锐化
ddx、ddy物理意义是寻找在某一个方向上的变化快慢,正常连续的颜色变换不会太大,边缘处变化应该是较明显的。有点像后期处理中的卷积查找边缘操作,于是乎可以用ddx、ddy来将边缘锐化。
1 | // 边缘锐化 |
计算面法线
如果对于坐标应用ddx,那不就相当于数学中计算切线。分别计算沿x轴的切线和y轴的切线,进行叉乘处理即可得到法线。由于同个面上坐标变化不大,所以面上的法线应该是一致的。模型是网格组成的,对于三角形上的一个点的法线,使用切线叉乘计算的法线当然是原原本本的网格法线,而未经插值处理。
1 | // 计算面法线 |
更多应用
- 优化IBL的接缝问题:https://www.yuque.com/sugelameiyoudi-jadcc/okgm7e/cp846a
- 模型对深度值ddxddy从而描边:https://www.chinuno.com/blogv4/ta100-2300hlsl/
- 利用偏导数做抗锯齿:https://www.yuque.com/sugelameiyoudi-jadcc/okgm7e/fmiu0b#OJVza