2012-05-28 8 views
7

Sto lavorando con punti di luce omnidirezionali. Ho già implementato la mappatura delle ombre utilizzando una texture cubemap come allegato di colore di 6 framebuffer e codificando la distanza da luce a frammento in ciascun pixel di esso.Mappatura shadow omnidirezionale con cubemap di profondità

Ora vorrei, se questo è possibile, cambiare la mia applicazione in questo modo:

  • 1) allegare una texture di profondità cubemap al buffer di profondità del mio framebuffer, invece dei colori.
  • 2) solo profondità di rendering, non scrivere colore in questo passaggio
  • 3) nel passaggio principale, leggere la profondità dalla texture cubemap, convertirla in una distanza e verificare se il frammento corrente è occluso dal leggero o no

Il mio problema si verifica quando si converte un valore di profondità dalla mappa del cubo in una distanza. Io uso il vettore light-to-framment (nello spazio mondo) per recuperare il mio valore di profondità nella cubemap. A questo punto, non so quale dei sei volti venga usato, né quali coordinate della trama 2D corrispondano al valore di profondità che sto leggendo. Quindi, come posso convertire il valore di profondità a una distanza?

Ecco frammenti di mio codice per illustrare:

profondità di struttura:

glGenTextures(1, &TextureHandle); 
glBindTexture(GL_TEXTURE_CUBE_MAP, TextureHandle); 
for (int i = 0; i < 6; ++i) 
    glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT, 
       Width, Height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0); 

glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 

glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 

frame buffer costruzione:

for (int i = 0; i < 6; ++i) 
{ 
    glGenFramebuffers(1, &FBO->FrameBufferID); 
    glBindFramebuffer(GL_FRAMEBUFFER, FBO->FrameBufferID); 
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, 
      GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, TextureHandle, 0); 
    glDrawBuffer(GL_NONE); 
} 

Il pezzo di frammento di shader che sto cercando di scrivere di raggiungere il mio codice:

float ComputeShadowFactor(samplerCubeShadow ShadowCubeMap, vec3 VertToLightWS) 
{ 
    float ShadowVec = texture(ShadowCubeMap, vec4(VertToLightWS, 1.0)); 
    ShadowVec = DepthValueToDistance(ShadowVec); 
    if (ShadowVec * ShadowVec > dot(VertToLightWS, VertToLightWS)) 
     return 1.0; 

    return 0.0; 
} 

La funzione DepthValueToDistance è il mio problema reale.

+0

"C'è una soluzione al mio problema?" Converti distanza in profondità, confronta con profondità memorizzata in cubemap. "Inapplicabile per le ombre delle mappe dei cubi" Penso che 5..7 anni fa qualcuno ha chiamato "ron frazier", ha scritto un articolo sull'utilizzo di cubemaps per la mappatura delle ombre. – SigTerm

+0

@SigTerm http://www.ronfrazier.net/apparition/index.html?appmain=research/index.html è ciò di cui penso tu stia parlando. –

+0

@JohnRiselvato: Sì. I ** think ** era [this] (http://www.ronfrazier.net/apparition/index.html?appmain=research/index.html) articolo. Ovviamente è possibile fare qualcosa di simile con la moderna OpenGL/migliore precisione (ci sono trame in virgola mobile, GLSL, '* shadow' sampler, ecc.) - Non ho uno snippet funzionante nelle vicinanze e non ho voglia di scriverne uno . – SigTerm

risposta

10

Quindi, la soluzione era convertire il vettore da leggero a frammento in un valore di profondità, invece di convertire la profondità letta dalla mappa del cubo in una distanza.

Ecco il codice Shader modificata:

float VectorToDepthValue(vec3 Vec) 
{ 
    vec3 AbsVec = abs(Vec); 
    float LocalZcomp = max(AbsVec.x, max(AbsVec.y, AbsVec.z)); 

    const float f = 2048.0; 
    const float n = 1.0; 
    float NormZComp = (f+n)/(f-n) - (2*f*n)/(f-n)/LocalZcomp; 
    return (NormZComp + 1.0) * 0.5; 
} 

float ComputeShadowFactor(samplerCubeShadow ShadowCubeMap, vec3 VertToLightWS) 
{ 
    float ShadowVec = texture(ShadowCubeMap, vec4(VertToLightWS, 1.0)); 
    if (ShadowVec + 0.0001 > VectorToDepthValue(VertToLightWS)) 
     return 1.0; 

    return 0.0; 
} 

analitico su VectorToDepthValue(vec3 Vec):

LocalZComp corrisponde a quello che sarebbe il componente Z della proposta Vec nel tronco corrispondenza del cubemap. In realtà è il componente più grande di Vec (ad esempio, se Vec.y è il componente più grande, guarderemo su Y + o sul lato Y della cubemap).

Se si guarda a questo wikipedia article, capirai la matematica subito dopo (l'ho tenuto in forma formale per la comprensione), che converte semplicemente lo LocalZComp in un valore Z normalizzato (tra in [-1..1]) e quindi mapparlo in [0..1], che è l'intervallo effettivo per i valori del buffer di profondità. (supponendo che tu non l'abbia cambiato). n e f sono i valori vicini e lontani dei frottumi utilizzati per generare la cubemap.

ComputeShadowFactor quindi basta confrontare il valore di profondità della mappa del cubo con il valore di profondità calcolato dal vettore frammento-luce (denominato VertToLightWS qui), inoltre aggiungere un piccolo bias di profondità (che mancava nella domanda) e restituisce 1 se il frammento non è occluso dalla luce.

4

Vorrei aggiungere ulteriori dettagli sulla derivazione.

Lasciate V essere il vettore di direzione da luce a frammento.

Come già detto Benlitz, il Z valore nel rispettivo lato cubo tronco/"spazio eye" può essere calcolato prendendo il massimo dei valori assoluti delle V s' componenti.

Z = max(abs(V.x),abs(V.y),abs(V.z)) 

Poi, per essere precisi, dobbiamo neghiamo Z perché in OpenGL, i punti negativi asse Z in tronco schermo/vista.

Ora vogliamo ottenere il valore "compatibile" del buffer di profondità di quello -Z.

Guardando la matrice prospettiva OpenGL ...

http://www.songho.ca/opengl/files/gl_projectionmatrix_eq16.png

http://i.stack.imgur.com/mN7ke.png (collegamento di backup)

... vediamo che per ogni vettore omogenea moltiplicato per la matrice , il valore z risultante è completamente indipendente dalle componenti xey del vettore.

Così possiamo semplicemente moltiplicare questa matrice con il vettore omogeneo (0,0, -Z, 1) e si ottiene il vettore (componenti):

x = 0 
y = 0 
z = (-Z * -(f+n)/(f-n)) + (-2*f*n/(f-n)) 
w = Z 

Poi abbiamo bisogno di fare il punto di vista dividere, quindi si divide z da w (Z ) che ci dà:

z' = (f+n)/(f-n) - 2*f*n/(Z* (f-n)) 

Questa z' è normalizzata dispositivo di OpenGL coordinate (NDC) range [-1,1] e deve essere trasformato in un tampone di profondità compatibl e gamma di [0,1]:

z_depth_buffer_compatible = (z' + 1.0) * 0.5 

Ulteriori note:

  • Potrebbe avere senso per caricare i risultati di (f + n), (FN) e (f * n) come uniformi shader per risparmiare calcoli.

  • V deve essere nello spazio mondo dalla mappa delle ombre cubo è normalmente asse allineato nello spazio globale così la "max (abs (Vx), abs (Vy), abs (Vz))" - parte funziona solo se V è un vettore di direzione dello spazio mondiale.

Problemi correlati