GLSL Shader - Ton / Doygunluk / Parlaklığı Değiştir


14

Bir GLSL parça gölgelendirici kullanarak bir görüntünün renk tonunu değiştirmeye çalışıyorum. Photoshop'un Ton / Doygunluk Ayarı katmanına benzer bir şey elde etmek istiyorum.

Aşağıdaki görüntüde şu ana kadar neler aldığımı görebilirsiniz. Yeşil karenin tonunu değiştirmek istiyorum, bu yüzden sağdaki kırmızı kare gibi görünüyor, ancak bu gölgelendirici ile yarım kırmızı yarım pembe kare (ortadaki kare) alıyorum.
resim açıklamasını buraya girin

Parça gölgelendiricisinde yaptığım, dokunun rengini HSV'ye dönüştürmek, sonra köşe gölgelendiricisinden aldığım HSV rengini ekliyorum ve rengi tekrar RGB'ye dönüştürüyorum.
Neyi yanlış yapıyorum?

Parça gölgelendirici:

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

DÜZENLEME: Sam Hocevar'ın cevabında verdiği işlevleri kullanarak pembe bantlarla ilgili sorun çözüldü, ancak renk spektrumunun sadece yarısına ulaşabiliyorum. Tonu kırmızıdan yeşile değiştirebilirim, ancak maviye veya pembeye değiştiremem. resim açıklamasını buraya girin

Parça gölgelendiricisinde, şimdi bunu yapıyorum:

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

Eğer ifade etmedi int hi = int(h/60.0); float f = h/60.0 - float(hi);yerine int hi = int(h); float f = h - float(hi);? Buna neden olup olmadığını bilmiyorum.
kolrabi

@kolrabi Bunu denedim ama hala pembe gruplar alıyordum. Sonunda Sam Hocevar'ın cevabında sağladığı dönüşüm fonksiyonları ile bu problemi çözdüm.
miviclin

mivic: Sorulara cevap vermiyoruz. Cevabı kendi başınıza bulduysanız, sorunuza bir cevap gönderin.
Nicol Bolas

Yanıtlar:


22

Bu işlevler çok kötü performans gösterecektir. GPU göz önünde bulundurularak yazılan işlevleri kullanmanızı öneririm. İşte benim:

vec3 rgb2hsv(vec3 c)
{
    vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
    vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
    vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));

    float d = q.x - min(q.w, q.y);
    float e = 1.0e-10;
    return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}

vec3 hsv2rgb(vec3 c)
{
    vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}

Bu işlevler için aralığın H[0… 360] yerine [0… 1] olduğunu unutmayın, bu nedenle girişinizi uyarlamanız gerekir.

Kaynak: http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl


Bu işlevleri kullanmak sorunu çözdü. Artık pembe grup yok. Ama sanırım hala yanlış bir şey yapıyorum. Orijinal yayınımı daha fazla bilgi ile düzenledim. Teşekkürler.
miviclin

1
Gerçekten ilginç! Bu işlevlerin neden daha iyi performans gösterdiğini açıklayabilir misiniz? "GPU'yu akılda tutmak" gibi bir şey var mı?
Marco

4
@Marco GPU'lar büyük if()yapıları işlemede çok iyi değildir , ancak vektör işlemlerinde (çeşitli skaler değerlerde paralel işlemler) iyidir. Yukarıdaki işlevler asla kullanmaz if(), işlemleri paralelleştirmeye çalışır ve genel olarak daha az talimat kullanırlar. Bunlar genellikle daha hızlı olacakları için iyi göstergelerdir.
sam hocevar

Bu işlevler standart HSV formülleriyle tam olarak eşdeğer midir (yuvarlama hatalarını göz ardı ederek) veya yaklaşık mıdır?
Stefan Monov

3

Nicol Bolas'ın orijinal yazının yorumlarında önerdiği gibi, çözümü ayrı bir cevapta sorunuma gönderiyorum.

İlk sayı, orijinal yazıdaki görüntünün gösterdiği gibi pembe bantlarla oluşturulmuş resimdi. Sam Hocevar'ın cevabında ( /gamedev//a/59808/22302 ) verilen işlevleri kullanarak düzelttim .

İkinci sorun, doku pikselinin tonunu gölgelendiriciye gönderdiğim değerle çarpmamdı, bu da dokuların piksel tonundan bir sapma anlamına geliyordu, bu yüzden çarpma yerine bir ekleme yapmak zorunda kaldım.
Doygunluk ve parlaklık için hala bir çarpma işlemi yapıyorum çünkü aksi takdirde garip bir davranış elde ediyorum ve onları şu anda orijinal dokunun doygunluğu veya parlaklığından daha fazla artırmaya ihtiyacım yok.

Bu, şu anda kullandığım gölgelendiricinin ana () yöntemidir. Bununla tonu 0º'dan 360º'a kaydırabilir, görüntüyü desaturate edebilir ve parlaklığı azaltabilirim.

void main() {
    vec4 textureColor = texture2D(sTexture, vTextureCoord);
    vec3 fragRGB = textureColor.rgb;
    vec3 fragHSV = rgb2hsv(fragRGB).xyz;
    fragHSV.x += vHSV.x / 360.0;
    fragHSV.yz *= vHSV.yz;
    fragHSV.xyz = mod(fragHSV.xyz, 1.0);
    fragRGB = hsv2rgb(fragHSV);
    gl_FragColor = vec4(fragRGB, textureColor.w);
} 

2
ipucu: muhtemelen mod()renk tonunu istersiniz , ancak clamp()bunun yerine doygunluk ve parlaklık isteyebilirsiniz .
Gustavo Maciel
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.