2012-02-10 22 views
21

Czy istnieje sposób, aby skutecznie zmienić odcień tekstury 2D OpenGL za pomocą GLSL (fragment shader)?Jak zmienić odcień tekstury za pomocą GLSL?

Czy ktoś ma jakiś kod?

UPDATE: Jest to kod wynikające z user1118321 sugestia:

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); 
} 

I to jest wynik w porównaniu z wzorcem:

enter image description here

próbowałem przełączyć ja z Q wewnątrz atan, ale wynik jest nieprawidłowy nawet około 0 °

Masz jakaś podpowiedź?

Jeśli potrzebne dla porównania, jest to oryginalny obraz niemodyfikowana: enter image description here

+5

Jest to obrót wokół potrójnej RGB (1,1,1) Vector - można wyrazić jako że mnożenie macierzy w cieniującego – Flexo

+0

Jeśli zmiana odcienia jest stała wtedy można pominąć atan, sqrt, sin i cos. Zmieniasz je na współrzędne biegunowe, zwiększając kąt i przekształcając z powrotem. Możesz to zrobić matematyką z macierzą rotacji 2x2. Jeśli nie musisz wykonywać żadnej innej matematyki w przestrzeni yiq, możesz wstępnie obliczyć całą transformację.Pomnóż swój rgb2yiq z obrotem 2d (wyniesione do 3x3), a następnie z yiq2rgb, aby uzyskać jedną dużą matrycę 3x3, która wykonuje cały proces. Które, tak jak mówi @Flexo, to tylko obrót wokół wektora (1,1,1). –

Odpowiedz

23

Choć co @awoodland mówi jest poprawna, że ​​metoda może powodować problemy ze zmianami w luminancji, wierzę.

Systemy kolorów HSV i HLS są problematyczne z wielu powodów. Ostatnio rozmawiałem o tym z naukowcem zajmującym się kolorystyką, a jego rekomendacją było przekonwertowanie do przestrzeni YIQ lub YCbCr i dostosowanie kanałów chroma (I & Q lub Cb & Cr) odpowiednio. (Można dowiedzieć się, jak zrobić to here i here.)

Gdy w jednym z tych miejsc, można uzyskać odcień od kąta utworzonego przez kanały chroma, wykonując hue = atan(cr/cb) (oglądając dla cb == 0). Daje to wartość w radianach. Po prostu obróć go, dodając wielkość obrotu barwy. Gdy to zrobisz, możesz obliczyć wielkość barwy za pomocą chroma = sqrt(cr*cr+cb*cb). Aby powrócić do RGB, obliczyć nowe Cb i Cr (lub I & Q) przy użyciu Cr = chroma * sin (hue), . Następnie przekonwertuj z powrotem na RGB zgodnie z opisem na powyższych stronach internetowych.

EDYCJA: Oto rozwiązanie, które przetestowałem i wydaje mi się, że daje takie same wyniki, jak Twoje odniesienie. Prawdopodobnie można zwinąć niektóre iloczyn skalarny w mnoży macierzy:

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

Dziękuję, właśnie go zaimplementowałem, ale wygląda na to, że działa nieprawidłowo. W rzeczywistości daje zmianę odcienia, ale zupełnie różni się od dostosowania odcienia programu Photoshop (na przykład). Postanowiłem przekonwertować go na YIQ, ponieważ przy YCbCr miałem nawet najgorsze wyniki. Dlatego obliczyłem 'hue = atan2 (Q, I)', 'chroma = sqrt (I * I + Q * Q)', a następnie 'I = chroma * sin (odcień)', 'Q = chorma * cos (odcień)) '. Czy to jest poprawne? Czy czegoś brakuje? – Andrea3000

+0

Wydaje mi się to poprawne. Jakie masz rezultaty? (Czy tylko odcień jest niewłaściwy, czy też luminarz i nasycenie zostają pomieszane?) Czy wyniki wyglądają bardziej jak to, czego oczekujesz, jeśli odwrócisz I i Q w wywołaniu 'atan2()'? Może gdybyś opublikował jakiś kod, moglibyśmy to sprawdzić dla ciebie. – user1118321

+0

Dziękuję za uwagę, zaktualizowałem pytanie za pomocą kodu i migawki wyniku. – Andrea3000

3

Andrea3000, porównując przykłady YIQ w internecie, natknąłem się na komentarz, ale myślę, że jest problem z wersją „zaktualizowanego” z Twój kod .... jestem pewien, że twoje definicje 'mat3' są odwrócone/flopped na kolejność kolumn/rzędów ... (być może dlatego nadal masz kłopoty) ...

FYI: Zamawianie macierzy OpenGL : "W przypadku większej liczby macierzy są wypełniane w porządku kolumna-większa, tzn. Pierwsze wartości X są pierwszą kolumną, drugą wartością X jest następna kolumna itd." Zobacz: http://www.opengl.org/wiki/GLSL_Types

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