0
0
mirror of https://github.com/obsproject/obs-studio.git synced 2024-09-20 04:42:18 +02:00

obs-filters: Add maxRGB tonemapper for SDR

Preserves saturation better than our Reinhard.
This commit is contained in:
jpark37 2023-11-11 12:44:00 -08:00 committed by Lain
parent 95cab7178e
commit 35f8481498
3 changed files with 84 additions and 24 deletions

View File

@ -4,8 +4,8 @@ uniform float4x4 ViewProj;
uniform texture2d image; uniform texture2d image;
uniform float multiplier; uniform float multiplier;
uniform float hdr_input_maximum_nits; uniform float input_maximum_nits;
uniform float hdr_output_maximum_nits; uniform float output_maximum_nits;
sampler_state textureSampler { sampler_state textureSampler {
Filter = Linear; Filter = Linear;
@ -50,10 +50,22 @@ float4 PSMaxrgb(FragData f_in) : TARGET
float4 rgba = image.Sample(textureSampler, f_in.uv); float4 rgba = image.Sample(textureSampler, f_in.uv);
rgba.rgb *= multiplier; rgba.rgb *= multiplier;
rgba.rgb = rec709_to_rec2020(rgba.rgb); rgba.rgb = rec709_to_rec2020(rgba.rgb);
rgba.rgb = maxRGB_eetf_linear_to_linear(rgba.rgb, hdr_input_maximum_nits, hdr_output_maximum_nits); rgba.rgb = maxRGB_eetf_linear_to_linear(rgba.rgb, input_maximum_nits, output_maximum_nits);
rgba.rgb = rec2020_to_rec709(rgba.rgb); rgba.rgb = rec2020_to_rec709(rgba.rgb);
float multiplier_i = 1. / multiplier; rgba.rgb *= 1. / multiplier;
rgba.rgb *= multiplier_i; return rgba;
}
float4 PSMaxrgbSdr(FragData f_in) : TARGET
{
float4 rgba = image.Sample(textureSampler, f_in.uv);
rgba.rgb *= multiplier;
rgba.rgb = rec709_to_rec2020(rgba.rgb);
rgba.rgb = maxRGB_eetf_linear_to_linear(rgba.rgb, input_maximum_nits, output_maximum_nits);
rgba.rgb = rec2020_to_rec709(rgba.rgb);
rgba.rgb *= 10000. / output_maximum_nits;
rgba.rgb = pow(saturate(rgba.rgb), float3(1. / 2.4, 1. / 2.4, 1. / 2.4));
rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb);
return rgba; return rgba;
} }
@ -74,3 +86,12 @@ technique MaxRGB
pixel_shader = PSMaxrgb(f_in); pixel_shader = PSMaxrgb(f_in);
} }
} }
technique MaxRGBSDR
{
pass
{
vertex_shader = VSHdrTonemap(v_in);
pixel_shader = PSMaxrgbSdr(f_in);
}
}

View File

@ -50,9 +50,12 @@ HdrTonemap.Description="OBS can perform HDR to SDR tone mapping automatically. O
HdrTonemap.ToneTransform="Tone Transform" HdrTonemap.ToneTransform="Tone Transform"
HdrTonemap.SdrReinhard="SDR: Reinhard" HdrTonemap.SdrReinhard="SDR: Reinhard"
HdrTonemap.HdrMaxrgb="HDR: maxRGB" HdrTonemap.HdrMaxrgb="HDR: maxRGB"
HdrTonemap.SdrMaxrgb="SDR: maxRGB"
HdrTonemap.SdrWhiteLevel="SDR White Level" HdrTonemap.SdrWhiteLevel="SDR White Level"
HdrTonemap.HdrInputMaximum="HDR Input Maximum" HdrTonemap.HdrInputMaximum="HDR Input Maximum"
HdrTonemap.HdrOutputMaximum="HDR Output Maximum" HdrTonemap.HdrOutputMaximum="HDR Output Maximum"
HdrTonemap.SdrInputMaximum="SDR Input Maximum"
HdrTonemap.SdrOutputMaximum="SDR Output Maximum"
ScrollFilter.SpeedX="Horizontal Speed" ScrollFilter.SpeedX="Horizontal Speed"
ScrollFilter.SpeedY="Vertical Speed" ScrollFilter.SpeedY="Vertical Speed"
ScrollFilter.LimitWidth="Limit Width" ScrollFilter.LimitWidth="Limit Width"

View File

