2012-11-03 14 views
8

Attualmente sto lavorando a un piccolo progetto in C++ e OpenGL e sto cercando di implementare uno strumento per la selezione dei colori simile a quello in Photoshop, come di seguito.Interpolazione colore OpenGL

enter image description here

Tuttavia sto avendo problemi con l'interpolazione della grande piazza. Lavorando sul mio computer desktop con una 8800 GTS, il risultato era simile ma la fusione non era così liscia.

Questo è il codice che sto usando:

GLfloat swatch[] = { 0,0,0, 1,1,1, mR,mG,mB, 0,0,0 }; 
GLint swatchVert[] = { 400,700, 400,500, 600,500, 600,700 }; 

glVertexPointer(2, GL_INT, 0, swatchVert); 
glColorPointer(3, GL_FLOAT, 0, swatch); 
glDrawArrays(GL_QUADS, 0, 4); 

Passando il mio laptop con Intel Graphics HD 3000, questo risultato è stato anche peggio, con nessun cambiamento nel codice.

http://i.imgur.com/wSJI2.png

ho pensato che fosse OpenGL dividere il quad in due triangoli, così ho provato il rendering utilizzando triangoli e interpolando il colore nel centro della piazza me stesso ma ancora doesnt corrispondono del tutto il risultato che speravo .

enter image description here

risposta

11

ho subito fatto un/frammento vertex shader che interpola con successo i colori:

#ifdef GL_ES 
precision highp float; 
#endif 

uniform vec2 resolution; 

void main(void) 
{ 
    vec2 p = gl_FragCoord.xy/resolution.xy; 
    float gray = 1.0 - p.x; 
    float red = p.y; 
    gl_FragColor = vec4(red, gray*red, gray*red, 1.0); 
} 

Ecco il risultato: enter image description here

sua applicazione su un quad cede ora il risultato corretto perché l'interpolazione viene eseguita realmente su tutta la superficie utilizzando le coordinate x e . Vedi la spiegazione dettagliata di @ datenwolf sul motivo per cui funziona.

MODIFICA 1 Per ottenere l'intera gamma di colori in un selettore di colori funzionale, è possibile modificare la tonalità in modo interattivo (vedere https://stackoverflow.com/a/9234854/570738).

live demo online: http://goo.gl/Ivirl

#ifdef GL_ES 
precision highp float; 
#endif 

uniform float time; 
uniform vec2 resolution; 

const vec4 kRGBToYPrime = vec4 (0.299, 0.587, 0.114, 0.0); 
const vec4 kRGBToI  = vec4 (0.596, -0.275, -0.321, 0.0); 
const vec4 kRGBToQ  = vec4 (0.212, -0.523, 0.311, 0.0); 

const vec4 kYIQToR = vec4 (1.0, 0.956, 0.621, 0.0); 
const vec4 kYIQToG = vec4 (1.0, -0.272, -0.647, 0.0); 
const vec4 kYIQToB = vec4 (1.0, -1.107, 1.704, 0.0); 

const float PI = 3.14159265358979323846264; 

void adjustHue(inout vec4 color, float hueAdjust) { 
    // Convert to YIQ 
    float YPrime = dot (color, kRGBToYPrime); 
    float I  = dot (color, kRGBToI); 
    float Q  = dot (color, kRGBToQ); 

    // Calculate the hue and chroma 
    float hue  = atan (Q, I); 
    float chroma = sqrt (I * I + Q * Q); 

    // Make the user's adjustments 
    hue += hueAdjust; 

    // Convert back to YIQ 
    Q = chroma * sin (hue); 
    I = chroma * cos (hue); 

    // Convert back to RGB 
    vec4 yIQ = vec4 (YPrime, I, Q, 0.0); 
    color.r = dot (yIQ, kYIQToR); 
    color.g = dot (yIQ, kYIQToG); 
    color.b = dot (yIQ, kYIQToB); 
} 

void main(void) 
{ 
    vec2 p = gl_FragCoord.xy/resolution.xy; 
    float gray = 1.0 - p.x; 
    float red = p.y; 
    vec4 color = vec4(red, gray*red, gray*red, 1.0); 
    adjustHue(color, mod(time, 2.0*PI)); 
    gl_FragColor = color; 
} 

EDIT 2: Se necessario, shader per l'uso con le coordinate di texture (da applicare sul quad con struttura coordinate da 0 a 1) dovrebbe essere simile a questo. Non testato.

Fragment Shader:

void main(void) 
{ 
    vec2 p = gl_TexCoord[0].st; 
    float gray = 1.0 - p.x; 
    float red = p.y; 
    gl_FragColor = vec4(red, gray*red, gray*red, 1.0); 
} 

