2009-05-15 21 views
5

Al renderizar una escena de polígonos texturizados, me gustaría ser capaz de cambiar entre la representación de los colores originales y un modo de "escala de grises". He intentado lograr esto usando operaciones de combinación de color y mezcla; Nada de eso funcionó (con la mezcla no pude encontrar un glBlendFunc() que logró algo remotamente parecido a lo que quería, y operaciones de la matriz de color ...are discussed here).cómo implementar la representación en escala de grises en OpenGL?

Una solución que me viene a la mente (pero también es bastante costosa) es capturar la pantalla en cada fotograma y convertir la textura resultante en una escala de grises y mostrarla ... (Donde dije escala de grises realmente quise decir algo una saturación baja, pero supongo que para la mayoría de las posibles soluciones no diferirá tanto de la escala de grises).

¿Qué otras opciones tengo?

Respuesta

9

El uso de este dispositivo de OpenGL por defecto utiliza el espacio de color RGB, que no almacena una saturación explícita. Necesita un enfoque para extraer la saturación, modificarla y volver a cambiarla.

Mi sugerencia anterior, que simplemente utilizaba la longitud del vector RGB para representar 0 en la luminancia, era incorrecta, ya que no tenía en cuenta la escala, me disculpo.

El crédito para el nuevo fragmento corto va para el usuario regular "RTFM_FTW" de ## opengl y ## opengl3 en FreeNode/IRC, y le permite modificar la saturación directamente sin calcular el costoso RGB-> HSV-> RGB conversión, que es exactamente lo que quieres. Aunque el código HSV es inferior con respecto a tu pregunta, dejo que se quede.

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

Si desea más control que sólo la saturación, es necesario convertir a HSL o HSV espacio de color. Como se muestra a continuación usando un sombreador de fragmentos GLSL.

Lea la especificación OpenGL 3.0 y GLSL 1.30 disponible en http://www.opengl.org/registry para aprender a usar la funcionalidad 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); 
} 
+0

"v" no está definido. En cuanto a otros algoritmos para hsv2rgb, parece que esto realmente debe ser "valor" Además, la última declaración de caso (caso 5) también debe ser el caso predeterminado. –

2

Si está trabajando en contra de una moderna OpenGL-suficiente, diría pixel shaders es una solución muy adecuada aquí. Ya sea por enganchar en el sombreado de cada polígono, ya que hacen, o haciendo un solo cuádruple de pantalla completa en un segundo paso que acaba de leer cada píxel, convierte a escala de grises, y escribe de nuevo. A menos que su resolución, el hardware de gráficos y la tasa de fotogramas objetivo sean de alguna manera "extremas", eso debería ser factible actualmente en la mayoría de los casos.

2

Para la mayoría de los Escritorios Renderizar-Texturizar ya no es tan caro, todos los componentes como compiz, aero, etc. y efectos como la floración o la profundidad de campo que se ven en títulos recientes dependen de ello.

En realidad, no conviene convertir la textura de la pantalla per se a escala de grises; es posible que desee dibujar un cuadrángulo con la textura y un sombreador de fragmentos que transforme los valores en escala de grises.

Otra opción es tener dos conjuntos de sombreadores de fragmentos para sus triángulos, uno solo copiando el atributo gl_FrontColor como lo haría la línea de pieles fija, y otro que escribe valores en escala de grises en el búfer de la pantalla.

Una tercera opción podría ser modos de color indexados, si configura uüp una paleta de escala de grises, pero ese modo podría estar obsoleto y mal soportado por ahora; Además, se pierde mucha funcionalidad, como la fusión, si no recuerdo mal.

+0

yup, sin mezcla con modo colorindex. ¿Qué quieres decir con "todo compiz, aero, etc."? – zyndor

+0

Quiere decir que los renderizadores de ventanas de escritorio usan renderizado para textura a menudo hoy en día (es probable que sea un programador de administrador de ventanas). – Triang3l

Cuestiones relacionadas