【着色器实现Distorted Outline外描边轮廓扭曲效果_Shader效果第二篇】

本文介绍了如何使用Unity的着色器来实现一种扭曲的外描边轮廓效果。通过展示效果图片、属性栏配置以及完整的代码实现,详细解析了这一技术美术技巧。
摘要由CSDN通过智能技术生成

效果如图:

请添加图片描述

属性栏如图

请添加图片描述

属性块:

关于属性的内容可以看这篇:【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定义了混合模式。
  • ZWriteCull控制深度写入和剔除。

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"
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

暴走约伯

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

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

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

打赏作者

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

抵扣说明:

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

余额充值