GLSL Shader - Thay đổi Hue / Saturation / Độ sáng


14

Tôi đang cố gắng thay đổi màu sắc của hình ảnh bằng cách sử dụng trình đổ bóng mảnh GLSL. Tôi muốn đạt được một cái gì đó tương tự như lớp Hue / Saturation Điều chỉnh của Photoshop.

Trong hình ảnh sau đây bạn có thể thấy những gì tôi đã có cho đến nay. Tôi muốn thay đổi màu sắc của hình vuông màu xanh lá cây để nó trông giống hình vuông màu đỏ ở bên phải, nhưng với shader này, tôi nhận được một nửa hình vuông màu đỏ nửa hồng (hình vuông ở giữa).
nhập mô tả hình ảnh ở đây

Những gì tôi đang làm trong trình đổ bóng mảnh là chuyển đổi màu của kết cấu thành HSV, sau đó tôi thêm màu HSV mà tôi nhận được từ trình tạo bóng đỉnh cho nó và tôi chuyển đổi màu trở lại RGB.
Tôi đang làm gì sai?

Mảnh vỡ mảnh:

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

EDIT: Sử dụng các chức năng Sam Hocevar cung cấp trong câu trả lời của mình, vấn đề với các dải màu hồng đã được giải quyết, nhưng tôi chỉ có thể đạt được một nửa phổ màu. Tôi có thể thay đổi màu sắc từ màu đỏ sang màu xanh lá cây, nhưng tôi không thể thay đổi màu sắc thành màu xanh hoặc màu hồng. nhập mô tả hình ảnh ở đây

Trong shader mảnh, tôi đang làm điều này:

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

Ý bạn không phải là int hi = int(h/60.0); float f = h/60.0 - float(hi);thay vì int hi = int(h); float f = h - float(hi);? Mặc dù vậy, không biết đó là nguyên nhân.
kolrabi

@kolrabi Tôi đã thử điều đó nhưng tôi vẫn nhận được các dải màu hồng. Cuối cùng tôi đã giải quyết vấn đề đó bằng các hàm chuyển đổi mà Sam Hocevar cung cấp trong câu trả lời của anh ấy.
miviclin

@mivic: Chúng tôi không đặt câu trả lời trong câu hỏi. Nếu bạn tự tìm thấy câu trả lời, sau đó đăng câu trả lời cho câu hỏi của bạn.
Nicol Bolas

Câu trả lời:


22

Các chức năng này sẽ thực hiện rất tệ. Tôi đề nghị sử dụng các chức năng được ghi với GPU. Đây là của tôi:

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

Lưu ý rằng đối với các chức năng này, phạm vi cho H là [0, 1] thay vì [0, 360], do đó bạn sẽ phải điều chỉnh đầu vào của mình.

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


Sử dụng các chức năng này đã giải quyết vấn đề. Không còn dải màu hồng nữa. Nhưng tôi nghĩ rằng tôi vẫn đang làm điều gì đó sai. Tôi đã chỉnh sửa bài viết gốc của mình với nhiều thông tin hơn. Cảm ơn.
miviclin

1
Thật sự thú vị! Bạn có thể giải thích tại sao các chức năng này hoạt động tốt hơn? Điều gì giống như "có GPU trong tâm trí"?
Marco

4
GPU @Marco không giỏi xử lý các if()cấu trúc lớn , nhưng lại rất giỏi trong các hoạt động véc tơ (hoạt động song song trên một số giá trị vô hướng). Các chức năng trên không bao giờ sử dụng if(), cố gắng song song các hoạt động và nói chung chúng sử dụng ít hướng dẫn hơn. Đây thường là những chỉ số tốt cho thấy chúng sẽ nhanh hơn.
sam hocevar

Các hàm này có chính xác tương đương với các công thức HSV tiêu chuẩn (bỏ qua các lỗi làm tròn) hay chúng là một xấp xỉ?
Stefan Monov

3

Như Nicol Bolas đã đề xuất trong các nhận xét của bài đăng gốc, tôi sẽ đăng giải pháp cho vấn đề của mình bằng một câu trả lời riêng.

Vấn đề đầu tiên là hình ảnh được hiển thị với các dải màu hồng, như hình ảnh trong bài đăng gốc cho thấy. Tôi đã sửa nó bằng các hàm Sam Hocevar cung cấp trong câu trả lời của anh ấy ( /gamedev//a/59808/22302 ).

Vấn đề thứ hai là tôi đã nhân màu sắc của pixel của kết cấu với giá trị mà tôi đang gửi cho trình đổ bóng, nghĩa là một phần bù từ màu của pixel của hoạ tiết, vì vậy tôi phải thực hiện phép cộng thay vì nhân.
Tôi vẫn thực hiện phép nhân cho độ bão hòa và độ sáng bởi vì tôi có một hành vi kỳ lạ khác và tôi thực sự không cần tăng chúng hơn nữa so với độ bão hòa hoặc độ sáng của kết cấu ban đầu tại thời điểm này.

Đây là phương thức main () của shader tôi đang sử dụng ngay bây giờ. Với điều này, tôi có thể thay đổi màu sắc từ 0 to đến 360 độ, làm bão hòa hình ảnh và giảm độ sáng.

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
mẹo: bạn có thể muốn mod()màu sắc, nhưng độ bão hòa và độ sáng bạn có thể muốn clamp()thay thế.
Gustavo Maciel
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.