Back to shaders
Shader test bench
Auto Colors Histogram.fs
runnable fragment
Complete GLSL fragment shader. Stronghold runs it directly when the browser can compile it.
Code
uniform sampler2D inputImage;
uniform sampler2D sourceHistogram;
uniform int colorMode;
uniform int colorAdjustment;
uniform float maxGain;
uniform float threshold;
uniform float timeSmoothing;
precision mediump float;
/*
{
"CATEGORIES" : [
"Color Adjustment", "Histogram"
],
"DESCRIPTION" : "",
"ISFVSN" : "2",
"INPUTS" : [
{
"NAME" : "inputImage",
"TYPE" : "image"
},
{
"NAME" : "sourceHistogram",
"TYPE" : "image"
},
{
"DEFAULT": 0,
"LABEL": "Normalization Mode",
"LABELS": [
"Average",
"Darker",
"Brighter",
"RGB"
],
"NAME": "colorMode",
"TYPE": "long",
"VALUES": [
0,
1,
2,
3
]
},
{
"DEFAULT": 1,
"LABEL": "Adjustment Type",
"LABELS": [
"Contrast",
"Gamma"
],
"NAME": "colorAdjustment",
"TYPE": "long",
"VALUES": [
0,
1
]
},
{
"NAME" : "maxGain",
"TYPE" : "float",
"MAX" : 8,
"DEFAULT" : 2,
"MIN" : 1
},
{
"NAME" : "threshold",
"TYPE" : "float",
"MAX" : 1.0,
"DEFAULT" : 0.02,
"MIN" : 0.0
},
{
"NAME" : "timeSmoothing",
"TYPE" : "float",
"MAX" : 1.0,
"DEFAULT" : 0.0,
"MIN" : 0.0
}
],
"PASSES" : [
{
"WIDTH" : "2",
"TARGET" : "minmax",
"HEIGHT" : "1"
},
{
"WIDTH" : "2",
"TARGET" : "smoothedminmax",
"HEIGHT" : "1",
"PERSISTENT": true,
"FLOAT": true
},
{
}
],
"CREDIT" : "VIDVOX"
}
*/
float minChannel (vec3 c) {
float tmpVal = c.r;
if (c.g < tmpVal)
tmpVal = c.g;
if (c.b < tmpVal)
tmpVal = c.b;
return tmpVal;
}
float maxChannel (vec3 c) {
float tmpVal = c.r;
if (c.g > tmpVal)
tmpVal = c.g;
if (c.b > tmpVal)
tmpVal = c.b;
return tmpVal;
}
void main() {
vec4 returnMe = vec4(0.0);
if (PASSINDEX==0) {
//float minOrMax = ((gl_FragCoord.xy / u_resolution.xy).x < 0.5) ? 0.0 : 1.0;
vec4 accum = vec4(0.0);
// go through each pixel of sourceHistogram
// find the darkest and brightest for each color channel
if ((gl_FragCoord.xy / u_resolution.xy).x < 0.5) {
returnMe.a = 1.0;
for(int i=0;i<256;++i) {
vec4 histo = texture2D(sourceHistogram, (vec2(float(i) / u_resolution.xy),0.5));
accum += histo;
if (returnMe.r == 0.0) {
if (accum.r > threshold) {
returnMe.r = float(i)/255.0;
}
}
if (returnMe.g == 0.0) {
if (accum.g > threshold) {
returnMe.g = float(i)/255.0;
}
}
if (returnMe.b == 0.0) {
if (accum.b > threshold) {
returnMe.b = float(i)/255.0;
}
}
if ((returnMe.r != 0.0)&&(returnMe.g != 0.0)&&(returnMe.b != 0.0)) {
break;
}
}
}
else {
returnMe = vec4(1.0);
for(int i=255;i>=0;--i) {
vec4 histo = texture2D(sourceHistogram, (vec2(float(i) / u_resolution.xy),0.5));
accum += histo;
if (returnMe.r == 1.0) {
if (accum.r > threshold) {
returnMe.r = float(i)/255.0;
}
}
if (returnMe.g == 1.0) {
if (accum.g > threshold) {
returnMe.g = float(i)/255.0;
}
}
if (returnMe.b == 1.0) {
if (accum.b > threshold) {
returnMe.b = float(i)/255.0;
}
}
if ((returnMe.r != 1.0)&&(returnMe.g != 1.0)&&(returnMe.b != 1.0)) {
break;
}
}
}
}
else if (PASSINDEX==1) {
vec4 freshPixel = texture2D(minmax, (gl_FragCoord.xy) / u_resolution.xy);
vec4 stalePixel = texture2D(smoothedminmax, (gl_FragCoord.xy) / u_resolution.xy);
returnMe = mix(freshPixel,stalePixel,timeSmoothing);
if (colorMode == 0) {
// Average
returnMe = vec4((returnMe.r + returnMe.g + returnMe.b) / 3.0);
}
else if (colorMode == 1) {
// we want to subtract the biggest and maximize the spread
// find the brightest rgb channel
float tmpVal = maxChannel(returnMe.rgb);
returnMe = vec4(tmpVal);
}
else if (colorMode == 2) {
// we want to subtract the smallest and minimize the spread
// find the darkest rgb channel in min
float tmpVal = minChannel(returnMe.rgb);
returnMe = vec4(tmpVal);
}
}
else if (PASSINDEX==2) {
returnMe = IMG_THIS_PIXEL(inputImage);
vec4 minVal = texture2D(smoothedminmax, (vec2(0.5,0.5) / u_resolution.xy));
vec4 maxVal = texture2D(smoothedminmax, (vec2(1.5,0.5) / u_resolution.xy));
if (colorAdjustment == 0) {
vec4 contrastVal = (colorMode == 1) ? (maxVal - minVal) : 1.0 / (maxVal - minVal);
if (colorMode == 1) {
float iMaxGain = 1.0 / maxGain;
if (contrastVal.r < iMaxGain)
contrastVal.r = iMaxGain;
if (contrastVal.g < iMaxGain)
contrastVal.g = iMaxGain;
if (contrastVal.b < iMaxGain)
contrastVal.b = iMaxGain;
}
else {
if (contrastVal.r > maxGain)
contrastVal.r = maxGain;
if (contrastVal.g > maxGain)
contrastVal.g = maxGain;
if (contrastVal.b > maxGain)
contrastVal.b = maxGain;
}
returnMe.r = (returnMe.r - minVal.r) * contrastVal.r;
returnMe.g = (returnMe.g - minVal.g) * contrastVal.g;
returnMe.b = (returnMe.b - minVal.b) * contrastVal.b;
}
else if (colorAdjustment == 1) {
// the input gamma range is 0.0-1.0 (normalized). the actual gamma range i want to use is 0.0 - 5.0.
// however, actual gamma 0.0-1.0 is just as interesting as actual gamma 1.0-5.0, so we scale the normalized input to match...
vec4 realGamma = (colorMode == 1) ? 1.0 / (maxVal - minVal) - 1.0 : 1.0 / (maxVal - minVal) - 0.5;
if (realGamma.r > maxGain)
realGamma.r = maxGain;
if (realGamma.g > maxGain)
realGamma.g = maxGain;
if (realGamma.b > maxGain)
realGamma.b = maxGain;
float iMaxGain = 1.0 / maxGain;
if (realGamma.r < iMaxGain)
realGamma.r = iMaxGain;
if (realGamma.g < iMaxGain)
realGamma.g = iMaxGain;
if (realGamma.b < iMaxGain)
realGamma.b = iMaxGain;
returnMe.rgb = pow(returnMe.rgb, vec3(1.0/realGamma));
}
}
gl_FragColor = returnMe;
}