当前位置: 首页 > news >正文

unity shader ——屏幕故障

Unity屏幕故障

RGB颜色分离故障(RGB Split Glitch)

这种效果的基本思路是将RGB三个颜色通道分别进行偏移,从而产生颜色分离的视觉效果。(本文采用的是Shader + 脚本的方法)

初步实现的效果

//扰动函数float randomNoise(float x, float y) {return frac(sin(dot(float2(x, y), float2(12.9898, 78.233))) * 43758.5453);}fixed4 frag (v2f i) : SV_Target {float splitAmount = _Intensity * randomNoise(_Time.x, 2);// 采样各通道half4 colorR = tex2D(_MainTex,  float2(i.uv.x + splitAmount, i.uv.y));half4 colorG = tex2D(_MainTex,  i.uv);half4 colorB = tex2D(_MainTex,  float2(i.uv.x - splitAmount, i.uv.y));// 组合颜色并调整亮度half3 finalColor = half3(colorR.r, colorG.g, colorB.b);return half4(finalColor, 1.0);
}

也可以用三角函数和pow方法控制抖动的间隔、幅度,以及抖动的曲线,更自然

通过多个正弦函数相乘得到一个随时间变化的噪声值(实际上是模拟一种不规则的波动),然后通过`pow`函数和参数`_Amplitude`调整其形状,再乘以基础参数`_Amount`(控制整体强度)得到初始的偏移量。

fixed4 frag (v2f i) : SV_Target {// 计算动态偏移量(三角函数波动)float splitAmount = (1.0 + sin(_Time.x * 6.0)) * 0.5;splitAmount *= (1.0 + sin(_Time.x * 16.0)) * 0.5;splitAmount *= (1.0 + sin(_Time.x * 19.0)) * 0.5;splitAmount *= (1.0 + sin(_Time.x * 27.0)) * 0.5;splitAmount = pow(splitAmount, _Amplitude);splitAmount *= (0.05 * _Amount); // 结合参数//计算的是当前像素的UV坐标到屏幕中心(0.5,0.5)的距离。UV坐标的范围是(0,0)到(1,1),所以中心就是(0.5,0.5)。这个距离用来表示像素离屏幕中心的远近。float distance = length(i.uv.xy - float2(0.5, 0.5));splitAmount *=  _Fading ;//_Fading`来控制整个效果的整体强度(包括时间和空间)。splitAmount *= lerp(1, distance, _CenterFading);//控制效果在屏幕中心减弱(甚至消失),而在边缘增强。这样可以使故障效果主要出现在屏幕边缘,中心区域则比较正常,模拟一些镜头边缘色散的效果。// 采样各通道half3 colorR = tex2D(_MainTex,  float2(i.uv.x + splitAmount, i.uv.y)).rgb;half4 sceneColor= tex2D(_MainTex,  i.uv);half3 colorB= tex2D(_MainTex,  float2(i.uv.x - splitAmount, i.uv.y)).rgb;// 组合颜色并调整亮度half3 splitColor = half3(colorR.r, sceneColor.g, colorB.b);half3 finalColor = lerp(sceneColor.rgb, splitColor, _Fading);return half4(finalColor, 1.0);
}

可以结合脚本实现动态方向变化效果让RGB分离故障更加生动和多样化,模拟出真实设备故障时方向不固定的效果。

错位图块故障(Image Block Glitch)

错位图块故障效果的核心在于将图像分割成块状区域,并基于噪声函数对这些区域进行随机位移和颜色通道分离,模拟数字信号传输故障或显示设备故障的视觉效果。

