summaryrefslogtreecommitdiffstats
path: root/video/out/opengl/video_shaders.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/opengl/video_shaders.c')
-rw-r--r--video/out/opengl/video_shaders.c90
1 files changed, 82 insertions, 8 deletions
diff --git a/video/out/opengl/video_shaders.c b/video/out/opengl/video_shaders.c
index 14120d434b..f56c687903 100644
--- a/video/out/opengl/video_shaders.c
+++ b/video/out/opengl/video_shaders.c
@@ -368,6 +368,81 @@ void pass_delinearize(struct gl_shader_cache *sc, enum mp_csp_trc trc)
}
}
+// Apply the OOTF mapping from a given light type to display-referred light.
+// The extra peak parameter is used to scale the values before and after
+// the OOTF, and can be inferred using mp_trc_nom_peak
+void pass_ootf(struct gl_shader_cache *sc, enum mp_csp_light light, float peak)
+{
+ if (light == MP_CSP_LIGHT_DISPLAY)
+ return;
+
+ GLSLF("// apply ootf\n", sc);
+ GLSLF("color.rgb *= vec3(%f);\n", peak);
+
+ switch (light)
+ {
+ case MP_CSP_LIGHT_SCENE_HLG:
+ // HLG OOTF from BT.2100, assuming a reference display with a
+ // peak of 1000 cd/m² -> gamma = 1.2
+ GLSL(float luma = dot(color.rgb, vec3(0.2627, 0.6780, 0.0593));)
+ GLSLF("color.rgb *= vec3(%f * pow(luma, 0.2));\n",
+ (1000 / MP_REF_WHITE) / pow(12, 1.2));
+ break;
+ case MP_CSP_LIGHT_SCENE_709_1886:
+ // This OOTF is defined by encoding the result as 709 and then decoding
+ // it as 1886; although this is called 709_1886 we actually use the
+ // more precise (by one decimal) values from BT.2020 instead
+ GLSL(color.rgb = mix(color.rgb * vec3(4.5),
+ vec3(1.0993) * pow(color.rgb, vec3(0.45)) - vec3(0.0993),
+ lessThan(vec3(0.0181), color.rgb));)
+ GLSL(color.rgb = pow(color.rgb, vec3(2.4));)
+ break;
+ case MP_CSP_LIGHT_SCENE_1_2:
+ GLSL(color.rgb = pow(color.rgb, vec3(1.2));)
+ break;
+ default:
+ abort();
+ }
+
+ GLSLF("color.rgb /= vec3(%f);\n", peak);
+}
+
+// Inverse of the function pass_ootf, for completeness' sake. Note that the
+// inverse OOTF for MP_CSP_LIGHT_SCENE_HLG has no analytical solution and is
+// therefore unimplemented. Care must be used to never call this function
+// in that way.(In principle, a iterative algorithm can approach
+// the solution numerically, but this is tricky and we don't really need it
+// since mpv currently only supports outputting display-referred light)
+void pass_inverse_ootf(struct gl_shader_cache *sc, enum mp_csp_light light, float peak)
+{
+ if (light == MP_CSP_LIGHT_DISPLAY)
+ return;
+
+ GLSLF("// apply inverse ootf\n");
+ GLSLF("color.rgb *= vec3(%f);\n", peak);
+
+ switch (light)
+ {
+ case MP_CSP_LIGHT_SCENE_HLG:
+ // Has no analytical solution
+ abort();
+ break;
+ case MP_CSP_LIGHT_SCENE_709_1886:
+ GLSL(color.rgb = pow(color.rgb, vec3(1/2.4));)
+ GLSL(color.rgb = mix(color.rgb / vec3(4.5),
+ pow((color.rgb + vec3(0.0993)) / vec3(1.0993), vec3(1/0.45)),
+ lessThan(vec3(0.08145), color.rgb));)
+ break;
+ case MP_CSP_LIGHT_SCENE_1_2:
+ GLSL(color.rgb = pow(color.rgb, vec3(1/1.2));)
+ break;
+ default:
+ abort();
+ }
+
+ GLSLF("color.rgb /= vec3(%f);\n", peak);
+}
+
// Tone map from a known peak brightness to the range [0,1]
static void pass_tone_map(struct gl_shader_cache *sc, float ref_peak,
enum tone_mapping algo, float param)
@@ -452,20 +527,16 @@ void pass_color_map(struct gl_shader_cache *sc,
bool need_gamma = src.gamma != dst.gamma ||
src.primaries != dst.primaries ||
src_range != dst_range ||
- src.sig_peak > dst_range;
+ src.sig_peak > dst_range ||
+ src.light != dst.light;
if (need_gamma && !is_linear) {
pass_linearize(sc, src.gamma);
is_linear= true;
}
- // NOTE: When src.gamma = MP_CSP_TRC_ARIB_STD_B67, we would technically
- // need to apply the reference OOTF as part of the EOTF (which is what we
- // implement with pass_linearize), since HLG considers OOTF to be part of
- // the display's EOTF (as opposed to the camera's OETF) - although arguably
- // in our case this would be part of the ICC profile, not mpv. Either way,
- // in case somebody ends up complaining about HLG looking different from a
- // reference HLG display, this comment might be why.
+ if (src.light != dst.light)
+ pass_ootf(sc, src.light, mp_trc_nom_peak(src.gamma));
// Rescale the signal to compensate for differences in the encoding range
// and reference white level. This is necessary because of how mpv encodes
@@ -490,6 +561,9 @@ void pass_color_map(struct gl_shader_cache *sc,
GLSL(color.rgb = cms_matrix * color.rgb;)
}
+ if (src.light != dst.light)
+ pass_inverse_ootf(sc, dst.light, mp_trc_nom_peak(dst.gamma));
+
if (is_linear)
pass_delinearize(sc, dst.gamma);
}