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

@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… 360] 대신 [0… 1]이므로 입력을 조정해야합니다.

출처 : http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl


이러한 기능을 사용하면 문제가 해결되었습니다. 더 이상 분홍색 밴드가 없습니다. 그러나 나는 여전히 잘못하고 있다고 생각합니다. 추가 정보를 사용하여 원본 게시물을 편집했습니다. 감사.
miviclin

1
정말 흥미로운! 이 기능들이 왜 더 나은지 설명 할 수 있습니까? "GPU를 염두에 두는 것"은 무엇입니까?
Marco

4
@Marco GPU는 큰 if()구문 을 처리하는 데는 좋지 않지만 벡터 연산 (여러 스칼라 값에 대한 병렬 연산)에는 적합합니다. 위의 함수는을 사용하지 않고 if()연산을 병렬화하려고 시도하며 일반적으로 적은 명령을 사용합니다. 이들은 일반적으로 더 빠를 것이라는 좋은 지표입니다.
sam hocevar

이러한 함수는 표준 HSV 공식과 정확히 동일합니까 (반올림 오류는 무시) 아니면 근사값입니까?
Stefan Monov

3

Nicol Bolas가 원래 게시물의 의견에서 제안한 것처럼 솔루션에 대한 답변을 별도의 답변으로 게시하고 있습니다.

첫 번째 문제는 원본 게시물의 이미지가 보여주는 것처럼 분홍색 밴드로 렌더링되는 이미지였습니다. Sam Hocevar가 자신의 답변에 제공된 기능 ( /gamedev//a/59808/22302)을 사용하여 수정했습니다. )을 .

두 번째 문제는 텍스처의 픽셀 색조와 셰이더에 보내는 값을 곱한 것입니다.이 값은 텍스처의 픽셀 색조와의 오프셋을 의미하므로 곱셈 대신 추가를 수행해야했습니다.
나는 그렇지 않으면 이상한 행동을 취하기 때문에 채도와 명도에 대한 곱셈을 수행하고 실제로 원래 질감의 채도 또는 밝기보다 더 많이 증가시킬 필요가 없습니다.

이것은 지금 사용하고있는 셰이더의 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()대신 사용할 수도 있습니다 .
Gustavo Maciel
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.