Distorted Outline外描边轮廓扭曲效果
效果如图:
属性栏如图
属性块:
关于属性的内容可以看这篇:【Unity Shader 中Properties 相关介绍】
Shader "Distorted Outline"
{
Properties
{
_MainTex ("Main Texture", 2D) = "white" {}
_Color("Main Color", Color) = (1,1,1,1)
_Alpha("General Alpha", Range(0,1)) = 1
// 轮廓相关属性
_OutlineColor("Outline Base Color", Color) = (1,1,1,1)
_OutlineAlpha("Outline Base Alpha", Range(0,1)) = 1
_OutlineGlow("Outline Base Glow", Range(1,100)) = 1.5
_OutlineWidth("Outline Base Width", Range(0,0.2)) = 0.004
_OutlinePixelWidth("Outline Base Pixel Width", Int) = 1
[Space]
_OutlineTex("Outline Texture", 2D) = "white" {}
_OutlineTexXSpeed("Texture scroll speed X", Range(-50,50)) = 10
_OutlineTexYSpeed("Texture scroll speed Y", Range(-50,50)) = 0
[Space]
_OutlineDistortTex("Outline Distortion Texture", 2D) = "white" {}
_OutlineDistortAmount("Outline Distortion Amount", Range(0,2)) = 0.5
_OutlineDistortTexXSpeed("Distortion scroll speed X", Range(-50,50)) = 5
_OutlineDistortTexYSpeed("Distortion scroll speed Y", Range(-50,50)) = 5
}
- 定义了一个名为"Distorted Outline"的着色器。
Properties
块定义了可以在材质编辑器中调整的属性。
SubShader块:
SubShader
{
Tags {"Queue" = "Transparent" "RenderType"="Opaque" "IgnoreProjector" = "True" }
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
Cull Off
- 定义了一个子着色器。
Tags
定义了渲染队列、渲染类型和投影忽略标记。Blend
定义了混合模式。ZWrite
和Cull
控制深度写入和剔除。
CGPROGRAM:
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
- 定义了一个渲染通道。
- 使用了CGPROGRAM和相关指令来指定顶点和片段着色器。
appdata:
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
half4 color : COLOR;
};
- 定义了顶点着色器的输入结构。
v2f:
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
half2 uvOutTex : TEXCOORD1;
half2 uvOutDistTex : TEXCOORD2;
half4 color : COLOR;
};
- 定义了顶点着色器的输出结构。
声明采样:
sampler2D _MainTex;
half4 _MainTex_ST, _MainTex_TexelSize, _Color;
half _Alpha;
float _RandomSeed;
- 定义了纹理采样器和相关属性。
// 轮廓相关变量
half4 _OutlineColor;
half _OutlineAlpha, _OutlineGlow, _OutlineWidth;
int _OutlinePixelWidth;
- 定义了轮廓颜色、透明度、发光度、宽度和像素宽度。
sampler2D _OutlineTex;
half4 _OutlineTex_ST;
half _OutlineTexXSpeed, _OutlineTexYSpeed;
- 定义了轮廓纹理和滚动速度。
sampler2D _OutlineDistortTex;
half4 _OutlineDistortTex_ST;
half _OutlineDistortTexXSpeed, _OutlineDistortTexYSpeed, _OutlineDistortAmount;
- 定义了轮廓扭曲纹理和相关属性。
v2f vert顶点着色器函数:
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.uvOutTex = TRANSFORM_TEX(v.uv, _OutlineTex);
o.uvOutDistTex = TRANSFORM_TEX(v.uv, _OutlineDistortTex);
o.color = v.color;
return o;
}
- 顶点着色器函数,转换顶点坐标和纹理坐标。
片段着色器代码:
half4 frag (v2f i) : SV_Target
{
// 片段着色器代码,负责生成扭曲轮廓效果
// ...
}
ENDCG
}
}
- 片段着色器函数,负责渲染最终像素颜色。
ENDCG
标记CGPROGRAM代码块的结束。
片段着色器代码
half4 frag (v2f i) : SV_Target
{
float2 uvRect = i.uv;
half2 center = half2(0.5, 0.5);
half2 destUv = half2(_OutlinePixelWidth * _MainTex_TexelSize.x, _OutlinePixelWidth * _MainTex_TexelSize.y);
i.uvOutDistTex.x += ((_Time + _RandomSeed) * _OutlineDistortTexXSpeed) % 1;
i.uvOutDistTex.y += ((_Time + _RandomSeed) * _OutlineDistortTexYSpeed) % 1;
half outDistortAmnt = (tex2D(_OutlineDistortTex, i.uvOutDistTex).r - 0.5) * 0.2 * _OutlineDistortAmount;
destUv.x += outDistortAmnt;
destUv.y += outDistortAmnt;
half spriteLeft = tex2D(_MainTex, i.uv + half2(destUv.x, 0)).a;
half spriteRight = tex2D(_MainTex, i.uv - half2(destUv.x, 0)).a;
half spriteBottom = tex2D(_MainTex, i.uv + half2(0, destUv.y)).a;
half spriteTop = tex2D(_MainTex, i.uv - half2(0, destUv.y)).a;
half result = spriteLeft + spriteRight + spriteBottom + spriteTop;
half spriteTopLeft = tex2D(_MainTex, i.uv + half2(destUv.x, destUv.y)).a;
half spriteTopRight = tex2D(_MainTex, i.uv + half2(-destUv.x, destUv.y)).a;
half spriteBotLeft = tex2D(_MainTex, i.uv + half2(destUv.x, -destUv.y)).a;
half spriteBotRight = tex2D(_MainTex, i.uv + half2(-destUv.x, -destUv.y)).a;
result = result + spriteTopLeft + spriteTopRight + spriteBotLeft + spriteBotRight;
result = step(0.05, saturate(result));
i.uvOutTex.x += ((_Time + _RandomSeed) * _OutlineTexXSpeed) % 1;
i.uvOutTex.y += ((_Time + _RandomSeed) * _OutlineTexYSpeed) % 1;
half4 tempOutColor = tex2D(_OutlineTex, i.uvOutTex);
tempOutColor *= _OutlineColor;
_OutlineColor = tempOutColor;
half4 col = tex2D(_MainTex, i.uv) * i.color;
half originalAlpha = col.a;
result *= (1 - originalAlpha) * _OutlineAlpha;
half4 outline = _OutlineColor;
outline.rgb *= _OutlineGlow;
outline.a = result;
col = lerp(col, outline, result);
col.a *= _Alpha;
col *= _Color;
return col;
}
具体解释如下:
片段着色计算 | 注释 |
---|---|
half4 frag (v2f i) : SV_Target | 定义了一个片段着色器函数frag ,其输入参数是顶点着色器输出的结构体v2f i ,输出颜色为SV_Target 。 |
float2 uvRect = i.uv; | 将传入的纹理坐标赋值给uvRect 。 |
half2 center = half2(0.5, 0.5); | 设置一个中心点坐标,通常用于计算纹理的偏移。 |
half2 destUv = half2(_OutlinePixelWidth * _MainTex_TexelSize.x, _OutlinePixelWidth * _MainTex_TexelSize.y); | 计算一个偏移量,这个偏移量与像素宽度和纹理的像素大小有关。 |
i.uvOutDistTex.x += ((_Time + _RandomSeed) * _OutlineDistortTexXSpeed) % 1; | 计算一个用于纹理扭曲的UV坐标,通过时间、随机种子和扭曲速度来动态变化。 |
i.uvOutDistTex.y += ((_Time + _RandomSeed) * _OutlineDistortTexYSpeed) % 1; | 同上,但用于Y轴的扭曲。 |
half outDistortAmnt = (tex2D(_OutlineDistortTex, i.uvOutDistTex).r - 0.5) * 0.2 * _OutlineDistortAmount; | 从纹理_OutlineDistortTex 中采样,计算扭曲量,并将结果应用到偏移量destUv 。 |
destUv.x += outDistortAmnt; | 将扭曲量应用到X轴偏移。 |
destUv.y += outDistortAmnt; | 将扭曲量应用到Y轴偏移。 |
half spriteLeft = tex2D(_MainTex, i.uv + half2(destUv.x, 0)).a; | 采样纹理的左侧边缘。 |
half spriteRight = tex2D(_MainTex, i.uv - half2(destUv.x, 0)).a; | 采样纹理的右侧边缘。 |
half spriteBottom = tex2D(_MainTex, i.uv + half2(0, destUv.y)).a; | 采样纹理的底部边缘。 |
half spriteTop = tex2D(_MainTex, i.uv - half2(0, destUv.y)).a; | 采样纹理的顶部边缘。 |
half result = spriteLeft + spriteRight + spriteBottom + spriteTop; | 计算四个边缘的透明度总和。 |
half spriteTopLeft = tex2D(_MainTex, i.uv + half2(destUv.x, destUv.y)).a; | 采样纹理的左上角。 |
half spriteTopRight = tex2D(_MainTex, i.uv + half2(-destUv.x, destUv.y)).a; | 采样纹理的右上角。 |
half spriteBotLeft = tex2D(_MainTex, i.uv + half2(destUv.x, -destUv.y)).a; | 采样纹理的左下角。 |
half spriteBotRight = tex2D(_MainTex, i.uv + half2(-destUv.x, -destUv.y)).a; | 采样纹理的右下角。 |
result = result + spriteTopLeft + spriteTopRight + spriteBotLeft + spriteBotRight; | 将四个角的透明度加到结果中。 |
result = step(0.05, saturate(result)); | 使用step 函数将结果限制在0.05以上,确保边缘效果只在一定阈值以上显示。 |
i.uvOutTex.x += ((_Time + _RandomSeed) * _OutlineTexXSpeed) % 1; | 计算一个用于边缘纹理的UV坐标,通过时间、随机种子和边缘纹理速度来动态变化。 |
i.uvOutTex.y += ((_Time + _RandomSeed) * _OutlineTexYSpeed) % 1; | 同上,但用于Y轴的边缘纹理。 |
half4 tempOutColor = tex2D(_OutlineTex, i.uvOutTex); | 从边缘纹理_OutlineTex 中采样颜色。 |
tempOutColor *= _OutlineColor; | 将边缘纹理的颜色与边缘颜色相乘。 |
_OutlineColor = tempOutColor; | 更新边缘颜色。 |
half4 col = tex2D(_MainTex, i.uv) * i.color; | 采样主纹理,并与顶点颜色相乘。 |
half originalAlpha = col.a; | 获取原始透明度。 |
result *= (1 - originalAlpha) * _OutlineAlpha; | 计算边缘效果的透明度,与原始透明度和边缘透明度相乘。 |
half4 outline = _OutlineColor; | 创建一个边缘颜色。 |
outline.rgb *= _OutlineGlow; | 将边缘颜色的RGB值与边缘发光强度相乘。 |
outline.a = result; | 设置边缘颜色的透明度。 |
col = lerp(col, outline, result); | 使用lerp 函数将原始颜色与边缘颜色混合,混合比例由result 决定。 |
col.a *= _Alpha; | 将最终颜色的透明度与全局透明度相乘。 |
col *= _Color; | 将最终颜色与主颜色相乘。 |
return col; | 返回最终颜色。 |
Fallback "Sprites/Default"
}
完整代码
Shader "Distorted Outline"
{
Properties
{
_MainTex ("Main Texture", 2D) = "white" {}
_Color("Main Color", Color) = (1,1,1,1)
_Alpha("General Alpha", Range(0,1)) = 1
_OutlineColor("Outline Base Color", Color) = (1,1,1,1)
_OutlineAlpha("Outline Base Alpha", Range(0,1)) = 1
_OutlineGlow("Outline Base Glow", Range(1,100)) = 1.5
_OutlineWidth("Outline Base Width", Range(0,0.2)) = 0.004
_OutlinePixelWidth("Outline Base Pixel Width", Int) = 1
[Space]
_OutlineTex("Outline Texture", 2D) = "white" {}
_OutlineTexXSpeed("Texture scroll speed X", Range(-50,50)) = 10
_OutlineTexYSpeed("Texture scroll speed Y", Range(-50,50)) = 0
[Space]
_OutlineDistortTex("Outline Distortion Texture", 2D) = "white" {}
_OutlineDistortAmount("Outline Distortion Amount", Range(0,2)) = 0.5
_OutlineDistortTexXSpeed("Distortion scroll speed X", Range(-50,50)) = 5
_OutlineDistortTexYSpeed("Distortion scroll speed Y", Range(-50,50)) = 5
}
SubShader
{
Tags {"Queue" = "Transparent" "RenderType"="Opaque" "IgnoreProjector" = "True" }
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
Cull Off
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
half4 color : COLOR;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
half2 uvOutTex : TEXCOORD1;
half2 uvOutDistTex : TEXCOORD2;
half4 color : COLOR;
};
sampler2D _MainTex;
half4 _MainTex_ST, _MainTex_TexelSize, _Color;
half _Alpha;
float _RandomSeed;
half4 _OutlineColor;
half _OutlineAlpha, _OutlineGlow, _OutlineWidth;
int _OutlinePixelWidth;
sampler2D _OutlineTex;
half4 _OutlineTex_ST;
half _OutlineTexXSpeed, _OutlineTexYSpeed;
sampler2D _OutlineDistortTex;
half4 _OutlineDistortTex_ST;
half _OutlineDistortTexXSpeed, _OutlineDistortTexYSpeed, _OutlineDistortAmount;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.uvOutTex = TRANSFORM_TEX(v.uv, _OutlineTex);
o.uvOutDistTex = TRANSFORM_TEX(v.uv, _OutlineDistortTex);
o.color = v.color;
return o;
}
half4 frag (v2f i) : SV_Target
{
float2 uvRect = i.uv;
half2 center = half2(0.5, 0.5);
half2 destUv = half2(_OutlinePixelWidth * _MainTex_TexelSize.x, _OutlinePixelWidth * _MainTex_TexelSize.y);
i.uvOutDistTex.x += ((_Time + _RandomSeed) * _OutlineDistortTexXSpeed) % 1;
i.uvOutDistTex.y += ((_Time + _RandomSeed) * _OutlineDistortTexYSpeed) % 1;
half outDistortAmnt = (tex2D(_OutlineDistortTex, i.uvOutDistTex).r - 0.5) * 0.2 * _OutlineDistortAmount;
destUv.x += outDistortAmnt;
destUv.y += outDistortAmnt;
half spriteLeft = tex2D(_MainTex, i.uv + half2(destUv.x, 0)).a;
half spriteRight = tex2D(_MainTex, i.uv - half2(destUv.x, 0)).a;
half spriteBottom = tex2D(_MainTex, i.uv + half2(0, destUv.y)).a;
half spriteTop = tex2D(_MainTex, i.uv - half2(0, destUv.y)).a;
half result = spriteLeft + spriteRight + spriteBottom + spriteTop;
half spriteTopLeft = tex2D(_MainTex, i.uv + half2(destUv.x, destUv.y)).a;
half spriteTopRight = tex2D(_MainTex, i.uv + half2(-destUv.x, destUv.y)).a;
half spriteBotLeft = tex2D(_MainTex, i.uv + half2(destUv.x, -destUv.y)).a;
half spriteBotRight = tex2D(_MainTex, i.uv + half2(-destUv.x, -destUv.y)).a;
result = result + spriteTopLeft + spriteTopRight + spriteBotLeft + spriteBotRight;
result = step(0.05, saturate(result));
i.uvOutTex.x += ((_Time + _RandomSeed) * _OutlineTexXSpeed) % 1;
i.uvOutTex.y += ((_Time + _RandomSeed) * _OutlineTexYSpeed) % 1;
half4 tempOutColor = tex2D(_OutlineTex, i.uvOutTex);
tempOutColor *= _OutlineColor;
_OutlineColor = tempOutColor;
half4 col = tex2D(_MainTex, i.uv) * i.color;
half originalAlpha = col.a;
result *= (1 - originalAlpha) * _OutlineAlpha;
half4 outline = _OutlineColor;
outline.rgb *= _OutlineGlow;
outline.a = result;
col = lerp(col, outline, result);
col.a *= _Alpha;
col *= _Color;
return col;
}
ENDCG
}
}
Fallback "Sprites/Default"
}