diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index 7c8f602045..4d2be6ad66 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -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 diff --git a/video/out/opengl/video.c b/video/out/opengl/video.c index 0bfba10ae1..4a91db69f0 100644 --- a/video/out/opengl/video.c +++ b/video/out/opengl/video.c @@ -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}, diff --git a/video/out/opengl/video.h b/video/out/opengl/video.h index 236ab067c4..3b6533e0ef 100644 --- a/video/out/opengl/video.h +++ b/video/out/opengl/video.h @@ -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, diff --git a/video/out/opengl/video_shaders.c b/video/out/opengl/video_shaders.c index 5421589d06..95ac712f05 100644 --- a/video/out/opengl/video_shaders.c +++ b/video/out/opengl/video_shaders.c @@ -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;