A post-processing component for S&box that exposes five-point tone curve controls (Blacks, Shadows, Midtones, Highlights, Whites) and a LumaOnly toggle, and applies them to a shader material during Render(). It reads weighted property values for blending, sets them as shader attributes, loads a shader material, and issues a Blit call to apply the effect.
using Sandbox;
using System;
using Sandbox;
using Sandbox.Rendering;
/// <summary>
/// Basic 5 point curves adjustment
/// </summary>
[Title( "Tone Curves" )]
[Category( "Post Processing" )]
[Icon( "shape_line" )]
public sealed class CCSToneCurve : BasePostProcess<CCSToneCurve>
{
/// <summary>
/// Darkest parts of the image.
/// </summary>
[Property, Title("Blacks"), Range(-0.25f, 1.0f, 0)]
public float Blacks { get; set; } = 0.0f;
/// <summary>
/// Rolloff from midtones to shadow.
/// </summary>
[Property, Title("Shadows"), Range(0.0f, 1.0f, 0)]
public float Shadows { get; set; } = 0.25f;
/// <summary>
/// Parts of the image around 50%.
/// </summary>
[Property, Title("Midtones"), Range(0.0f, 1.0f, 0)]
public float Midtones { get; set; } = 0.5f;
/// <summary>
/// Rolloff from midtones to whites.
/// </summary>
[Property, Title("Highlights"), Range(0.0f, 1.0f, 0)]
public float Highlights { get; set; } = 0.75f;
/// <summary>
/// Brightest parts of the image.
/// </summary>
[Property, Title("Whites"), Range(0.0f, 1.5f, 0)]
public float Whites { get; set; } = 1.0f;
/// <summary>
/// Reduces saturation increase, you probably want this on.
/// </summary>
[Property, Title("Luma Only")]
public bool LumaOnly { get; set; }
// [Property, Title("Work in Linear")]
// public bool wLinear { get; set; }
public override void Render()
{
// Числовые значения через GetWeighted — поддерживает блендинг в PostProcess Volume
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 lumaOnlyW = LumaOnly;
// bool wLinearW = wLinear;
// Передаем значения в шейдер
Attributes.Set("Blacks", blacksW);
Attributes.Set("Shadows", shadowsW);
Attributes.Set("Midtones", midtonesW);
Attributes.Set("Highlights", highlightsW);
Attributes.Set("Whites", whitesW);
// Bool передаем как int (0/1) для надежной работы с HLSL
Attributes.Set("LumaOnly", lumaOnlyW ? 1 : 0);
// Attributes.Set("wLinear", wLinearW ? 1 : 0);
// Загружаем материал с проверкой на null
var material = Material.FromShader("postprocess/ccs_tonecurves.shader");
if (material == null)
{
Log.Warning("CCSToneCurve: Material 'materials/postprocess/ccs_tonecurves.vmat' not found!");
return;
}
// Применяем эффект через современный Blit API
// Приоритет 8000 перенесен из оригинального кода
var blit = BlitMode.WithBackbuffer(material, Stage.AfterPostProcess, 8000, true);
Blit(blit, "CCSToneCurve");
}
}