Editor code that defines PostFxSettings (a set of postprocessing options) and a helper SuperShotPostFx that applies those settings to a camera GameObject by creating corresponding Component instances and returns an IDisposable scope that destroys them when disposed.
using System;
using System.Collections.Generic;
using Sandbox;
namespace Editor.SuperShot;
public sealed class PostFxSettings
{
[Property, Group( "Bloom" ), Title( "Bloom" )]
public bool BloomEnabled { get; set; } = false;
[Property, Group( "Bloom" ), Title( "Strength" ), Range( 0f, 3f ), ShowIf( nameof( BloomEnabled ), true )]
public float BloomStrength { get; set; } = 0.6f;
[Property, Group( "Bloom" ), Title( "Threshold" ), Range( 0f, 5f ), ShowIf( nameof( BloomEnabled ), true )]
public float BloomThreshold { get; set; } = 1.2f;
[Property, Group( "Tonemapping" ), Title( "Tonemapping" )]
public bool TonemapEnabled { get; set; } = false;
[Property, Group( "Tonemapping" ), Title( "Exposure" ), Range( -3f, 3f ), ShowIf( nameof( TonemapEnabled ), true )]
public float Exposure { get; set; } = 0f;
[Property, Group( "Color Adjustments" ), Title( "Color Adjustments" )]
public bool ColorEnabled { get; set; } = false;
[Property, Group( "Color Adjustments" ), Title( "Brightness" ), Range( 0f, 2f ), ShowIf( nameof( ColorEnabled ), true )]
public float ColorBrightness { get; set; } = 1f;
[Property, Group( "Color Adjustments" ), Title( "Contrast" ), Range( 0f, 2f ), ShowIf( nameof( ColorEnabled ), true )]
public float ColorContrast { get; set; } = 1f;
[Property, Group( "Color Adjustments" ), Title( "Saturation" ), Range( 0f, 2f ), ShowIf( nameof( ColorEnabled ), true )]
public float ColorSaturation { get; set; } = 1f;
[Property, Group( "Color Adjustments" ), Title( "Hue Rotate" ), Range( 0f, 360f ), ShowIf( nameof( ColorEnabled ), true )]
public float ColorHueRotate { get; set; } = 0f;
[Property, Group( "Depth of Field" ), Title( "Depth of Field" )]
public bool DofEnabled { get; set; } = false;
[Property, Group( "Depth of Field" ), Title( "Focal Distance" ), Range( 0f, 2000f ), ShowIf( nameof( DofEnabled ), true )]
public float DofFocalDistance { get; set; } = 200f;
[Property, Group( "Depth of Field" ), Title( "Focus Range" ), Range( 0f, 2000f ), ShowIf( nameof( DofEnabled ), true )]
public float DofFocusRange { get; set; } = 500f;
[Property, Group( "Depth of Field" ), Title( "Blur Size" ), Range( 0f, 100f ), ShowIf( nameof( DofEnabled ), true )]
public float DofBlurSize { get; set; } = 30f;
[Property, Group( "Depth of Field" ), Title( "Blur Foreground" ), ShowIf( nameof( DofEnabled ), true )]
public bool DofFrontBlur { get; set; } = false;
[Property, Group( "Sharpen" ), Title( "Sharpen" )]
public bool SharpenEnabled { get; set; } = false;
[Property, Group( "Sharpen" ), Title( "Scale" ), Range( 0f, 2f ), ShowIf( nameof( SharpenEnabled ), true )]
public float SharpenScale { get; set; } = 0.5f;
[Property, Group( "Chromatic Aberration" ), Title( "Chromatic Aberration" )]
public bool ChromaEnabled { get; set; } = false;
[Property, Group( "Chromatic Aberration" ), Title( "Scale" ), Range( 0f, 1f ), ShowIf( nameof( ChromaEnabled ), true )]
public float ChromaScale { get; set; } = 0.3f;
[Property, Group( "Vignette (engine)" ), Title( "Vignette" )]
public bool VignetteEnabled { get; set; } = false;
[Property, Group( "Vignette (engine)" ), Title( "Intensity" ), Range( 0f, 1f ), ShowIf( nameof( VignetteEnabled ), true )]
public float VignetteIntensity { get; set; } = 0.6f;
[Property, Group( "Vignette (engine)" ), Title( "Roundness" ), Range( 0f, 1f ), ShowIf( nameof( VignetteEnabled ), true )]
public float VignetteRoundness { get; set; } = 0.5f;
[Property, Group( "Vignette (engine)" ), Title( "Smoothness" ), Range( 0f, 1f ), ShowIf( nameof( VignetteEnabled ), true )]
public float VignetteSmoothness { get; set; } = 1f;
[Property, Group( "Vignette (engine)" ), Title( "Color" ), ShowIf( nameof( VignetteEnabled ), true )]
public Color VignetteColor { get; set; } = Color.Black;
[Property, Group( "Film Grain (engine)" ), Title( "Film Grain" )]
public bool GrainEnabled { get; set; } = false;
[Property, Group( "Film Grain (engine)" ), Title( "Intensity" ), Range( 0f, 1f ), ShowIf( nameof( GrainEnabled ), true )]
public float GrainIntensity { get; set; } = 0.5f;
[Property, Group( "Film Grain (engine)" ), Title( "Response" ), Range( 0f, 1f ), ShowIf( nameof( GrainEnabled ), true )]
public float GrainResponse { get; set; } = 0.5f;
[Property, Group( "Pixelate" ), Title( "Pixelate" )]
public bool PixelateEnabled { get; set; } = false;
[Property, Group( "Pixelate" ), Title( "Scale" ), Range( 0f, 1f ), ShowIf( nameof( PixelateEnabled ), true )]
public float PixelateScale { get; set; } = 0.3f;
[Property, Group( "Ambient Occlusion" ), Title( "Ambient Occlusion" )]
public bool AoEnabled { get; set; } = false;
[Property, Group( "Ambient Occlusion" ), Title( "Intensity" ), Range( 0f, 1f ), ShowIf( nameof( AoEnabled ), true )]
public float AoIntensity { get; set; } = 1f;
[Property, Group( "Ambient Occlusion" ), Title( "Radius" ), Range( 1, 512 ), ShowIf( nameof( AoEnabled ), true )]
public int AoRadius { get; set; } = 128;
public bool Any =>
BloomEnabled || TonemapEnabled || ColorEnabled || DofEnabled || SharpenEnabled ||
ChromaEnabled || VignetteEnabled || GrainEnabled || PixelateEnabled || AoEnabled;
}
public static class SuperShotPostFx
{
public static IDisposable Apply( GameObject cameraGo, PostFxSettings fx )
{
var created = new List<Component>();
if ( cameraGo is null || fx is null || !fx.Any )
return new FxScope( created );
if ( fx.BloomEnabled )
{
var c = cameraGo.Components.Create<Bloom>();
c.Strength = fx.BloomStrength;
c.Threshold = fx.BloomThreshold;
created.Add( c );
}
if ( fx.TonemapEnabled )
{
var c = cameraGo.Components.Create<Tonemapping>();
c.ExposureCompensation = fx.Exposure;
created.Add( c );
}
if ( fx.ColorEnabled )
{
var c = cameraGo.Components.Create<ColorAdjustments>();
c.Brightness = fx.ColorBrightness;
c.Contrast = fx.ColorContrast;
c.Saturation = fx.ColorSaturation;
c.HueRotate = fx.ColorHueRotate;
created.Add( c );
}
if ( fx.DofEnabled )
{
var c = cameraGo.Components.Create<DepthOfField>();
c.FocalDistance = fx.DofFocalDistance;
c.FocusRange = fx.DofFocusRange;
c.BlurSize = fx.DofBlurSize;
c.FrontBlur = fx.DofFrontBlur;
c.BackBlur = true;
created.Add( c );
}
if ( fx.SharpenEnabled )
{
var c = cameraGo.Components.Create<Sharpen>();
c.Scale = fx.SharpenScale;
created.Add( c );
}
if ( fx.ChromaEnabled )
{
var c = cameraGo.Components.Create<ChromaticAberration>();
c.Scale = fx.ChromaScale;
created.Add( c );
}
if ( fx.VignetteEnabled )
{
var c = cameraGo.Components.Create<Vignette>();
c.Intensity = fx.VignetteIntensity;
c.Roundness = fx.VignetteRoundness;
c.Smoothness = fx.VignetteSmoothness;
c.Color = fx.VignetteColor;
created.Add( c );
}
if ( fx.GrainEnabled )
{
var c = cameraGo.Components.Create<FilmGrain>();
c.Intensity = fx.GrainIntensity;
c.Response = fx.GrainResponse;
created.Add( c );
}
if ( fx.PixelateEnabled )
{
var c = cameraGo.Components.Create<Pixelate>();
c.Scale = fx.PixelateScale;
created.Add( c );
}
if ( fx.AoEnabled )
{
var c = cameraGo.Components.Create<AmbientOcclusion>();
c.Intensity = fx.AoIntensity;
c.Radius = fx.AoRadius;
created.Add( c );
}
return new FxScope( created );
}
sealed class FxScope : IDisposable
{
readonly List<Component> _created;
public FxScope( List<Component> created ) => _created = created;
public void Dispose()
{
foreach ( var c in _created )
{
if ( c.IsValid() )
c.Destroy();
}
_created.Clear();
}
}
}