Components/PostProcessing/CCSCRT.cs
using Sandbox;
using System;
/// <summary>
/// Emulate the look of a CRT screen
/// </summary>
[Title( "CRT Emulation" )]
[Category( "Post Processing" )]
[Icon( "live_tv" )]
public sealed class CCSCRT : Component, Component.ExecuteInEditor
{
// [Property]
// public Texture crt_mask;
/// <summary>
/// Pattern to use for the CRT Mask.
/// </summary>
[Property, Title("CRT Mask Preset")]
public CRTPreset CRTPresetSelection { get; set; } = CRTPreset.SubPixelApertureGrille;
// http://filthypants.blogspot.com/2020/02/crt-shader-masks.html
// these patterns exploit the subpixels of modern screens to make them appear somewhat like a CRT would. it isn't perfect
// can add other presets in the future for things like texture based tiling patterns
public enum CRTPreset
{
[Description( "Vertical Pattern" )]
SubPixelApertureGrille,
[Description( "Alternating pattern, looks like shadow mask." )]
SubPixelShadowMask,
[Description( "Fake slot-mask pattern" )]
SubPixelSlotMask,
[Description( "RGB mask with black lines" )] //non sub pixel masks below here
FullPixelGrille,
[Description( "RGB alternating pattern" )] //non sub pixel masks below here
FullPixelShadowMask,
None // always put this at the end
}
/// <summary>
/// Blur the image slightly to create a "bloomy" look (works best with white enhance).
/// </summary>
[Property, Title("Softness"), Range( 0.0f, 10.0f, 0, true)]
public float bLevel { get; set; } = 1.0f;
/// <summary>
/// FLASHING LIGHTS WARNING! Enables Screen Flicker (defaults are sane).
/// </summary>
[Property, ToggleGroup("Flicker", Label = "Screen Flicker")]
public bool Flicker { get; set; }
/// <summary>
/// How much to flash, 1 = fully black(crazy). 0.02 = semi realistic.
/// </summary>
[Property, Title("Flicker Opacity"), Group("Flicker"), Range( 0.0f, 1.0f, 0, true)]
public float fOpacity { get; set; } = 0.02f;
/// <summary>
/// How often the screen flickers. Gets weird at high values.
/// </summary>
[Property, Title("Flicker Rate"), Group("Flicker"), Range( 0.0f, 100.0f, 0, true)]
public float fRate { get; set; } = 60.0f;
/// <summary>
/// Enable CRT Scan line emulation.
/// </summary>
[Property, ToggleGroup("ScanLines", Label = "Scan Lines")]
public bool ScanLines { get; set; }
/// <summary>
/// Not the actual number of lines on the screen, but a value which scales the number of lines.
/// </summary>
[Property, Title("Line Count"), Group("ScanLines"), Range( 0.0f, 1000.0f, 1, true)]
public float slCount { get; set; } = 480.0f;
/// <summary>
/// How dark the scanlines are.
/// </summary>
[Property, Title("Line Opacity"), Group("ScanLines"), Range( 0.0f, 1.0f, 0, true)]
public float slOpacity { get; set; } = 0.1f;
/// <summary>
/// Rate at which to scroll the scanlines up or down the screen. high speed + opacity = FLASHING LIGHTS!
/// </summary>
[Property, Title("Scroll Rate"), Group("ScanLines"), Range( -100.0f, 100.0f, 0, true)]
public float slScroll { get; set; } = 1.2f;
/// <summary>
/// brings back some brighter colors, but some of the crt effect is lost.
/// </summary>
[Property, Title("Enhance Whites")]
public bool rWhite { get; set; }
/// <summary>
/// Ensures the effect is applied AFTER others in the stack.(you must disable and re-enable the component for the change to take place), in this case the CRT shader will become the lowest, meaning even letterboxing will be applied BEFORE this one.
/// </summary>
[Property, Title("Low Priority")]
public bool fPriority { get; set; }
IDisposable renderHook;
protected override void OnEnabled()
{
renderHook?.Dispose();
var cc = Components.Get<CameraComponent>( true );
//ideally this shader should run AFTER the UI is drawn so it can change on-screen ui but I don't think you can do that yet?
//idk i don't have any UI to test with.
// https://github.com/Facepunch/sbox-issues/issues/4672 like this but everything on screen not just ui. (ui only would b cool tho)
//another nice to have would be to render this BEFORE the lens distortion or add lens distortion to this shader (could add pri switch to lens distortion for that)
//to simulate a crt's curved screen, but currently because this uses sub-pixel tricks to make it look like a CRT
// that wouldn't work at all.
if(fPriority)
renderHook = cc.AddHookBeforeOverlay( "CCSCRT", 9999, RenderEffect );
else
renderHook = cc.AddHookBeforeOverlay( "CCSCRT", 4000, RenderEffect );
}
protected override void OnDisabled()
{
renderHook?.Dispose();
renderHook = null;
}
RenderAttributes attributes = new RenderAttributes();
public void RenderEffect( SceneCamera camera )
{
if ( !camera.EnablePostProcessing )
return;
// attributes.Set( "crt_mask", crt_mask ); //enable later for texture based masks
attributes.Set( "ScanLines", ScanLines );
attributes.Set( "slCount", slCount );
attributes.Set( "slOpacity", slOpacity );
attributes.Set( "slScroll", slScroll );
attributes.Set( "Flicker", Flicker );
attributes.Set( "fOpacity", fOpacity );
attributes.Set( "fRate", fRate );
attributes.Set( "rWhite", rWhite );
attributes.Set( "bLevel", bLevel );
attributes.Set( "style", (int)CRTPresetSelection );
Graphics.GrabFrameTexture( "ColorBuffer", attributes );
// Graphics.GrabDepthTexture( "DepthBuffer", attributes );
Graphics.Blit( Material.Load( "materials/postprocess/ccs_crt.vmat" ), attributes );
}
}