2012-02-10 19 views
21

GLSL (parça gölgelendirici) kullanarak bir 2D OpenGL dokusu tonunu etkili bir şekilde değiştirmek için bir yol var mı?GLSL ile bir doku tonunu nasıl değiştirebilirim?

Birisi bunun için bir kod var mı?

GÜNCELLEME: Bu user1118321 öneri kaynaklanan kodudur:

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

Ve bu referans ile karşılaştırıldığında sonucudur:

enter image description here

Birlikte I geçmek için çalıştık Q içinde atan ama sonuçta bile 0 °

var herhangi bir ipucu?

karşılaştırma için Gerekirse

, bu orijinal değiştirilmemiş görüntü: Ne @awoodland diyor doğrudur enter image description here

+5

O (1,1,1) vektörü etrafında üçlü RGB bir rotasyon var - gölgelendiriciye – Flexo

+0

bir matris çarpımı olarak ifade edebilirim ki, senin tonu kayması sonra atlayabilirsiniz sabit ise atan, sqrt, sin, ve cos. Yaptığınız şey onları kutupsal koordinatlara dönüştürmek, açıya eklemek ve geri dönüştürmek. Bu matematiği 2x2 rotasyon matrisi ile yapabilirsiniz. Yiq alanında başka bir matematik yapmanız gerekmiyorsa, tüm dönüşümü önceden hesaplayabilirsiniz.Rgb2yiq'inizi 2d döndürme ile (3x3'e kadar çikarilir) ve daha sonra yiq2rgb ile tüm islemi yapan büyük bir 3x3 matrisini elde edin. @Flexo'nun söylediği gibi (1,1,1) vektörü etrafında sadece bir dönüş. –

cevap

23

iken, bu yöntem parlaklıkta değişikliklerle sorunlarına neden olabilir, inanıyorum.

HSV ve HLS renk sistemleri bir dizi nedenden ötürü sorunludur. Son zamanlarda bu konuyla ilgili bir renk bilimcisi ile konuştum ve önerisi YIQ veya YCbCr alanına dönüştürmek ve buna göre chroma kanallarını (I & Q veya Cb & Cr) uygun şekilde ayarlamaktı. (Bunu here ve here nasıl öğrenebilir.)

kez bu alanların birinde, sen hue = atan(cr/cb) yaparak, kroma kanallar tarafından oluşturulan açıdan tonu elde edebilirsiniz (cb izliyor == 0). Bu size radyanlarda bir değer verir. Sadece ton döndürme miktarını ekleyerek döndürün. Bunu yaptıktan sonra, kroma büyüklüğünü chroma = sqrt(cr*cr+cb*cb) ile hesaplayabilirsiniz. RGB'ye geri dönmek için Cr = chroma * sin (hue), Cb = chroma * cos (hue) kullanarak yeni Cb ve Cr (veya I & Q) hesaplayın. Daha sonra yukarıdaki web sayfalarında açıklandığı gibi tekrar RGB'ye dönün.

DÜZENLEME: İşte test ettiğim ve bana referansınızla aynı sonuçları veren bir çözüm. Muhtemelen matris çarpar içine nokta bazı ürünler daraltabilirsiniz:

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

Teşekkür ederim, daha yeni uyguladık ama doğru çalışmıyor gibi görünmüyor. Aslında bir renk tonu değişikliği sağlıyor ancak Photoshop ton ayarından tamamen farklıdır (örneğin). YCQ'ye dönüştürmeye karar verdim çünkü YCbCr ile en kötü sonuçları bile elde ettim. Bu yüzden, 'hue = atan2 (Q, I) ',' chroma = sqrt (I * I + Q * Q) 've' I = chroma * sin (hue) ',' Q = chorma * cos (hue) hesapladım.) '. Doğru mu? Bir şey mi eksik? – Andrea3000

+0

Bu bana doğru görünüyor. Ne sonuçları alıyorsun? (Sadece renk tonu mu, yoksa parlaklık ve doygunluk dağınık mıdır?) Sonuçlar, atan2() 'çağrısında I ve Q değerini ters çevirirseniz beklediğiniz gibi daha mı görünür? Belki bir kod yazmış olsaydın, senin için iki kez kontrol edebilirdik. – user1118321

+0

İlginiz için teşekkür ederiz, soruyu kodla ve sonucun anlık görüntüsüyle güncelledik. – Andrea3000

3

Andrea3000, net YIQ örneklerini karşılaştırarak, ben senin gönderme karşılaştım, ama bir 'güncellenen' sürümü ile ilgili bir sorun olduğunu düşünüyorum kodunuz .... eminim 'mat3' tanımları sütun/satır sıralamasında çevirme/bırakma ... (belki de bu yüzden hala sorun yaşıyorsanız) ...

FYI: OpenGL matris siparişi : "Daha fazla değer için, matrisler sütun ana düzeninde doldurulur. Yani, ilk X değerleri ilk sütundur, ikinci X değerleri sonraki sütundur ve benzerleri." Bkz: http://www.opengl.org/wiki/GLSL_Types

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