vo_opengl: add new HDR tone mapping algorithm

I call it `mobius` because apparently the form f(x) = (cx+a)/(dx+b) is
called a Möbius transform, which is the algorithm this is based on. In
the extremes it becomes `reinhard` (param=0.0 and `clip` (param=1.0),
smoothly transitioning between the two depending on the parameter.

This is a useful tone mapping algorithm since the tunable mobius
transform allows the user to decide the trade-off between color accuracy
and detail preservation on a continuous scale. The default of 0.3 is
already far more accurate than `reinhard` while also being reasonably
good at preserving highlights, without suffering from the overall
brightness drop and color distortion of `hable`.

For these reasons, make this the new default. Also expand and improve
the documentation for these tone mapping functions.
This commit is contained in:
Niklas Haas 2017-06-09 09:16:06 +02:00 committed by Ricardo Constantino
parent 30cd963b25
commit d8a3b10f45
No known key found for this signature in database
GPG Key ID: EFD16019AE4FF531
4 changed files with 41 additions and 4 deletions

View File

@ -4645,13 +4645,27 @@ The following video options are currently all specific to ``--vo=opengl`` and
display. Valid values are:
clip
Hard-clip any out-of-range values.
Hard-clip any out-of-range values. Use this when you care about
perfect color accuracy for in-range values at the cost of completely
distorting out-of-range values. Not generally recommended.
mobius
Generalization of Reinhard to a Möbius transform with linear section.
Smoothly maps out-of-range values while retaining contrast and colors
for in-range material as much as possible. Use this when you care about
color accuracy more than detail preservation. This is somewhere in
between ``clip`` and ``reinhard``, depending on the value of
``--tone-mapping-param``. (default)
reinhard
Reinhard tone mapping algorithm. Very simple continuous curve.
Preserves dynamic range and peak but uses nonlinear contrast.
Preserves overall image brightness but uses nonlinear contrast, which
results in flattening of details and degradation in color accuracy.
hable
Similar to ``reinhard`` but preserves dark contrast better (slightly
sigmoidal). Developed by John Hable for use in video games. (default)
Similar to ``reinhard`` but preserves both dark and bright details
better (slightly sigmoidal), at the cost of slightly darkening
everything. Developed by John Hable for use in video games. Use this
when you care about detail preservation more than color/brightness
accuracy. This is roughly equivalent to
``--hdr-tone-mapping=reinhard --tone-mapping-param=0.24``.
gamma
Fits a logarithmic transfer between the tone curves.
linear
@ -4662,6 +4676,12 @@ The following video options are currently all specific to ``--vo=opengl`` and
Set tone mapping parameters. Ignored if the tone mapping algorithm is not
tunable. This affects the following tone mapping algorithms:
mobius
Specifies the transition point from linear to mobius transform. Every
value below this point is guaranteed to be mapped 1:1. The higher the
value, the more accurate the result will be, at the cost of losing
bright details. Defaults to 0.3, which due to the steep initial slope
still preserves in-range colors fairly accurately.
reinhard
Specifies the local contrast coefficient at the display peak. Defaults
to 0.5, which means that in-gamut values will be about half as bright

View File

@ -328,6 +328,7 @@ const struct m_sub_options gl_video_conf = {
OPT_INTRANGE("target-brightness", target_brightness, 0, 1, 100000),
OPT_CHOICE("hdr-tone-mapping", hdr_tone_mapping, 0,
({"clip", TONE_MAPPING_CLIP},
{"mobius", TONE_MAPPING_MOBIUS},
{"reinhard", TONE_MAPPING_REINHARD},
{"hable", TONE_MAPPING_HABLE},
{"gamma", TONE_MAPPING_GAMMA},

View File

@ -91,6 +91,7 @@ enum blend_subs_mode {
enum tone_mapping {
TONE_MAPPING_CLIP,
TONE_MAPPING_MOBIUS,
TONE_MAPPING_REINHARD,
TONE_MAPPING_HABLE,
TONE_MAPPING_GAMMA,

View File

@ -377,6 +377,21 @@ static void pass_tone_map(struct gl_shader_cache *sc, float ref_peak,
GLSL(color.rgb = clamp(color.rgb, 0.0, 1.0);)
break;
case TONE_MAPPING_MOBIUS: {
float j = isnan(param) ? 0.3 : param;
// solve for M(j) = j; M(ref_peak) = 1.0; M'(j) = 1.0
// where M(x) = scale * (x+a)/(x+b)
float a = -j*j * (ref_peak - 1) / (j*j - 2*j + ref_peak),
b = (j*j - 2*j*ref_peak + ref_peak) / (ref_peak - 1);
GLSLF("color.rgb = mix(vec3(%f) * (color.rgb + vec3(%f))\n"
" / (color.rgb + vec3(%f)),\n"
" color.rgb,\n"
" lessThanEqual(color.rgb, vec3(%f)));\n",
(b*b + 2*b*j + j*j) / (b - a), a, b, j);
break;
}
case TONE_MAPPING_REINHARD: {
float contrast = isnan(param) ? 0.5 : param,
offset = (1.0 - contrast) / contrast;