2012-02-10 4 views
21

Есть ли способ эффективно изменить оттенок 2D-текстуры OpenGL с использованием GLSL (фрагментарный шейдер)?Как изменить оттенок текстуры с помощью GLSL?

У кого-то есть код?

UPDATE: Это код, полученный из 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); 
} 

И это результат по сравнению со ссылкой:

enter image description here

Я попытался переключиться я с Q внутри atan, но результат неправильный даже около 0 °

У вас есть любой намек?

При необходимости для сравнения, это оригинальное неизмененное изображение: enter image description here

+5

Это вращение RGB тройного вокруг (1,1,1) вектора - вы можете выразить, что в качестве матричного умножения в затенении – Flexo

+0

Если ваш оттенок сдвиг постоянен, то вы можете пропустить atan, sqrt, sin и cos. То, что вы делаете, это преобразование их в полярные координаты, добавление к углу и преобразование назад. Вы можете сделать эту математику с матрицей вращения 2x2. Если вам не нужно выполнять какую-либо другую математику в пространстве yiq, вы можете предварительно вычислить все преобразование.Умножьте свой rgb2yiq на 2d-поворот (дополняется до 3x3), а затем с yiq2rgb, чтобы получить одну большую матрицу 3x3, которая выполняет весь процесс. Который, как говорит @Flexo, - это просто поворот вокруг (1,1,1) вектора. –

ответ

23

Хотя то, что @awoodland говорит правильно, что метод может вызвать проблемы с изменением яркости, я считаю.

Системы цветов HSV и HLS являются проблематичными по ряду причин. Я недавно общался с цветным ученым, и его рекомендация заключалась в том, чтобы преобразовать в пространство YIQ или YCbCr и отрегулировать каналы цветности (I & Q, или Cb & Cr) соответственно. (Вы можете узнать, как сделать это here и here.)

После того, как в одном из этих пространств, вы можете получить оттенок от угла, образованного каналами цветности, делая hue = atan(cr/cb) (наблюдая за Сь == 0). Это дает вам ценность в радианах. Просто поверните его, добавив количество вращения оттенка. Как только вы это сделаете, вы можете рассчитать величину цветности с chroma = sqrt(cr*cr+cb*cb). Чтобы вернуться в RGB, вычислите новые Cb и Cr (или I & Q), используя Cr = chroma * sin (hue), Cb = chroma * cos (hue). Затем конвертируйте обратно в RGB, как описано на приведенных выше веб-страницах.

EDIT: Вот решение, которое я тестировал и, похоже, дает мне те же результаты, что и ваша ссылка. Вы, наверное, можно свернуть некоторые из продуктов точечных матриц размножаются:

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; 
} 
+0

Спасибо, я только что внедрил его, но он работает некорректно. На самом деле он дает изменение оттенка, но он полностью отличается от настройки оттенка Photoshop (например). Я решил преобразовать его в YIQ, потому что с YCbCr у меня были даже худшие результаты. Поэтому я вычислил 'hue = atan2 (Q, I)', 'chroma = sqrt (I * I + Q * Q)', а затем 'I = chroma * sin (hue)', 'Q = chorma * cos (hue) '. Правильно ли это? Я что-то упускаю? – Andrea3000

+0

Это кажется правильным для меня. Какие результаты вы получаете? (Является ли только оттенок неправильным, или яркость и насыщенность тоже запутались?) Получают ли результаты больше похоже на то, что вы ожидаете, если вы отмените I и Q в вызове 'atan2()'? Возможно, если вы разместите какой-то код, мы сможем его проверить. – user1118321

+0

Благодарим за внимание, я обновил вопрос с помощью кода и моментального снимка результата. – Andrea3000

3

Andrea3000, сравнивая примеры YIQ в сети, я наткнулся на ваш проводок, но я думаю, что есть проблема с «обновленной» версией ваш код .... я уверен, что ваши определения «mat3» flip/flopped для упорядочивания столбцов/строк ... (возможно, поэтому у вас все еще были проблемы) ...

FYI: Задание матрицы OpenGL : «Для большего количества значений матрицы заполняются в основном порядке столбца. То есть первые значения X являются первым столбцом, а вторыми значениями X являются следующий столбец и т. Д.». См: http://www.opengl.org/wiki/GLSL_Types

mat2(
float, float, //first column 
float, float); //second column 
Смежные вопросы