Unity Shader 极简实践3——step,lerp 和 smoothstep 应用

《质量效应》谢泼德指挥官

0.本文示例代码地址

GitHub

1. step

step 函数的逻辑是

step (a, x)
{
  if (x < a) 
  {
    return 0;
  }
  else
  {
    return 1;
  }
}

step 通常用来取代 if-else 的代码,下面看一个使用 step 函数的示例

Shader "Custom_Shader/Function_step"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Amount ("Amount", float) = 0.5
        _Color ("Color", Color) = (0,0,0,1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float4 _Color;
            float _Amount;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = step(_Amount, length(i.uv - 0.5)) * _Color;
                return col;
            }
            ENDCG
        }
    }
}

核心代码是

  fixed4 col = step(_Amount, length(i.uv - 0.5)) * _Color;
  return col;

片元着色器中,若当前片元距离中心超过 _Amount 值时返回颜色值 _Color,否则返回黑色。这里的内置函数 length 作用是计算两个参数的距离。效果图:

step实现效果

2. lerp

lerp 函数的定义是

lerp(a, b, w)
{
  return a + w*(b-a)
}

当 w = 0 时,返回a,当 w = 1 时返回b,否则返回对 a 和
b 的差值,w 越接近0,返回结果越接近a,w越接近1,返回结果🈷️接近1,通常用来计算一些渐变量。

lerp (a, b, w) 中,a 和 b 需要同类型,可以是数值或向量
一个使用 lerp 的案例

Shader "Custom_Shader/Function_lerp"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Color ("Color", Color) = (1.0, 1.0, 1.0, 1.0)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float4 _Color;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return lerp(0, 1, length(i.uv - 0.5)) * _Color;
            }
            ENDCG
        }
    }
}

核心代码

return lerp(0, 1, length(i.uv - 0.5)) * _Color;

实现了一个渐变效果,图片中心 uv 值为 (0.5,0.5),从图片中心向外渐变,距离中心越近,颜色越接近黑色,距离越远,颜色越接近 _Color 颜色,效果图


lerp渐变

3. smoothstep

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

float smoothstep(float a, float b, float x) 
{
  x = clamp((x - a) / (b- a), 0.0, 1.0); 
  return x * x * (3 - 2 * x);
}

简单来说就是:

  • 在 a < b 的情况下,当 x < a 时,返回 0,当 x > b 时,返回 1,否则在 0和 1之间平滑过渡,如图:
    smoothstep(-2, 3, x)的函数图像:
    smoothstep(-2, 3, x)
  • 在 a > b 的情况下,当 x < b 时,返回1,当 x > a 时,返回0,否则在 1 和 0 之间平滑过渡,交换上述 a 和 b 的位置,可以得到 smoothstep(3, -2, x)的函数图像 :
    smoothstep(3, -2, x)

两个 smoothstep 进行减法运算可以得到一些波形图,例如 smoothstep(1, 2, x) - smoothstep(2, 3, x)的函数图像

减法运算

想要增加波峰的持续宽度,可以构造 smoothstep(1,2, x) - smoothstep(3, 4, x),图像如下:
减法运算

smoothstep 的函数图像我们可以得到灵感,如何在 Shader 中使用平滑过渡函数

Shader "Custom_Shader/Circle"
{
    Properties
    {
        _Color ("MainColor", Color) = (1,1,1,1)
        _Start ("Start", Range(0, 0.5)) = 0.1
        _Inner ("Inner", Range(0, 0.5)) = 0.2
        _Outer ("Outer", Range(0, 0.5)) = 0.22
        _End ("End", Range(0, 0.5)) = 0.3
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            float4 _Color;
            float _Start;
            float _End;
            float _Inner;
            float _Outer;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            fixed4 singleSmoothstep(float _Start, float _End, v2f i) {
                return smoothstep(_Start, _Inner, length(i.uv - 0.5));
            }

            fixed4 doubleSmoothstep(float _Start, float _End, float _Inner, float _Outer, v2f i) {
                float value = smoothstep(_Start, _Inner, length(i.uv - 0.5));
                float value2 = smoothstep(_Outer, _End, length(i.uv - 0.5));
            }
            fixed4 frag (v2f i) : SV_Target
            {
                return _Color * singleSmoothstep(_Start, _End, i);
            }
            ENDCG
        }
    }
}

提供了单一的平滑过渡方法 singleSmoothstep 和经过减法运算的 doubleSmoothstep 方法,当返回 singleSmoothstep(0.1, 0.35, x) 时得到的过渡效果:

平滑过渡,_Start=0.1, _End=0.35

交换参数位置,得到 singleSmoothstep(0.35, 0.1, x) 的平滑过渡效果:
平滑过渡

当返回 doubleSmoothstep(0.05, 0.2, 0.2, 0.35, x)时得到的过渡效果

双平滑过渡

当返回 doubleSmoothstep(0.05, 0.15, 0.25, 0.4, x)时得到当过渡效果

双平滑过渡

当返回 doubleSmoothstep(0.14, 0.15, 0.29, 0.3, x)时得到当过渡效果

平滑过渡

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342