2012-02-10 31 views
21

有沒有辦法使用GLSL(片段着色器)有效地改變2D OpenGL紋理的色調?如何用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

我試圖開關I與Q內atan但結果是錯誤的,即使在0°左右

你有沒有任何提示?

如果需要作比較,這是原始未修改的圖像: enter image description here

+5

這是RGB各地(1,1,1)向量三重旋轉 - 你可以表達作爲着色 – Flexo 2012-02-10 21:00:48

+0

矩陣乘法如果你的色調偏移是恆定的,那麼你可以跳過atan,sqrt,sin和cos。你正在做的是將它們轉換爲極座標,增加角度並轉換回來。你可以用2x2旋轉矩陣來做這個數學運算。如果您不需要在yiq空間中執行任何其他數學運算,則可以預先計算整個變換。將你的rgb2yiq與2d旋轉相乘(填充爲3x3),然後用yiq2rgb得到一個大的3x3矩陣,完成整個過程。像@Flexo所說的就是圍繞(1,1,1)向量的旋轉。 – 2012-09-07 18:19:29

回答

23

雖然什麼@awoodland說是正確的,這種方法可能會導致在亮度變化的問題,我相信。

由於多種原因,HSV和HLS顏色系統存在問題。我最近和一位色彩科學家討論過這個問題,他的建議是轉換爲YIQ或YCbCr空間,並相應地調整色度通道(I & Q或Cb & Cr)。 (你可以學習如何做到這一點herehere。)

一旦這些空間之一,你可以從色度通道形成的角度色調,通過做hue = atan(cr/cb)(留意CB == 0)。這給你一個弧度值。只需添加色相旋轉量即可旋轉它。一旦你這樣做了,你可以用chroma = sqrt(cr*cr+cb*cb)來計算色度的大小。要返回RGB,請使用Cr = chroma * sin (hue)Cb = chroma * cos (hue)計算新的Cb和Cr(或I & Q)。然後按照上述網頁中的描述轉換回RGB。

編輯:這是我已經測試的解決方案,似乎給我與您的參考相同的結果。你也許可以摺疊的一些點產品進入矩陣乘法:

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 )'。這是對的嗎?我錯過了什麼嗎? – Andrea3000 2012-02-11 15:00:52

+0

這對我來說似乎是正確的。你得到了什麼結果? (只是色相錯誤,或者亮度和飽和度也越來越混亂?)如果您在'atan2()'調用中反轉I和Q,結果是否更像您所期望的?也許如果你發佈了一些代碼,我們可以爲你檢查一下。 – user1118321 2012-02-11 17:25:53

+0

感謝您的關注,我使用代碼更新了問題並提供了結果快照。 – Andrea3000 2012-02-11 18:31:21

3

Andrea3000,在網上比較YIQ例子,我遇到了你的帖子,但我認爲是的「更新」版本的問題你的代碼....我確定你的'mat3'定義在列/行順序上翻轉/翻轉...(也許這就是爲什麼你仍然有麻煩)...

FYI:OpenGL矩陣排序:「對於更多的值,矩陣按列主要順序填充,也就是說,第一個X值是第一列,第二個X值是下一列,等等。」請參閱:http://www.opengl.org/wiki/GLSL_Types

mat2(
float, float, //first column 
float, float); //second column