Saya mencoba mengubah rona gambar menggunakan shader fragmen GLSL. Saya ingin mencapai sesuatu yang mirip dengan layer Hue / Saturation Adjustment Photoshop.
Pada gambar berikut ini Anda bisa melihat apa yang saya dapatkan sejauh ini. Saya ingin mengubah rona kotak hijau sehingga terlihat seperti kotak merah di sebelah kanan, tetapi dengan shader ini saya mendapatkan setengah merah setengah merah muda persegi (kotak di tengah).
Apa yang saya lakukan di shader fragmen adalah mengubah warna tekstur menjadi HSV, kemudian saya menambahkan warna HSV yang saya dapatkan dari vertex shader ke dalamnya dan saya mengubah warna kembali ke RGB.
Apa yang saya lakukan salah?
Shader fragmen:
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: Menggunakan fungsi yang disediakan oleh Sam Hocevar dalam jawabannya, masalah dengan pita merah muda telah terpecahkan, tetapi saya hanya dapat mencapai setengah dari spektrum warna. Saya bisa mengubah rona dari merah ke hijau, tetapi saya tidak bisa mengubahnya menjadi biru atau merah muda.
Di fragmen shader, saya melakukan ini sekarang:
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);
}
sumber
int hi = int(h/60.0); float f = h/60.0 - float(hi);
bukanint hi = int(h); float f = h - float(hi);
? Namun, tidak tahu apakah itu penyebabnya.Jawaban:
Fungsi-fungsi ini akan berkinerja sangat buruk. Saya sarankan menggunakan fungsi yang ditulis dengan GPU dalam pikiran. Ini milik saya:
Perhatikan bahwa untuk fungsi-fungsi ini kisaran untuk
H
[0 ... 1] bukan [0 ... 360], jadi Anda harus menyesuaikan input Anda.Sumber: http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
sumber
if()
konstruksi besar , tetapi pandai operasi vektor (operasi paralel pada beberapa nilai skalar). Fungsi di atas tidak pernah digunakanif()
, mencoba memparalelkan operasi, dan secara umum mereka menggunakan lebih sedikit instruksi. Ini biasanya merupakan indikator yang baik bahwa mereka akan menjadi lebih cepat.Seperti yang disarankan Nicol Bolas dalam komentar posting asli, saya memposting solusi untuk masalah saya dalam jawaban terpisah.
Masalah pertama adalah gambar yang dirender dengan pita merah muda, seperti yang ditunjukkan gambar dalam posting asli. Saya memperbaikinya menggunakan fungsi yang disediakan oleh Sam Hocevar dalam jawabannya ( /gamedev//a/59808/22302 ).
Masalah kedua adalah bahwa saya mengalikan rona piksel tekstur dengan nilai yang saya kirim ke shader, yang dimaksudkan untuk mengimbangi rona piksel tekstur, jadi saya harus melakukan penambahan alih-alih penggandaan.
Saya masih melakukan penggandaan untuk saturasi dan kecerahan karena saya mendapatkan perilaku aneh sebaliknya, dan saya tidak benar-benar perlu menambahkannya lebih jauh daripada saturasi atau kecerahan tekstur asli saat ini.
Ini adalah metode utama () dari shader yang saya gunakan saat ini. Dengan ini saya dapat mengubah rona dari 0º ke 360º, desaturate gambar, dan mengurangi kecerahan.
sumber
mod()
rona, tetapi saturasi dan kecerahan Anda mungkin inginclamp()
sebaliknya.