平面故障效果

图块抖动

  • 主要原理是对uv进行分层,然后使用噪声函数对不同层级的uv进行抖动。
Tile Jitter
1
2
3
4
5
6
7
8
9
void TileJitterGlitch(out half2 uv, half jitterPower, half aspectRatio)
{
half tileBaseValue = uv.y;
half tile = ceil(tileBaseValue * _JitterTileDensity * aspectRatio);
half speed = _JitterSpeed / _JitterTileDensity;
half randomValue = RandomNoise(_Time.w * speed * tile);
half jitterValue = randomValue * jitterPower;
uv.x += jitterValue;
}

RGB颜色分离

  • 主要原理是对RGB三个通道采用不同的uv偏移值进行分别采样。一般会选取一个通道采用原始uv值,另两个通道使用偏移后的uv进行采样。
RGB Split
1
2
3
4
5
6
7
8
9
10
11
half4 RGBSplitGlitch(half2 uv, half jitterPower)
{
half rgbSplitValue = _RGBSplitPower * jitterPower;
half2 uvR = half2(uv.x + rgbSplitValue, uv.y);
half2 uvB = half2(uv.x - rgbSplitValue, uv.y);
half4 colorR = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uvR);
half4 colorG = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv);
half4 colorB = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uvB);
half colorA = max(max(colorR.a, colorG.a), colorB.a) - rgbSplitValue * _RGBSplitFadeSpeed;
return half4(colorR.r * colorR.a, colorG.g * colorG.a, colorB.b * colorB.a, colorA);
}

三维故障效果

后处理方案

  • 主要思路是:把Glitch对象单独拿出来,在场景绘制完后,再将Glitch对象的颜色与深度单独绘制到RT上,并进行Glitch处理,处理完毕后再融回主相机的ColorTarget中。
  • 具体实现:
    • 新增Layer,命名为Glitch,并将Glitch对象的Layer设置为Glitch。
    • 将主相机FilteringSettings中的Glitch Layer去掉,可以得到不含Glitch对象的颜色图和深度图:
      场景颜色
      场景颜色
      场景深度
      场景深度
    • 绘制完场景之后,将RenderTarget设置为新的RT,再将Glitch对象的颜色和深度绘制到RT上:
      角色颜色
      角色颜色
      角色深度
      角色深度
    • 最终对Glitch对象的颜色图和深度图进行Glitch处理,并利用深度图处理与场景物体的遮挡关系,再将颜色图融回主相机的ColorTarget中。
    • 注意:该方案使用了额外的RT,增加了带宽的消耗。

投影器方案

  • 主要思路是:为Glitch对象创建一个可以包裹住自身的Cube对象,在绘制完场景之后,将Glitch对象投影到Cube对象上,最终再对Cube对象进行Glitch处理。
  • 具体实现:
    • 在Glitch对象上添加Glitcher脚本,在运行时Glitcher脚本会根据其节点下的Renderer对象计算出最大的包围盒,并以此为依据创建Cube对象。
    • 在Cube对象使用的材质球中,根据_CameraDepthTexture获取其包裹对象所占用的像素,再根据_CameraOpaqueTexture获取包裹对象的颜色,最终再进行Glitch处理。
      • 根据_CameraDepthTexture获取其包裹对象所占用的像素:
      Get Depth Step
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      half GetDepthStep(Varyings input)
      {
      half2 screenUV = (input.positionCS.xy / _ScaledScreenParams.xy);
      half depth = SampleSceneDepth(screenUV);
      #ifndef UNITY_REVERSED_Z
      depth = lerp(UNITY_NEAR_CLIP_VALUE, 1, depth);
      #endif
      float3 worldPos = ComputeWorldSpacePosition(uv, depth, UNITY_MATRIX_I_VP);
      float3 objectPos = mul(UNITY_MATRIX_I_M, float4(worldPos, 1));
      half max_step = step(step(0.5, objectPos.x) + step(0.5, objectPos.y) + step(0.5, objectPos.z), 0);
      half min_step = step(-0.5, objectPos.x) * step(-0.5, objectPos.y) * step(-0.5, objectPos.z);
      return max_step * min_step;
      }

附录

噪声模拟

Random Noise
1
2
3
4
half RandomNoise(half val)
{
return frac(sin(val) * 100) * 2 - 1;
}
  • 推导过程:
    f(x) = frac(x)
    f(x) = frac(x)
    f(x) = sin(x)
    f(x) = sin(x)
    f(x) = frac(sin(x))
    f(x) = frac(sin(x))
    f(x) = frac(sin(x) * 10)
    f(x) = frac(sin(x) * 10)
    f(x) = frac(sin(x) * 100)
    f(x) = frac(sin(x) * 100)
    frac(sin(x) * 100) * 2 - 1
    frac(sin(x) * 100) * 2 - 1