2016-08-27 02:28:24 +10:00
|
|
|
#ifndef GAMMA_MANAGEMENT_H
|
|
|
|
#define GAMMA_MANAGEMENT_H
|
|
|
|
|
2017-11-30 06:40:28 +11:00
|
|
|
///////////////////////////////// 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.
|
|
|
|
|
|
|
|
|
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:
|
2017-11-30 06:40:28 +11:00
|
|
|
static const float ntsc_gamma = 2.2; // Best to use NTSC for PAL too?
|
|
|
|
static const float pal_gamma = 2.8; // Never actually 2.8 in practice
|
2016-08-20 06:26:12 +10:00
|
|
|
// 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.)
|
2017-11-30 06:40:28 +11:00
|
|
|
static const float crt_reference_gamma_high = 2.5; // In (2.35, 2.55)
|
|
|
|
static const float crt_reference_gamma_low = 2.35; // In (2.35, 2.55)
|
|
|
|
static const float lcd_reference_gamma = 2.5; // To match CRT
|
|
|
|
static const float crt_office_gamma = 2.2; // Circuitry-adjusted for NTSC
|
|
|
|
static const float lcd_office_gamma = 2.2; // Approximates sRGB
|
2016-08-20 06:26:12 +10:00
|
|
|
#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
|
2017-11-30 06:40:28 +11:00
|
|
|
static const 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:
|
2017-11-30 06:40:28 +11:00
|
|
|
inline float get_crt_gamma() { return crt_gamma; }
|
|
|
|
inline float get_gba_gamma() { return gba_gamma; }
|
|
|
|
inline float get_lcd_gamma() { return lcd_gamma; }
|
2016-08-20 06:26:12 +10:00
|
|
|
#else
|
2017-11-30 06:40:28 +11:00
|
|
|
inline float get_crt_gamma() { return crt_reference_gamma_high; }
|
|
|
|
inline float get_gba_gamma() { return 3.5; } // Game Boy Advance; in (3.0, 4.0)
|
|
|
|
inline float get_lcd_gamma() { return lcd_office_gamma; }
|
2016-08-20 06:26:12 +10:00
|
|
|
#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:
|
2017-11-30 06:40:28 +11:00
|
|
|
inline float get_intermediate_gamma() { return intermediate_gamma; }
|
|
|
|
inline float get_input_gamma() { return input_gamma; }
|
|
|
|
inline float get_output_gamma() { return output_gamma; }
|
2016-08-20 06:26:12 +10:00
|
|
|
#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:
|
2017-11-30 06:40:28 +11:00
|
|
|
inline float get_intermediate_gamma() { return ntsc_gamma; }
|
2016-08-20 06:26:12 +10:00
|
|
|
#ifdef SIMULATE_CRT_ON_LCD
|
2017-11-30 06:40:28 +11:00
|
|
|
inline float get_input_gamma() { return get_crt_gamma(); }
|
|
|
|
inline float get_output_gamma() { return get_lcd_gamma(); }
|
2016-08-20 06:26:12 +10:00
|
|
|
#else
|
|
|
|
#ifdef SIMULATE_GBA_ON_LCD
|
2017-11-30 06:40:28 +11:00
|
|
|
inline float get_input_gamma() { return get_gba_gamma(); }
|
|
|
|
inline float get_output_gamma() { return get_lcd_gamma(); }
|
2016-08-20 06:26:12 +10:00
|
|
|
#else
|
|
|
|
#ifdef SIMULATE_LCD_ON_CRT
|
2017-11-30 06:40:28 +11:00
|
|
|
inline float get_input_gamma() { return get_lcd_gamma(); }
|
|
|
|
inline float get_output_gamma() { return get_crt_gamma(); }
|
2016-08-20 06:26:12 +10:00
|
|
|
#else
|
|
|
|
#ifdef SIMULATE_GBA_ON_CRT
|
2017-11-30 06:40:28 +11:00
|
|
|
inline float get_input_gamma() { return get_gba_gamma(); }
|
|
|
|
inline float get_output_gamma() { return get_crt_gamma(); }
|
2016-08-20 06:26:12 +10:00
|
|
|
#else // Don't simulate anything:
|
2017-11-30 06:40:28 +11:00
|
|
|
inline float get_input_gamma() { return ntsc_gamma; }
|
|
|
|
inline float get_output_gamma() { return ntsc_gamma; }
|
2016-08-20 06:26:12 +10:00
|
|
|
#endif // SIMULATE_GBA_ON_CRT
|
|
|
|
#endif // SIMULATE_LCD_ON_CRT
|
|
|
|
#endif // SIMULATE_GBA_ON_LCD
|
|
|
|
#endif // SIMULATE_CRT_ON_LCD
|
|
|
|
#endif // OVERRIDE_FINAL_GAMMA
|
|
|
|
|
2017-11-30 06:40:28 +11:00
|
|
|
// 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.
|
2016-08-20 06:26:12 +10:00
|
|
|
#ifndef GAMMA_ENCODE_EVERY_FBO
|
|
|
|
#ifdef FIRST_PASS
|
2017-11-30 06:40:28 +11:00
|
|
|
static const bool linearize_input = true;
|
|
|
|
inline float get_pass_input_gamma() { return get_input_gamma(); }
|
2016-08-20 06:26:12 +10:00
|
|
|
#else
|
2017-11-30 06:40:28 +11:00
|
|
|
static const bool linearize_input = false;
|
|
|
|
inline float get_pass_input_gamma() { return 1.0; }
|
2016-08-20 06:26:12 +10:00
|
|
|
#endif
|
|
|
|
#ifdef LAST_PASS
|
2017-11-30 06:40:28 +11:00
|
|
|
static const bool gamma_encode_output = true;
|
|
|
|
inline float get_pass_output_gamma() { return get_output_gamma(); }
|
2016-08-20 06:26:12 +10:00
|
|
|
#else
|
2017-11-30 06:40:28 +11:00
|
|
|
static const bool gamma_encode_output = false;
|
|
|
|
inline float get_pass_output_gamma() { return 1.0; }
|
2016-08-20 06:26:12 +10:00
|
|
|
#endif
|
|
|
|
#else
|
2017-11-30 06:40:28 +11:00
|
|
|
static const bool linearize_input = true;
|
|
|
|
static const bool gamma_encode_output = true;
|
2016-08-20 06:26:12 +10:00
|
|
|
#ifdef FIRST_PASS
|
2017-11-30 06:40:28 +11:00
|
|
|
inline float get_pass_input_gamma() { return get_input_gamma(); }
|
2016-08-20 06:26:12 +10:00
|
|
|
#else
|
2017-11-30 06:40:28 +11:00
|
|
|
inline float get_pass_input_gamma() { return get_intermediate_gamma(); }
|
2016-08-20 06:26:12 +10:00
|
|
|
#endif
|
|
|
|
#ifdef LAST_PASS
|
2017-11-30 06:40:28 +11:00
|
|
|
inline float get_pass_output_gamma() { return get_output_gamma(); }
|
2016-08-20 06:26:12 +10:00
|
|
|
#else
|
2017-11-30 06:40:28 +11:00
|
|
|
inline float get_pass_output_gamma() { return get_intermediate_gamma(); }
|
2016-08-20 06:26:12 +10:00
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2017-11-30 06:40:28 +11:00
|
|
|
// Users might want to know if bilinear filtering will be gamma-correct:
|
|
|
|
static const bool gamma_aware_bilinear = !linearize_input;
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////// COLOR ENCODING/DECODING FUNCTIONS /////////////////////
|
|
|
|
|
|
|
|
inline float4 encode_output(const float4 color)
|
2016-08-20 06:26:12 +10:00
|
|
|
{
|
2017-11-30 06:40:28 +11:00
|
|
|
if(gamma_encode_output)
|
2016-08-20 06:26:12 +10:00
|
|
|
{
|
2017-11-30 06:40:28 +11:00
|
|
|
if(assume_opaque_alpha)
|
2016-08-20 06:26:12 +10:00
|
|
|
{
|
2017-11-30 06:40:28 +11:00
|
|
|
return float4(pow(color.rgb, float3(1.0/get_pass_output_gamma())), 1.0);
|
2016-08-20 06:26:12 +10:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-11-30 06:40:28 +11:00
|
|
|
return float4(pow(color.rgb, float3(1.0/get_pass_output_gamma())), color.a);
|
2016-08-20 06:26:12 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return color;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-30 06:40:28 +11:00
|
|
|
inline float4 decode_input(const float4 color)
|
2016-08-20 06:26:12 +10:00
|
|
|
{
|
2017-11-30 06:40:28 +11:00
|
|
|
if(linearize_input)
|
2016-08-20 06:26:12 +10:00
|
|
|
{
|
2017-11-30 06:40:28 +11:00
|
|
|
if(assume_opaque_alpha)
|
2016-08-20 06:26:12 +10:00
|
|
|
{
|
2017-11-30 06:40:28 +11:00
|
|
|
return float4(pow(color.rgb, float3(get_pass_input_gamma())), 1.0);
|
2016-08-20 06:26:12 +10:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-11-30 06:40:28 +11:00
|
|
|
return float4(pow(color.rgb, float3(get_pass_input_gamma())), color.a);
|
2016-08-20 06:26:12 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return color;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-30 06:40:28 +11:00
|
|
|
inline float4 decode_gamma_input(const float4 color, const float3 gamma)
|
|
|
|
{
|
|
|
|
if(assume_opaque_alpha)
|
|
|
|
{
|
|
|
|
return float4(pow(color.rgb, gamma), 1.0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return float4(pow(color.rgb, gamma), color.a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//TODO/FIXME: I have no idea why replacing the lookup wrappers with this macro fixes the blurs being offset ¯\_(ツ)_/¯
|
2017-12-01 02:03:34 +11:00
|
|
|
//#define tex2D_linearize(C, D) decode_input(vec4(texture(C, D)))
|
|
|
|
// EDIT: it's the 'const' in front of the coords that's doing it
|
2016-08-20 06:26:12 +10:00
|
|
|
|
2017-11-30 06:40:28 +11:00
|
|
|
/////////////////////////// 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:
|
|
|
|
inline float4 tex1D_linearize(const sampler1D tex, const float tex_coords)
|
|
|
|
{ return decode_input(tex1D(tex, tex_coords)); }
|
|
|
|
|
|
|
|
inline float4 tex1D_linearize(const sampler1D tex, const float2 tex_coords)
|
|
|
|
{ return decode_input(tex1D(tex, tex_coords)); }
|
|
|
|
|
|
|
|
inline float4 tex1D_linearize(const sampler1D tex, const float tex_coords, const int texel_off)
|
|
|
|
{ return decode_input(tex1D(tex, tex_coords, texel_off)); }
|
|
|
|
|
|
|
|
inline float4 tex1D_linearize(const sampler1D tex, const float2 tex_coords, const int texel_off)
|
|
|
|
{ return decode_input(tex1D(tex, tex_coords, texel_off)); }
|
|
|
|
|
|
|
|
inline float4 tex1D_linearize(const sampler1D tex, const float tex_coords, const float dx, const float dy)
|
|
|
|
{ return decode_input(tex1D(tex, tex_coords, dx, dy)); }
|
|
|
|
|
|
|
|
inline float4 tex1D_linearize(const sampler1D tex, const float2 tex_coords, const float dx, const float dy)
|
|
|
|
{ return decode_input(tex1D(tex, tex_coords, dx, dy)); }
|
|
|
|
|
|
|
|
inline float4 tex1D_linearize(const sampler1D tex, const float tex_coords, const float dx, const float dy, const int texel_off)
|
|
|
|
{ return decode_input(tex1D(tex, tex_coords, dx, dy, texel_off)); }
|
|
|
|
|
|
|
|
inline float4 tex1D_linearize(const sampler1D tex, const float2 tex_coords, const float dx, const float dy, const int texel_off)
|
|
|
|
{ return decode_input(tex1D(tex, tex_coords, dx, dy, texel_off)); }
|
|
|
|
|
|
|
|
// tex1Dbias:
|
|
|
|
inline float4 tex1Dbias_linearize(const sampler1D tex, const float4 tex_coords)
|
|
|
|
{ return decode_input(tex1Dbias(tex, tex_coords)); }
|
|
|
|
|
|
|
|
inline float4 tex1Dbias_linearize(const sampler1D tex, const float4 tex_coords, const int texel_off)
|
|
|
|
{ return decode_input(tex1Dbias(tex, tex_coords, texel_off)); }
|
|
|
|
|
|
|
|
// tex1Dfetch:
|
|
|
|
inline float4 tex1Dfetch_linearize(const sampler1D tex, const int4 tex_coords)
|
|
|
|
{ return decode_input(tex1Dfetch(tex, tex_coords)); }
|
|
|
|
|
|
|
|
inline float4 tex1Dfetch_linearize(const sampler1D tex, const int4 tex_coords, const int texel_off)
|
|
|
|
{ return decode_input(tex1Dfetch(tex, tex_coords, texel_off)); }
|
|
|
|
|
|
|
|
// tex1Dlod:
|
|
|
|
inline float4 tex1Dlod_linearize(const sampler1D tex, const float4 tex_coords)
|
|
|
|
{ return decode_input(tex1Dlod(tex, tex_coords)); }
|
|
|
|
|
|
|
|
inline float4 tex1Dlod_linearize(const sampler1D tex, const float4 tex_coords, const int texel_off)
|
|
|
|
{ return decode_input(tex1Dlod(tex, tex_coords, texel_off)); }
|
|
|
|
|
|
|
|
// tex1Dproj:
|
|
|
|
inline float4 tex1Dproj_linearize(const sampler1D tex, const float2 tex_coords)
|
|
|
|
{ return decode_input(tex1Dproj(tex, tex_coords)); }
|
|
|
|
|
|
|
|
inline float4 tex1Dproj_linearize(const sampler1D tex, const float3 tex_coords)
|
|
|
|
{ return decode_input(tex1Dproj(tex, tex_coords)); }
|
|
|
|
|
|
|
|
inline float4 tex1Dproj_linearize(const sampler1D tex, const float2 tex_coords, const int texel_off)
|
|
|
|
{ return decode_input(tex1Dproj(tex, tex_coords, texel_off)); }
|
|
|
|
|
|
|
|
inline float4 tex1Dproj_linearize(const sampler1D tex, const float3 tex_coords, const int texel_off)
|
|
|
|
{ return decode_input(tex1Dproj(tex, tex_coords, texel_off)); }
|
2017-12-01 02:03:34 +11:00
|
|
|
*/
|
2017-11-30 06:40:28 +11:00
|
|
|
// tex2D:
|
2017-12-01 02:03:34 +11:00
|
|
|
inline float4 tex2D_linearize(const sampler2D tex, float2 tex_coords)
|
2017-11-30 06:40:28 +11:00
|
|
|
{ return decode_input(texture(tex, tex_coords)); }
|
|
|
|
|
2017-12-01 02:03:34 +11:00
|
|
|
inline float4 tex2D_linearize(const sampler2D tex, float3 tex_coords)
|
2017-11-30 06:40:28 +11:00
|
|
|
{ return decode_input(texture(tex, tex_coords.xy)); }
|
|
|
|
|
2017-12-01 02:03:34 +11:00
|
|
|
inline float4 tex2D_linearize(const sampler2D tex, float2 tex_coords, int texel_off)
|
2017-11-30 06:40:28 +11:00
|
|
|
{ return decode_input(textureLod(tex, tex_coords, texel_off)); }
|
|
|
|
|
2017-12-01 02:03:34 +11:00
|
|
|
inline float4 tex2D_linearize(const sampler2D tex, float3 tex_coords, int texel_off)
|
2017-11-30 06:40:28 +11:00
|
|
|
{ return decode_input(textureLod(tex, tex_coords.xy, texel_off)); }
|
|
|
|
|
|
|
|
//inline float4 tex2D_linearize(const sampler2D tex, const float2 tex_coords, const float2 dx, const float2 dy)
|
|
|
|
//{ return decode_input(texture(tex, tex_coords, dx, dy)); }
|
|
|
|
|
|
|
|
//inline float4 tex2D_linearize(const sampler2D tex, const float3 tex_coords, const float2 dx, const float2 dy)
|
|
|
|
//{ return decode_input(texture(tex, tex_coords, dx, dy)); }
|
|
|
|
|
|
|
|
//inline float4 tex2D_linearize(const sampler2D tex, const float2 tex_coords, const float2 dx, const float2 dy, const int texel_off)
|
|
|
|
//{ return decode_input(texture(tex, tex_coords, dx, dy, texel_off)); }
|
|
|
|
|
|
|
|
//inline float4 tex2D_linearize(const sampler2D tex, const float3 tex_coords, const float2 dx, const float2 dy, const int texel_off)
|
|
|
|
//{ return decode_input(texture(tex, tex_coords, dx, dy, texel_off)); }
|
|
|
|
|
|
|
|
// tex2Dbias:
|
|
|
|
//inline float4 tex2Dbias_linearize(const sampler2D tex, const float4 tex_coords)
|
|
|
|
//{ return decode_input(tex2Dbias(tex, tex_coords)); }
|
|
|
|
|
|
|
|
//inline float4 tex2Dbias_linearize(const sampler2D tex, const float4 tex_coords, const int texel_off)
|
|
|
|
//{ return decode_input(tex2Dbias(tex, tex_coords, texel_off)); }
|
|
|
|
|
|
|
|
// tex2Dfetch:
|
|
|
|
//inline float4 tex2Dfetch_linearize(const sampler2D tex, const int4 tex_coords)
|
|
|
|
//{ return decode_input(tex2Dfetch(tex, tex_coords)); }
|
|
|
|
|
|
|
|
//inline float4 tex2Dfetch_linearize(const sampler2D tex, const int4 tex_coords, const int texel_off)
|
|
|
|
//{ return decode_input(tex2Dfetch(tex, tex_coords, texel_off)); }
|
2017-12-01 02:03:34 +11:00
|
|
|
|
2017-11-30 06:40:28 +11:00
|
|
|
// tex2Dlod:
|
2017-12-01 02:03:34 +11:00
|
|
|
inline float4 tex2Dlod_linearize(const sampler2D tex, float4 tex_coords)
|
2017-11-30 06:40:28 +11:00
|
|
|
{ return decode_input(textureLod(tex, tex_coords.xy, 0.0)); }
|
|
|
|
|
2017-12-01 02:03:34 +11:00
|
|
|
inline float4 tex2Dlod_linearize(const sampler2D tex, float4 tex_coords, int texel_off)
|
2017-11-30 06:40:28 +11:00
|
|
|
{ return decode_input(textureLod(tex, tex_coords.xy, texel_off)); }
|
|
|
|
/*
|
|
|
|
// tex2Dproj:
|
|
|
|
inline float4 tex2Dproj_linearize(const sampler2D tex, const float3 tex_coords)
|
|
|
|
{ return decode_input(tex2Dproj(tex, tex_coords)); }
|
|
|
|
|
|
|
|
inline float4 tex2Dproj_linearize(const sampler2D tex, const float4 tex_coords)
|
|
|
|
{ return decode_input(tex2Dproj(tex, tex_coords)); }
|
|
|
|
|
|
|
|
inline float4 tex2Dproj_linearize(const sampler2D tex, const float3 tex_coords, const int texel_off)
|
|
|
|
{ return decode_input(tex2Dproj(tex, tex_coords, texel_off)); }
|
|
|
|
|
|
|
|
inline float4 tex2Dproj_linearize(const sampler2D tex, const float4 tex_coords, const int texel_off)
|
|
|
|
{ return decode_input(tex2Dproj(tex, tex_coords, texel_off)); }
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
// tex3D:
|
|
|
|
inline float4 tex3D_linearize(const sampler3D tex, const float3 tex_coords)
|
|
|
|
{ return decode_input(tex3D(tex, tex_coords)); }
|
|
|
|
|
|
|
|
inline float4 tex3D_linearize(const sampler3D tex, const float3 tex_coords, const int texel_off)
|
|
|
|
{ return decode_input(tex3D(tex, tex_coords, texel_off)); }
|
|
|
|
|
|
|
|
inline float4 tex3D_linearize(const sampler3D tex, const float3 tex_coords, const float3 dx, const float3 dy)
|
|
|
|
{ return decode_input(tex3D(tex, tex_coords, dx, dy)); }
|
|
|
|
|
|
|
|
inline float4 tex3D_linearize(const sampler3D tex, const float3 tex_coords, const float3 dx, const float3 dy, const int texel_off)
|
|
|
|
{ return decode_input(tex3D(tex, tex_coords, dx, dy, texel_off)); }
|
|
|
|
|
|
|
|
// tex3Dbias:
|
|
|
|
inline float4 tex3Dbias_linearize(const sampler3D tex, const float4 tex_coords)
|
|
|
|
{ return decode_input(tex3Dbias(tex, tex_coords)); }
|
|
|
|
|
|
|
|
inline float4 tex3Dbias_linearize(const sampler3D tex, const float4 tex_coords, const int texel_off)
|
|
|
|
{ return decode_input(tex3Dbias(tex, tex_coords, texel_off)); }
|
|
|
|
|
|
|
|
// tex3Dfetch:
|
|
|
|
inline float4 tex3Dfetch_linearize(const sampler3D tex, const int4 tex_coords)
|
|
|
|
{ return decode_input(tex3Dfetch(tex, tex_coords)); }
|
|
|
|
|
|
|
|
inline float4 tex3Dfetch_linearize(const sampler3D tex, const int4 tex_coords, const int texel_off)
|
|
|
|
{ return decode_input(tex3Dfetch(tex, tex_coords, texel_off)); }
|
|
|
|
|
|
|
|
// tex3Dlod:
|
|
|
|
inline float4 tex3Dlod_linearize(const sampler3D tex, const float4 tex_coords)
|
|
|
|
{ return decode_input(tex3Dlod(tex, tex_coords)); }
|
|
|
|
|
|
|
|
inline float4 tex3Dlod_linearize(const sampler3D tex, const float4 tex_coords, const int texel_off)
|
|
|
|
{ return decode_input(tex3Dlod(tex, tex_coords, texel_off)); }
|
|
|
|
|
|
|
|
// tex3Dproj:
|
|
|
|
inline float4 tex3Dproj_linearize(const sampler3D tex, const float4 tex_coords)
|
|
|
|
{ return decode_input(tex3Dproj(tex, tex_coords)); }
|
|
|
|
|
|
|
|
inline float4 tex3Dproj_linearize(const sampler3D tex, const float4 tex_coords, const int texel_off)
|
|
|
|
{ return decode_input(tex3Dproj(tex, 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.
|
|
|
|
//inline float4 tex2Dlod0_linearize(const sampler2D tex, const float2 tex_coords)
|
|
|
|
//{ return decode_input(tex2Dlod(tex, float4(tex_coords, 0.0, 0.0))); }
|
|
|
|
|
|
|
|
//inline float4 tex2Dlod0_linearize(const sampler2D tex, const float2 tex_coords, const int texel_off)
|
|
|
|
//{ return decode_input(tex2Dlod(tex, float4(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:
|
|
|
|
inline float4 tex2D_linearize_gamma(const sampler2D tex, const float2 tex_coords, const float3 gamma)
|
|
|
|
{ return decode_gamma_input(texture(tex, tex_coords), gamma); }
|
|
|
|
|
|
|
|
inline float4 tex2D_linearize_gamma(const sampler2D tex, const float3 tex_coords, const float3 gamma)
|
|
|
|
{ return decode_gamma_input(texture(tex, tex_coords.xy), gamma); }
|
|
|
|
|
|
|
|
//inline float4 tex2D_linearize_gamma(const sampler2D tex, const float2 tex_coords, const int texel_off, const float3 gamma)
|
|
|
|
//{ return decode_gamma_input(texture(tex, tex_coords, texel_off), gamma); }
|
|
|
|
|
|
|
|
//inline float4 tex2D_linearize_gamma(const sampler2D tex, const float3 tex_coords, const int texel_off, const float3 gamma)
|
|
|
|
//{ return decode_gamma_input(texture(tex, tex_coords, texel_off), gamma); }
|
|
|
|
|
|
|
|
//inline float4 tex2D_linearize_gamma(const sampler2D tex, const float2 tex_coords, const float2 dx, const float2 dy, const float3 gamma)
|
|
|
|
//{ return decode_gamma_input(texture(tex, tex_coords, dx, dy), gamma); }
|
|
|
|
|
|
|
|
//inline float4 tex2D_linearize_gamma(const sampler2D tex, const float3 tex_coords, const float2 dx, const float2 dy, const float3 gamma)
|
|
|
|
//{ return decode_gamma_input(texture(tex, tex_coords, dx, dy), gamma); }
|
|
|
|
|
|
|
|
//inline float4 tex2D_linearize_gamma(const sampler2D tex, const float2 tex_coords, const float2 dx, const float2 dy, const int texel_off, const float3 gamma)
|
|
|
|
//{ return decode_gamma_input(texture(tex, tex_coords, dx, dy, texel_off), gamma); }
|
|
|
|
|
|
|
|
//inline float4 tex2D_linearize_gamma(const sampler2D tex, const float3 tex_coords, const float2 dx, const float2 dy, const int texel_off, const float3 gamma)
|
|
|
|
//{ return decode_gamma_input(texture(tex, tex_coords, dx, dy, texel_off), gamma); }
|
|
|
|
/*
|
|
|
|
// tex2Dbias:
|
|
|
|
inline float4 tex2Dbias_linearize_gamma(const sampler2D tex, const float4 tex_coords, const float3 gamma)
|
|
|
|
{ return decode_gamma_input(tex2Dbias(tex, tex_coords), gamma); }
|
|
|
|
|
|
|
|
inline float4 tex2Dbias_linearize_gamma(const sampler2D tex, const float4 tex_coords, const int texel_off, const float3 gamma)
|
|
|
|
{ return decode_gamma_input(tex2Dbias(tex, tex_coords, texel_off), gamma); }
|
|
|
|
|
|
|
|
// tex2Dfetch:
|
|
|
|
inline float4 tex2Dfetch_linearize_gamma(const sampler2D tex, const int4 tex_coords, const float3 gamma)
|
|
|
|
{ return decode_gamma_input(tex2Dfetch(tex, tex_coords), gamma); }
|
|
|
|
|
|
|
|
inline float4 tex2Dfetch_linearize_gamma(const sampler2D tex, const int4 tex_coords, const int texel_off, const float3 gamma)
|
|
|
|
{ return decode_gamma_input(tex2Dfetch(tex, tex_coords, texel_off), gamma); }
|
2017-12-01 02:03:34 +11:00
|
|
|
*/
|
2017-11-30 06:40:28 +11:00
|
|
|
// tex2Dlod:
|
2017-12-01 02:03:34 +11:00
|
|
|
inline float4 tex2Dlod_linearize_gamma(const sampler2D tex, float4 tex_coords, float3 gamma)
|
2017-11-30 06:40:28 +11:00
|
|
|
{ return decode_gamma_input(textureLod(tex, tex_coords.xy, 0.0), gamma); }
|
|
|
|
|
2017-12-01 02:03:34 +11:00
|
|
|
inline float4 tex2Dlod_linearize_gamma(const sampler2D tex, float4 tex_coords, int texel_off, float3 gamma)
|
2017-11-30 06:40:28 +11:00
|
|
|
{ return decode_gamma_input(textureLod(tex, tex_coords.xy, texel_off), gamma); }
|
2017-12-01 02:03:34 +11:00
|
|
|
|
2017-11-30 06:40:28 +11:00
|
|
|
|
|
|
|
#endif // GAMMA_MANAGEMENT_H
|
2016-08-27 02:28:24 +10:00
|
|
|
|