Estoy tratando de cambiar el tono de una imagen usando un sombreador de fragmentos GLSL. Quiero lograr algo similar a la capa de ajuste de tono / saturación de Photoshop.
En la siguiente imagen puedes ver lo que tengo hasta ahora. Quiero cambiar el tono del cuadrado verde para que se vea como el cuadrado rojo a la derecha, pero con este sombreador obtengo un cuadrado medio rojo medio rosado (el cuadrado en el medio).
Lo que estoy haciendo en el sombreador de fragmentos es convertir el color de la textura a HSV, luego agrego el color HSV que obtengo del sombreador de vértices y vuelvo a convertir el color a RGB.
¿Qué estoy haciendo mal?
Sombreador de fragmentos:
precision mediump float;
varying vec2 vTextureCoord;
varying vec3 vHSV;
uniform sampler2D sTexture;
vec3 convertRGBtoHSV(vec3 rgbColor) {
float r = rgbColor[0];
float g = rgbColor[1];
float b = rgbColor[2];
float colorMax = max(max(r,g), b);
float colorMin = min(min(r,g), b);
float delta = colorMax - colorMin;
float h = 0.0;
float s = 0.0;
float v = colorMax;
vec3 hsv = vec3(0.0);
if (colorMax != 0.0) {
s = (colorMax - colorMin ) / colorMax;
}
if (delta != 0.0) {
if (r == colorMax) {
h = (g - b) / delta;
} else if (g == colorMax) {
h = 2.0 + (b - r) / delta;
} else {
h = 4.0 + (r - g) / delta;
}
h *= 60.0;
if (h < 0.0) {
h += 360.0;
}
}
hsv[0] = h;
hsv[1] = s;
hsv[2] = v;
return hsv;
}
vec3 convertHSVtoRGB(vec3 hsvColor) {
float h = hsvColor.x;
float s = hsvColor.y;
float v = hsvColor.z;
if (s == 0.0) {
return vec3(v, v, v);
}
if (h == 360.0) {
h = 0.0;
}
int hi = int(h);
float f = h - float(hi);
float p = v * (1.0 - s);
float q = v * (1.0 - (s * f));
float t = v * (1.0 - (s * (1.0 - f)));
vec3 rgb;
if (hi == 0) {
rgb = vec3(v, t, p);
} else if (hi == 1) {
rgb = vec3(q, v, p);
} else if (hi == 2) {
rgb = vec3(p, v, t);
} if(hi == 3) {
rgb = vec3(p, q, v);
} else if (hi == 4) {
rgb = vec3(t, p, v);
} else {
rgb = vec3(v, p, q);
}
return rgb;
}
void main() {
vec4 textureColor = texture2D(sTexture, vTextureCoord);
vec3 fragRGB = textureColor.rgb;
vec3 fragHSV = convertRGBtoHSV(fragRGB);
fragHSV += vHSV;
fragHSV.x = mod(fragHSV.x, 360.0);
fragHSV.y = mod(fragHSV.y, 1.0);
fragHSV.z = mod(fragHSV.z, 1.0);
fragRGB = convertHSVtoRGB(fragHSV);
gl_FragColor = vec4(convertHSVtoRGB(fragHSV), textureColor.w);
}
EDITAR: Usando las funciones que Sam Hocevar proporcionó en su respuesta, el problema con las bandas de color rosa está resuelto, pero solo puedo alcanzar la mitad del espectro de color. Puedo cambiar el tono de rojo a verde, pero no puedo cambiarlo a azul o rosa.
En el fragment shader, estoy haciendo esto ahora:
void main() {
vec4 textureColor = texture2D(sTexture, vTextureCoord);
vec3 fragRGB = textureColor.rgb;
vec3 fragHSV = rgb2hsv(fragRGB);
float h = vHSV.x / 360.0;
fragHSV.x *= h;
fragHSV.yz *= vHSV.yz;
fragHSV.x = mod(fragHSV.x, 1.0);
fragHSV.y = mod(fragHSV.y, 1.0);
fragHSV.z = mod(fragHSV.z, 1.0);
fragRGB = hsv2rgb(fragHSV);
gl_FragColor = vec4(hsv2rgb(fragHSV), textureColor.w);
}
fuente
int hi = int(h/60.0); float f = h/60.0 - float(hi);
lugar deint hi = int(h); float f = h - float(hi);
? Sin embargo, no sé si eso lo está causando.Respuestas:
Estas funciones funcionarán muy mal. Sugiero usar funciones que están escritas con la GPU en mente. Aquí están los míos:
Tenga en cuenta que para estas funciones el rango
H
es [0 ... 1] en lugar de [0 ... 360], por lo que tendrá que adaptar su entrada.Fuente: http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
fuente
if()
construcciones grandes , pero son buenas para operaciones vectoriales (operaciones paralelas en varios valores escalares). Las funciones anteriores nunca se usanif()
, intentan paralelizar operaciones y, en general, usan menos instrucciones. Estos suelen ser buenos indicadores de que serán más rápidos.Como Nicol Bolas sugirió en los comentarios de la publicación original, estoy publicando la solución a mi problema en una respuesta separada.
El primer problema fue que la imagen se representaba con bandas rosas, como lo muestra la imagen en la publicación original. Lo arreglé usando las funciones que Sam Hocevar proporcionó en su respuesta ( /gamedev//a/59808/22302 ).
El segundo problema fue que estaba multiplicando el tono del píxel de la textura por el valor que estaba enviando al sombreador, que está destinado a ser un desplazamiento del tono del píxel de las texturas, por lo que tuve que realizar una adición en lugar de una multiplicación.
Todavía realizo una multiplicación por saturación y brillo porque de lo contrario obtengo un comportamiento extraño, y realmente no necesito incrementarlos más allá de la saturación o brillo de la textura original en este momento.
Este es el método main () del sombreador que estoy usando en este momento. Con esto puedo cambiar el tono de 0º a 360º, desaturar la imagen y reducir el brillo.
fuente
mod()
el tono, pero la saturación y el brillo quizás quierasclamp()
hacerlo.