Shader实验室: smoothstep函数

Number of views 252

smoothstep可以用来生成0到1的平滑过渡值,它也叫平滑阶梯函数

image.png

上图演示的是通过smoothstep产生的距离场(Distance Field)效果,它是smoothstep函数的一个简单实现,同样smoothstep也符合重映射(可查阅Shader实验室: remap函数)的定义。

原理

smoothstep公式定义如下(通过源码分析):

float smoothstep(float t1, float t2, float x) {
  // Scale, bias and saturate x to 0..1 range
  // 还记得么?在remap算法中接触过
  x = clamp((x - t1) / (t2 - t1), 0.0, 1.0); 
  // Evaluate polynomial
  return x * x * (3 - 2 * x);
}

根据上式,我们找些数字代入,假设 t1 = -2,t2 = 3:

y = (x - (-2)) / (3 - (-2)) 。且 result = y * y * (3 - 2 * y)。

x还是取两个特殊值,分别为-2,3;

当x=-2,y=0,result = 0;

当x=3,y=1,result = 1;

此时可以作图如下:

image.png

可以看出,x在 t1 到 t2 值的范围内变化曲线由缓到快再到缓的过程,并且我们可以得出结论:

当x等于t1时,结果值为0;

当x等于t2时,结果值为1;

(ps: 值限制在0~1之间的原因是因为clamp函数的限制)

为了验证得出的结论并有个与结论对应的效果,我们交换t1与t2的值,也就是此时t1 = 3,t2 = -2,作图如下:

image.png

结合图像可知:
当x = t1 = 3时,结果值为0;
当x = t2 = -2时,结果值为1;

它再次验证了我们的结论,并且t1与t2值的位置也显得尤为重要,它决定了曲线变化的走向,请大家一定要记住该结论,并将它结合到我们的实际开发中。
我们已经分析了smoothstep函数的内部实现与原理,那么有一个问题不知道大家是否想过,假设有两个smoothstep函数(参数t1值都小于t2值),第二个smoothstep的t1值正好等于第一个smoothstep的t2值,并且两个smoothstep进行了相减操作,结果会如何:

假设第一个为:smoothstep(1, 2, x);

第二个为:smoothstep(2, 3, x);

作图如下:

image.png

根据结果可知,示例中的两个smoothstep进行相减操作时,在x的值为2时达到了峰值,并会往两边递减。

那如果我们把第二个smoothstep改为:
smoothstep(3, 4, x)结果又会如何呢?

image.png

同样的,峰值依旧会在第一个smoothstep中的t2值与第二个smoothstep中的t1值之间产生,并随之会往两边递减。

这也是我们在开发中经常会直接用到的结论,在实验部分我们也会用这个结论做一个圆环效果。

实验

  1. 新建Unlit Shader与Material。

  2. 打开Unlit Shader文件,并修改frag函数:

fixed4 frag (v2f i) : SV_Target
{
   // 先把uv坐标原点从左下角移至中心位置
   // 通过length计算点(uv.x,uv.y)到原点的距离
   // x大于等于t1=0.5时,结果为0
   // x小于等于t2=0.2时,结果为1
   return smoothstep(0.5, 0.2, length(i.uv - 0.5));
 }

3.呈现的效果如下:

image.png

  1. 实现圆环,修改frag代码如下:
fixed4 frag (v2f i) : SV_Target
{
    half l = length(i.uv - 0.5);
    float s1 = smoothstep(0.2, 0.3, l);
    float s2 = smoothstep(0.3, 0.4, l);
    return s1 - s2;
}
  1. 最终效果。

image.png

对Shader开发感兴趣的话,可一起关注下面的二维码噢。

image.png

0 Answers