Я пытаюсь изменить оттенок изображения, используя фрагментный шейдер GLSL. Я хочу добиться чего-то похожего на слой корректировки оттенка / насыщенности в Photoshop.
На следующем изображении вы можете увидеть, что я получил до сих пор. Я хочу изменить оттенок зеленого квадрата, чтобы он выглядел как красный квадрат справа, но с помощью этого шейдера я получаю наполовину красный наполовину розовый квадрат (квадрат посередине).
То, что я делаю в фрагментном шейдере, это преобразование цвета текстуры в HSV, затем я добавляю в него цвет HSV, полученный от вершинного шейдера, и преобразовываю цвет обратно в RGB.
Что я делаю неправильно?
Фрагмент шейдера:
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);
}
РЕДАКТИРОВАТЬ: Используя функции Сэма Hocevar, представленные в его ответе, проблема с розовыми полосами решена, но я могу достичь только половины цветового спектра. Я могу изменить оттенок с красного на зеленый, но я не могу изменить его на синий или розовый.
В фрагментном шейдере я делаю это сейчас:
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);
}
источник
int hi = int(h/60.0); float f = h/60.0 - float(hi);
вместоint hi = int(h); float f = h - float(hi);
? Не знаю, если это вызывает это, хотя.Ответы:
Эти функции будут выполнять очень плохо. Я предлагаю использовать функции, написанные для GPU. Вот мой:
Обратите внимание, что для этих функций диапазон для
H
[0… 1] вместо [0… 360], поэтому вам придется адаптировать свой вход.Источник: http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
источник
if()
конструкций, но хороши в векторных операциях (параллельные операции с несколькими скалярными значениями). Вышеуказанные функции никогда не используютif()
, стараются распараллелить операции, и в целом они используют меньше инструкций. Обычно это хорошие показатели того, что они будут быстрее.Как предложил Ник Болас в комментариях к исходному сообщению, я публикую решение своей проблемы в отдельном ответе.
Первой проблемой было изображение, отображаемое с розовыми полосами, как показано на оригинальном посте. Я исправил это, используя функции, которые Сэм Хочевар предоставил в своем ответе ( /gamedev//a/59808/22302 ).
Вторая проблема заключалась в том, что я умножал оттенок пикселя текстуры на значение, которое я отправлял в шейдер, что означает смещение от оттенка пикселя текстур, поэтому мне пришлось выполнить сложение вместо умножения.
Я все еще выполняю умножение для насыщенности и яркости, потому что иначе получаю странное поведение, и мне не нужно увеличивать их дальше, чем насыщенность или яркость исходной текстуры в данный момент.
Это метод main () шейдера, который я сейчас использую. Благодаря этому я могу изменить цветовой тон от 0º до 360º, обесцветить изображение и уменьшить яркость.
источник
mod()
оттенок, но насыщенность и яркость вы можетеclamp()
вместо этого.