Shader绘制2D圆-SmoothStep函数-学习Cherno教程笔记-(OpenGL/Vulkan/DirectX/Metal)-新手向

相关网址

Cherno原视频网址:https://www.youtube.com/watch?v=xf7Y988cPRk

shader网站:https://www.shadertoy.com/

GLSL函数解释网址:https://docs.gl/

此文是记录对第一个网址学习的笔记和思考的过程。

鄙人作为shader初学者,文中应该会有错误,欢迎指正。

前言

  • 基本思路

    用Quad顶点包围一个范围作为画布(Canvas)用glsl控制画出一个圆内alpha为1,圆外的范围的alpha为0。
    在这里插入图片描述

  • 如何控制在圆内的alpha为1,圆外的alpha为0

    用当前坐标点距离原点的长度、step函数来控制

基本实现画一个圆步骤

1.初步-根据长度绘画圆

  • shader和效果图

在这里插入图片描述

  • 说明

    1. uv相当于在xy坐标系的坐标,xy坐标系的原点在中心

    2. length(uv) = sqrt(u*u+v*v) 即长度

    3. 原点的坐标是(0, 0),distance是0,所以是黑色

      假设x点(0, 0.5), distance是0.5,所以是灰色

    所以形成

    • 靠近原点的颜色是黑色
    • 远离原点的颜色是灰色

2.初步-根据1-length(uv)绘画圆

  • shader+效果图
    在这里插入图片描述

  • 说明

    distance= 1-length(uv);

    原点的坐标是(0, 0),length(uv)是0,被1减去后,是白色1

    假设边缘点x(0, 1), length(uv)是1,被1减去后,是黑色0

    所以形成翻转颜色的效果

    • 原先靠近圆心是黑色0,远离圆心逐渐递增到白色1

    • 现在靠近圆心是白色1,远离圆心逐渐递减到黑色0

3.正式-绘画白色形状的圆

(1)if逻辑代码实现

  • shader代码+效果图

在这里插入图片描述

  • 说明

    if(distance > 0.0){
    	distance = 1.0;
    }
    

    表示原先大于0.0的颜色都白色,即灰色都是白色

(2)用shader的step函数

  • shader代码+效果图

在这里插入图片描述

  • 说明

    step(edge0, x); x<edge0 返回0,x>edge0 返回1

    step函数作用同

    if(distance > 0.0){
    	distance = 1.0;  
    }
    

    step(0, x);函数图像

    在这里插入图片描述
    从线性变成阶梯型

完善圆-实现绘画手环圆

1.if逻辑代码实现

  • shader+效果

在这里插入图片描述

  • 说明

    • 原本靠近圆心的distance为1,颜色为白色1,远离圆心的distance逐渐从1降为0,颜色从白色1到黑色0。

      但经历下面代码后成上图所示

      if(distance > 0.1){
      	col = vec3(0.0);
      }
      
    • 被白色包围的黑色范围形成说明

      以圆心为出发点,length(uv)在(0, 0.9)范围内的distance为(1, 0.1),满足if条件,使颜色为黑色0。

    • 白色范围

      以圆心为出发点,length(uv)在(0.9, 1)范围内的distance为(0.1, 0),不满足if条件,使颜色依旧是白色0。

    • 白色外面的黑色范围形成说明

      以圆心为出发点,length(uv)在(1, 1.1)范围内的distance为(0, -0.1),虽然不满足if条件不受第13行if代码块的影响,但是受第12行代码step函数的影响,早已为黑色。

    所以白色范围length为(0.9, 1)

2.用step函数代替if

  • shader+效果

