#ifndef GAMMA_MANAGEMENT_H #define GAMMA_MANAGEMENT_H ///////////////////////////////// MIT LICENSE //////////////////////////////// // Copyright (C) 2014 TroggleMonkey // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. ///////////////////////////////// DESCRIPTION //////////////////////////////// // This file provides gamma-aware tex*D*() and encode_output() functions. // Requires: Before #include-ing this file, the including file must #define // the following macros when applicable and follow their rules: // 1.) #define FIRST_PASS if this is the first pass. // 2.) #define LAST_PASS if this is the last pass. // 3.) If sRGB is available, set srgb_framebufferN = "true" for // every pass except the last in your .cgp preset. // 4.) If sRGB isn't available but you want gamma-correctness with // no banding, #define GAMMA_ENCODE_EVERY_FBO each pass. // 5.) #define SIMULATE_CRT_ON_LCD if desired (precedence over 5-7) // 6.) #define SIMULATE_GBA_ON_LCD if desired (precedence over 6-7) // 7.) #define SIMULATE_LCD_ON_CRT if desired (precedence over 7) // 8.) #define SIMULATE_GBA_ON_CRT if desired (precedence over -) // If an option in [5, 8] is #defined in the first or last pass, it // should be #defined for both. It shouldn't make a difference // whether it's #defined for intermediate passes or not. // Optional: The including file (or an earlier included file) may optionally // #define a number of macros indicating it will override certain // macros and associated constants are as follows: // static constants with either static or uniform constants. The // 1.) OVERRIDE_STANDARD_GAMMA: The user must first define: // static const float ntsc_gamma // static const float pal_gamma // static const float crt_reference_gamma_high // static const float crt_reference_gamma_low // static const float lcd_reference_gamma // static const float crt_office_gamma // static const float lcd_office_gamma // 2.) OVERRIDE_DEVICE_GAMMA: The user must first define: // static const float crt_gamma // static const float gba_gamma // static const float lcd_gamma // 3.) OVERRIDE_FINAL_GAMMA: The user must first define: // static const float input_gamma // static const float intermediate_gamma // static const float output_gamma // (intermediate_gamma is for GAMMA_ENCODE_EVERY_FBO.) // 4.) OVERRIDE_ALPHA_ASSUMPTIONS: The user must first define: // static const bool assume_opaque_alpha // The gamma constant overrides must be used in every pass or none, // and OVERRIDE_FINAL_GAMMA bypasses all of the SIMULATE* macros. // OVERRIDE_ALPHA_ASSUMPTIONS may be set on a per-pass basis. // Usage: After setting macros appropriately, ignore gamma correction and // replace all tex*D*() calls with equivalent gamma-aware // tex*D*_linearize calls, except: // 1.) When you read an LUT, use regular tex*D or a gamma-specified // function, depending on its gamma encoding: // tex*D*_linearize_gamma (takes a runtime gamma parameter) // 2.) If you must read pass0's original input in a later pass, use // tex2D_linearize_ntsc_gamma. If you want to read pass0's // input with gamma-corrected bilinear filtering, consider // creating a first linearizing pass and reading from the input // of pass1 later. // Then, return encode_output(color) from every fragment shader. // Finally, use the global gamma_aware_bilinear boolean if you want // to statically branch based on whether bilinear filtering is // gamma-correct or not (e.g. for placing Gaussian blur samples). // // Detailed Policy: // tex*D*_linearize() functions enforce a consistent gamma-management policy // based on the FIRST_PASS and GAMMA_ENCODE_EVERY_FBO settings. They assume // their input texture has the same encoding characteristics as the input for // the current pass (which doesn't apply to the exceptions listed above). // Similarly, encode_output() enforces a policy based on the LAST_PASS and // GAMMA_ENCODE_EVERY_FBO settings. Together, they result in one of the // following two pipelines. // Typical pipeline with intermediate sRGB framebuffers: // linear_color = pow(pass0_encoded_color, input_gamma); // intermediate_output = linear_color; // Automatic sRGB encoding // linear_color = intermediate_output; // Automatic sRGB decoding // final_output = pow(intermediate_output, 1.0/output_gamma); // Typical pipeline without intermediate sRGB framebuffers: // linear_color = pow(pass0_encoded_color, input_gamma); // intermediate_output = pow(linear_color, 1.0/intermediate_gamma); // linear_color = pow(intermediate_output, intermediate_gamma); // final_output = pow(intermediate_output, 1.0/output_gamma); // Using GAMMA_ENCODE_EVERY_FBO is much slower, but it's provided as a way to // easily get gamma-correctness without banding on devices where sRGB isn't // supported. // // Use This Header to Maximize Code Reuse: // The purpose of this header is to provide a consistent interface for texture // reads and output gamma-encoding that localizes and abstracts away all the // annoying details. This greatly reduces the amount of code in each shader // pass that depends on the pass number in the .cgp preset or whether sRGB // FBO's are being used: You can trivially change the gamma behavior of your // whole pass by commenting or uncommenting 1-3 #defines. To reuse the same // code in your first, Nth, and last passes, you can even put it all in another // header file and #include it from skeleton .cg files that #define the // appropriate pass-specific settings. // // Rationale for Using Three Macros: // This file uses GAMMA_ENCODE_EVERY_FBO instead of an opposite macro like // SRGB_PIPELINE to ensure sRGB is assumed by default, which hopefully imposes // a lower maintenance burden on each pass. At first glance it seems we could // accomplish everything with two macros: GAMMA_CORRECT_IN / GAMMA_CORRECT_OUT. // This works for simple use cases where input_gamma == output_gamma, but it // breaks down for more complex scenarios like CRT simulation, where the pass // number determines the gamma encoding of the input and output. /////////////////////////////// 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 const 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 // Set decoding/encoding gammas for the current pass. Use static constants for // linearize_input and gamma_encode_output, because they aren't derived, and // they let the compiler do dead-code elimination. #ifndef GAMMA_ENCODE_EVERY_FBO #ifdef FIRST_PASS const bool linearize_input = true; float get_pass_input_gamma() { return get_input_gamma(); } #else const bool linearize_input = false; float get_pass_input_gamma() { return 1.0; } #endif #ifdef LAST_PASS const bool gamma_encode_output = true; float get_pass_output_gamma() { return get_output_gamma(); } #else const bool gamma_encode_output = false; float get_pass_output_gamma() { return 1.0; } #endif #else const bool linearize_input = true; const 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 // Users might want to know if bilinear filtering will be gamma-correct: const bool gamma_aware_bilinear = !linearize_input; ////////////////////// COLOR ENCODING/DECODING FUNCTIONS ///////////////////// vec4 encode_output(const vec4 color) { if(gamma_encode_output) { if(assume_opaque_alpha) { 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; } } vec4 decode_input(const vec4 color) { if(linearize_input) { if(assume_opaque_alpha) { 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 decode_gamma_input(const vec4 color, const vec3 gamma) { if(assume_opaque_alpha) { return vec4(pow(color.rgb, vec3(gamma)), 1.0); } else { return vec4(pow(color.rgb, vec3(gamma)), color.a); } } /////////////////////////// TEXTURE LOOKUP WRAPPERS ////////////////////////// // "SMART" LINEARIZING TEXTURE LOOKUP FUNCTIONS: // Provide a wide array of linearizing texture lookup wrapper functions. The // Cg shader spec Retroarch uses only allows for 2D textures, but 1D and 3D // lookups are provided for completeness in case that changes someday. Nobody // is likely to use the *fetch and *proj functions, but they're included just // in case. The only tex*D texture sampling functions omitted are: // - tex*Dcmpbias // - tex*Dcmplod // - tex*DARRAY* // - tex*DMS* // - Variants returning integers // Standard line length restrictions are ignored below for vertical brevity. /* // tex1D: vec4 tex1D_linearize(const sampler1D texture, const float tex_coords) { return decode_input(tex1D(texture, tex_coords)); } vec4 tex1D_linearize(const sampler1D texture, const vec2 tex_coords) { return decode_input(tex1D(texture, tex_coords)); } vec4 tex1D_linearize(const sampler1D texture, const float tex_coords, const int texel_off) { return decode_input(tex1D(texture, tex_coords, texel_off)); } vec4 tex1D_linearize(const sampler1D texture, const vec2 tex_coords, const int texel_off) { return decode_input(tex1D(texture, tex_coords, texel_off)); } vec4 tex1D_linearize(const sampler1D texture, const float tex_coords, const float dx, const float dy) { return decode_input(tex1D(texture, tex_coords, dx, dy)); } vec4 tex1D_linearize(const sampler1D texture, const vec2 tex_coords, const float dx, const float dy) { return decode_input(tex1D(texture, tex_coords, dx, dy)); } vec4 tex1D_linearize(const sampler1D texture, const float tex_coords, const float dx, const float dy, const int texel_off) { return decode_input(tex1D(texture, tex_coords, dx, dy, texel_off)); } vec4 tex1D_linearize(const sampler1D texture, const vec2 tex_coords, const float dx, const float dy, const int texel_off) { return decode_input(tex1D(texture, tex_coords, dx, dy, texel_off)); } // tex1Dbias: vec4 tex1Dbias_linearize(const sampler1D texture, const vec4 tex_coords) { return decode_input(tex1Dbias(texture, tex_coords)); } vec4 tex1Dbias_linearize(const sampler1D texture, const vec4 tex_coords, const int texel_off) { return decode_input(tex1Dbias(texture, tex_coords, texel_off)); } // tex1Dfetch: vec4 tex1Dfetch_linearize(const sampler1D texture, const int4 tex_coords) { return decode_input(tex1Dfetch(texture, tex_coords)); } vec4 tex1Dfetch_linearize(const sampler1D texture, const int4 tex_coords, const int texel_off) { return decode_input(tex1Dfetch(texture, tex_coords, texel_off)); } // tex1Dlod: vec4 tex1Dlod_linearize(const sampler1D texture, const vec4 tex_coords) { return decode_input(tex1Dlod(texture, tex_coords)); } vec4 tex1Dlod_linearize(const sampler1D texture, const vec4 tex_coords, const int texel_off) { return decode_input(tex1Dlod(texture, tex_coords, texel_off)); } // tex1Dproj: vec4 tex1Dproj_linearize(const sampler1D texture, const vec2 tex_coords) { return decode_input(tex1Dproj(texture, tex_coords)); } vec4 tex1Dproj_linearize(const sampler1D texture, const vec3 tex_coords) { return decode_input(tex1Dproj(texture, tex_coords)); } vec4 tex1Dproj_linearize(const sampler1D texture, const vec2 tex_coords, const int texel_off) { return decode_input(tex1Dproj(texture, tex_coords, texel_off)); } vec4 tex1Dproj_linearize(const sampler1D texture, const vec3 tex_coords, const int texel_off) { return decode_input(tex1Dproj(texture, tex_coords, texel_off)); } */ // tex2D: vec4 tex2D_linearize(const sampler2D tex, const vec2 tex_coords) { return decode_input(vec4(texture(tex, tex_coords))); } vec4 tex2D_linearize(const sampler2D tex, const vec3 tex_coords) { return decode_input(vec4(texture(tex, tex_coords))); } vec4 tex2D_linearize(const sampler2D tex, const vec2 tex_coords, const int texel_off) { return decode_input(vec4(texture(tex, tex_coords, texel_off))); } vec4 tex2D_linearize(const sampler2D tex, const vec3 tex_coords, const int texel_off) { return decode_input(vec4(texture(tex, tex_coords, texel_off))); } vec4 tex2D_linearize(const sampler2D tex, const vec2 tex_coords, const vec2 dx, const vec2 dy) { return decode_input(vec4(texture(tex, tex_coords, dx, dy))); } vec4 tex2D_linearize(const sampler2D tex, const vec3 tex_coords, const vec2 dx, const vec2 dy) { return decode_input(vec4(texture(tex, tex_coords, dx, dy))); } vec4 tex2D_linearize(const sampler2D tex, const vec2 tex_coords, const vec2 dx, const vec2 dy, const int texel_off) { return decode_input(vec4(texture(tex, tex_coords, dx, dy, texel_off))); } vec4 tex2D_linearize(const sampler2D tex, const vec3 tex_coords, const vec2 dx, const vec2 dy, const int texel_off) { return decode_input(vec4(texture(tex, tex_coords, dx, dy, texel_off))); } // tex2Dbias: vec4 tex2Dbias_linearize(const sampler2D tex, const vec4 tex_coords) { return decode_input(vec4(tex2Dbias(tex, tex_coords))); } vec4 tex2Dbias_linearize(const sampler2D tex, const vec4 tex_coords, const int texel_off) { return decode_input(vec4(tex2Dbias(tex, tex_coords, texel_off))); } // tex2Dfetch: vec4 tex2Dfetch_linearize(const sampler2D tex, const ivec4 tex_coords) { return decode_input(vec4(texture2Dfetch(tex, tex_coords))); } vec4 tex2Dfetch_linearize(const sampler2D tex, const ivec4 tex_coords, const int texel_off) { return decode_input(vec4(texture2Dfetch(tex, tex_coords, texel_off))); } // tex2Dlod: vec4 tex2Dlod_linearize(const sampler2D tex, const vec4 tex_coords) { return decode_input(vec4(texture2Dlod(tex, tex_coords))); } vec4 tex2Dlod_linearize(const sampler2D tex, const vec4 tex_coords, const int texel_off) { return decode_input(vec4(texture2Dlod(tex, tex_coords, texel_off))); } // tex2Dproj: vec4 tex2Dproj_linearize(const sampler2D tex, const vec3 tex_coords) { return decode_input(vec4(tex2Dproj(tex, tex_coords))); } vec4 tex2Dproj_linearize(const sampler2D tex, const vec4 tex_coords) { return decode_input(vec4(tex2Dproj(tex, tex_coords))); } vec4 tex2Dproj_linearize(const sampler2D tex, const vec3 tex_coords, const int texel_off) { return decode_input(vec4(tex2Dproj(tex, tex_coords, texel_off))); } vec4 tex2Dproj_linearize(const sampler2D tex, const vec4 tex_coords, const int texel_off) { return decode_input(vec4(tex2Dproj(tex, tex_coords, texel_off))); } /* // tex3D: vec4 tex3D_linearize(const sampler3D texture, const vec3 tex_coords) { return decode_input(tex3D(texture, tex_coords)); } vec4 tex3D_linearize(const sampler3D texture, const vec3 tex_coords, const int texel_off) { return decode_input(tex3D(texture, tex_coords, texel_off)); } vec4 tex3D_linearize(const sampler3D texture, const vec3 tex_coords, const vec3 dx, const vec3 dy) { return decode_input(tex3D(texture, tex_coords, dx, dy)); } vec4 tex3D_linearize(const sampler3D texture, const vec3 tex_coords, const vec3 dx, const vec3 dy, const int texel_off) { return decode_input(tex3D(texture, tex_coords, dx, dy, texel_off)); } // tex3Dbias: vec4 tex3Dbias_linearize(const sampler3D texture, const vec4 tex_coords) { return decode_input(tex3Dbias(texture, tex_coords)); } vec4 tex3Dbias_linearize(const sampler3D texture, const vec4 tex_coords, const int texel_off) { return decode_input(tex3Dbias(texture, tex_coords, texel_off)); } // tex3Dfetch: vec4 tex3Dfetch_linearize(const sampler3D texture, const int4 tex_coords) { return decode_input(tex3Dfetch(texture, tex_coords)); } vec4 tex3Dfetch_linearize(const sampler3D texture, const int4 tex_coords, const int texel_off) { return decode_input(tex3Dfetch(texture, tex_coords, texel_off)); } // tex3Dlod: vec4 tex3Dlod_linearize(const sampler3D texture, const vec4 tex_coords) { return decode_input(tex3Dlod(texture, tex_coords)); } vec4 tex3Dlod_linearize(const sampler3D texture, const vec4 tex_coords, const int texel_off) { return decode_input(tex3Dlod(texture, tex_coords, texel_off)); } // tex3Dproj: vec4 tex3Dproj_linearize(const sampler3D texture, const vec4 tex_coords) { return decode_input(tex3Dproj(texture, tex_coords)); } vec4 tex3Dproj_linearize(const sampler3D texture, const vec4 tex_coords, const int texel_off) { return decode_input(tex3Dproj(texture, tex_coords, texel_off)); } */ // NONSTANDARD "SMART" LINEARIZING TEXTURE LOOKUP FUNCTIONS: // This narrow selection of nonstandard tex2D* functions can be useful: // tex2Dlod0: Automatically fill in the tex2D LOD parameter for mip level 0. vec4 tex2Dlod0_linearize(const sampler2D texture, const vec2 tex_coords) { return decode_input(vec4(texture2Dlod(texture, vec4(tex_coords, 0.0, 0.0)))); } vec4 tex2Dlod0_linearize(const sampler2D texture, const vec2 tex_coords, const int texel_off) { return decode_input(vec4(texture2Dlod(texture, vec4(tex_coords, 0.0, 0.0), texel_off))); } // MANUALLY LINEARIZING TEXTURE LOOKUP FUNCTIONS: // Provide a narrower selection of tex2D* wrapper functions that decode an // input sample with a specified gamma value. These are useful for reading // LUT's and for reading the input of pass0 in a later pass. // tex2D: vec4 tex2D_linearize_gamma(const sampler2D tex, const vec2 tex_coords, const vec3 gamma) { return decode_gamma_input(vec4(texture(tex, tex_coords), vec3(gamma))); } vec4 tex2D_linearize_gamma(const sampler2D tex, const vec3 tex_coords, const vec3 gamma) { return decode_gamma_input(vec4(texture(tex, tex_coords), vec3(gamma))); } vec4 tex2D_linearize_gamma(const sampler2D tex, const vec2 tex_coords, const int texel_off, const vec3 gamma) { return decode_gamma_input(vec4(texture(tex, tex_coords, texel_off), vec3(gamma))); } vec4 tex2D_linearize_gamma(const sampler2D tex, const vec3 tex_coords, const int texel_off, const vec3 gamma) { return decode_gamma_input(vec4(texture(tex, tex_coords, texel_off), vec3(gamma))); } vec4 tex2D_linearize_gamma(const sampler2D tex, const vec2 tex_coords, const vec2 dx, const vec2 dy, const vec3 gamma) { return decode_gamma_input(vec4(texture(tex, tex_coords, dx, dy), vec3(gamma))); } vec4 tex2D_linearize_gamma(const sampler2D tex, const vec3 tex_coords, const vec2 dx, const vec2 dy, const vec3 gamma) { return decode_gamma_input(vec4(texture(tex, tex_coords, dx, dy), vec3(gamma))); } vec4 tex2D_linearize_gamma(const sampler2D tex, const vec2 tex_coords, const vec2 dx, const vec2 dy, const int texel_off, const vec3 gamma) { return decode_gamma_input(vec4(texture(tex, tex_coords, dx, dy, texel_off), vec3(gamma))); } vec4 tex2D_linearize_gamma(const sampler2D tex, const vec3 tex_coords, const vec2 dx, const vec2 dy, const int texel_off, const vec3 gamma) { return decode_gamma_input(vec4(texture(tex, tex_coords, dx, dy, texel_off), vec3(gamma))); } // tex2Dbias: vec4 tex2Dbias_linearize_gamma(const sampler2D tex, const vec4 tex_coords, const vec3 gamma) { return decode_gamma_input(vec4(tex2Dbias(tex, tex_coords), vec3(gamma))); } vec4 tex2Dbias_linearize_gamma(const sampler2D tex, const vec4 tex_coords, const int texel_off, const vec3 gamma) { return decode_gamma_input(vec4(tex2Dbias(tex, tex_coords, texel_off), vec3(gamma))); } // tex2Dfetch: vec4 tex2Dfetch_linearize_gamma(const sampler2D tex, const int4 tex_coords, const vec3 gamma) { return decode_gamma_input(vec4(tex2Dfetch(tex, tex_coords), vec3(gamma))); } vec4 tex2Dfetch_linearize_gamma(const sampler2D tex, const int4 tex_coords, const int texel_off, const vec3 gamma) { return decode_gamma_input(vec4(tex2Dfetch(tex, tex_coords, texel_off), vec3(gamma))); } // tex2Dlod: vec4 tex2Dlod_linearize_gamma(const sampler2D tex, const vec4 tex_coords, const vec3 gamma) { return decode_gamma_input(vec4(tex2Dlod(tex, tex_coords), vec3(gamma))); } vec4 tex2Dlod_linearize_gamma(const sampler2D tex, const vec4 tex_coords, const int texel_off, const vec3 gamma) { return decode_gamma_input(vec4(tex2Dlod(tex, tex_coords, texel_off), vec3(gamma))); } #endif // GAMMA_MANAGEMENT_H