初步效果

  // 随机噪声函数 (低成本)float randomNoise(float2 seed){return frac(sin(dot(seed * floor(_Time.y * _Speed), float2(17.13, 3.71))) * 43758.5453123);}float2 blockGlitch(float2 uv)
{// 第一步:基于uv和噪声函数生成方格块float2 block = floor(uv * _BlockSize);float noise = randomNoise(block);// 第二步:强度二次筛选float displaceNoise = pow(noise, 8.0) * pow(noise, 3.0);displaceNoise *= _Intensity;// 创建随机偏移方向float randomAngle = noise * 6.283185; // 0-2PIfloat2 offsetDir = float2(cos(randomAngle), sin(randomAngle));// 返回位移向量return offsetDir * displaceNoise * _OffsetScale;
}fixed4 frag (v2f i) : SV_Target{// 计算块状故障位移float2 glitchOffset = blockGlitch(i.uv);// 第三步:对颜色通道进行采样half colorR = tex2D(_MainTex, i.uv).r;half colorG = tex2D(_MainTex, i.uv + float2(glitchOffset.x, 0)).g;half colorB = tex2D(_MainTex, i.uv - float2(glitchOffset.y, 0)).b;return fixed4(colorR, colorG, colorB, 1.0);}

结合RGB颜色分离故障(RGB Split Glitch)

 fixed4 frag (v2f i) : SV_Target{// 1. 生成块状噪声half2 block = randomNoise(floor(i.uv * _BlockSize));// 2. 计算位移噪声float displaceNoise = pow(block.x, _DisplacePower);// 3. 计算RGB分离噪声float splitRGBNoise = pow(randomNoise(7.2341), 17.0);// 4. 计算RGB分离偏移float offsetX = displaceNoise - splitRGBNoise * _MaxRGBSplitX;float offsetY = displaceNoise - splitRGBNoise * _MaxRGBSplitY;// 5. 添加额外噪声扰动float noiseX = _NoiseScale * randomNoise(13.0);float noiseY = _NoiseScale * randomNoise(7.0);// 6. 组合最终偏移向量float2 offset = float2(offsetX * noiseX, offsetY * noiseY);// 7. 采样各通道half4 colorR = tex2D(_MainTex, i.uv);half4 colorG = tex2D(_MainTex, i.uv + offset);half4 colorB = tex2D(_MainTex, i.uv - offset);// 8. 混合RGB分离效果half3 combinedColor = half3(colorR.r, colorG.g, colorB.b);half3 finalColor = lerp(colorR.rgb, combinedColor, _SplitIntensity);return half4(finalColor, colorR.a);}

错位线条故障(Line Block Glitch)

错位线条故障(Line Block Glitch)的核心是生成随机的线条块(沿y方向/沿x方向) 。对UV进行随机偏移(主要是x方向) 在YUV颜色空间调整颜色,增强故障效果 ,将故障后的颜色与原图混合即可

  float trunc(float x, float num_levels){return floor(x * num_levels) / num_levels;}float randomNoise(float2 c){return frac(sin(dot(c.xy, float2(12.9898, 78.233))) * 43758.5453);}float3 rgb2yuv(float3 rgb){float3 yuv;yuv.x = dot(rgb, float3(0.299, 0.587, 0.114));yuv.y = dot(rgb, float3(-0.14713, -0.28886, 0.436));yuv.z = dot(rgb, float3(0.615, -0.51499, -0.10001));return yuv;}float3 yuv2rgb(float3 yuv){float3 rgb;rgb.r = yuv.x + yuv.z * 1.13983;rgb.g = yuv.x + dot(float2(-0.39465, -0.58060), yuv.yz);rgb.b = yuv.x + yuv.y * 2.03211;return rgb;}fixed4 frag (v2f i) : SV_Target{//生成随机强度梯度线条float truncTime = trunc(_Time.x* _LineSpeed, 4.0);       float uv_trunc = randomNoise(trunc(i.uv.yy, float2(8, 8)) + 100.0 * truncTime);float uv_randomTrunc = 6.0 * trunc(_Time.x, 24.0 * uv_trunc);//生成随机梯度的非等宽线条float blockLine_random = 0.5 * randomNoise(trunc(i.uv.yy + uv_randomTrunc, float2(8 * _LinesWidth, 8 * _LinesWidth)));blockLine_random += 0.5 * randomNoise(trunc(i.uv.yy + uv_randomTrunc, float2(7, 7)));blockLine_random = blockLine_random * 2.0 - 1.0;    blockLine_random = sign(blockLine_random) * saturate((abs(blockLine_random) - _Amount) / (0.4));blockLine_random = lerp(0, blockLine_random, _Offset);// 生成源色调的blockLine Glitchfloat2 uv_blockLine = i.uv;uv_blockLine = saturate(uv_blockLine + float2(0.1 * blockLine_random, 0));float4 blockLineColor = tex2D(_MainTex, abs(uv_blockLine));// 将RGB转到YUV空间,并做色调偏移// RGB -> YUVfloat3 blockLineColor_yuv = rgb2yuv(blockLineColor.rgb);// adjust Chrominance | 色度blockLineColor_yuv.y /= 1.0 - 3.0 * abs(blockLine_random) * saturate(0.5 - blockLine_random);// adjust Chroma | 浓度blockLineColor_yuv.z += 0.125 * blockLine_random * saturate(blockLine_random - 0.5);float3 blockLineColor_rgb = yuv2rgb(blockLineColor_yuv);// 与源场景图进行混合float4 sceneColor =tex2D(_MainTex,  i.uv);return lerp(sceneColor, float4(blockLineColor_rgb, blockLineColor.a), _Alpha);}

图块抖动故障(Tile Jitter Glitch)

图块抖动故障的核心原理是通过将屏幕分割成多个图块区域,并让部分图块随时间产生位置偏移,模拟显示设备故障或信号干扰的效果。

 float randomNoise(float2 seed){return frac(sin(dot(seed, float2(12.9898, 78.233))) * 43758.5453);}// 获取像素尺寸float2 getPixelSize(){return float2(1.0 / _ScreenParams.x, 1.0 / _ScreenParams.y);}fixed4 frag (v2f i) : SV_Target{//计算像素尺寸float2 pixelSize = getPixelSize();float timeFactor = _Time.y * _JitterSpeed;float randomness = lerp(1.0, randomNoise(float2(timeFactor, i.uv.y)), _Randomness);float strength = 0.5 + 0.5 * cos(timeFactor * _Frequency) * randomness;float splitFactor;if(_Direction == 0) // 水平方向splitFactor = fmod(i.uv.y * _SplittingNumber, 2.0);else // 垂直方向splitFactor = fmod(i.uv.x * _SplittingNumber, 2.0);float2 jitterOffset = float2(0, 0);if(splitFactor < 1.0){float jitterValue = cos(timeFactor * 10.0) * _JitterAmount * strength;if(_Direction == 0) // 水平方向抖动jitterOffset.x = pixelSize.x * jitterValue;else // 垂直方向抖动jitterOffset.y = pixelSize.y * jitterValue;}float2 jitterUV = i.uv + jitterOffset;half4 sceneColor = tex2D(_MainTex, jitterUV);//添加RGB分离half4 colorR = tex2D(_MainTex, jitterUV + float2(pixelSize.x * 2, 0));half4 colorB = tex2D(_MainTex, jitterUV - float2(pixelSize.x * 2, 0));return half4(colorR.r, sceneColor.g, colorB.b, sceneColor.a);}

扫描线抖动故障(Scan Line Jitter Glitch)

扫描线抖动故障的核心原理是通过在水平方向随机偏移扫描线的位置,模拟显示同步故障的效果。


float randomNoise(float2 seed)
{return frac(sin(dot(seed, float2(12.9898, 78.233))) * 43758.5453);
}fixed4 frag (v2f i) : SV_Target
{float jitter = randomNoise(float2(i.uv.y, _Time.x)) * 2.0 - 1.0;jitter = step(_JitterThreshold, abs(jitter)) * jitter * _JitterAmount;// float verticalJitter = sin(i.uv.x * 100 + _Time.y * _JitterSpeed * 2) * _VerticalJitter;// float2 jitterOffset = float2(jitter, verticalJitter);//half4 sceneColor = tex2D(_MainTex,  frac(i.uv + float2(jitterOffset, 0)));float2 jitterUV = i.uv + float2(jitter, 0);half4 sceneColor = tex2D(_MainTex, jitterUV);return sceneColor;}

数字条纹故障(Digital Stripe Glitch)

数字条纹故障(Digital Stripe Glitch)核心原理:​​通过CPU生成动态噪声纹理,在GPU中进行UV偏移和颜色混合​​,模拟数字信号故障的视觉效果

先生成一个包含随机颜色条纹的噪声纹理

using UnityEngine;public class GlitchEffectController : MonoBehaviour
{[Header("纹理设置")]public int noiseTextureWidth = 64;public int noiseTextureHeight = 64;[Range(0.7f, 0.99f)] public float stripLength = 0.98f; public float updateInterval = 0.2f; [Header("Shader参数")]public Material glitchMaterial;private Texture2D _noiseTexture;private float _updateTimer;private void Start(){// 创建噪声纹理CreateNoiseTexture();// 将纹理传递给Shaderif (glitchMaterial != null)glitchMaterial.SetTexture("_NoiseTex", _noiseTexture);}private void Update(){// 定时更新噪声纹理_updateTimer += Time.deltaTime;if (_updateTimer >= updateInterval){UpdateNoiseTexture();_updateTimer = 0f;}}private void CreateNoiseTexture(){// 创建新纹理_noiseTexture = new Texture2D(noiseTextureWidth, noiseTextureHeight,TextureFormat.RGBA32, false);_noiseTexture.wrapMode = TextureWrapMode.Repeat;_noiseTexture.filterMode = FilterMode.Point; // 保持锐利边缘// 初始填充纹理UpdateNoiseTexture();}private void UpdateNoiseTexture(){if (_noiseTexture == null) return;Color currentColor = Random.ColorHSV(0f, 1f, 0.8f, 1f, 0.8f, 1f, 1f, 1f);for (int y = 0; y < _noiseTexture.height; y++){for (int x = 0; x < _noiseTexture.width; x++){if (Random.value > stripLength){currentColor = Random.ColorHSV(0f, 1f, 0.8f, 1f, 0.8f, 1f, 1f, 1f);}_noiseTexture.SetPixel(x, y, currentColor);}}_noiseTexture.Apply();if (glitchMaterial != null)glitchMaterial.SetTexture("_NoiseTex", _noiseTexture);}private void OnDestroy(){if (_noiseTexture != null)Destroy(_noiseTexture);}
}

然后利用生成的噪声纹理对主纹理进行扭曲

 fixed4 frag (v2f i) : SV_Target{half4 stripNoise = tex2D(_NoiseTex,  i.uv);half threshold = 1.001 - _Indensity * 1.001;half uvShift = step(threshold, pow(abs(stripNoise.x), 3)) * _Indensity;float2 uv = frac(i.uv + stripNoise.yz * uvShift);half4 source = tex2D(_MainTex,  uv);#ifndef NEED_TRASH_FRAMEreturn source;#endif// 基于废弃帧插值half stripIndensity = step(threshold, pow(abs(stripNoise.w), 3)) * _ColorIntensity;half3 color = lerp(source, _StripColor, stripIndensity).rgb;return float4(color, source.a);}

模拟噪点故障(Analog Noise Glitch)

模拟噪点故障(Analog Noise Glitch)的核心原理是用随机噪声搞坏画面,模仿老电视信号故障的雪花点画面。就想象一个老电视,看着看着突然「刺啦」闪雪花(撒噪点)色彩突然褪成黑白(亮度抖动)坏了,坏得还要有点规律(用UV位置控制)

float randomNoise(float2 seed)
{return frac(sin(dot(seed, float2(12.9898, 78.233))) * 43758.5453);
}fixed4 frag (v2f i) : SV_Target
{half4 sceneColor = tex2D(_MainTex, i.uv);half4 noiseColor = sceneColor;//当某一刻的随机强度值大于亮度抖动阈值时,将原先的RGB颜色对应的luminance强度,呈现出黑白灰度的表现。half luminance = dot(noiseColor.rgb, fixed3(0.22, 0.707, 0.071));if (randomNoise(float2(_Time.x * _Speed, _Time.x * _Speed)) > _LuminanceJitterThreshold){noiseColor = float4(luminance, luminance, luminance, luminance);}//用noise去扰动原先场景图的颜色值float noiseX = randomNoise(_Time.x * _Speed + i.uv/ float2(-213, 5.53));float noiseY = randomNoise(_Time.x * _Speed - i.uv / float2(213, -5.53));float noiseZ = randomNoise(_Time.x * _Speed + i.uv / float2(213, 5.53));noiseColor.rgb += 0.25 * float3(noiseX,noiseY,noiseZ) - 0.125;noiseColor = lerp(sceneColor, noiseColor, _Fading);return noiseColor;
}

屏幕跳跃故障(Screen Jump Glitch)

屏幕跳跃故障(Screen Jump Glitch)的核心是使用时间偏移来影响UV坐标,校正的UV小数部分与原始UV插值,创建均匀梯度式扰动效果。

fixed4 frag (v2f i) : SV_Target
{float timeFactor = _Time.y * _JumpSpeed;float jumpTime = frac(timeFactor);float2 jumpUV = i.uv;// 对每个方向分量单独处理if (_Direction.x > 0.5){float perturbedX = frac(i.uv.x + jumpTime * _JumpAmount);jumpUV.x = lerp(i.uv.x, perturbedX, _JumpIntensity);}if (_Direction.y > 0.5){float perturbedY = frac(i.uv.y + jumpTime * _JumpAmount);jumpUV.y = lerp(i.uv.y, perturbedY, _JumpIntensity);}float smoothFactor = smoothstep(0.0, _Smoothness, frac(timeFactor));jumpUV = lerp(i.uv, jumpUV, smoothFactor * _JumpIntensity);half4 sceneColor = tex2D(_MainTex, jumpUV);return sceneColor;
}

屏幕抖动故障(Screen Shake Glitch)

屏幕抖动故障的核心原理是通过在水平和垂直方向同时施加随时间变化的随机偏移量,模拟设备震动或信号干扰导致的屏幕整体不规则晃动效果。

 float randomNoise(float2 seed){return frac(sin(dot(seed + float2(_RandomSeed, _RandomSeed), float2(127.1, 311.7))) * 43758.5453);}fixed4 frag (v2f i) : SV_Target{float time = _Time.y * _ShakeSpeed;float shakeX = (randomNoise(float2(time, 2.0)) - 0.5) * _ShakeIntensity * _HorizontalAmount;float shakeY = (randomNoise(float2(time, 3.0)) - 0.5) * _ShakeIntensity * _VerticalAmount;float2 shakeUV = i.uv + float2(shakeX, shakeY);half4 sceneColor = tex2D(_MainTex, shakeUV);return sceneColor;}

波动抖动故障(Wave Jitter Glitch)

波动抖动故障的核心原理是通过在垂直/水平方向生成基于噪声的波形函数,将其转换为屏幕坐标的偏移量,并应用RGB通道分离采样,从而创建动态的波浪状画面扭曲效果。

float noise(float2 seed)
{return frac(sin(dot(seed, float2(12.9898, 78.233))) * 43758.5453);
}fixed4 frag (v2f i) : SV_Target
{half strength = 0.5 + 0.5 * cos(_Time.y * _Frequency);	float2 screenSize = float2(_ScreenParams.x, _ScreenParams.y);if (_Direction == 0) // 水平抖动{// 准备UV (Y坐标 * 屏幕高度)float uv_y = i.uv.y * screenSize.y;// 计算波形噪声float noise_wave_1 = noise(float2(uv_y *_WaveScale, _Time.y * _Speed * 20)) * (strength * _Amount *_WaveAmplitude );float noise_wave_2 = noise(float2(uv_y *(_WaveScale * 2), _Time.y * _Speed * 10)) * (strength * _Amount * (_WaveAmplitude * 0.125));float noise_wave = noise_wave_1 * noise_wave_2 / screenSize.x;//计算水平偏移float uv_x = i.uv.x + noise_wave;//计算RGB分离偏移float rgbSplit_offset = (_RGBSplit * 50 + (20.0 * strength + 1.0)) * noise_wave / screenSize.x;//采样RGB通道half4 colorG = tex2D(_MainTex, float2(uv_x, i.uv.y));half4 colorRB = tex2D(_MainTex, float2(uv_x + rgbSplit_offset, i.uv.y));//组合颜色return half4(colorRB.r, colorG.g, colorRB.b, colorG.a);}else // 垂直抖动{float uv_x = i.uv.x * screenSize.x;float noise_wave_1 = noise(float2(uv_x * _WaveScale, _Time.y * _Speed * 20)) * (strength * _Amount * _WaveAmplitude);float noise_wave_2 = noise(float2(uv_x *(_WaveScale * 2), _Time.y * _Speed * 10)) * (strength * _Amount *(_WaveAmplitude * 0.125));float noise_wave = noise_wave_1 * noise_wave_2 / screenSize.y;float uv_y = i.uv.y + noise_wave;float rgbSplit_offset = (_RGBSplit * 50 + (20.0 * strength + 1.0)) * noise_wave / screenSize.y;half4 colorG = tex2D(_MainTex, float2(i.uv.x, uv_y));half4 colorRB = tex2D(_MainTex, float2(i.uv.x, uv_y + rgbSplit_offset));return half4(colorRB.r, colorG.g, colorRB.b, colorG.a);}}

以上就是全部的内容了,非常感谢大佬的文章,我只是站在巨人的肩膀上学习了他的方法。

 高品质后处理:十种故障艺术(Glitch Art)算法的总结与实现 - 知乎https://zhuanlan.zhihu.com/p/148256756

http://www.dtcms.com/a/327897.html

相关文章:

  • hashmap如何解决碰撞
  • Pytorch编译
  • 1.Ansible 自动化介绍
  • 网站测评-利用缓存机制实现XSS的分步测试方法
  • 设置默认的pip下载清华源(国内镜像源)和pip使用清华源
  • SQL tutorials
  • 鸿蒙下载图片保存到相册,截取某个组件保存到相册
  • 农业园区气象站在高标准农田的用处
  • 行业热点丨智能仿真时代:电子工程多物理场解决方案创新实践
  • USLR:一款用于脑MRI无偏倚平滑纵向配准的开源工具|文献速递-医学影像算法文献分享
  • 体育数据api接口,足球api篮球api电竞api,比赛赛事数据api
  • vmware虚拟机Ubuntu系统奔溃修复
  • 西安国际数字科创产业园:政策赋能筑长安数字产业集群
  • Linux学习-软件编程(标准IO)
  • 【ROS2】ROS2 基础学习教程 以lerobot-so100为例
  • specCPU2017在麒麟系统的简单测试
  • VisionPro——1.VP与C#联合
  • 前端/在vscode中创建Vue3项目
  • 【实时Linux实战系列】实时环境监测系统架构设计
  • 多奥电梯智能化解决方案的深度解读与结构化总结,内容涵盖系统架构、功能模块、应用场景与社会价值四大维度,力求全面展示该方案的技术先进性与应用前景。
  • HTTPS服务
  • 重构与性能的平衡术:先优化结构,再优化速度
  • 机器学习—— TF-IDF文本特征提取评估权重 + Jieba 库进行分词(以《红楼梦》为例)
  • A1-MPLS-LDP配置
  • 【MongoDB】简单理解聚合操作,案例解析
  • MongoDB分析insert源代码
  • Android init.rc详解
  • 【Linux】init和bash的区别
  • CentOS 7.9 升级 GLibc 2.34
  • secureCRT ymodem协议连续传输文件速率下降