Skip to content

Afterglow effect #28

@chrisgleissner

Description

@chrisgleissner

Extend crt_effect.effect — Physically Based Phosphor Afterglow

This PR extends crt_effect.effect to implement a physically accurate CRT phosphor afterglow model. It replaces the // TODO afterglow placeholder with a decaying-maximum luminance function matching real phosphor persistence.


Shader Update (insert at existing TODO in crt_effect.effect)

// -----------------------------------------------------------------------------
// CRT phosphor afterglow: decaying-maximum persistence model
// -----------------------------------------------------------------------------

uniform float afterglow_duration_ms <
    string name = "Afterglow Duration (ms)";
    string description = "Duration of phosphor persistence in milliseconds";
    float minimum = 0.0;
    float maximum = 3000.0;
    float step = 10.0;
> = 500.0;

uniform int afterglow_curve <
    string name = "Afterglow Curve";
    string description = "Decay curve shaping (Instant, Gradual, Rapid, Long Tail)";
> = 1;

uniform texture2d image;
uniform texture2d prev_frame;
uniform float elapsed_time;

sampler_state linear_clamp {
    Filter = Linear;
    AddressU = Clamp;
    AddressV = Clamp;
};

// ApplyAfterglow — integrates phosphor persistence model
float3 ApplyAfterglow(float2 uv, float3 current_rgb)
{
    float4 prev = prev_frame.Sample(linear_clamp, uv);
    float duration_s = max(afterglow_duration_ms, 1.0) / 1000.0;
    float3 tau = float3(0.6, 0.8, 0.3) * duration_s;

    float curve_scale = 1.0;
    switch (afterglow_curve) {
        case 0: curve_scale = 4.0;  break; // InstantFade
        case 1: curve_scale = 1.0;  break; // GradualFade
        case 2: curve_scale = 2.0;  break; // RapidFade
        case 3: curve_scale = 0.5;  break; // LongTail
    }
    tau *= curve_scale;

    float3 decay = exp(-elapsed_time / max(tau, 0.001));
    float3 persisted = max(current_rgb, prev.rgb * decay);
    return persisted;
}

In your fragment shader main function, modify:

float4 color = image.Sample(linear_clamp, inp.uv);

to:

float3 rgb = image.Sample(linear_clamp, inp.uv).rgb;
rgb = ApplyAfterglow(inp.uv, rgb);
float4 color = float4(rgb, 1.0);

Re-enable Plugin Properties

obs_property_t *afterglow_duration_prop = obs_properties_add_int_slider(
    effects_props, "afterglow_duration_ms",
    obs_module_text("AfterglowDuration"), 0, 3000, 10);
obs_property_set_long_description(afterglow_duration_prop,
    obs_module_text("AfterglowDuration.Description"));

obs_property_t *afterglow_curve_prop = obs_properties_add_list(
    effects_props, "afterglow_curve", obs_module_text("AfterglowCurve"),
    OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
obs_property_list_add_int(afterglow_curve_prop, obs_module_text("AfterglowCurve.InstantFade"), 0);
obs_property_list_add_int(afterglow_curve_prop, obs_module_text("AfterglowCurve.GradualFade"), 1);
obs_property_list_add_int(afterglow_curve_prop, obs_module_text("AfterglowCurve.RapidFade"), 2);
obs_property_list_add_int(afterglow_curve_prop, obs_module_text("AfterglowCurve.LongTail"), 3);
obs_property_set_long_description(afterglow_curve_prop,
    obs_module_text("AfterglowCurve.Description"));

Apply parameters during rendering:

float duration_ms = (float)obs_data_get_int(settings, "afterglow_duration_ms");
int curve = (int)obs_data_get_int(settings, "afterglow_curve");
gs_effect_set_float(gs_effect_get_param_by_name(effect, "afterglow_duration_ms"), duration_ms);
gs_effect_set_int(gs_effect_get_param_by_name(effect, "afterglow_curve"), curve);

LLM Integration Instruction

You are updating crt_effect.effect to implement a physically based phosphor afterglow. Locate the // TODO for afterglow. Insert the ApplyAfterglow() function and modify the main fragment shader to call it. Ensure uniforms: afterglow_duration_ms, afterglow_curve, image, prev_frame, elapsed_time exist. Do not alter unrelated code or passes. Add comment: // CRT phosphor afterglow: decaying-maximum persistence model.


Summary

  • Integrates true CRT phosphor persistence into existing CRT shader.
  • Uses per-channel exponential decay and configurable duration/curve.
  • Re-enables and maps existing OBS UI properties.
  • Backwards-compatible and localized via existing text keys.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions