2012-02-10 85 views
21

Có cách nào thay đổi hiệu quả màu sắc của kết cấu 2D OpenGL bằng GLSL (trình đổ bóng phân đoạn) không?Làm thế nào để thay đổi màu sắc của một kết cấu với GLSL?

Có ai đó có một số mã cho nó không?

UPDATE: Đây là mã kết quả từ user1118321 gợi ý:

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

Và đây là kết quả so sánh với một tài liệu tham khảo:

enter image description here

Tôi đã cố gắng để chuyển đổi tôi với Q bên trong nhưng kết quả là sai, ngay cả xung quanh 0 °

Bạn đã có bất kỳ gợi ý nào?

Nếu cần thiết để so sánh, đây là bản gốc hình ảnh chưa sửa đổi: enter image description here

+5

Đó là một vòng quay của RGB ba xung quanh (1,1,1) vector - bạn có thể bày tỏ rằng như một phép nhân ma trận trong shader – Flexo

+0

Nếu sự thay đổi màu sắc của bạn là không đổi thì bạn có thể bỏ qua atan, sqrt, sin và cos. Những gì bạn đang làm là chuyển đổi chúng thành các tọa độ cực, thêm vào góc và chuyển đổi ngược lại. Bạn có thể thực hiện phép toán này với ma trận xoay 2x2. Nếu bạn không cần thực hiện bất kỳ phép tính nào khác trong không gian yiq, bạn có thể tính toán trước toàn bộ biến đổi.Nhân rgb2yiq của bạn với vòng quay 2d (đệm ra thành 3x3) và sau đó với yiq2rgb để lấy một ma trận 3x3 lớn thực hiện toàn bộ quá trình. Mà, như @ Flexo nói chỉ là một vòng quay quanh (1,1,1) vectơ. –

Trả lời

23

Trong khi những gì @awoodland nói là đúng, phương pháp mà có thể gây ra các vấn đề với những thay đổi về độ sáng, tôi tin.

Hệ thống màu HSV và HLS có vấn đề vì một số lý do. Tôi đã nói chuyện với một nhà khoa học màu về điều này gần đây, và đề nghị của ông là chuyển đổi sang không gian YIQ hoặc YCbCr và điều chỉnh các kênh chroma (I & Q, hoặc Cb & Cr) cho phù hợp. (Bạn có thể học cách để làm điều đó herehere.)

Khi một trong những không gian, bạn có thể nhận được màu sắc từ các góc tạo thành bởi các kênh chroma, bằng cách làm hue = atan(cr/cb) (xem cho cb == 0). Điều này mang lại cho bạn một giá trị bằng radian. Đơn giản chỉ cần xoay nó bằng cách thêm số lượng quay màu sắc. Một khi bạn đã làm điều đó, bạn có thể tính toán độ lớn của sắc độ với chroma = sqrt(cr*cr+cb*cb). Để quay lại RGB, hãy tính Cb và Cr mới (hoặc I & Q) bằng cách sử dụng Cr = chroma * sin (hue), Cb = chroma * cos (hue). Sau đó chuyển đổi trở lại RGB như được mô tả trên các trang web ở trên.

EDIT: Đây là giải pháp mà tôi đã thử nghiệm và dường như cung cấp cho tôi kết quả tương tự như tham chiếu của bạn. Bạn có lẽ có thể sụp đổ một số sản phẩm chấm vào nhân lên ma trận:

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

Cảm ơn bạn, tôi đã thực hiện nó nhưng nó dường như không hoạt động chính xác .. Nó thực sự cho một sự thay đổi màu sắc nhưng nó hoàn toàn khác với điều chỉnh màu sắc của Photoshop (ví dụ). Tôi đã quyết định chuyển nó thành YIQ bởi vì với YCbCr tôi đã có kết quả tồi tệ nhất. Vì vậy tôi đã tính toán 'hue = atan2 (Q, I)', 'chroma = sqrt (I * I + Q * Q)' và sau đó 'I = chroma * sin (màu sắc)', 'Q = chorma * cos (màu sắc)) '. Nó có đúng không? Tui bỏ lỡ điều gì vậy? – Andrea3000

+0

Điều đó có vẻ đúng với tôi. Bạn nhận được kết quả gì? (Chỉ là màu sắc sai, hoặc là độ sáng và độ bão hòa nhận được sai lầm, quá?) Làm các kết quả trông giống như những gì bạn mong đợi nếu bạn đảo ngược I và Q trong 'atan2()' gọi? Có thể nếu bạn đăng một số mã, chúng tôi có thể kiểm tra lại nó cho bạn. – user1118321

+0

Cảm ơn sự quan tâm của bạn, tôi đã cập nhật câu hỏi bằng mã và ảnh chụp nhanh kết quả. – Andrea3000

3

Andrea3000, khi so sánh ví dụ YIQ trên mạng, tôi đã xem qua thông báo của bạn, nhưng tôi nghĩ rằng có một vấn đề với phiên bản 'cập nhật' của mã của bạn .... Tôi chắc chắn các định nghĩa 'mat3' của bạn được lật/thả trên cột/hàng đặt hàng ... (có thể đó là lý do tại sao bạn vẫn gặp khó khăn) ...

FYI: OpenGL matrix ordering : "Đối với nhiều giá trị hơn, ma trận được điền vào theo thứ tự cột lớn. Đó là, giá trị X đầu tiên là cột đầu tiên, giá trị X thứ hai là cột tiếp theo, v.v." Xem: http://www.opengl.org/wiki/GLSL_Types

mat2(
float, float, //first column 
float, float); //second column 
Các vấn đề liên quan