Shader LineRenderer实现电波效果
基础阅读
效果展示
Shader源码
Shader "ShadersHub/LaserBeam"
{
Properties
{
_MiddleColor("_MiddleColor color", Color) = (1,1,1,1)
_EdgeColor("Edge color", Color) = (1,0,0,1)
_Noise("Noise texture", 2D) = "gray"{}
_NoiseIntensity("Noise intensity", Float) = 1
_ScrollSpeed("Scroll speed", Float) = 1
_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 // 传统透明度
//Blend One OneMinusSrcAlpha // 预乘透明度
//Blend One One // 叠加
//Blend OneMinusDstColor One // 柔和叠加
//Blend DstColor Zero // 相乘——正片叠底
//Blend DstColor SrcColor // 两倍相乘
Blend One 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
{
float2 uv : TEXCOORD0;
fixed4 color : COLOR;
float4 worldPos : TEXCOORD1;
float4 vertex : SV_POSITION;
};
sampler2D _Noise;
float4 _Noise_ST;
fixed4 _MiddleColor;
fixed4 _EdgeColor;
half _NoiseIntensity;
half _ScrollSpeed;
half _StartBoost;
half _EndBoost;
half _LineLength;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.color = v.color;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
//perlin noise texture
fixed noise = tex2D(_Noise, (i.worldPos.xy * _Noise_ST.xy + _Time.x * _ScrollSpeed) + _Noise_ST.zw).r;
noise = noise * 2 - 1;
i.uv.y += noise * _NoiseIntensity;
half centerness = 1 - abs(i.uv.y - .5) * 2;
float middle = step(0.85, centerness);
middle *= abs(sin(i.uv.x * 8 + _Time.x * _ScrollSpeed));
float edge = step(0.5, centerness) - middle;
edge *= abs(sin(i.uv.x * 15 + _Time.x * _ScrollSpeed));
fixed4 col = _MiddleColor * middle + _EdgeColor * edge;
col.rgb += _StartBoost * smoothstep(1, 0, (i.uv.x * _LineLength));
col.rgb += _EndBoost * smoothstep(_LineLength - 1, _LineLength, (i.uv.x * _LineLength));
col *= i.color;
col.rgb *= col.a;
col.rgb *= 2;
col.a *= .5;
return col;
}
ENDCG
}
}
}
Shader源码分析
看到这里,我建议shader新手看看本文开头的两篇基础知识介绍。老司机接着往下看。
middle *= abs(sin(i.uv.x * 8 + _Time.x * _ScrollSpeed)); 和 edge *= abs(sin(i.uv.x * 15 + _Time.x * _ScrollSpeed)); 这两行是前两篇文章中没有的,这里引入正弦函数的作用是,让线条在 横向上 出现 周期性波动的片段效果 和 周期性水平位移效果。
Perlin噪声
关于图形学中的噪声,参考下冯乐乐的【图形学】谈谈噪声
上面源码中的关于perlin噪声的三行代码理解:
fixed noise = tex2D(_Noise, (i.worldPos.xy * _Noise_ST.xy + _Time.x * _ScrollSpeed) + _Noise_ST.zw).r;
noise = noise * 2 - 1;
i.uv.y += noise * _NoiseIntensity;
上面第一行是针对noise噪声纹理采集,本来可以直接用Shader内置函数TRANSFORM_TEX搞定,无奈我们的动态效果需要根据时间来做周期性变化,也就是需要加入时间因子。所以我们不得不自己采集noise纹理,于是就有了 fixed noise = tex2D(_Noise, (i.worldPos.xy * _Noise_ST.xy + _Time.x * _ScrollSpeed) + _Noise_ST.zw).r。这个表达式的取值范围是[0,1];我们直接把这个范围内的随机值作用在uv.y上(i.uv.y += noise * _NoiseIntensity),也就是直接随机性影响线条纵向的像素点。Step函数在本例中作用就是纵向剔除像素,加上噪声,就形成了纵向随机性的像素剔除效果。
noise = noise * 2 - 1这一行上面还没有提到,运行时,我们先注释掉,看效果,你会发现线条横向上被切了一刀似的,然后打开注释,就完美了。注释前后静态截图对比:
vs
noise = noise * 2 - 1公式对uv.y做了一个范围在[-1,1]上的随机分布,这个有点像 线性回归。这些巧妙的处理方法,展现了数学之美,经验多了,自然而然就会拿到实际项目中来应用了。图形学中还有句话叫做 “计算机图形学第一准则:近似原则如果她看上去是对的它就是对的”。
本篇至此完结,欢迎指正交流(或邮件1136468882@qq.com)!