GLSLシェーダー-色相/彩度/輝度の変更


14

GLSLフラグメントシェーダーを使用して画像の色相を変更しようとしています。Photoshopの色相/彩度調整レイヤーに似たものを実現したい。

次の画像では、私がこれまでに得たものを見ることができます。緑色の正方形の色相を変更して右側の赤い正方形のようにしたいのですが、このシェーダーでは半分の赤い半分のピンクの正方形(中央の正方形)が得られます。
ここに画像の説明を入力してください

フラグメントシェーダーで行っているのは、テクスチャの色をHSVに変換してから、頂点シェーダーから取得したHSV色を追加して、色をRGBに変換し直すことです。
私は何を間違えていますか?

フラグメントシェーダー:

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

編集: 彼の答えで提供されたSam Hocevar関数を使用して、ピンクバンドの問題は解決されましたが、カラースペクトルの半分にしか到達できません。色相を赤から緑に変更することはできますが、青またはピンクに変更することはできません。 ここに画像の説明を入力してください

フラグメントシェーダーでは、今これを行っています。

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

じゃint hi = int(h/60.0); float f = h/60.0 - float(hi);なくてint hi = int(h); float f = h - float(hi);?ただし、それが原因かどうかはわかりません。
コラビ

@kolrabi私はそれを試してみましたが、まだピンクのバンドを取得していました。Sam Hocevarが答えで提供した変換関数を使用して、ようやくこの問題を解決しました。
miviclin

@mivic:質問には答えを入れません。自分で答えを見つけた場合は、質問への答えを投稿してください。
ニコルボラス

回答:


22

これらの機能のパフォーマンスは非常に悪くなります。GPUを念頭に置いて作成された関数を使用することをお勧めします。ここに私のものがあります:

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

これらの関数の範囲Hは[0…360]ではなく[0…1]であるため、入力を調整する必要があることに注意してください。

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


これらの関数を使用して問題を解決しました。これ以上ピンクのバンドはありません。しかし、私はまだ何か間違ったことをしていると思います。元の投稿を詳細情報で編集しました。ありがとう。
ミビクリン

1
本当に面白いです!これらの機能のパフォーマンスが向上する理由を説明できますか?「GPUを念頭に置いて」とはどのようなものですか?
マルコ

4
@Marco GPUはif()、大きな構造の処理にはあまり適していませんが、ベクトル演算(複数のスカラー値の並列演算)には優れています。上記の関数はを決して使用せずif()、操作の並列化を試みます。一般に、使用する命令は少なくなります。これらは通常、より高速になることを示す良い指標です。
サムホセバー

これらの関数は、標準のHSV公式とまったく同じですか(丸め誤差を無視)、または近似ですか?
ステファンモノフ

3

ニコル・ボーラスが元の投稿のコメントで示唆したように、私は別の回答で私の問題の解決策を投稿しています。

最初の問題は、元の投稿の画像が示すように、画像がピンクの帯でレンダリングされていたことです。彼の答え(/gamedev//a/59808/22302)で提供されているSam Hocevar関数を使用して修正しました。

2番目の問題は、テクスチャのピクセルの色相にシェーダーに送信した値を掛けることでした。これは、テクスチャのピクセルの色相からのオフセットであるため、乗算の代わりに加算を実行する必要がありました。
さもなければ奇妙な振る舞いをするため、彩度と明るさの乗算を実行します。また、現時点で元のテクスチャの彩度または明るさよりもさらに大きくする必要はありません。

これは、現在使用しているシェーダーのmain()メソッドです。これにより、色相を0ºから360ºにシフトし、画像の彩度を下げ、輝度を下げることができます。

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
ヒント:おそらくmod()色相が必要ですが、clamp()代わりに彩度と明度を使用することもできます。
グスタボマシエル14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.