Pergunta

Ao renderizar uma cena de polígonos texturizados, eu gostaria de poder alternar entre a renderização nas cores originais e um modo de "escala de cinza". Eu tenho tentado conseguir isso usando operações de mistura e matriz colorida; Nada disso funcionou (com a mistura, não consegui encontrar um glblendfunc () que alcançou algo remotamente parecido com o que eu queria e as operações da matriz colorida ... são discutidos aqui).

Uma solução que vem à mente (mas também é bastante cara) é capturar a tela cada quadro e converter a textura resultante em uma escala de cinza e exibir isso em vez Mas acho que, para a maioria das soluções possíveis, não diferirá muito da escala de cinza).

Que outras opções eu tenho?

Foi útil?

Solução

O OpenGL Framebuffer padrão usa o espaço de cor RGB, que não armazena uma saturação explícita. Você precisa de uma abordagem para extrair a saturação, modificá -la e alterá -la novamente.

Minha sugestão anterior que simplesmente usava o comprimento do vetor RGB para representar 0 na luminância estava incorreta, pois não levou em consideração a escala, peço desculpas.

O crédito pelo novo snippet curto vai para o usuário regular "rtfm_ftw" de ## opengl e ## opengl3 no freeenode/irc, e permite modificar a saturação diretamente sem calcular a conversão rgb-> hsv-> rgb, que é exatamente o que você quer. Embora o código HSV seja inferior em relação à sua pergunta, eu deixei ficar.

void main( void ) 
{ 
    vec3 R0 = texture2DRect( S, gl_TexCoord[0].st ).rgb;
    gl_FragColor = vec4( mix( vec3( dot( R0, vec3( 0.2125, 0.7154, 0.0721 ) ) ),
        R0, T ), gl_Color.a ); 
}

Se você deseja mais controle do que apenas a saturação, precisará converter para o espaço de cores HSL ou HSV. Como mostrado abaixo usando um shader de fragmento GLSL.

Leia as especificações OpenGL 3.0 e GLSL 1.30 disponíveis em http://www.opengl.org/registry Para aprender a usar a funcionalidade GLSL v1.30.

#version 130
#define RED 0
#define GREEN 1
#define BLUE 2

in vec4 vertexIn;
in vec4 colorIn;
in vec2 tcoordIn;
out vec4 pixel;
Sampler2D tex;
vec4 texel;
const float epsilon = 1e-6;

vec3 RGBtoHSV(vec3 color)
{
    /* hue, saturation and value are all in the range [0,1> here, as opposed to their
       normal ranges of: hue: [0,360>, sat: [0, 100] and value: [0, 256> */
    int sortindex[3] = {RED,GREEN,BLUE};
    float rgbArr[3] = float[3](color.r, color.g, color.b);

    float hue, saturation, value, diff;
    float minCol, maxCol;
    int minIndex, maxIndex;

    if(color.g < color.r)
        swap(sortindex[0], sortindex[1]);
    if(color.b < color.g)
        swap(sortindex[1], sortindex[2]);
    if(color.r < color.b)
        swap(sortindex[2], sortindex[0]);

    minIndex = sortindex[0];
    maxIndex = sortindex[2];
    minCol = rgbArr[minIndex];
    maxCol = rgbArr[maxIndex];

    diff = maxCol - minCol;

    /* Hue */
    if( diff < epsilon){
        hue = 0.0;
    }
    else if(maxIndex == RED){
        hue = ((1.0/6.0) * ( (color.g - color.b) / diff )) + 1.0;
        hue = fract(hue);
    }
    else if(maxIndex == GREEN){
        hue = ((1.0/6.0) * ( (color.b - color.r) / diff )) + (1.0/3.0);
    }
    else if(maxIndex == BLUE){
        hue = ((1.0/6.0) * ( (color.r - color.g) / diff )) + (2.0/3.0);        
    }

    /* Saturation */
    if(maxCol < epsilon)
        saturation = 0;
    else
        saturation = (maxCol - minCol) / maxCol;

    /* Value */
    value = maxCol;

    return vec3(hue, saturation, value);
}
vec3 HSVtoRGB(vec3 color)
{
    float f,p,q,t, hueRound;
    int hueIndex;
    float hue, saturation, value;
    vec3 result;

    /* just for clarity */
    hue = color.r;
    saturation = color.g;
    value = color.b;

    hueRound = floor(hue * 6.0);
    hueIndex = int(hueRound) % 6;
    f = (hue * 6.0) - hueRound;
    p = value * (1.0 - saturation);
    q = value * (1.0 - f*saturation);
    t = value * (1.0 - (1.0 - f)*saturation);

    switch(hueIndex)
    {
        case 0:
            result = vec3(value,t,p);
        break;
        case 1:
            result = vec3(q,value,p);
        break;
        case 2:
            result = vec3(p,value,t);
        break;
        case 3:
            result = vec3(p,q,value);
        break;
        case 4:
            result = vec3(t,p,value);
        break;
        default:
            result = vec3(value,p,q);
        break;
    }
    return result;
}
void main(void)
{
    vec4 srcColor;
    vec3 hsvColor;
    vec3 rgbColor;
    texel = Texture2D(tex, tcoordIn);
    srcColor = texel*colorIn;
    hsvColor = RGBtoHSV(srcColor.rgb);
    /* You can do further changes here, if you want. */
    hsvColor.g = 0; /* Set saturation to zero */
    rgbColor = HSVtoRGB(hsvColor);
    pixel = vec4(rgbColor.r, rgbColor.g, rgbColor.b, srcColor.a);
}

Outras dicas

Se você está trabalhando contra um OpenGL moderno, eu diria Shaders de pixel é uma solução muito adequada aqui. Sejando ao sombreamento de cada polígono como eles renderizam ou fazendo um único quadro de tela cheia em uma segunda passagem que apenas lê cada pixel, converte em escala de cinza e o escreve de volta. A menos que sua resolução, hardware gráfico e quadros de destino sejam de alguma forma "extremamente", isso deve ser factível atualmente na maioria dos casos.

Para a maioria dos desktops, a renderização para a textura não é mais tão cara, tudo de compiz, aero, etc. e efeitos como Bloom ou profundidade de campo vistos em títulos recentes dependem disso.

Na verdade, você não converte a textura da tela em caso em escala de cinza, desejará desenhar um quadciado de tamanho com a textura e um shader de fragmento transformando os valores em escala de cinza.

Outra opção é ter dois conjuntos de shaders de fragmentos para seus triângulos, um apenas copiando o atributo GL_FrontColor como o Pieline de função fixa, e outro que grava valores de escala de cinza no buffer de tela.

Uma terceira opção pode ser modos de cores indexados, se você definir uma paleta de escala de cinza, mas esse modo poderá ser preso e pouco apoiado até agora; Além disso, você perde muitas funcionalidades como a mistura, se bem me lembro.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top