A post-process effect component that simulates VHS-style vertical scrolling and horizontal tracking distortion. It exposes tunable properties (warp size, speed, distortion, chromatic aberration, static, deinterlace skew), reads blended values via GetWeighted, sets shader attributes, loads a postprocess shader material and issues a Blit to apply the effect.
using Sandbox;
using System;
using Sandbox;
using Sandbox.Rendering;
/// <summary>
/// Simulation of the vertical scrolling - horizontal "tracking" effect on old VHS tapes.
/// </summary>
[Title( "VHS Distortion" )]
[Category( "Post Processing" )]
[Icon( "dehaze" )]
public sealed class CCSVHSD : BasePostProcess<CCSVHSD>
{
/// <summary>
/// How big are the distortion lines.
/// </summary>
[Property, Title("Warp Size"), Range(0f, 20.0f)]
public float warp_size { get; set; } = 12.0f;
/// <summary>
/// Enhance the distorted areas.
/// </summary>
[Property, Title("Warp Distortion Multiplier"), Range(-0.5f, 0.5f)]
public float warp_distort { get; set; } = 0.05f;
/// <summary>
/// How fast the distorted areas scroll.
/// </summary>
[Property, Title("Warp Speed"), Range(-30.0f, 30.0f)]
public float warp_speed { get; set; } = 6.0f;
/// <summary>
/// number of smaller lines to chop the main warp lines into.
/// </summary>
[Property, Title("Warp Variation"), Range(-30.0f, 30.0f)]
public float warp_random { get; set; } = 1.5f;
/// <summary>
/// How fast the distorted areas scroll.
/// </summary>
[Property, Title("Chromatic Abberation"), Range(-0.25f, 0.25f)]
public float ca { get; set; } = 0.02f;
/// <summary>
/// Amount of static that each warp line has
/// </summary>
[Property, Title("Static Amount"), Range(0.0f, 1.0f)]
public float Static { get; set; } = 0.5f;
/// <summary>
/// high frequency horizontal ripple distortion effect on the entire image.
/// </summary>
[Property, Title("DeInterlace Skew"), Range(0.0f, 20.0f)]
public float dSkew { get; set; } = 0.5f;
public override void Render()
{
// Числовые значения через GetWeighted — поддерживает блендинг в PostProcess Volume
float warpSizeW = GetWeighted(x => x.warp_size);
float warpDistortW = GetWeighted(x => x.warp_distort);
float warpSpeedW = GetWeighted(x => x.warp_speed);
float warpRandomW = GetWeighted(x => x.warp_random);
float caW = GetWeighted(x => x.ca);
float staticW = GetWeighted(x => x.Static);
float dSkewW = GetWeighted(x => x.dSkew);
// Передаем значения в шейдер
Attributes.Set("warp_size", warpSizeW);
Attributes.Set("warp_speed", warpSpeedW);
Attributes.Set("warp_random", warpRandomW);
Attributes.Set("warp_distort", warpDistortW);
Attributes.Set("ca", caW);
Attributes.Set("Static", staticW);
Attributes.Set("dSkew", dSkewW);
// Загружаем материал с проверкой на null
var material = Material.FromShader("postprocess/ccs_vhs_distort.shader");
if (material == null)
{
Log.Warning("CCSVHSD: Material 'materials/postprocess/ccs_vhsd.vmat' not found!");
return;
}
// Применяем эффект через современный Blit API
// Приоритет 9000 перенесен из оригинального кода
var blit = BlitMode.WithBackbuffer(material, Stage.AfterPostProcess, 9000, true);
Blit(blit, "CCSVHSD");
}
}