【着色器实现Television信号三原色闪烁效果_Shader效果第五篇】

本文详细介绍了如何使用Unity的着色器技术来实现Television信号的三原色(红、绿、蓝)闪烁效果,通过算法编程达到游戏引擎中的视觉特效,适合技术美术和游戏开发爱好者学习。
摘要由CSDN通过智能技术生成

效果如下

请添加图片描述

属性栏如下

在这里插入图片描述

属性块:

关于属性的内容可以看这篇:【Unity Shader 中Properties 相关介绍】

    Properties
    {
        _MainTex ("Main Texture", 2D) = "white" {}
		_Color("Main Color", Color) = (1,1,1,1)

		_ChromAberrAmount("ChromAberr Amount", Range(0, 1)) = 1
		_ChromAberrAlpha("ChromAberr Alpha", Range(0, 1)) = 0.4
		_GlitchAmount("Glitch Amount", Range(0, 20)) = 3
		_GlitchSize("Glitch Size", Range(0.25, 5)) = 1
		_PixelateSize("Pixelate size", Range(4,512)) = 32
    }
  • 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;
				half4 color : COLOR;
            };
  • 定义了顶点着色器的输出结构。

声明采样:

            sampler2D _MainTex;
            half4 _MainTex_ST, _MainTex_TexelSize, _Color;
            float _RandomSeed;

			half _ChromAberrAmount, _ChromAberrAlpha;
			half _GlitchAmount, _GlitchSize;
			half _PixelateSize;
属性声明详细解释如下
名称注释
sampler2D _MainTex;声明了一个名为_MainTex的2D纹理采样器。这通常用于存储和采样主纹理图像。
名称注释
half4 _MainTex_ST, _MainTex_TexelSize, _Color;声明了三个half4类型的属性:
_MainTex_ST通常用于存储纹理的平铺(tiling)和偏移(offset)参数。
_MainTex_TexelSize存储纹理的像素尺寸,这有助于进行像素级的纹理操作。
_Color一个颜色值,用于修改或混合纹理的颜色。
名称注释
_ChromAberrAmount这是一个半精度浮点数变量,用于存储色像差(Chromatic Aberration)的量。色像差是一种光学现象,其中不同波长的光在通过透镜时聚焦在不同的点上,导致图像边缘出现颜色分离。在图形渲染中,色像差可以用来模拟镜头效果或创造特定的视觉效果。
_ChromAberrAlpha这是另一个半精度浮点数变量,可能用于控制色像差效果的透明度或强度。例如,它可以决定色像差效果在图像中的混合程度。
_GlitchAmount这个变量用于存储"glitch"效果的量。Glitch效果通常指的是图像或视频中的随机干扰或错误,可以用来创造一种数字故障或复古的视觉效果。
_GlitchSize这个变量可能用于控制glitch效果的尺寸或范围,比如干扰区域的大小或者干扰效果的强度。
_PixelateSize这个变量用于控制像素化(Pixelation)效果的大小。像素化是一种将图像分割成大块像素区域的效果,使得图像看起来更加粗糙或低分辨率。

v2f vert顶点着色器函数:

            v2f vert (appdata v)
            {
                v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.color = v.color;
                return o;
            }
  • 顶点着色器函数,转换顶点坐标和纹理坐标。

片段着色器结构体:

            half4 frag (v2f i) : SV_Target
            {
				// 片段着色器代码,负责生成扭曲轮廓效果
				// ...
            }
            ENDCG
        }
    }
  • 片段着色器函数,负责渲染最终像素颜色。
  • ENDCG标记CGPROGRAM代码块的结束。

rand结构体:

            half rand(half2 seed, half offset) {
	            return (frac(sin(dot(seed, half2(12.9898, 78.233))) * 43758.5453) + offset) % 1.0;
            }
代码解释
half rand(half2 seed, half offset) {定义了一个名为rand的函数,它接受两个参数,seed是一个half2类型,即包含两个半精度浮点数的向量,通常用于生成随机数的种子;offset是一个half类型,用于对生成的随机数进行偏移。
return (frac(sin(dot(seed, half2(12.9898, 78.233))) * 43758.5453) + offset) % 1.0;这是函数的主体,它执行以下操作:
dot(seed, half2(12.9898, 78.233))计算seed向量与常数向量(12.9898, 78.233)的点积。点积是一种向量运算,可以产生一个标量值。
sin(...)对点积的结果应用正弦函数。正弦函数是一种周期性函数,可以产生在-1到1之间的值。
* 43758.5453将正弦函数的结果乘以一个常数。这个常数可能是为了调整结果的分布或范围。
frac(...)应用frac函数,即取小数部分,这将正弦函数的结果限制在0到1的范围内(不包括1)。
+ offsetfrac函数的结果加上offset参数,实现对随机数的偏移。
% 1.0对上一步的结果取模1.0,确保最终结果也在0到1的范围内(不包括1)。
}结束函数定义。
小结:

