opengl - ¿Cómo cambiar el tono de una textura con GLSL?
filter hsv (2)
Andrea3000, al comparar los ejemplos de YIQ en la red, me encontré con su publicación, pero creo que hay un problema con la versión "actualizada" de su código ... estoy seguro de que sus definiciones de "mat3" se invierten o fallan. el orden de la columna / fila ... (tal vez por eso todavía tenías problemas) ...
FYI: orden de la matriz OpenGL: "Para más valores, las matrices se completan en orden de columna mayor. Es decir, los primeros valores X son la primera columna, los segundos valores X son la columna siguiente, y así sucesivamente". Ver: http://www.opengl.org/wiki/GLSL_Types
mat2(
float, float, //first column
float, float); //second column
¿Hay alguna forma de cambiar de manera eficiente el tono de una textura 2D OpenGL usando GLSL (fragment shader)?
¿Alguien tiene algún código para eso?
ACTUALIZACIÓN: este es el código resultante de la sugerencia user1118321:
uniform sampler2DRect texture;
const mat3 rgb2yiq = mat3(0.299, 0.587, 0.114, 0.595716, -0.274453, -0.321263, 0.211456, -0.522591, 0.311135);
const mat3 yiq2rgb = mat3(1.0, 0.9563, 0.6210, 1.0, -0.2721, -0.6474, 1.0, -1.1070, 1.7046);
uniform float hue;
void main() {
vec3 yColor = rgb2yiq * texture2DRect(texture, gl_TexCoord[0].st).rgb;
float originalHue = atan(yColor.b, yColor.g);
float finalHue = originalHue + hue;
float chroma = sqrt(yColor.b*yColor.b+yColor.g*yColor.g);
vec3 yFinalColor = vec3(yColor.r, chroma * cos(finalHue), chroma * sin(finalHue));
gl_FragColor = vec4(yiq2rgb*yFinalColor, 1.0);
}
Y este es el resultado comparado con una referencia:
He intentado cambiar I con Q dentro de atan pero el resultado es incorrecto incluso alrededor de 0 °
¿Tienes alguna pista?
Si es necesario para comparar, esta es la imagen original no modificada:
Si bien lo que @awoodland dice es correcto, creo que ese método puede causar problemas con los cambios en la luminancia.
Los sistemas de color HSV y HLS son problemáticos por varias razones. Hablé con un científico del color sobre esto recientemente, y su recomendación fue convertir al espacio YIQ o YCbCr y ajustar los canales de croma (I & Q, o Cb & Cr) en consecuencia. (Puedes aprender cómo hacer eso here y here ).
Una vez en uno de esos espacios, puedes obtener el tono del ángulo formado por los canales de croma, haciendo hue = atan(cr/cb)
(observando cb == 0). Esto te da un valor en radianes. Simplemente gírelo agregando la cantidad de rotación del tono. Una vez que haya hecho eso, puede calcular la magnitud del croma con chroma = sqrt(cr*cr+cb*cb)
. Para volver a RGB, calcule el nuevo Cb y Cr (o I y Q) usando Cr = chroma * sin (hue)
, Cb = chroma * cos (hue)
. A continuación, vuelva a convertir a RGB como se describe en las páginas web anteriores.
EDITAR: Aquí hay una solución que he probado y parece darme los mismos resultados que su referencia. Probablemente pueda colapsar algunos de los productos de puntos en multiplicaciones de matriz:
uniform sampler2DRect inputTexture;
uniform float hueAdjust;
void main ()
{
const vec4 kRGBToYPrime = vec4 (0.299, 0.587, 0.114, 0.0);
const vec4 kRGBToI = vec4 (0.596, -0.275, -0.321, 0.0);
const vec4 kRGBToQ = vec4 (0.212, -0.523, 0.311, 0.0);
const vec4 kYIQToR = vec4 (1.0, 0.956, 0.621, 0.0);
const vec4 kYIQToG = vec4 (1.0, -0.272, -0.647, 0.0);
const vec4 kYIQToB = vec4 (1.0, -1.107, 1.704, 0.0);
// Sample the input pixel
vec4 color = texture2DRect (inputTexture, gl_TexCoord [ 0 ].xy);
// Convert to YIQ
float YPrime = dot (color, kRGBToYPrime);
float I = dot (color, kRGBToI);
float Q = dot (color, kRGBToQ);
// Calculate the hue and chroma
float hue = atan (Q, I);
float chroma = sqrt (I * I + Q * Q);
// Make the user''s adjustments
hue += hueAdjust;
// Convert back to YIQ
Q = chroma * sin (hue);
I = chroma * cos (hue);
// Convert back to RGB
vec4 yIQ = vec4 (YPrime, I, Q, 0.0);
color.r = dot (yIQ, kYIQToR);
color.g = dot (yIQ, kYIQToG);
color.b = dot (yIQ, kYIQToB);
// Save the result
gl_FragColor = color;
}