#ifndef GAMMA_MANAGEMENT_H #define GAMMA_MANAGEMENT_H /////////////////////////////// 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 bool assume_opaque_alpha = false; #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 bool linearize_input = true; float get_pass_input_gamma() { return get_input_gamma(); } #else bool linearize_input = false; float get_pass_input_gamma() { return 1.0; } #endif #ifdef LAST_PASS bool gamma_encode_output = true; float get_pass_output_gamma() { return get_output_gamma(); } #else bool gamma_encode_output = false; float get_pass_output_gamma() { return 1.0; } #endif #else bool linearize_input = true; bool gamma_encode_output = true; #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) { if(linearize_input = true) { if(assume_opaque_alpha = true) { 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) { if(gamma_encode_output = true) { if(assume_opaque_alpha = true) { 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) //{ return decode_input(vec4(texture(tex, tex_coords, texel_off))); } #endif // GAMMA_MANAGEMENT_H