这个rand函数的目的是生成一个基于种子和偏移的伪随机数。在图形编程中,这种类型的随机数生成器通常用于创建纹理、噪声或其他需要随机性效果的场景。由于使用了sindot函数,这个随机数生成器能够产生相对平滑的随机分布,而不是完全随机的值。通过调整seedoffset参数,可以在不同的纹理坐标或不同的效果之间生成不同的随机数序列。

rand2CustomTime结构体:

            half rand2CustomTime(half2 seed, half offset, half customTime) {
	            return (frac(sin(dot(seed * floor(50 + (customTime % 1.0) * 12.), half2(127.1, 311.7))) * 43758.5453123) + offset) % 1.0;
}

片段着色器代码

            half4 frag (v2f i) : SV_Target
            {
				float2 uvRect = i.uv;
				i.uv = floor(i.uv * _PixelateSize) / _PixelateSize;
				half4 col = tex2D(_MainTex, i.uv) * i.color;
				half2 uvGlitch = uvRect;
				uvGlitch.y -= 0.5;
				half lineNoise = pow(rand2(floor(uvGlitch * half2(24., 19.) * _GlitchSize) * 4.0, _RandomSeed), 3.0) * _GlitchAmount
					* pow(rand2(floor(uvGlitch * half2(38., 14.) * _GlitchSize) * 4.0, _RandomSeed), 3.0);
				col = tex2D(_MainTex, i.uv + half2(lineNoise * 0.02 * rand2(half2(2.0, 1), _RandomSeed), 0)) * i.color;
				half4 r = tex2D(_MainTex, i.uv + half2(_ChromAberrAmount/10, 0)) * i.color;
				half4 b = tex2D(_MainTex, i.uv + half2(-_ChromAberrAmount/10, 0)) * i.color;
				col = half4(r.r * r.a, col.g, b.b * b.a, max(max(r.a, b.a) * _ChromAberrAlpha, col.a));
				col *= _Color;
                return col;
            }

具体的代码注释如下:

片段着色计算注释
half4 frag (v2f i) : SV_Target定义了一个片段着色器函数frag,它接收一个从顶点着色器传递过来的结构体v2f i,输出颜色为SV_Target
float2 uvRect = i.uv;将传入的纹理坐标赋值给uvRect
i.uv = floor(i.uv * _PixelateSize) / _PixelateSize;
i.uv是传入片段着色器的纹理坐标。
floor(...)向下取整函数,将坐标值向下取整到最近的整数。
* _PixelateSize将纹理坐标乘以一个像素化大小的值,再通过floor函数处理,实现像素化效果。
/ _PixelateSize将处理后的坐标重新除以_PixelateSize,确保纹理坐标在正确的范围内。
half4 col = tex2D(_MainTex, i.uv) * i.color;
tex2D(_MainTex, i.uv)从主纹理_MainTex中采样颜色,使用处理后的纹理坐标i.uv
* i.color将采样得到的颜色与传入的片段颜色i.color相乘,应用颜色混合。
half2 uvGlitch = uvRect;
uvRect可能是从顶点着色器传入的纹理坐标。将这些坐标赋值给uvGlitch,用于后续的glitch效果处理。
uvGlitch.y -= 0.5;uvGlitch的y坐标减去0.5,这可能是为了调整glitch效果的位置。
片段着色计算拆解
half lineNoise = pow(rand2(floor(uvGlitch * half2(24., 19.) * _GlitchSize) * 4.0, _RandomSeed), 3.0) * _GlitchAmount * pow(rand2(floor(uvGlitch * half2(38., 14.) * _GlitchSize) * 4.0, _RandomSeed), 3.0);拆解分析如下
rand2(...)可能是一个自定义的随机数生成函数,用于生成纹理坐标的随机偏移。
floor(...)对纹理坐标乘以一个常数向量和_GlitchSize,然后向下取整,生成随机偏移的基础值。
pow(...)将随机数的值的三次方,增加随机效果的强度。
* _GlitchAmount将生成的随机值乘以_GlitchAmount,控制glitch效果的强度。
片段着色计算拆解
col = tex2D(_MainTex, i.uv + half2(lineNoise * 0.02 * rand2(half2(2.0, 1), _RandomSeed), 0)) * i.color;再次从_MainTex纹理中采样颜色,但这次纹理坐标i.uv加上了由lineNoise计算出的随机偏移,实现glitch效果。
* i.color将采样得到的颜色与传入的片段颜色i.color相乘,应用颜色混合。
half4 r = tex2D(_MainTex, i.uv + half2(_ChromAberrAmount/10, 0)) * i.color;从主纹理中采样红色通道的颜色,纹理坐标i.uv加上了色像差偏移(_ChromAberrAmount/10),模拟色像差效果。
half4 b = tex2D(_MainTex, i.uv + half2(-_ChromAberrAmount/10, 0)) * i.color;从主纹理中采样蓝色通道的颜色,纹理坐标i.uv减去了色像差偏移(-_ChromAberrAmount/10),模拟色像差效果。
col = half4(r.r * r.a, col.g, b.b * b.a, max(max(r.a, b.a) * _ChromAberrAlpha, col.a));将红色通道的值与红色通道的透明度相乘,蓝色通道的值与蓝色通道的透明度相乘,然后将它们组合成一个新的颜色值。
max(...)取红色和蓝色通道透明度的最大值,并乘以_ChromAberrAlpha,控制色像差效果的透明度。
max(max(r.a, b.a) * _ChromAberrAlpha, col.a)取色像差透明度和原始颜色透明度的最大值,确保最终颜色的透明度。
col *= _Color;将颜色与主颜色相乘。
return col;返回最终颜色。
返回
	Fallback "Sprites/Default"
}

