Shader smoothstep实现线条渐变色
效果展示
细节说明
(也是下面准备shader实现的三个要点):
- 线条有三段颜色拼接(代号1 2 3);
- 红黑(1 2)之间有渐变过度;
- 黑绿(2 3)之间没有渐变过度。
具体实现
线条创建
基础线条载体就是LineRenderer组件。先在场景中创建一个空对象,然后挂在LineRenderer组件,保持默认设置就好。设置线条起点(-7,0,0)和终点(7,0,0),线条宽度0.5,以及线条颜色。
如图:
创建Shader(LineGradualColor)
- 创建Shader。首先创建一个UnlitShader模板,命名为LineGradualColor.shader。接着创建一个材质球,命名为LineGradualColor。
-
打开LineGradualColor.shader,修改第一行shader名称为
Shader “ShadersHub/LineGradualColor”
; -
设置刚刚创建的材质球的shader为
ShadersHub/LineGradualColor
(即刚刚创建的shader);
Shader源码
Shader "ShadersHub/LineGradualColor"
{
Properties
{
_StartBoost("Start boost", Float) = .5
_EndBoost("End boost", Float) = .5
_LineLength("LineLength", Float) = 2
}
SubShader
{
Tags { "RenderType"="Transparent" "RenderType" = "Transparent" }
LOD 100
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
fixed4 color:COLOR;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
fixed4 color : COLOR;
};
half _StartBoost;
half _EndBoost;
half _LineLength;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
o.color = v.color;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = fixed4(0,0,0,1);
col.r += _StartBoost * smoothstep(1, 0, (i.uv.x * _LineLength));
col.g += _EndBoost * smoothstep(_LineLength - 1, _LineLength, (i.uv.x * _LineLength));
//col *= i.color;//这是线条LineRenderer原始颜色,可以打开注释看看效果
return col;
}
ENDCG
}
}
}
Shader源码分析
顶点函数中很简单,只是将模型顶点中的三个属性拿过来存到v2f的实例中,传输到片段着色器中。
重点在片段函数。
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = fixed4(0,0,0,1);
col.r += _StartBoost * smoothstep(1, 0, (i.uv.x * _LineLength));
col.g += _EndBoost * smoothstep(_LineLength - 1, _LineLength, (i.uv.x * _LineLength));
//col *= i.color;
return col;
}
ENDCG
}
一开始展示的线条中,分为三段,就是这里的三行代码。主体色是纯黑色,即代码:
fixed4 col = fixed4(0,0,0,1)
;第一段红色,第三段绿色的控制代码分别为:
col.r += _StartBoost * smoothstep(1, 0, (i.uv.x * _LineLength))
和
col.g += _EndBoost * smoothstep(_LineLength – 1, _LineLength, (i.uv.x * _LineLength))
。
混色过程:首先整条是纯黑色,rgb通道值都是0,然后r通道加上一个值,有效范围是[0,1],然后b通道加上一个值,有效范围也是[0,1]。那么为什么只有一开始一段被混入红色,最后一段被混入绿色呢,那就是smoothstep这个函数起了很大的作用,否则整段线条颜色就是(r,g,0,1),其中r,g为任意有效值。从代码中看,r通道的计算,uv.x被限制在[0,1]之间;g通道的计算,uv.x被限定在[3,4]之间,所以uv.x对应的区间[1,3]不受影响,颜色保持默认值黑色。
最终结果解释了,那么smoothstep(a,b,x)函数原理又是什么呢?
请参考链接
。
- 值域在[0,1]
- a>b时,图像趋势递增
- a<b时,图像趋势递减
至此,我们不难理解
col.r += _StartBoost 0 smoothstep(1, 0, (i.uv.x * _LineLength))
在(uv.x * _LineLength)值域在[0,4],然后
smoothstep(1, 0, (i.uv.x * _LineLength))
在[0,1]上的值由1
平滑过度
减为0,后面一直为0;这就解释了为什么第一段有r通道叠加,第二段第三段没有r通道叠加。平滑过度,正是红到黑渐变的原因。
同理第三段的绿色也一样理解。
smoothstep(_LineLength – 1, _LineLength, (i.uv.x * _LineLength))
公式对应的图像正好是上面的
参考链接
。
(i.uv.x * _LineLength)
在[0,3]上对应的值是0,[3,4]上对应的值从0
平滑过度
到1,所以第二段第三段,黑绿就这么产生了。现在有个问题,为什么二三段不是渐变过度呢?因为,编辑器中
_EndBoost
值设置得太大(我的本机上设置的是1000),也就是说即使
smoothstep(_LineLength – 1, _LineLength, (i.uv.x * _LineLength))
在[3,4]上的值是0到1平滑过度,但是再乘上1000,这个过度就变得很陡了,再加上单个通道有效值仅仅是[0,1],也就是0到1000的值域中只有那么千分之一段有效,这个曲线的切线斜率(变化率)可以脑补下多大。所以就没有了渐变过度。
本篇至此完结,欢迎指正交流(或邮件1136468882@qq.com)!