158 lines
5.5 KiB
GLSL
158 lines
5.5 KiB
GLSL
#version 330 core
|
|
|
|
out vec4 FragColor;
|
|
in vec2 TexCoord;
|
|
|
|
uniform sampler2D InputTexture;
|
|
uniform float temperature; // Target Correlated Color Temperature (Kelvin, e.g., 1000-20000)
|
|
uniform float tint; // Green-Magenta shift (-100 to +100)
|
|
|
|
// --- Constants ---
|
|
|
|
// sRGB Primaries to XYZ (D65)
|
|
const mat3 M_RGB_2_XYZ_D65 = mat3(
|
|
0.4124564, 0.3575761, 0.1804375,
|
|
0.2126729, 0.7151522, 0.0721750,
|
|
0.0193339, 0.1191920, 0.9503041
|
|
);
|
|
|
|
// XYZ (D65) to sRGB Primaries
|
|
const mat3 M_XYZ_2_RGB_D65 = mat3(
|
|
3.2404542, -1.5371385, -0.4985314,
|
|
-0.9692660, 1.8760108, 0.0415560,
|
|
0.0556434, -0.2040259, 1.0572252
|
|
);
|
|
|
|
// Standard Illuminant D65 XYZ (Normalized Y=1) - Our Target White
|
|
const vec3 WHITEPOINT_D65 = vec3(0.95047, 1.00000, 1.08883);
|
|
|
|
// Bradford CAT Matrices
|
|
const mat3 M_BRADFORD = mat3(
|
|
0.8951, 0.2664, -0.1614,
|
|
-0.7502, 1.7135, 0.0367,
|
|
0.0389, -0.0685, 1.0296
|
|
);
|
|
const mat3 M_BRADFORD_INV = mat3(
|
|
0.9869929, -0.1470543, 0.1599627,
|
|
0.4323053, 0.5183603, 0.0492912,
|
|
-0.0085287, 0.0400428, 0.9684867
|
|
);
|
|
|
|
// --- Helper Functions ---
|
|
|
|
// Approximate Kelvin Temperature to XYZ Chromaticity (xy) -> XYZ coordinates
|
|
// Uses simplified polynomial fits for different temperature ranges.
|
|
// Note: More accurate methods often use look-up tables or spectral calculations.
|
|
vec3 kelvinToXYZ(float kelvin) {
|
|
float temp = clamp(kelvin, 1000.0, 20000.0);
|
|
float x, y;
|
|
|
|
// Calculate xy chromaticity coordinates based on temperature
|
|
// Formulas from: http://www.brucelindbloom.com/index.html?Eqn_T_to_xy.html (with slight adaptations)
|
|
float t = temp;
|
|
float t2 = t * t;
|
|
float t3 = t2 * t;
|
|
|
|
// Calculate x coordinate
|
|
if (t >= 1000.0 && t <= 4000.0) {
|
|
x = -0.2661239e9 / t3 - 0.2343589e6 / t2 + 0.8776956e3 / t + 0.179910;
|
|
} else { // t > 4000.0 && t <= 25000.0
|
|
x = -3.0258469e9 / t3 + 2.1070379e6 / t2 + 0.2226347e3 / t + 0.240390;
|
|
}
|
|
|
|
// Calculate y coordinate based on x
|
|
float x2 = x * x;
|
|
float x3 = x2 * x;
|
|
if (t >= 1000.0 && t <= 2222.0) {
|
|
y = -1.1063814 * x3 - 1.34811020 * x2 + 2.18555832 * x - 0.18709;
|
|
} else if (t > 2222.0 && t <= 4000.0) {
|
|
y = -0.9549476 * x3 - 1.37418593 * x2 + 2.09137015 * x - 0.16748867;
|
|
} else { // t > 4000.0 && t <= 25000.0
|
|
y = 3.0817580 * x3 - 5.8733867 * x2 + 3.75112997 * x - 0.37001483;
|
|
}
|
|
|
|
// Convert xyY (Y=1) to XYZ
|
|
if (y < 1e-6) return vec3(0.0); // Avoid division by zero
|
|
float Y = 1.0;
|
|
float X = (x / y) * Y;
|
|
float Z = ((1.0 - x - y) / y) * Y;
|
|
|
|
return vec3(X, Y, Z);
|
|
}
|
|
|
|
// Apply Bradford Chromatic Adaptation Transform
|
|
vec3 adaptXYZ(vec3 xyzColor, vec3 sourceWhiteXYZ, vec3 destWhiteXYZ) {
|
|
vec3 sourceCone = M_BRADFORD * sourceWhiteXYZ;
|
|
vec3 destCone = M_BRADFORD * destWhiteXYZ;
|
|
|
|
// Avoid division by zero if source cone response is zero
|
|
// (shouldn't happen with typical white points but good practice)
|
|
if (sourceCone.r < 1e-6 || sourceCone.g < 1e-6 || sourceCone.b < 1e-6) {
|
|
return xyzColor; // Return original color if adaptation is impossible
|
|
}
|
|
|
|
vec3 ratio = destCone / sourceCone;
|
|
|
|
mat3 adaptationMatrix = M_BRADFORD_INV * mat3(ratio.x, 0, 0, 0, ratio.y, 0, 0, 0, ratio.z) * M_BRADFORD;
|
|
|
|
return adaptationMatrix * xyzColor;
|
|
}
|
|
|
|
// --- Main White Balance Function ---
|
|
vec3 applyWhiteBalance(vec3 linearSRGBColor, float tempK, float tintVal) {
|
|
|
|
// 1. Convert input Linear sRGB (D65) to XYZ D65
|
|
vec3 inputXYZ = M_RGB_2_XYZ_D65 * linearSRGBColor;
|
|
|
|
// 2. Calculate the XYZ white point of the source illuminant (from Kelvin temp)
|
|
// This is the white we want to adapt *FROM*
|
|
vec3 sourceWhiteXYZ = kelvinToXYZ(tempK);
|
|
|
|
// 3. Apply Bradford CAT to adapt from sourceWhiteXYZ to D65
|
|
vec3 adaptedXYZ = adaptXYZ(inputXYZ, sourceWhiteXYZ, WHITEPOINT_D65);
|
|
|
|
// 4. Convert adapted XYZ (now relative to D65) back to Linear sRGB
|
|
vec3 adaptedLinearSRGB = M_XYZ_2_RGB_D65 * adaptedXYZ;
|
|
|
|
// 5. Apply Tint adjustment (Post-CAT approximation)
|
|
// This shifts color balance along a Green<->Magenta axis.
|
|
// We scale RGB components slightly based on the tint value.
|
|
// A common method is to affect Green opposite to Red/Blue.
|
|
if (abs(tintVal) > 0.01) {
|
|
// Map tint -100..100 to a scaling factor, e.g., -0.1..0.1
|
|
float tintFactor = tintVal / 1000.0; // Smaller scale factor for subtle tint
|
|
|
|
// Apply scaling: Increase G for negative tint, Decrease G for positive tint
|
|
// Compensate R and B slightly in the opposite direction.
|
|
// Coefficients below are heuristic and may need tuning for perceptual feel.
|
|
float rScale = 1.0 + tintFactor * 0.5;
|
|
float gScale = 1.0 - tintFactor * 1.0;
|
|
float bScale = 1.0 + tintFactor * 0.5;
|
|
|
|
vec3 tintScaleVec = vec3(rScale, gScale, bScale);
|
|
|
|
// Optional: Normalize scale vector to preserve luminance (roughly)
|
|
// Calculate luminance of the scale vector itself
|
|
float scaleLum = dot(tintScaleVec, vec3(0.2126, 0.7152, 0.0722));
|
|
if (scaleLum > 1e-5) {
|
|
tintScaleVec /= scaleLum; // Normalize
|
|
}
|
|
|
|
adaptedLinearSRGB *= tintScaleVec;
|
|
}
|
|
|
|
return adaptedLinearSRGB;
|
|
}
|
|
|
|
void main() {
|
|
vec4 texColor = texture(InputTexture, TexCoord);
|
|
vec3 linearInputColor = texColor.rgb; // Assuming input texture is already linear sRGB
|
|
|
|
// Calculate white balanced color
|
|
vec3 whiteBalancedColor = applyWhiteBalance(linearInputColor, temperature, tint);
|
|
|
|
// Ensure output is non-negative
|
|
whiteBalancedColor = max(whiteBalancedColor, vec3(0.0));
|
|
|
|
FragColor = vec4(whiteBalancedColor, texColor.a);
|
|
} |