2016-08-27 02:28:24 +10:00
|
|
|
#ifndef GAMMA_MANAGEMENT_H
|
|
|
|
#define GAMMA_MANAGEMENT_H
|
|
|
|
|
2016-08-20 06:26:12 +10:00
|
|
|
/////////////////////////////// BASE CONSTANTS ///////////////////////////////
|
|
|
|
|
|
|
|
// Set standard gamma constants, but allow users to override them:
|
|
|
|
#ifndef OVERRIDE_STANDARD_GAMMA
|
|
|
|
// Standard encoding gammas:
|
|
|
|
const float ntsc_gamma = 2.2; // Best to use NTSC for PAL too?
|
|
|
|
const float pal_gamma = 2.8; // Never actually 2.8 in practice
|
|
|
|
// Typical device decoding gammas (only use for emulating devices):
|
|
|
|
// CRT/LCD reference gammas are higher than NTSC and Rec.709 video standard
|
|
|
|
// gammas: The standards purposely undercorrected for an analog CRT's
|
|
|
|
// assumed 2.5 reference display gamma to maintain contrast in assumed
|
|
|
|
// [dark] viewing conditions: http://www.poynton.com/PDFs/GammaFAQ.pdf
|
|
|
|
// These unstated assumptions about display gamma and perceptual rendering
|
|
|
|
// intent caused a lot of confusion, and more modern CRT's seemed to target
|
|
|
|
// NTSC 2.2 gamma with circuitry. LCD displays seem to have followed suit
|
|
|
|
// (they struggle near black with 2.5 gamma anyway), especially PC/laptop
|
|
|
|
// displays designed to view sRGB in bright environments. (Standards are
|
|
|
|
// also in flux again with BT.1886, but it's underspecified for displays.)
|
|
|
|
const float crt_reference_gamma_high = 2.5; // In (2.35, 2.55)
|
|
|
|
const float crt_reference_gamma_low = 2.35; // In (2.35, 2.55)
|
|
|
|
const float lcd_reference_gamma = 2.5; // To match CRT
|
|
|
|
const float crt_office_gamma = 2.2; // Circuitry-adjusted for NTSC
|
|
|
|
const float lcd_office_gamma = 2.2; // Approximates sRGB
|
|
|
|
#endif // OVERRIDE_STANDARD_GAMMA
|
|
|
|
|
|
|
|
// Assuming alpha == 1.0 might make it easier for users to avoid some bugs,
|
|
|
|
// but only if they're aware of it.
|
|
|
|
#ifndef OVERRIDE_ALPHA_ASSUMPTIONS
|
2016-09-02 02:26:09 +10:00
|
|
|
bool assume_opaque_alpha = false;
|
2016-08-20 06:26:12 +10:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////// DERIVED CONSTANTS AS FUNCTIONS ///////////////////////
|
|
|
|
|
|
|
|
// gamma-management.h should be compatible with overriding gamma values with
|
|
|
|
// runtime user parameters, but we can only define other global constants in
|
|
|
|
// terms of static constants, not uniform user parameters. To get around this
|
|
|
|
// limitation, we need to define derived constants using functions.
|
|
|
|
|
|
|
|
// Set device gamma constants, but allow users to override them:
|
|
|
|
#ifdef OVERRIDE_DEVICE_GAMMA
|
|
|
|
// The user promises to globally define the appropriate constants:
|
|
|
|
float get_crt_gamma() { return crt_gamma; }
|
|
|
|
float get_gba_gamma() { return gba_gamma; }
|
|
|
|
float get_lcd_gamma() { return lcd_gamma; }
|
|
|
|
#else
|
|
|
|
float get_crt_gamma() { return crt_reference_gamma_high; }
|
|
|
|
float get_gba_gamma() { return 3.5; } // Game Boy Advance; in (3.0, 4.0)
|
|
|
|
float get_lcd_gamma() { return lcd_office_gamma; }
|
|
|
|
#endif // OVERRIDE_DEVICE_GAMMA
|
|
|
|
|
|
|
|
// Set decoding/encoding gammas for the first/lass passes, but allow overrides:
|
|
|
|
#ifdef OVERRIDE_FINAL_GAMMA
|
|
|
|
// The user promises to globally define the appropriate constants:
|
|
|
|
float get_intermediate_gamma() { return intermediate_gamma; }
|
|
|
|
float get_input_gamma() { return input_gamma; }
|
|
|
|
float get_output_gamma() { return output_gamma; }
|
|
|
|
#else
|
|
|
|
// If we gamma-correct every pass, always use ntsc_gamma between passes to
|
|
|
|
// ensure middle passes don't need to care if anything is being simulated:
|
|
|
|
float get_intermediate_gamma() { return ntsc_gamma; }
|
|
|
|
#ifdef SIMULATE_CRT_ON_LCD
|
|
|
|
float get_input_gamma() { return get_crt_gamma(); }
|
|
|
|
float get_output_gamma() { return get_lcd_gamma(); }
|
|
|
|
#else
|
|
|
|
#ifdef SIMULATE_GBA_ON_LCD
|
|
|
|
float get_input_gamma() { return get_gba_gamma(); }
|
|
|
|
float get_output_gamma() { return get_lcd_gamma(); }
|
|
|
|
#else
|
|
|
|
#ifdef SIMULATE_LCD_ON_CRT
|
|
|
|
float get_input_gamma() { return get_lcd_gamma(); }
|
|
|
|
float get_output_gamma() { return get_crt_gamma(); }
|
|
|
|
#else
|
|
|
|
#ifdef SIMULATE_GBA_ON_CRT
|
|
|
|
float get_input_gamma() { return get_gba_gamma(); }
|
|
|
|
float get_output_gamma() { return get_crt_gamma(); }
|
|
|
|
#else // Don't simulate anything:
|
|
|
|
float get_input_gamma() { return ntsc_gamma; }
|
|
|
|
float get_output_gamma() { return ntsc_gamma; }
|
|
|
|
#endif // SIMULATE_GBA_ON_CRT
|
|
|
|
#endif // SIMULATE_LCD_ON_CRT
|
|
|
|
#endif // SIMULATE_GBA_ON_LCD
|
|
|
|
#endif // SIMULATE_CRT_ON_LCD
|
|
|
|
#endif // OVERRIDE_FINAL_GAMMA
|
|
|
|
|
|
|
|
#ifndef GAMMA_ENCODE_EVERY_FBO
|
|
|
|
#ifdef FIRST_PASS
|
2016-09-02 02:26:09 +10:00
|
|
|
bool linearize_input = true;
|
2016-08-20 06:26:12 +10:00
|
|
|
float get_pass_input_gamma() { return get_input_gamma(); }
|
|
|
|
#else
|
2016-09-02 02:26:09 +10:00
|
|
|
bool linearize_input = false;
|
2016-08-20 06:26:12 +10:00
|
|
|
float get_pass_input_gamma() { return 1.0; }
|
|
|
|
#endif
|
|
|
|
#ifdef LAST_PASS
|
2016-09-02 02:26:09 +10:00
|
|
|
bool gamma_encode_output = true;
|
2016-08-20 06:26:12 +10:00
|
|
|
float get_pass_output_gamma() { return get_output_gamma(); }
|
|
|
|
#else
|
2016-09-02 02:26:09 +10:00
|
|
|
bool gamma_encode_output = false;
|
2016-08-20 06:26:12 +10:00
|
|
|
float get_pass_output_gamma() { return 1.0; }
|
|
|
|
#endif
|
|
|
|
#else
|
2016-09-02 02:26:09 +10:00
|
|
|
bool linearize_input = true;
|
|
|
|
bool gamma_encode_output = true;
|
2016-08-20 06:26:12 +10:00
|
|
|
#ifdef FIRST_PASS
|
|
|
|
float get_pass_input_gamma() { return get_input_gamma(); }
|
|
|
|
#else
|
|
|
|
float get_pass_input_gamma() { return get_intermediate_gamma(); }
|
|
|
|
#endif
|
|
|
|
#ifdef LAST_PASS
|
|
|
|
float get_pass_output_gamma() { return get_output_gamma(); }
|
|
|
|
#else
|
|
|
|
float get_pass_output_gamma() { return get_intermediate_gamma(); }
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
vec4 decode_input(const vec4 color)
|
|
|
|
{
|
2016-09-02 02:26:09 +10:00
|
|
|
if(linearize_input = true)
|
2016-08-20 06:26:12 +10:00
|
|
|
{
|
2016-09-02 02:26:09 +10:00
|
|
|
if(assume_opaque_alpha = true)
|
2016-08-20 06:26:12 +10:00
|
|
|
{
|
|
|
|
return vec4(pow(color.rgb, vec3(get_pass_input_gamma())), 1.0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return vec4(pow(color.rgb, vec3(get_pass_input_gamma())), color.a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return color;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
vec4 encode_output(const vec4 color)
|
|
|
|
{
|
2016-09-02 02:26:09 +10:00
|
|
|
if(gamma_encode_output = true)
|
2016-08-20 06:26:12 +10:00
|
|
|
{
|
2016-09-02 02:26:09 +10:00
|
|
|
if(assume_opaque_alpha = true)
|
2016-08-20 06:26:12 +10:00
|
|
|
{
|
|
|
|
return vec4(pow(color.rgb, vec3(1.0/get_pass_output_gamma())), 1.0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return vec4(pow(color.rgb, vec3(1.0/get_pass_output_gamma())), color.a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return color;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define tex2D_linearize(C, D) decode_input(vec4(texture(C, D)))
|
|
|
|
//vec4 tex2D_linearize(const sampler2D tex, const vec2 tex_coords)
|
|
|
|
//{ return decode_input(vec4(texture(tex, tex_coords))); }
|
|
|
|
|
|
|
|
//#define tex2D_linearize(C, D, E) decode_input(vec4(texture(C, D, E)))
|
|
|
|
//vec4 tex2D_linearize(const sampler2D tex, const vec2 tex_coords, const int texel_off)
|
2016-08-27 02:28:24 +10:00
|
|
|
//{ return decode_input(vec4(texture(tex, tex_coords, texel_off))); }
|
|
|
|
|
|
|
|
#endif // GAMMA_MANAGEMENT_H
|