在这里插入图片描述

  • 说明

    • col *= vec3(1.0 - step(0.02, distance))的作用相当于

      if(distance > 0.1){
      	col = vec3(0.0);
      }
      
    • 设第13行*以的中间值c2如下

      c2 = vec3(1 - step(0.02, distance));// step(0.02, distance)使得大于0.02的为1,小于0.02的为0
      
    • c2的值

      以圆心为出发点,length(uv)在(0, 0.98)范围内的distance为(1, 0.02),step(0.02, distance)后为1,被1减去后,为黑色0

      以圆心为出发点,length(uv)在(0.98, 1)范围内的distance为(0.02, 0),step(0.02, distance)后为0,被1减去后,为白色1

    • col的值-第11行的代码,并执行第13行的*代码

      vec3 col = vec3(step(0.0, distance));
      col *= c2;
      

      以圆心为出发点,length(uv)在(0, 0.98)范围内的distance为(1, 0.02)的col是白色1,与上述同范围的c2黑色0相乘为0黑色。

      以圆心为出发点,length(uv)在(0.98, 1)范围内的distance为(0.02, 0)的col是白色1,与上述同范围的c2白色1相乘为1白色。

    所以白色的范围length是(0.98, 1),所以很小

用smoothstep代替step函数

1.smoothstep函数

  • 说明

    https://docs.gl/sl4/smoothstep

    // 当edge0 < edge1时,当x < edg0时,返回0,当x > edg1时,返回1,当x在edg0和edg1之间时,返回x。
    // 当edge1 < edge0时,当x < edg1时,返回1,当x > edg0时,返回0,当x在edg0和edg1之间时,返回x。这个有点难理解,是我在下面情况测试出来的,没有官方说明,也许不对
    smoothstep(edge0, edge1, x);
    

    等同于

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

    自己测试smoothstep的结论,测试过程在末尾

    // 当edge1 < edge0时,当x < edg1时,返回1,当x > edg0时,返回0,当x在edg0和edg1之间时,返回x。
    // 当edge0 = edge1 ,smoothstep退化成step
    // 但当edge0=edge1=0,smoothstep(edge0,edge1,x);无论x是什么都返回0!
    smoothstep(edge0, edge1, x);
    
  • 表示的阶梯型函数图形

    在这里插入图片描述

    如图所示,在e0 e1范围更平滑,不像step那么突兀

2.使用smoothstep

  • 代替后的shader代码+效果图

    在这里插入图片描述

    // 其中fade = 0.005,即smoothstep的第二个参数16行替换成smoothstep函数是给外圈边缘平滑过渡的
    第17行替换成smoothstep函数是给内圈边缘平滑过渡的
    

    在这里插入图片描述

3.完善shader代码

  • 当前代码的不足

    由于之前第17行代码有点冗余,所以可以简短一点,写完如下,效果依旧保持良好不变

    在这里插入图片描述

    vec3(smoothstep(thickness, thickness - fade, distance));
    等价于
    vec3(1.0 - smoothstep(thickness - fade, thickness, distance));
    
  • 第17行代码说明

    • 简要说明

      原先是递增,改后是递减

    • 翻转图示

      在这里插入图片描述

      在这里插入图片描述

      原先小于0.095为0,大于0.1是1——>现变成小于0.095为1,大于0.1为0

    • 结合代码和效果图具体说明

      • 原先的效果-递增

         c2 = vec3(1 - smoothstep(0.1 - 0.005, 0.1, distance));
         // distance < 0.095 为0,distance > 0.1为1
         c2 = vec3(1 - smoothstep(0.095, 0.1, distance));
        

        length(uv)在(0, 0.9)范围内的distance为(1, 0.1),smoothstep后为1,被1减去后,为黑色0

        length(uv)在(0.9, 0.905)范围内的distance为(0.1, 0.095),smoothstep后为不变,被1减去后,为平缓的白色(0.9, 0.905)之间

        length(uv)在(0.905, 1)范围内的distance为(0.095, 0),smoothstep后为0,被1减去后,为白色1

      • 改后的效果-递减

        说明当edge0 > edge1时函数的意义发生变化
        (这个变化上面介绍smoothstep时也说了,不知是否正确,如有错,欢迎指正,验证过程在末尾。)

        c2 = vec3(smoothstep(0.1, 0.1 - 0.005, distance))
        // distance < 0.095 为1,distance > 0.1为0,
        c2 = vec3(smoothstep(0.1, 0.095, distance))
        

        length(uv)在(0, 0.9)范围内的distance为(1, 0.1),>0.1,smoothstep后为黑色0

        length(uv)在(0.9, 0.905)范围内的distance为(0.1, 0.095),smoothstep后为不变,被1减去后,为平缓的白色(0.9, 0.905)之间

        length(uv)在(0.905, 1)范围内的distance为(0.095, 0),<0.095,smoothstep后为白色1

      所以结果不变。