Un passthrough vertex:

void main() 
{ 
    gl_TexCoord[0]=gl_MultiTexCoord0; 
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; 
} 
+0

Grazie per la risposta, ho poca esperienza con GLSL anche se mi sembra di ricordare di aver letto se vuoi usare uno shader devi implementare tutto perché sostituisce completamente la pipeline OpenGL? C'è un modo semplice per usare lo shader che hai fornito e applicarlo solo a una chiamata di glDrawArrays()? –

+0

@ Will-of-fortune: fino a quando rimani con OpenGL-2.1 e sotto puoi usare gli shader in modo selettivo. Cioè utilizzare la pipeline di funzioni fisse per la maggior parte delle cose, ma uno shader solo su alcuni. Solo con core OpenGL-3 e successivo uno * deve * usare shader. Ma onestamente: usare gli shader rende la vita molto più semplice, quindi consiglio vivamente di usarli ovunque. – datenwolf

+0

@ Will-of-fortune In genere utilizzo framework o motore grafico che semplificano la gestione degli shader. Tuttavia, vi rimando a questo: http://nehe.gamedev.net/article/glsl_an_introduction/25007/ Passate alla sezione "GLSL API Come utilizzare GLSL nella vostra applicazione OpenGL". Questa sezione ti mostrerà come incorporare glsl shader meglio di quanto potrei in questi commenti. – num3ric

9

OpenGL utilizza l'interpolazione baricentrica sui valori emessi da un vertex per i valori di ingresso per-frammento di uno shader frammento . Quello che vedi qui è l'effetto di dividere il quad in triangoli come hai già fatto a indovinare.

Ora guardate: c'è il triangolo in alto a destra che è rosso in esso, e quindi interpola graziosamente verso il basso con una quantità rossa in esso. E poi c'è il triangolo in basso a sinistra con solo grigio in esso.Ovviamente il rosso del triangolo in alto a destra non contribuirà al grigio di quello in basso a sinistra.

Il problema è che l'interpolazione avviene nello spazio RGB, ma per il risultato desiderato dovrebbe essere collocato nello spazio HSV o HSL, dove H (Hue) sarebbe mantenuto costante.

Tuttavia non è solo una questione di interpolazione. Ciò che ti ostacola è che i colori non si interpolano linearmente; la maggior parte dei display ha una funzione non lineare applicata, nota anche come "gamma" (in realtà la gamma è l'esponente della potenza applicata ai valori di input).

Il contesto OpenGL può trovarsi in uno spazio colore corretto o meno. Devi testare per questo. Quindi, sapendo in quale spazio colore risiede il framebuffer, è necessario applicare una trasformazione per frammento delle coordinate baricentriche (valutata usando uno shader di vertici) in valori di frammento usando uno shader di frammenti.

3

non mi è piaciuta la parte superiore risposta votato dal momento che è così letterale e veramente solo si concentra sul rosso nella risposta semplice così ho voluto entrare con una semplice alternativa in base al colore dei vertici:

#version 330 core 

smooth in vec4 color; 
smooth in vec2 uv; 

out vec4 colorResult; 

void main(){ 
    float uvX = 1.0 - uv.st.x; 
    float uvY = uv.st.y; 

    vec4 white = vec4(1, 1, 1, 1); 
    vec4 black = vec4(0, 0, 0, 1); 

    colorResult = mix(mix(color, white, uvX), black, uvY); 
} 

Questo è semplice da spiegare e ragionare. mix è un'interpolazione lineare o lerp in glsl. Quindi mescolo tra il colore del vertice e il bianco lungo l'asse x inverso, questo mi dà il bianco in alto a sinistra e il colore puro in alto a destra. Con il risultato che mi mischio al nero lungo l'asse y dandomi il nero sul fondo.

Spero che questo aiuti, mi sono imbattuto in questa risposta e ho trovato che non era molto utile per ottenere qualcosa di diverso da tavolozze rosse, verdi o blu hard-baked, e il bit adjustHue non era semplicemente quello che volevo.

Fondamentalmente per arrivare a questo lavoro che si desidera impostare l'tutti i colori al vertice per il colore desiderato, e assicurare il vostro coordinate UV sono impostati:

TL: UV(0, 0), COLOR(YOUR DESIRED COLOR) 
BL: UV(0, 1), COLOR(YOUR DESIRED COLOR) 
BR: UV(1, 1), COLOR(YOUR DESIRED COLOR) 
TR: UV(1, 0), COLOR(YOUR DESIRED COLOR)