完整代码

Shader "Television"
{
    Properties
    {
        _MainTex ("Main Texture", 2D) = "white" {}
		_Color("Main Color", Color) = (1,1,1,1)

		_ChromAberrAmount("ChromAberr Amount", Range(0, 1)) = 1
		_ChromAberrAlpha("ChromAberr Alpha", Range(0, 1)) = 0.4
		_GlitchAmount("Glitch Amount", Range(0, 20)) = 3
		_GlitchSize("Glitch Size", Range(0.25, 5)) = 1
		_PixelateSize("Pixelate size", Range(4,512)) = 32
    }

    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;
				half4 color : COLOR;
            };

            sampler2D _MainTex;
            half4 _MainTex_ST, _MainTex_TexelSize, _Color;
            float _RandomSeed;

			half _ChromAberrAmount, _ChromAberrAlpha;
			half _GlitchAmount, _GlitchSize;
			half _PixelateSize;

            v2f vert (appdata v)
            {
                v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.color = v.color;
                return o;
            }
            half rand(half2 seed, half offset) {
	            return (frac(sin(dot(seed, half2(12.9898, 78.233))) * 43758.5453) + offset) % 1.0;
            }

            half rand2(half2 seed, half offset) {
	            return (frac(sin(dot(seed * floor(50 + (_Time % 1.0) * 12.), half2(127.1, 311.7))) * 43758.5453123) + offset) % 1.0;
            }

            half rand2CustomTime(half2 seed, half offset, half customTime) {
	            return (frac(sin(dot(seed * floor(50 + (customTime % 1.0) * 12.), half2(127.1, 311.7))) * 43758.5453123) + offset) % 1.0;
}

            half4 frag (v2f i) : SV_Target
            {
				float2 uvRect = i.uv;
				i.uv = floor(i.uv * _PixelateSize) / _PixelateSize;
				half4 col = tex2D(_MainTex, i.uv) * i.color;
				half2 uvGlitch = uvRect;
				uvGlitch.y -= 0.5;
				half lineNoise = pow(rand2(floor(uvGlitch * half2(24., 19.) * _GlitchSize) * 4.0, _RandomSeed), 3.0) * _GlitchAmount
					* pow(rand2(floor(uvGlitch * half2(38., 14.) * _GlitchSize) * 4.0, _RandomSeed), 3.0);
				col = tex2D(_MainTex, i.uv + half2(lineNoise * 0.02 * rand2(half2(2.0, 1), _RandomSeed), 0)) * i.color;
				half4 r = tex2D(_MainTex, i.uv + half2(_ChromAberrAmount/10, 0)) * i.color;
				half4 b = tex2D(_MainTex, i.uv + half2(-_ChromAberrAmount/10, 0)) * i.color;
				col = half4(r.r * r.a, col.g, b.b * b.a, max(max(r.a, b.a) * _ChromAberrAlpha, col.a));
				col *= _Color;
                return col;
            }
            ENDCG
        }
    }
	Fallback "Sprites/Default"
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

暴走约伯

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

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

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

打赏作者

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

抵扣说明:

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

余额充值