用smoothstep的小Bug

1.Bug说明

  • 当thickness=1时,会导致中间有一个很小很小的黑色洞

    在这里插入图片描述

  • 解释——待实际验证

    由smoothstep的减法导致的

    // distance < 0.995 为1,distance > 1为0
    c2 = vec3(smoothstep(1, 0.995, distance))
    

    注意:由图像所示,不止>1,接近1的位置也是0

    在这里插入图片描述

    length(uv)在(0, 0.002)范围内的distance为(1, 0.998),在(0.995,1)之间,但是0.998靠近1,smoothstep后为接近黑色0,所以原点呈现黑色。

    tips:这接近1的位置为0是我自己猜的,有待验证和询问别人,还是那句话,欢迎指正。

2.解决方法

方法一

初始化thickness += fade,可以解决洞的问题

在这里插入图片描述

  • 解释——待实际验证

    // distance < 1 为1,distance > 1.005为0
    c2 = vec3(smoothstep(1.005, 1, distance))
    

    length(uv)在(0, 0.002)范围内的distance为(1, 0.998),不在(1.005,1)之间,而是<1,smoothstep后为白色1。

    tips:欢迎指正

方法二

推荐,代码更好看吧

  • 解决方式

    将thickness+fade代码写入smoothstep中

    在这里插入图片描述

  • 解释——同上

    // distance < 1 为1,distance > 1.005为0
    c2 = vec3(smoothstep(1.005, 1, distance))
    

    length(uv)在(0, 0.002)范围内的distance为(1, 0.998),不在(1.005,1)之间,而是<1,smoothstep后为白色1。

    tips:如有错,欢迎指正

  • 新问题

    会有新问题,即thickness=0时,fade=0.005时,有个0.005的厚度薄圆轮廓(如上两个箭头所指)

  • 解决方法

    这问题迫不得已,cherno没说解决方案。

    我自己测试,当thickness=0时,只需调整fade为0就不会有薄圆了

  • 说明为何有效

    1.先说薄圆轮廓产生原因

    设fade= 0.1, thickness = 0

    float distance = 1.0 - length(uv); 
    vec3 col = vec3(smoothstep(0, 0.1, distance)); 
    // distance < 0 为1,distance > 0.1为0
    col *= vec3(smoothstep(0.1, 0, distance));
    
    • uv为圆内黑色范围的点

      length(uv)在(0, 0.9)范围

      1.distance > 0.1
      2.col = 1
      3.col = 1 * 0 = 0;// * 0因为distance > 0.1为0
      

      所以呈现黑色

    • uv为圆上薄圆轮廓范围的点

      length(uv)在(0.9, 1)范围

      1.0 < distance < 0.1
      2.0 < col < 0.1
      3.col = (0, 0.1) * (0, 0.1) = (0, 0.1);
      

      col在(0, 0.1)范围,接近0.1的部分是黑色0,接近0的部分(边缘)是白色1,所以在边缘部分呈现薄圆

    2.将fade=0时,薄圆消失

    设fade= 0, thickness = 0

    float distance = 1.0 - length(uv); 
    vec3 col = vec3(smoothstep(0, 0, distance));
    
    col *= vec3(smoothstep(0, 0, distance));
    

    不管distance是多少,smoothstep(0, 0, distance)返回0,所以col是黑色,与背景融合在一起,所以轮廓消失。

测试smoothstep(edg0, edg1, distance)

