From e047cc0931a22d277d7ccd14588f905d7852f7e0 Mon Sep 17 00:00:00 2001 From: Niklas Haas Date: Mon, 16 May 2016 02:44:30 +0200 Subject: vo_opengl: implement more HDR tonemapping algorithms This is now a configurable option, with tunable parameters. I got inspiration for these algorithms off wikipedia. "simple" seems to work pretty well, but not well enough to make it a reasonable default. Some other notable candidates: - Local functions (e.g. based on local contrast or gradient) - Clamp with soft knee (linear up to a point) - Mapping in CIE L*Ch. Map L smoothly, clamp C and h. - Color appearance models These will have to be implemented some other time. Note that the parameter "peak_src" to pass_tone_map should, in principle, be auto-detected from the SEI information of the source file where available. This will also have to be implemented in a later commit. --- video/out/opengl/video.c | 17 +++++++++++++---- video/out/opengl/video.h | 9 +++++++++ video/out/opengl/video_shaders.c | 40 ++++++++++++++++++++++++++++++++++++++++ video/out/opengl/video_shaders.h | 2 ++ 4 files changed, 64 insertions(+), 4 deletions(-) (limited to 'video') diff --git a/video/out/opengl/video.c b/video/out/opengl/video.c index 7470f9668d..7224e1af22 100644 --- a/video/out/opengl/video.c +++ b/video/out/opengl/video.c @@ -321,6 +321,7 @@ const struct gl_video_opts gl_video_opts_def = { .prescale_passes = 1, .prescale_downscaling_threshold = 2.0f, .target_brightness = 250, + .tone_mapping_param = NAN, }; const struct gl_video_opts gl_video_opts_hq_def = { @@ -350,6 +351,7 @@ const struct gl_video_opts gl_video_opts_hq_def = { .prescale_passes = 1, .prescale_downscaling_threshold = 2.0f, .target_brightness = 250, + .tone_mapping_param = NAN, }; static int validate_scaler_opt(struct mp_log *log, const m_option_t *opt, @@ -379,6 +381,12 @@ const struct m_sub_options gl_video_conf = { OPT_CHOICE_C("target-prim", target_prim, 0, mp_csp_prim_names), OPT_CHOICE_C("target-trc", target_trc, 0, mp_csp_trc_names), OPT_INTRANGE("target-brightness", target_brightness, 0, 1, 100000), + OPT_CHOICE("hdr-tone-mapping", hdr_tone_mapping, 0, + ({"clip", TONE_MAPPING_CLIP}, + {"simple", TONE_MAPPING_SIMPLE}, + {"gamma", TONE_MAPPING_GAMMA}, + {"linear", TONE_MAPPING_LINEAR})), + OPT_FLOAT("tone-mapping-param", tone_mapping_param, 0), OPT_FLAG("pbo", pbo, 0), SCALER_OPTS("scale", SCALER_SCALE), SCALER_OPTS("dscale", SCALER_DSCALE), @@ -2249,12 +2257,13 @@ static void pass_colormanage(struct gl_video *p, bool display_scaled, pass_linearize(p->sc, trc_src); // For HDR, the assumption of reference brightness = display brightness - // is discontinued. Instead, we have to rescale the brightness to match - // the display (and clip out-of-range values) + // is discontinued. Instead, we have to tone map the brightness to + // the display using some algorithm. if (p->image_params.gamma == MP_CSP_TRC_SMPTE_ST2084 && !display_scaled) { + GLSLF("// HDR tone mapping\n"); int reference_brightness = 10000; // As per SMPTE ST.2084 - GLSLF("color.rgb = clamp(%f * color.rgb, 0.0, 1.0);\n", - (float)reference_brightness / p->opts.target_brightness); + pass_tone_map(p->sc, reference_brightness, p->opts.target_brightness, + p->opts.hdr_tone_mapping, p->opts.tone_mapping_param); } // Adapt to the right colorspace if necessary diff --git a/video/out/opengl/video.h b/video/out/opengl/video.h index 5d8c938535..01c0cca78b 100644 --- a/video/out/opengl/video.h +++ b/video/out/opengl/video.h @@ -103,6 +103,13 @@ enum prescalers { PRESCALE_NNEDI3, }; +enum tone_mapping { + TONE_MAPPING_CLIP, + TONE_MAPPING_SIMPLE, + TONE_MAPPING_GAMMA, + TONE_MAPPING_LINEAR, +}; + struct gl_video_opts { int dumb_mode; struct scaler_config scaler[4]; @@ -112,6 +119,8 @@ struct gl_video_opts { int target_prim; int target_trc; int target_brightness; + int hdr_tone_mapping; + float tone_mapping_param; int linear_scaling; int correct_downscaling; int sigmoid_upscaling; diff --git a/video/out/opengl/video_shaders.c b/video/out/opengl/video_shaders.c index c63f32ca7d..237e1f39d2 100644 --- a/video/out/opengl/video_shaders.c +++ b/video/out/opengl/video_shaders.c @@ -313,6 +313,46 @@ void pass_delinearize(struct gl_shader_cache *sc, enum mp_csp_trc trc) } } +// Tone map from one brightness to another +void pass_tone_map(struct gl_shader_cache *sc, float peak_src, float peak_dst, + enum tone_mapping algo, float param) +{ + // First we renormalize to the output range + float scale = peak_src / peak_dst; + GLSLF("color.rgb *= vec3(%f);\n", scale); + + // Then we use some algorithm to map back to [0,1] + switch (algo) { + case TONE_MAPPING_CLIP: + GLSL(color.rgb = clamp(color.rgb, 0.0, 1.0);) + break; + + case TONE_MAPPING_SIMPLE: { + float contrast = isnan(param) ? 0.5 : param, + offset = (1.0 - contrast) / contrast; + GLSLF("color.rgb = color.rgb / (color.rgb + vec3(%f));\n", offset); + GLSLF("color.rgb *= vec3(%f);\n", (scale + offset) / scale); + break; + } + + case TONE_MAPPING_GAMMA: { + float gamma = isnan(param) ? 1.8 : param; + GLSLF("color.rgb = pow(color.rgb / vec3(%f), vec3(%f));\n", + scale, 1.0/gamma); + break; + } + + case TONE_MAPPING_LINEAR: { + float coeff = isnan(param) ? 1.0 : param; + GLSLF("color.rgb = vec3(%f) * color.rgb;\n", coeff / scale); + break; + } + + default: + abort(); + } +} + // Wide usage friendly PRNG, shamelessly stolen from a GLSL tricks forum post. // Obtain random numbers by calling rand(h), followed by h = permute(h) to // update the state. Assumes the texture was hooked. diff --git a/video/out/opengl/video_shaders.h b/video/out/opengl/video_shaders.h index 1f4496fbab..e43efadeb4 100644 --- a/video/out/opengl/video_shaders.h +++ b/video/out/opengl/video_shaders.h @@ -38,6 +38,8 @@ void pass_sample_oversample(struct gl_shader_cache *sc, struct scaler *scaler, void pass_linearize(struct gl_shader_cache *sc, enum mp_csp_trc trc); void pass_delinearize(struct gl_shader_cache *sc, enum mp_csp_trc trc); +void pass_tone_map(struct gl_shader_cache *sc, float peak_src, float peak_dst, + enum tone_mapping algo, float param); void pass_sample_deband(struct gl_shader_cache *sc, struct deband_opts *opts, AVLFG *lfg); -- cgit v1.2.3