2009-05-15 9 views
5

Durante il rendering di una scena di poligoni strutturati, mi piacerebbe poter passare dal rendering ai colori originali e alla modalità "scala di grigi". Ho cercato di ottenere questo risultato utilizzando le operazioni di fusione e color matrix; niente di tutto ciò ha funzionato (con la fusione non sono riuscito a trovare un glBlendFunc() che abbia ottenuto qualcosa che assomigliasse a quello che volevo e operazioni con la matrice di colori ...are discussed here).come implementare il rendering in scala di grigi in OpenGL?

Una soluzione che viene in mente (ma è anche piuttosto costoso) è quella di catturare lo schermo ogni fotogramma e convertire la trama risultante in uno in scala di grigi e visualizzare quello invece ... (Dove ho detto la scala di grigi in realtà intendevo qualcosa con una bassa saturazione, ma suppongo che per la maggior parte delle possibili soluzioni non differirà molto dalla scala di grigi).

Quali altre opzioni ho?

risposta

9

Il framebuffer OpenGL predefinito utilizza lo spazio colore RGB, che non memorizza una saturazione esplicita. È necessario un approccio per estrarre la saturazione, modificarla e modificarla nuovamente.

Il mio suggerimento precedente che utilizzava semplicemente la lunghezza del vettore RGB per rappresentare 0 in luminanza era errato, poiché non prendeva in considerazione il ridimensionamento, mi scuso.

Il credito per il nuovo snippet breve va all'utente normale "RTFM_FTW" da ## opengl e ## opengl3 su FreeNode/IRC e consente di modificare direttamente la saturazione senza calcolare il costoso RGB-> HSV-> RGB conversione, che è esattamente quello che vuoi. Anche se il codice HSV è inferiore rispetto alla tua domanda, l'ho lasciato stare.

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 si desidera un maggiore controllo non solo la saturazione, è necessario convertire a HSL o HSV spazio colore. Come mostrato sotto usando uno shader di frammenti GLSL.

Leggere le specifiche OpenGL 3.0 e GLSL 1.30 disponibili su http://www.opengl.org/registry per informazioni sull'utilizzo della funzionalità 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" non è definito. Guardando ad altri algoritmi per hsv2rgb, sembra che questo dovrebbe essere effettivamente "valore" Inoltre, la dichiarazione del caso finale (caso 5) dovrebbe essere anche il caso predefinito. –

2

Se stai lavorando contro una OpenGL abbastanza moderna, direi che pixel shaders è una soluzione molto adatta qui. O agganciando l'ombreggiatura di ogni poligono mentre eseguono il rendering, oppure eseguendo un singolo quad a schermo intero in una seconda passata che legge solo ogni pixel, converte in scala di grigi e lo scrive di nuovo. A meno che la risoluzione, l'hardware grafico e il framerate di destinazione siano in qualche modo "estremi", nella maggior parte dei casi ciò dovrebbe essere fattibile in questi giorni.

2

Per la maggior parte dei desktop, Render-To-Texture non è più così costoso, tutto il compiz, aerodinamico, ecc. E gli effetti come la fioritura o la profondità di campo visti nei titoli recenti dipendono da questo.

In realtà non si converte la trama dello schermo di per sé in scala di grigi, si vorrebbe disegnare un quadriciclo di dimensioni quadrate con la trama e uno shader di frammenti che trasformano le valure in scala di grigi.

Un'altra opzione consiste nell'avere due set di ombreggiatori di frammenti per i triangoli, uno copia l'attributo gl_FrontColor come la funzione fissa pieline e un altro che scrive valori di scala di grigi nel buffer dello schermo.

Una terza opzione potrebbe essere le modalità di colore indicizzate, se si imposta uüp una tavolozza in scala di grigi, ma quella modalità potrebbe essere deprecata e mal supportata; inoltre perdi molte funzionalità come la fusione, se non ricordo male.

+0

sì, nessuna miscelazione con la modalità colorindex. cosa intendi "tutto di compiz, aereo, ecc"? – zyndor

+0

Significa che i renderizzatori di finestre per desktop usano spesso il rendering in texture (probabilmente è un programmatore di window manager). – Triang3l