edge0 < edge1与edge0 > edge1,smoothstep函数的意义不同

  • 前言

    这段较难理解但是关键,有点不好说明,可以跳过,是用OpenGL的glsl测试smoothstep函数

  • glsl关键代码

    ....
    // fragment阶段
    void main()
    {
    	float distance = 1.0 - length(Input.LocalPosition);// distance = 1-圆心的距离
    	float circle = smoothstep(0.5, Input.Fade, distance);
    	o_Color = vec4(circle, circle, circle, 1);
    }
    

    可见,Input.Fade是变量,可以调节它是否小于0.5还是大于0.5,来验证上述是否正确

  • 当Fade=0.51大于0.5时

    在这里插入图片描述

    float circle = smoothstep(0.5, 0.51, distance);

    • 圆内白色:

      distance在(1, 0.51)范围,>(大于)0.51,smoothstep后circle是白色1。

    • 圆外黑色:

      distance在(0.5, 0)范围,<(小于)0.5,smoothstep后circle是黑色0。

  • 当Fade=0.483小于0.5时

    在这里插入图片描述

    float circle = smoothstep(0.5, 0.483, distance);

    • 圆内黑色:

      distance在(1, 0.5)范围,>(大于)0.5,smoothstep后circle是黑色0。

    • 圆外白色:

      distance在(0.483, 0)范围,<(小于)0.483,smoothstep后circle是白色1。

edge0=edge1

  • edge0=edge1=0

    void main()// 部分glsl的fragment代码
    {
    	float distance = 1.0 - length(Input.LocalPosition);// distance = 1-圆心的距离
    	float circle = smoothstep(0, 0, distance);
    	o_Color = vec4(circle, circle, circle, 1);
    }
    

    结果

    在这里插入图片描述

    说明smoothstep(0, 0, distance);返回0

  • edge0=edge1=0.2

    void main()// 部分glsl的fragment代码
    {
    	float distance = 1.0 - length(Input.LocalPosition);// distance = 1-圆心的距离
    	float circle = smoothstep(0.2, 0.2, distance);
    	o_Color = vec4(circle, circle, circle, 1);
    }
    

    在这里插入图片描述

    白色范围因distance>0.2所以smoothstep(0.2, 0.2, distance)返回1

    黑色范围因distance<0.2所以smoothstep(0.2, 0.2, distance)返回0

  • edge0=edge1=0.8

    void main()// 部分glsl的fragment代码
    {
    	float distance = 1.0 - length(Input.LocalPosition);// distance = 1-圆心的距离
    	float circle = smoothstep(0.8, 0.8, distance);
    	o_Color = vec4(circle, circle, circle, 1);
    }
    

    在这里插入图片描述

    白色范围因distance>0.8所以smoothstep(0.8, 0.8, distance)返回1

    黑色范围因distance<0.8所以smoothstep(0.8, 0.8, distance)返回0

结论

smoothstep(0, 0, distance);返回0
smoothstep(0.2, 0.2, distance);退化成step(0.2, distance);distance大于0.2返回1,小于0.2返回0
smoothstep(0.8, 0.8, distance);退化成step(0.8, distance);distance大于0.8返回1,小于0.8返回0
  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
SmoothStep函数是一个用于生成平滑过渡的函数。它接受三个参数:t1,t2和x。首先,x的值会被标准化到0到1的范围内,公式为(x-t1)/(t2-t1)。然后,这个标准化后的值会通过一个三次多项式函数进行平滑处理,公式为x*x*(3-2*x)。最后,函数返回处理后的值。 SmoothStep函数的作用是在给定的范围内生成平滑的过渡效果。当x的值在t1和t2之间时,函数的返回值会在0到1之间变化。当x小于t1时,返回值为0;当x大于t2时,返回值为1。在t1和t2之间的过渡区域内,返回值会平滑地从0过渡到1。 SmoothStep函数可以用于各种应用,比如在图形渲染中实现平滑的颜色过渡效果,或者在动画中实现平滑的运动效果。 引用\[1\]中给出了SmoothStep函数的具体实现代码,而引用\[2\]中提到了一个类似于SmoothStep函数的公式,它也可以用于生成平滑过渡效果。这个公式是saturate((x-0.5)/max(0.001,a)+0.5),它将x的值在某一范围内进行缩放,并将结果限定在0到1之间。 总结来说,SmoothStep函数是一个用于生成平滑过渡效果的函数,它可以通过调整参数t1和t2来控制过渡的范围,而函数的返回值会在0到1之间平滑地变化。 #### 引用[.reference_title] - *1* [【Shader Graph】SmoothStep节点详解及其应用](https://blog.csdn.net/weixin_61427881/article/details/127839417)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Unity Shader中常用函数及其几何意义--持续更新ing](https://blog.csdn.net/u010778229/article/details/107285163)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘建杰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值