Estou tentando alterar o tom de uma imagem usando um shader de fragmento GLSL. Quero obter algo semelhante à camada de ajuste de matiz / saturação do Photoshop.
Na imagem a seguir, você pode ver o que tenho até agora. Quero alterar a tonalidade do quadrado verde para que ele se pareça com o quadrado vermelho à direita, mas com esse sombreador recebo um quadrado meio vermelho meio rosa (o quadrado no meio).
O que estou fazendo no shader de fragmento é converter a cor da textura em HSV, depois adiciono a cor HSV que recebo do shader de vértice e converto a cor novamente em RGB.
O que estou fazendo de errado?
Shader de fragmento:
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);
}
EDIT: Usando as funções que Sam Hocevar forneceu em sua resposta, o problema com as faixas cor-de-rosa está resolvido, mas só consigo atingir metade do espectro de cores. Posso alterar o tom de vermelho para verde, mas não posso alterá-lo para azul ou rosa.
No shader de fragmento, estou fazendo isso agora:
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);
}
fonte
int hi = int(h/60.0); float f = h/60.0 - float(hi);
vez deint hi = int(h); float f = h - float(hi);
? Mas não sei se isso está causando isso.Respostas:
Essas funções terão um desempenho muito ruim. Sugiro o uso de funções escritas com a GPU em mente. Aqui estão os meus:
Observe que para essas funções o intervalo para
H
é [0… 1] em vez de [0… 360], portanto você terá que adaptar sua entrada.Fonte: http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
fonte
if()
construções, mas são boas para operações vetoriais (operações paralelas em vários valores escalares). As funções acima nunca usamif()
, tentam paralelizar operações e, em geral, usam menos instruções. Esses geralmente são bons indicadores de que eles serão mais rápidos.Como sugeriu Nicol Bolas nos comentários da postagem original, estou postando a solução para o meu problema em uma resposta separada.
A primeira edição foi a imagem sendo renderizada com faixas cor de rosa, como mostra a imagem no post original. Corrigi-o usando as funções que Sam Hocevar forneceu em sua resposta ( /gamedev//a/59808/22302 ).
A segunda questão era que eu estava multiplicando a tonalidade do pixel da textura pelo valor que estava enviando ao shader, que deveria ser um deslocamento da tonalidade do pixel das texturas, então tive que realizar uma adição em vez de uma multiplicação.
Ainda faço uma multiplicação para saturação e brilho porque, de outra forma, recebo um comportamento estranho, e não preciso incrementá-los mais do que a saturação ou o brilho da textura original no momento.
Este é o método main () do shader que estou usando agora. Com isso, posso mudar o tom de 0º para 360º, dessaturar a imagem e reduzir o brilho.
fonte
mod()
a tonalidade, mas saturação e brilho talvez queiraclamp()
.