J'essaie de changer la teinte d'une image à l'aide d'un shader de fragment GLSL. Je veux réaliser quelque chose de similaire au calque de réglage de la teinte / saturation de Photoshop.
Dans l'image suivante, vous pouvez voir ce que j'ai jusqu'à présent. Je veux changer la teinte du carré vert pour qu'il ressemble au carré rouge à droite, mais avec ce shader, j'obtiens un carré moitié rouge moitié rose (le carré au milieu).
Ce que je fais dans le fragment shader, c'est la conversion de la couleur de la texture en HSV, puis j'y ajoute la couleur HSV que j'obtiens du vertex shader et je reconvertis la couleur en RVB.
Qu'est-ce que je fais mal?
Shader de fragment:
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);
}
ÉDITER: En utilisant les fonctions fournies par Sam Hocevar dans sa réponse, le problème avec les bandes roses est résolu, mais je ne peux atteindre que la moitié du spectre de couleurs. Je peux changer la teinte du rouge au vert, mais je ne peux pas la changer en bleu ou rose.
Dans le fragment shader, je fais ceci maintenant:
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);
}
la source
int hi = int(h/60.0); float f = h/60.0 - float(hi);
place deint hi = int(h); float f = h - float(hi);
? Je ne sais pas si cela en est la cause.Réponses:
Ces fonctions fonctionneront très mal. Je suggère d'utiliser des fonctions écrites avec le GPU à l'esprit. Voici les miens:
Notez que pour ces fonctions, la plage de
H
[0… 1] au lieu de [0… 360], vous devrez donc adapter votre entrée.Source: http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
la source
if()
constructions, mais sont bons pour les opérations vectorielles (opérations parallèles sur plusieurs valeurs scalaires). Les fonctions ci-dessus n'utilisent jamaisif()
, essaient de paralléliser les opérations et, en général, elles utilisent moins d'instructions. Ce sont généralement de bons indicateurs qu'ils vont être plus rapides.Comme Nicol Bolas l'a suggéré dans les commentaires du post original, je poste la solution à mon problème dans une réponse séparée.
Le premier problème était que l'image était rendue avec des bandes roses, comme le montre l'image dans le message d'origine. Je l'ai corrigé en utilisant les fonctions fournies par Sam Hocevar dans sa réponse ( /gamedev//a/59808/22302 ).
Le deuxième problème était que je multipliais la teinte du pixel de la texture par la valeur que j'envoyais au shader, ce qui est censé être un décalage par rapport à la teinte du pixel des textures, j'ai donc dû effectuer un ajout au lieu d'une multiplication.
J'effectue toujours une multiplication pour la saturation et la luminosité, car j'obtiens un comportement étrange autrement, et je n'ai pas vraiment besoin de les augmenter plus que la saturation ou la luminosité de la texture d'origine pour le moment.
Il s'agit de la méthode main () du shader que j'utilise actuellement. Avec cela, je peux changer la teinte de 0º à 360º, désaturer l'image et réduire la luminosité.
la source
mod()
la teinte, mais la saturation et la luminosité vous voudrez peut-être à laclamp()
place.