本节主要介绍 shader 中一些重要的内置函数用法。
length
length
函数用于计算向量的长度(模长),例如对于一个二维向量vec2(3,4),可以根据勾股定理求得长度
c
length(vec2(3,4)) == 5
利用length函数,我们可以绘制一个简单的圆形
c
void main(){
// 将坐标归一化到[-1,1]
vec2 st = 2.0*(gl_FragCoord.xy/vec2(1000.0,1000.0)) - vec2(1.0);
float d = length(st-vec2(0,0));
if(d<0.3){
gl_FragColor = vec4(0.0,1.0,0.0,1.0);
} else {
discard;
}
}
dot
dot
函数用于计算两个向量的点积。
点积是两个向量相同位置的对应分量相乘后相加的结果。两个向量相乘的几何意义可以看成一个向量在另一个向量方向上的投影长度与另一个向量长度相乘。向量相乘具有如下公式
也可以表示成:
可以看出当两个向量垂直的话,其点积为0。我们可以此特性同样可以绘制一个圆
c
void main(){
// 将坐标归一化到[-1,1]
vec2 st = 2.0*(gl_FragCoord.xy/vec2(1000.0,1000.0)) - vec2(1.0);
float d = dot(st-vec2(-0.2,0.0),st-vec2(0.2,0.0));
if(d<=0.0){
gl_FragColor = vec4(0.0,1.0,0.0,1.0);
}else{
discard;
}
}
abs
取绝对值函数,这个很好理解。
c
abs(-0.1); // 0.1
abs(1.0); // 1.0
step
step
函数用于生成一个阶梯函数,它将根据阈值条件返回0或1
c
step(1,2) // 返回1.0,因为2>1
step(2,2) // 返回1.0,因为2=2
step(3,2) // 返回0.0,因为2<3
step函数用于边界过渡,由于只返回0和1的特性,可以使用 step 函数来代替if-else
的书写,在shader中我们推荐用 step 来代替if-else,这样可以提高性能。
例如绘制一个距离y轴0.3范围内的区域。
c
void main(){
// 将坐标归一化到[-1,1]
vec2 st = 2.0*(gl_FragCoord.xy/vec2(1000.0,1000.0)) - vec2(1.0);
float d = dot(st-vec2(-0.2,0.0),st-vec2(0.2,0.0));
float green = step(abs(st.x),0.3);
gl_FragColor = vec4(0.0,green,0.0,1.0);
}
green 值刚好只能取0 或者 1,为0就是黑色,为1就是绿色。
我们改写一下,再增加一个到x轴0.3范围内的区域。
c
void main(){
// 将坐标归一化到[-1,1]
vec2 st = 2.0*(gl_FragCoord.xy/vec2(1000.0,1000.0)) - vec2(1.0);
float d = dot(st-vec2(-0.2,0.0),st-vec2(0.2,0.0));
float green = step(abs(st.x),0.3) + step(abs(st.y),0.3);
gl_FragColor = vec4(0.0,green,0.0,1.0);
}
实际上累加的效果如下图,中间区域同时满足x轴和y轴,所以值为2,只不过片元着色器对于大于1的值自动转换成1了
同时我们可以看到两个step叠加之后的效果,那假如相减呢?试试看
c
void main(){
// 将坐标归一化到[-1,1]
vec2 st = 2.0*(gl_FragCoord.xy/vec2(1000.0,1000.0)) - vec2(1.0);
float d = dot(st-vec2(-0.2,0.0),st-vec2(0.2,0.0));
float green = step(abs(st.x),0.3) - step(abs(st.y),0.3);
gl_FragColor = vec4(0.0,green,0.0,1.0);
}
减法减去了第二个区域
换成乘法试试,则是两个区域的交集。
smoothstep
smoothstep
函数是一个平滑阶梯插值函数,它在两个阈值之间产生平滑的过渡,
scss
smoothstep(起始值,结束值,处在这个区域的某个值) // 返回[0,1]区域内的值
函数图像如下,当值小于起点时全是0,大于终点则全为1,在这之间则按照绿色曲线过渡,有点类似于css中动画效果
smoothstep 函数有很大作用,一般用于过渡或者发光效果,还有例如消除锯齿的作用。
c
void main(){
// 将坐标归一化到[-1,1]
vec2 st = 2.0*(gl_FragCoord.xy/vec2(1000.0,1000.0)) - vec2(1.0);
gl_FragColor = vec4(0.0,smoothstep(-1.0,1.0,st.x),0.0,1.0);
}
绘制一个边缘模糊圆(消除锯齿感觉)
c
void main(){
// 将坐标归一化到[-1,1]
vec2 st = 2.0*(gl_FragCoord.xy/vec2(1000.0,1000.0)) - vec2(1.0);
float d = length(st);
float green = 1.0-smoothstep(0.2-0.005,0.2+0.005,d);
gl_FragColor = vec4(0.0,green,0.0,1.0);
}
直观解释就是让d=0.2两边各划出0.005区域作为过渡区域来实现从绿色到黑色的渐变。
实现一个日光效果
c
void main(){
// 将坐标归一化到[-1,1]
vec2 st = 2.0*(gl_FragCoord.xy/vec2(1000.0,1000.0)) - vec2(1.0);
float d = length(st);
float v = 1.0-smoothstep(0.2,0.6,d);
gl_FragColor = vec4(vec3(v),1.0);
}
mix
mix
函数在GLSL中用于执行线性混合或插值。smoothstep 是陡峭-缓慢-缓慢-陡峭
过程,而 mix 函数则波澜不惊,均速上升
c
mix(a,b,x) // 返回a*(1-x)+b*x
当a=0,b=1时刚好有:
c
mix(0.0,1.0,0.3) // 返回0.3
clamp
clamp函数和mix有点类似,clamp函数用于将一个值限定在某个范围
c
clamp(x,a,b) // 当x<a时返回a,当x>b时返回b,否则返回本身
函数图像如下:
数学函数
当然还有很多关于数学的函数,使用起来比较简单,这里我就不一一举例子了。
函数 | 描述 |
---|---|
abs(x) |
返回 x 的绝对值 |
ceil(x) |
返回不小于 x 的最小整数 |
floor(x) |
返回不大于 x 的最大整数 |
round(x) |
返回最接近 x 的整数 |
fract(x) |
返回 x 的小数部分 |
sqrt(x) |
返回 x 的平方根 |
pow(x, y) |
返回 x 的 y 次方 |
exp(x) |
返回自然数e的x次方 |
log(x) |
返回 x 的自然对数 |
sin(x) |
正弦函数 |
cos(x) |
余弦函数 |
tan(x) |
正切函数 |
asin(x) |
反正弦函数 |
acos(x) |
反余弦函数 |
atan(x) |
反正切函数 |
sin(x) |
正弦函数 |
cos(x) |
余弦函数 |
tan(x) |
正切函数 |
asin(x) |
反正弦函数 |
acos(x) |
反余弦函数 |
atan(x) |
反正切函数 |
下一节将使用这些函数结合距离场法来实现一些绘图