A post-processing component that implements a color keyer/overlay. It exposes properties for key color, image texture, blending, transform and options, sets shader attributes, loads a material and issues a blit to apply the effect.
using Sandbox;
using Sandbox.Rendering;
/// <summary>
/// Key out a color from the image or apply an image overlay on top of the screen.
/// </summary>
[Title( "Color Keyer" )]
[Category( "Post Processing" )]
[Icon( "colorize" )]
public sealed class CCSColorKey : BasePostProcess<CCSColorKey>
{
/// <summary>
/// Color to key out.
/// </summary>
[Property, Title("Key Color")]
public Color keyColor { get; set; } = new Color(0.0f, 0.83f, 0.0f, 1.0f);
/// <summary>
/// Texture to use for the image to be keyed / overlaid.
/// </summary>
[Property]
public Texture image_texture { get; set; }
// === Blending ===
/// <summary>
/// Background blend tolerance.
/// </summary>
// ИСПРАВЛЕНО: Убраны лишние аргументы (0, true) и [Header]
[Property, Title("Background Blend"), Range(0.0f, 1.0f)]
public float bBlend { get; set; } = 0.203f;
/// <summary>
/// Foreground blend tolerance.
/// </summary>
[Property, Title("Foreground Blend"), Range(0.0f, 1.0f)]
public float fBlend { get; set; } = 0.203f;
/// <summary>
/// Adjusts the opacity of the area which is keyed out.
/// </summary>
[Property, Title("Mask Opacity"), Range(0.0f, 1.0f)]
public float bOpacity { get; set; } = 1.0f;
/// <summary>
/// Adjusts the opacity of the main image.
/// </summary>
[Property, Title("Foreground Opacity"), Range(0.0f, 1.0f)]
public float fOpacity { get; set; } = 1.0f;
// === Image Transform ===
/// <summary>
/// Horizontal scale of the image.
/// </summary>
[Property, Title("Horizontal Scale"), Range(0.01f, 20.0f)]
public float xScale { get; set; } = 1.0f;
/// <summary>
/// Vertical scale of the image.
/// </summary>
[Property, Title("Vertical Scale"), Range(0.01f, 20.0f)]
public float yScale { get; set; } = 1.0f;
/// <summary>
/// Horizontal image position offset from center.
/// </summary>
[Property, Title("Horizontal Position"), Range(-1.0f, 1.0f)]
public float xPos { get; set; } = 0.0f;
/// <summary>
/// Vertical image position offset from center.
/// </summary>
[Property, Title("Vertical Position"), Range(-1.0f, 1.0f)]
public float yPos { get; set; } = 0.0f;
// === Extras ===
/// <summary>
/// Swap foreground and background layers.
/// </summary>
[Property, Title("Overlay Mode")]
public bool kSwap { get; set; }
/// <summary>
/// Allow the image texture to repeat.
/// </summary>
[Property, Title("Tile Image")]
public bool iTile { get; set; } = true;
/// <summary>
/// Don't color key at all, just overlay.
/// </summary>
[Property, Title("Disable Keying")]
public bool dKey { get; set; }
/// <summary>
/// Blend mode for compositing. Based on Photoshop/Clip Studio formulas.
/// </summary>
[Property, Title("Blend Mode")]
public BlendMode BlendModeSelection { get; set; } = BlendMode.Normal;
public enum BlendMode
{
[Description("Normal Opacity blending")]
Normal,
[Description("Light areas become Brighter (Linear Dodge)")]
Additive,
[Description("Subtract blend layer from base")]
Subtract,
[Description("Multiply colors together")]
Multiply,
[Description("Divide base by blend")]
Divide,
[Description("Screen blend - lighter result")]
Screen,
[Description("Color Dodge - brightens base using blend color")]
ColorDodge
}
public override void Render()
{
// Float значения через GetWeighted для поддержки PostProcess Volume
float bBlendW = GetWeighted(x => x.bBlend);
float fBlendW = GetWeighted(x => x.fBlend);
float bOpacityW = GetWeighted(x => x.bOpacity);
float fOpacityW = GetWeighted(x => x.fOpacity);
float xScaleW = GetWeighted(x => x.xScale);
float yScaleW = GetWeighted(x => x.yScale);
float xPosW = GetWeighted(x => x.xPos);
float yPosW = GetWeighted(x => x.yPos);
// Bool, Color, Texture, Enum берем напрямую
Color keyColorW = keyColor;
Texture imageTextureW = image_texture;
bool kSwapW = kSwap;
bool iTileW = iTile;
bool dKeyW = dKey;
BlendMode blendModeW = BlendModeSelection;
// Передаем атрибуты в шейдер
Attributes.Set("keyColor", new Vector4(keyColorW.r, keyColorW.g, keyColorW.b, keyColorW.a));
Attributes.Set("image_texture", imageTextureW);
Attributes.Set("bBlend", bBlendW);
Attributes.Set("fBlend", fBlendW);
Attributes.Set("bOpacity", bOpacityW);
Attributes.Set("fOpacity", fOpacityW);
Attributes.Set("xScale", xScaleW);
Attributes.Set("yScale", yScaleW);
Attributes.Set("xPos", xPosW);
Attributes.Set("yPos", yPosW);
// Bool → int (0/1) для надежной работы с HLSL
Attributes.Set("kSwap", kSwapW ? 1 : 0);
Attributes.Set("iTile", iTileW ? 1 : 0);
Attributes.Set("dKey", dKeyW ? 1 : 0);
Attributes.Set("BlendMode", (int)blendModeW);
// Загружаем материал с проверкой на null
var material = Material.FromShader("postprocess/ccs_colorkey.shader");
if (material == null)
{
Log.Warning("CCSColorKey: Material 'materials/postprocess/ccs_colorkey.vmat' not found!");
return;
}
// Применяем эффект через современный Blit API
// Приоритет 2001 перенесен из оригинального кода
var blit = BlitMode.WithBackbuffer(material, Stage.AfterPostProcess, 2001, true);
Blit(blit, "CCSColorKey");
}
}