test/FisheyeEffect.cs
/// <summary>
/// GoPro-style fisheye lens distortion effect. Simulates the barrel distortion,
/// chromatic aberration, and heavy vignetting of a wide-angle action camera lens.
/// Attach to a camera or use inside a PostProcessVolume.
/// </summary>
[Title( "Fisheye Lens" )]
[Category( "Post Processing" )]
[Icon( "camera" )]
public sealed class FisheyeEffect : BasePostProcess<FisheyeEffect>
{
	[Property, Range( 0, 1 ), Group( "General" )]
	public float Intensity { get; set; } = 1.0f;

	/// <summary>
	/// Barrel distortion strength. Higher values push more pixels outward
	/// from center, creating the characteristic fisheye bulge.
	/// </summary>
	[Property, Range( 0, 2 ), Group( "Distortion" )]
	public float BulgeAmount { get; set; } = 0.6f;

	/// <summary>
	/// Zoom factor to crop into the distorted image and hide the black
	/// borders at the edges. 1.0 = no zoom, higher = more cropped.
	/// </summary>
	[Property, Range( 0.5f, 2 ), Group( "Distortion" )]
	public float Zoom { get; set; } = 1.0f;

	/// <summary>
	/// Per-channel radial aberration. Separates R/G/B at different distortion
	/// strengths so colors fringe toward the edges, like a real wide-angle lens.
	/// </summary>
	[Property, Range( 0, 1 ), Group( "Aberration" )]
	public float ChromaticAberration { get; set; } = 0.3f;

	/// <summary>
	/// Lateral (transverse) chromatic dispersion. Shifts red and blue channels
	/// horizontally in opposite directions for additional color fringing.
	/// </summary>
	[Property, Range( 0, 1 ), Group( "Aberration" )]
	public float Dispersion { get; set; } = 0.1f;

	/// <summary>
	/// Edge darkening intensity. Wide-angle lenses lose significant light
	/// toward the frame edges due to the steep angle of incidence.
	/// </summary>
	[Property, Range( 0, 1 ), Group( "Vignette" )]
	public float VignetteIntensity { get; set; } = 0.5f;

	/// <summary>
	/// How circular the vignette is. 1.0 = perfectly round, 0.0 = follows
	/// the rectangular aspect ratio of the frame.
	/// </summary>
	[Property, Range( 0, 1 ), Group( "Vignette" )]
	public float VignetteRoundness { get; set; } = 0.8f;

	public override void Render()
	{
		float intensity = GetWeighted( x => x.Intensity );
		if ( intensity.AlmostEqual( 0.0f ) )
		{
			return;
		}

		Attributes.Set( "intensity", intensity );
		Attributes.Set( "bulgeAmount", GetWeighted( x => x.BulgeAmount ) );
		Attributes.Set( "zoom", GetWeighted( x => x.Zoom ) );
		Attributes.Set( "chromaticAberration", GetWeighted( x => x.ChromaticAberration ) );
		Attributes.Set( "dispersion", GetWeighted( x => x.Dispersion ) );
		Attributes.Set( "vignetteIntensity", GetWeighted( x => x.VignetteIntensity ) );
		Attributes.Set( "vignetteRoundness", GetWeighted( x => x.VignetteRoundness ) );

		// Render after built-in post-processing effects, same stage as VHS/NTSC
		var shader = Material.FromShader( "test/shaders/postprocess/pp_fisheye.shader" );
		var blit = BlitMode.WithBackbuffer( shader, Sandbox.Rendering.Stage.AfterPostProcess, 300, false );
		Blit( blit, "Fisheye Lens" );
	}
}