A post-processing component called CCSAnalogNoise that implements a TV static style effect. It exposes tuning properties (opacity, blur, tone curve controls), loads three shader materials, sets shader parameters, and issues three blit passes (analog noise, tone curves, box blur).
using Sandbox;
using System;
using Sandbox;
using Sandbox.Rendering;
/// <summary>
/// Generate random noise (not very good).
/// </summary>
[Title( "TV Static" )]
[Category( "Post Processing" )]
[Icon( "broken_image" )]
public sealed class CCSAnalogNoise : BasePostProcess<CCSAnalogNoise>
{
/// <summary>
/// noise-image blend, please note: the fine tune controls effect the entire image
/// </summary>
[Property, Title("Opacity"), Range(0.00f, 1.0f, 0, true)]
public float fOpacity { get; set; } = 0.75f;
/// <summary>
/// Makes the noise less stretched out.
/// </summary>
[Property, Title("Don't Stretch"), Group("Fine Tune")]
public bool bAspect { get; set; }
/// <summary>
/// How much to blur the image + noise
/// </summary>
[Property, Title("Blur Radius"), Range(0.0f, 20.0f, 1, true), Group("Fine Tune")]
public float bRadius { get; set; } = 4.0f;
/// <summary>
/// Darkest parts of the image. cool value: -0.25
/// </summary>
[Property, Title("Blacks"), Group("Fine Tune"), Range(-0.25f, 1.0f, 0)]
public float Blacks { get; set; } = 0.0f;
/// <summary>
/// Rolloff from midtones to shadow. cool value: 0.0
/// </summary>
[Property, Title("Shadows"), Group("Fine Tune"), Range(0.0f, 1.0f, 0)]
public float Shadows { get; set; } = 0.25f;
/// <summary>
/// Parts of the image around 50%. cool value: 0.05
/// </summary>
[Property, Title("Midtones"), Group("Fine Tune"), Range(0.0f, 1.0f, 0)]
public float Midtones { get; set; } = 0.5f;
/// <summary>
/// Rolloff from midtones to whites. cool value: 0.365
/// </summary>
[Property, Title("Highlights"), Group("Fine Tune"), Range(0.0f, 1.0f, 0)]
public float Highlights { get; set; } = 0.75f;
/// <summary>
/// Brightest parts of the image. cool value: 1.35
/// </summary>
[Property, Title("Whites"), Group("Fine Tune"), Range(0.0f, 1.5f, 0)]
public float Whites { get; set; } = 1.0f;
// Приватные свойства не сериализуются, но используются в шейдере
private float bMulti { get; set; } = 8.0f;
private bool bCircle { get; set; } = false;
private bool bLoop { get; set; } = true;
public override void Render()
{
// Числовые значения через GetWeighted — поддерживает блендинг в PostProcess Volume
float fOpacityW = GetWeighted(x => x.fOpacity);
float bRadiusW = GetWeighted(x => x.bRadius);
float BlacksW = GetWeighted(x => x.Blacks);
float ShadowsW = GetWeighted(x => x.Shadows);
float MidtonesW = GetWeighted(x => x.Midtones);
float HighlightsW = GetWeighted(x => x.Highlights);
float WhitesW = GetWeighted(x => x.Whites);
// Bool берем напрямую — GetWeighted с bool вызывает NullReferenceException
bool bAspectW = bAspect;
bool bCircleW = bCircle;
bool bLoopW = bLoop;
// Загружаем материалы с проверкой на null
var analogMat = Material.FromShader("postprocess/ccs_analog.shader");
var tonecurvesMat = Material.FromShader("postprocess/ccs_tonecurves.shader");
var boxblurMat = Material.FromShader("postprocess/ccs_boxfilter.shader");
if (analogMat == null || tonecurvesMat == null || boxblurMat == null)
{
Log.Warning("CCSAnalogNoise: One or more materials not found!");
return;
}
// ПРОХОД 1: Analog noise
Attributes.Set("fOpacity", fOpacityW);
var blit1 = BlitMode.WithBackbuffer(analogMat, Stage.AfterPostProcess, 8000, true);
Blit(blit1, "CCSAnalogNoise_Pass1");
// ПРОХОД 2: Tone curves
Attributes.Set("Blacks", BlacksW);
Attributes.Set("Shadows", ShadowsW);
Attributes.Set("Midtones", MidtonesW);
Attributes.Set("Highlights", HighlightsW);
Attributes.Set("Whites", WhitesW);
var blit2 = BlitMode.WithBackbuffer(tonecurvesMat, Stage.AfterPostProcess, 8000, true);
Blit(blit2, "CCSAnalogNoise_Pass2");
// ПРОХОД 3: Box blur
Attributes.Set("bRadius", bRadiusW);
Attributes.Set("bMulti", bMulti);
// Bool передаем как int (0/1) для надежной работы с HLSL
Attributes.Set("bAspect", bAspectW ? 1 : 0);
Attributes.Set("bCircle", bCircleW ? 1 : 0);
Attributes.Set("bLoop", bLoopW ? 1 : 0);
var blit3 = BlitMode.WithBackbuffer(boxblurMat, Stage.AfterPostProcess, 8000, true);
Blit(blit3, "CCSAnalogNoise_Pass3");
}
}