GLSL Shader - เปลี่ยน Hue / Saturation / Brightness


14

ฉันพยายามเปลี่ยนเฉดสีของภาพโดยใช้ตัวแยกส่วน GLSL ฉันต้องการบรรลุสิ่งที่คล้ายกับเลเยอร์ Hue / Saturation Adjustment ของ Photoshop

ในภาพต่อไปนี้คุณสามารถเห็นสิ่งที่ฉันได้รับจนถึง ฉันต้องการเปลี่ยนสีของสี่เหลี่ยมสีเขียวเพื่อให้ดูเหมือนว่าสี่เหลี่ยมสีแดงด้านขวา แต่ด้วย shader นี้ฉันได้ครึ่งสี่เหลี่ยมสีชมพูครึ่งสีแดง (สี่เหลี่ยมตรงกลาง)
ป้อนคำอธิบายรูปภาพที่นี่

สิ่งที่ฉันทำในตัวแบ่งส่วนคือการแปลงสีของพื้นผิวเป็น HSV จากนั้นฉันเพิ่มสี HSV ที่ฉันได้รับจากจุดยอดสุดยอดไปยังมันและฉันแปลงสีกลับเป็น RGB
ผมทำอะไรผิดหรือเปล่า?

ชิ้นส่วน shader:

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 ที่ให้ไว้ในคำตอบของเขาปัญหาเกี่ยวกับแถบสีชมพูได้รับการแก้ไข แต่ฉันสามารถทำได้เพียงครึ่งหนึ่งของสเปกตรัมสี ฉันสามารถเปลี่ยนสีจากสีแดงเป็นสีเขียวได้ แต่ฉันไม่สามารถเปลี่ยนเป็นสีน้ำเงินหรือสีชมพูได้ ป้อนคำอธิบายรูปภาพที่นี่

ใน shader ส่วนฉันทำตอนนี้:

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

@kolrabi ฉันได้ลองแล้ว แต่ฉันยังคงได้วงสีชมพู ในที่สุดฉันก็ได้แก้ไขปัญหาดังกล่าวด้วยฟังก์ชั่นการแปลงที่ Sam Hocevar ให้ไว้ในคำตอบของเขา
miviclin

@mivic: เราไม่ได้ตอบคำถาม หากคุณพบคำตอบด้วยตนเองให้โพสต์คำตอบสำหรับคำถามของคุณ
Nicol Bolas

คำตอบ:


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 … 1] แทนที่จะเป็น [0 … 360] ดังนั้นคุณจะต้องปรับอินพุตของคุณ

ที่มา: http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl


การใช้ฟังก์ชั่นเหล่านี้แก้ปัญหาได้ ไม่มีวงสีชมพูมากขึ้น แต่ฉันคิดว่าฉันยังคงทำอะไรผิด ฉันแก้ไขโพสต์ดั้งเดิมของฉันด้วยข้อมูลเพิ่มเติม ขอบคุณ
miviclin

1
น่าสนใจจริงๆ! คุณอธิบายได้ไหมว่าทำไมฟังก์ชั่นเหล่านี้ถึงทำงานได้ดีขึ้น? "การมี GPU อยู่ในใจ" เป็นอย่างไร
Marco

4
@Marco GPUs ไม่ดีในการจัดการงานif()สร้างขนาดใหญ่แต่ใช้งานเวกเตอร์ได้ดี ฟังก์ชั่นด้านบนไม่เคยใช้if()พยายามที่จะขนานการดำเนินงานและโดยทั่วไปพวกเขาใช้คำแนะนำน้อยลง สิ่งเหล่านี้มักเป็นตัวบ่งชี้ที่ดีว่าพวกเขากำลังจะเร็วขึ้น
sam hocevar

ฟังก์ชั่นเหล่านี้เทียบเท่ากับสูตรมาตรฐาน HSV (ละเว้นข้อผิดพลาดในการปัดเศษ) หรือไม่หรือว่าเป็นการประมาณ?
Stefan Monov

3

ดังที่ Nicol Bolas แนะนำในความคิดเห็นของโพสต์ต้นฉบับฉันโพสต์วิธีแก้ปัญหาของฉันด้วยคำตอบแยกต่างหาก

ปัญหาแรกคือภาพที่แสดงด้วยแถบสีชมพูขณะที่รูปภาพในโพสต์ต้นฉบับแสดง ฉันแก้ไขโดยใช้ฟังก์ชั่น Sam Hocevar ที่มีให้ในคำตอบของเขา ( /gamedev//a/59808/22302 )

ปัญหาที่สองคือฉันเพิ่มสีสันของพิกเซลของพื้นผิวด้วยค่าที่ฉันส่งไปยัง shader ซึ่งหมายถึงเป็นการชดเชยจากเฉดสีพิกเซลของพื้นผิวดังนั้นฉันจึงต้องทำการบวกแทนการคูณ
ฉันยังคงทำการคูณเพื่อความอิ่มตัวของสีและความสว่างเพราะฉันได้รับพฤติกรรมแปลก ๆ อย่างอื่นและฉันไม่จำเป็นต้องเพิ่มพวกมันมากไปกว่าความอิ่มตัวหรือความสว่างของพื้นผิวดั้งเดิมในขณะนี้

นี่เป็นวิธีการหลัก () ของ shader ที่ฉันใช้อยู่ตอนนี้ ด้วยสิ่งนี้ฉันสามารถเปลี่ยนเฉดสีจาก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()แทน
Gustavo Maciel
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.