@ -3,6 +3,7 @@
enum hdr_tonemap_transform { enum hdr_tonemap_transform {
TRANSFORM_SDR_REINHARD, TRANSFORM_SDR_REINHARD,
TRANSFORM_HDR_MAXRGB, TRANSFORM_HDR_MAXRGB,
TRANSFORM_SDR_MAXRGB,
}; };
struct hdr_tonemap_filter_data { struct hdr_tonemap_filter_data {
@ -10,13 +11,15 @@ struct hdr_tonemap_filter_data {
gs_effect_t *effect; gs_effect_t *effect;
gs_eparam_t *param_multiplier; gs_eparam_t *param_multiplier;
gs_eparam_t *param_hdr_input_maximum_nits; gs_eparam_t *param_input_maximum_nits;
gs_eparam_t *param_hdr_output_maximum_nits; gs_eparam_t *param_output_maximum_nits;
enum hdr_tonemap_transform transform; enum hdr_tonemap_transform transform;
float sdr_white_level_nits_i; float sdr_white_level_nits_i;
float hdr_input_maximum_nits; float hdr_input_maximum_nits;
float hdr_output_maximum_nits; float hdr_output_maximum_nits;
float sdr_input_maximum_nits;
float sdr_output_maximum_nits;
}; };
static const char *hdr_tonemap_filter_get_name(void *unused) static const char *hdr_tonemap_filter_get_name(void *unused)
@ -46,10 +49,10 @@ static void *hdr_tonemap_filter_create(obs_data_t *settings,
filter->param_multiplier = filter->param_multiplier =
gs_effect_get_param_by_name(filter->effect, "multiplier"); gs_effect_get_param_by_name(filter->effect, "multiplier");
filter->param_hdr_input_maximum_nits = gs_effect_get_param_by_name( filter->param_input_maximum_nits = gs_effect_get_param_by_name(
filter->effect, "hdr_input_maximum_nits"); filter->effect, "input_maximum_nits");
filter->param_hdr_output_maximum_nits = gs_effect_get_param_by_name( filter->param_output_maximum_nits = gs_effect_get_param_by_name(
filter->effect, "hdr_output_maximum_nits"); filter->effect, "output_maximum_nits");
obs_source_update(context, settings); obs_source_update(context, settings);
return filter; return filter;
@ -77,6 +80,10 @@ static void hdr_tonemap_filter_update(void *data, obs_data_t *settings)
(float)obs_data_get_int(settings, "hdr_input_maximum_nits"); (float)obs_data_get_int(settings, "hdr_input_maximum_nits");
filter->hdr_output_maximum_nits = filter->hdr_output_maximum_nits =
(float)obs_data_get_int(settings, "hdr_output_maximum_nits"); (float)obs_data_get_int(settings, "hdr_output_maximum_nits");
filter->sdr_input_maximum_nits =
(float)obs_data_get_int(settings, "sdr_input_maximum_nits");
filter->sdr_output_maximum_nits =
(float)obs_data_get_int(settings, "sdr_output_maximum_nits");
} }
static bool transform_changed(obs_properties_t *props, obs_property_t *p, static bool transform_changed(obs_properties_t *props, obs_property_t *p,
@ -86,13 +93,22 @@ static bool transform_changed(obs_properties_t *props, obs_property_t *p,
obs_data_get_int(settings, "transform"); obs_data_get_int(settings, "transform");
const bool reinhard = transform == TRANSFORM_SDR_REINHARD; const bool reinhard = transform == TRANSFORM_SDR_REINHARD;
const bool maxrgb = transform == TRANSFORM_HDR_MAXRGB; const bool maxrgb_hdr = transform == TRANSFORM_HDR_MAXRGB;
const bool maxrgb_sdr = transform == TRANSFORM_SDR_MAXRGB;
obs_property_set_visible( obs_property_set_visible(
obs_properties_get(props, "sdr_white_level_nits"), reinhard); obs_properties_get(props, "sdr_white_level_nits"), reinhard);
obs_property_set_visible( obs_property_set_visible(obs_properties_get(props,
obs_properties_get(props, "hdr_input_maximum_nits"), maxrgb); "hdr_input_maximum_nits"),
obs_property_set_visible( maxrgb_hdr);
obs_properties_get(props, "hdr_output_maximum_nits"), maxrgb); obs_property_set_visible(obs_properties_get(props,
"hdr_output_maximum_nits"),
maxrgb_hdr);
obs_property_set_visible(obs_properties_get(props,
"sdr_input_maximum_nits"),
maxrgb_sdr);
obs_property_set_visible(obs_properties_get(props,
"sdr_output_maximum_nits"),
maxrgb_sdr);
UNUSED_PARAMETER(p); UNUSED_PARAMETER(p);
return true; return true;
@ -113,6 +129,8 @@ static obs_properties_t *hdr_tonemap_filter_properties(void *data)
TRANSFORM_SDR_REINHARD); TRANSFORM_SDR_REINHARD);
obs_property_list_add_int(p, obs_module_text("HdrTonemap.HdrMaxrgb"), obs_property_list_add_int(p, obs_module_text("HdrTonemap.HdrMaxrgb"),
TRANSFORM_HDR_MAXRGB); TRANSFORM_HDR_MAXRGB);
obs_property_list_add_int(p, obs_module_text("HdrTonemap.SdrMaxrgb"),
TRANSFORM_SDR_MAXRGB);
obs_property_set_modified_callback(p, transform_changed); obs_property_set_modified_callback(p, transform_changed);
p = obs_properties_add_int(props, "sdr_white_level_nits", p = obs_properties_add_int(props, "sdr_white_level_nits",
@ -127,6 +145,14 @@ static obs_properties_t *hdr_tonemap_filter_properties(void *data)
props, "hdr_output_maximum_nits", props, "hdr_output_maximum_nits",
obs_module_text("HdrTonemap.HdrOutputMaximum"), 5, 10000, 1); obs_module_text("HdrTonemap.HdrOutputMaximum"), 5, 10000, 1);
obs_property_int_set_suffix(p, " nits"); obs_property_int_set_suffix(p, " nits");
p = obs_properties_add_int(
props, "sdr_input_maximum_nits",
obs_module_text("HdrTonemap.SdrInputMaximum"), 5, 10000, 1);
obs_property_int_set_suffix(p, " nits");
p = obs_properties_add_int(
props, "sdr_output_maximum_nits",
obs_module_text("HdrTonemap.SdrOutputMaximum"), 5, 10000, 1);
obs_property_int_set_suffix(p, " nits");
UNUSED_PARAMETER(data); UNUSED_PARAMETER(data);
return props; return props;
@ -138,6 +164,8 @@ static void hdr_tonemap_filter_defaults(obs_data_t *settings)
obs_data_set_default_int(settings, "sdr_white_level_nits", 300); obs_data_set_default_int(settings, "sdr_white_level_nits", 300);
obs_data_set_default_int(settings, "hdr_input_maximum_nits", 4000); obs_data_set_default_int(settings, "hdr_input_maximum_nits", 4000);
obs_data_set_default_int(settings, "hdr_output_maximum_nits", 1000); obs_data_set_default_int(settings, "hdr_output_maximum_nits", 1000);
obs_data_set_default_int(settings, "sdr_input_maximum_nits", 1000);
obs_data_set_default_int(settings, "sdr_output_maximum_nits", 300);
} }
static void hdr_tonemap_filter_render(void *data, gs_effect_t *effect) static void hdr_tonemap_filter_render(void *data, gs_effect_t *effect)
@ -173,19 +201,26 @@ static void hdr_tonemap_filter_render(void *data, gs_effect_t *effect)
gs_effect_set_float(filter->param_multiplier, gs_effect_set_float(filter->param_multiplier,
multiplier); multiplier);
gs_effect_set_float( gs_effect_set_float(
filter->param_hdr_input_maximum_nits, filter->param_input_maximum_nits,
filter->hdr_input_maximum_nits); (filter->transform == TRANSFORM_SDR_MAXRGB)
? filter->sdr_input_maximum_nits
: filter->hdr_input_maximum_nits);
gs_effect_set_float( gs_effect_set_float(
filter->param_hdr_output_maximum_nits, filter->param_output_maximum_nits,
filter->hdr_output_maximum_nits); (filter->transform == TRANSFORM_SDR_MAXRGB)
? filter->sdr_output_maximum_nits
: filter->hdr_output_maximum_nits);
gs_blend_state_push(); gs_blend_state_push();
gs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA); gs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA);
const char *const tech_name = const char *const tech_name =
(filter->transform == TRANSFORM_HDR_MAXRGB) (filter->transform == TRANSFORM_SDR_REINHARD)
? "MaxRGB" ? "Reinhard"
: "Reinhard"; : ((filter->transform ==
TRANSFORM_HDR_MAXRGB)
? "MaxRGB"
: "MaxRGBSDR");
obs_source_process_filter_tech_end(filter->context, obs_source_process_filter_tech_end(filter->context,
filter->effect, 0, 0, filter->effect, 0, 0,
tech_name); tech_name);
@ -217,7 +252,8 @@ hdr_tonemap_filter_get_color_space(void *data, size_t count,
enum gs_color_space space = source_space; enum gs_color_space space = source_space;
if (source_space == GS_CS_709_EXTENDED || source_space == GS_CS_SRGB) { if (source_space == GS_CS_709_EXTENDED || source_space == GS_CS_SRGB) {
if (filter->transform == TRANSFORM_SDR_REINHARD) { if ((filter->transform == TRANSFORM_SDR_REINHARD) ||
filter->transform == TRANSFORM_SDR_MAXRGB) {
space = GS_CS_SRGB; space = GS_CS_SRGB;
for (size_t i = 0; i < count; ++i) { for (size_t i = 0; i < count; ++i) {
if (preferred_spaces[i] != GS_CS_SRGB) { if (preferred_spaces[i] != GS_CS_SRGB) {