Problemi di mappatura dell'ombra per la prima volta


9

Ho implementato la mappatura shadow di base per la prima volta in OpenGL usando shader e sto affrontando alcuni problemi. Di seguito puoi vedere un esempio della mia scena renderizzata:

inserisci qui la descrizione dell'immagine

Il processo di mappatura dell'ombra che sto seguendo è che eseguo il rendering della scena sul framebuffer utilizzando una matrice di visualizzazione dal punto di vista della luce e le matrici di proiezione e modello utilizzate per il rendering normale.

Nel secondo passaggio, invio la matrice MVP sopra dal punto di vista della luce allo shader di vertice che trasforma la posizione in spazio luminoso. Lo shader di frammenti divide la prospettiva e cambia la posizione in coordinate di trama.

Ecco il mio shader vertice,


#version 150 core

uniform mat4 ModelViewMatrix;
uniform mat3 NormalMatrix;
uniform mat4 MVPMatrix;
uniform mat4 lightMVP;

uniform float scale;

in vec3 in_Position;
in vec3 in_Normal;
in vec2 in_TexCoord;

smooth out vec3 pass_Normal;
smooth out vec3 pass_Position;
smooth out vec2 TexCoord;
smooth out vec4 lightspace_Position;

void main(void){
    pass_Normal = NormalMatrix * in_Normal; 
    pass_Position = (ModelViewMatrix * vec4(scale * in_Position, 1.0)).xyz;
    lightspace_Position = lightMVP * vec4(scale * in_Position, 1.0);
    TexCoord = in_TexCoord;

    gl_Position = MVPMatrix * vec4(scale * in_Position, 1.0);
}

E il mio shader di frammenti,


#version 150 core

struct Light{
    vec3 direction;
};

uniform Light light;
uniform sampler2D inSampler;
uniform sampler2D inShadowMap;

smooth in vec3 pass_Normal;
smooth in vec3 pass_Position;
smooth in vec2 TexCoord;
smooth in vec4 lightspace_Position;

out vec4 out_Color;


float CalcShadowFactor(vec4 lightspace_Position){

    vec3 ProjectionCoords = lightspace_Position.xyz / lightspace_Position.w;

    vec2 UVCoords;
    UVCoords.x = 0.5 * ProjectionCoords.x + 0.5;
    UVCoords.y = 0.5 * ProjectionCoords.y + 0.5;

    float Depth = texture(inShadowMap, UVCoords).x;
    if(Depth < (ProjectionCoords.z + 0.001)) return 0.5;
    else return 1.0;
}

void main(void){

    vec3 Normal = normalize(pass_Normal);
    vec3 light_Direction = -normalize(light.direction);
    vec3 camera_Direction = normalize(-pass_Position);
    vec3 half_vector = normalize(camera_Direction + light_Direction);

    float diffuse = max(0.2, dot(Normal, light_Direction));
    vec3 temp_Color = diffuse * vec3(1.0);

    float specular = max( 0.0, dot( Normal, half_vector) );

    float shadowFactor = CalcShadowFactor(lightspace_Position);

    if(diffuse != 0 && shadowFactor > 0.5){
        float fspecular = pow(specular, 128.0);
        temp_Color += fspecular;
    }

    out_Color = vec4(shadowFactor * texture(inSampler, TexCoord).xyz * temp_Color, 1.0);
}

Uno dei problemi è l'ombreggiamento di se stessi, come puoi vedere nella foto, la cassa ha la sua ombra proiettata su se stessa. Quello che ho provato è abilitare l'offset poligonale (ovvero glEnable (POLYGON_OFFSET_FILL), glPolygonOffset (GLfloat, GLfloat)) ma non è cambiato molto. Come vedi nello shader di frammenti, ho messo un valore di offset statico di 0,001 ma devo cambiare il valore in base alla distanza della luce per ottenere effetti più desiderabili, il che non è molto utile. Ho anche provato a usare l'abbattimento frontale quando eseguo il rendering sul framebuffer, ma non è cambiato molto.

L'altro problema è che i pixel esterni al frustum della vista della Luce vengono ombreggiati. L'unico oggetto che dovrebbe essere in grado di proiettare ombre è la cassa. Immagino che dovrei scegliere una proiezione più appropriata e visualizzare le matrici, ma non sono sicuro di come farlo. Quali sono alcune pratiche comuni, dovrei scegliere una proiezione ortografica?

Da googling un po ', capisco che questi problemi non sono così banali. Qualcuno ha qualche soluzione facile da implementare per questi problemi. Potresti darmi qualche consiglio aggiuntivo?

Per favore, chiedimi se hai bisogno di maggiori informazioni sul mio codice.

Ecco un confronto con e senza mappatura dell'ombra di un primo piano della cassa. L'auto-ombra è più visibile.


Sembra che dovresti descrivere i tuoi problemi in modo più dettagliato. Hai una frase sui tuoi problemi e la frase successiva suggerisce una soluzione per uno. Dicci esattamente quali sono i problemi e cosa hai già fatto per provare a risolverlo.
MichaelHouse

Ho modificato il mio post, spero sia meglio ora.
Grieverheart,

Risposte:


6

Non dovresti ombreggiare i poligoni che si trovano di fronte alla luce.

Ho cambiato il tuo shader di frammenti con il codice sottostante.

float CalcShadowFactor(vec4 lightspace_Position)
{

    vec3 ProjectionCoords = lightspace_Position.xyz / lightspace_Position.w;

    vec2 UVCoords;
    UVCoords.x = 0.5 * ProjectionCoords.x + 0.5;
    UVCoords.y = 0.5 * ProjectionCoords.y + 0.5;

    float Depth = texture(inShadowMap, UVCoords).x;
    if(Depth < (ProjectionCoords.z + 0.001)) return 0.5;
    else return 1.0;
}

void main(void)
{

    vec3 Normal = normalize(pass_Normal);
    vec3 light_Direction = -normalize(light.direction);
    vec3 camera_Direction = normalize(-pass_Position);
    vec3 half_vector = normalize(camera_Direction + light_Direction);

    float fndotl = dot(Normal, light_Direction);
    float shadowFactor = CalcShadowFactor(lightspace_Position);
    float diffuse = max(0.0, fndotl) * shadowFactor + 0.2;
    vec3 temp_Color = vec3(diffuse);

    float specular = max( 0.0, dot( Normal, half_vector) );

    if(shadowFactor > 0.9){
        float fspecular = pow(specular, 128.0);
        temp_Color += fspecular;
    }

    out_Color = vec4(shadowFactor * texture(inSampler, TexCoord).xyz * temp_Color, 1.0);
}

8

Per quanto riguarda l'ombreggiamento di sé, non sono sicuro di cosa ti riferisca - non vedo alcuna "acne ombra" sulla cassa del tuo screenshot. Se vuoi dire che i volti di fronte alla luce sono scuri, beh, certo che lo sono - dovrebbero esserlo! :) La faccia laterale sembra un po 'strana, divisa la diagonale in metà illuminata e metà in ombra. Se stai usando N punto L per l'illuminazione e hai buone normali (cioè normali rigide su questo cubo, non normali levigate) che non dovrebbero accadere.

Usando l'offset poligonale e / o disegnando le facce posteriori nella mappa dell'ombra sono le soluzioni standard (beh ... soluzioni alternative) per l'acne dell'ombra.

Per quanto riguarda la matrice di proiezione, dovresti pensare alla luce come a una videocamera. Se è una luce puntiforme, sarebbe una videocamera prospettica e se è una luce direzionale sarebbe come una videocamera ortografica. Costruisci la matrice di proiezione esattamente come faresti per una fotocamera, usando il campo visivo della luce, le proporzioni, ecc.

Per limitare il pixel shader in modo da disegnare solo i pixel nel frustum della vista della luce, una volta calcolati i raggi UV della mappa dell'ombra, controlla se sono compresi tra 0 e 1 e, in discardcaso contrario (o imposta il colore della luce su zero). Un altro modo è quello di impostare la trama della mappa ombra sulla modalità "aggancia al bordo" e impostare un colore del bordo pari a zero, che assicurerà che tutto ciò che è esterno alla mappa ombra sia completamente ombreggiato.


Ecco un primo piano della scatola e un confronto con e senza collegamento per la mappatura dell'ombra . L'auto-ombra è più visibile qui. Potresti spiegare perché dovresti usare una fotocamera prospettica per punti luce e ortografica per direzionale?
Grieverheart,

Va bene, in quello scatto sembra un'ombra d'acne. Disegnare facce posteriori nella mappa ombra (e non facce frontali) dovrebbe risolverlo. So che hai detto che l'hai provato, ma forse qualcosa è andato storto? E 'solo glCullFace(GL_FRONT). Per quanto riguarda il punto vs direzionale, è tutto sui raggi. I raggi della telecamera convergono in un punto in una telecamera in prospettiva e i raggi della luce convergono in un punto in una luce point; i raggi della videocamera sono paralleli per una videocamera ortografica e i raggi luminosi sono paralleli per una luce direzionale.
Nathan Reed,

Vedo, ha senso. Ecco un altro confronto con e senza l'abbattimento del viso. Quello in fondo ha abilitato l'abbattimento del viso. Cercherò di utilizzare la proiezione ortografica per la mia matrice di visualizzazione della luce, che non dovrebbe influire sul problema dell'auto-ombreggiamento, giusto? Forse la mia gamma di clip è troppo grande (cioè ho 0,1 a 100)?
Grieverheart,

Sembrano esattamente uguali. Stai usando l'abbattimento sul retro per il resto della scena, giusto? Hai fatto glEnable(GL_CULL_FACE)?
Nathan Reed,

Non sono esattamente gli stessi, guarda attentamente la scatola. Nella foto sopra, la linea d'ombra è convessa mentre quella nella parte inferiore è concava.
Grieverheart,

3

I pixel esterni alla vista luci sono scuri perché vengono campionati dall'esterno della trama della profondità delle luci.

Quando si trasforma il vertice dalla matrice di proiezione, si ottengono le coordinate della clip [lightspace_Position] del frammento. Lo stai quindi convertendo in coordinate di trama [UVCoords]. Tuttavia, le coordinate dello spazio di clip di un frammento possono essere ancora al di fuori dell'intervallo da -1,0 a 1,0 (fuori dallo schermo e quindi troncato) e quindi le coordinate della trama convertite possono essere al di fuori dell'intervallo da 0,0 a 1,0 della trama.

Prova questo:

if(UVCoords.x >= 0.0 && UVCoords.x <= 1.0 && UVCoords.y >= 0.0 && UVCoords.y <= 1.0 && (Depth < (ProjectionCoords.z + 0.001)))
    return 0.5;

Puoi anche moltiplicare una matrice di polarizzazione nel tuo [lightMVP] ed evitare la conversione delle coordinate nel tuo shader di frammenti.


La matrice di pregiudizio è di solito il modo in cui le persone vanno. È molto più veloce del codice di ramificazione in uno shader di frammenti.
Ian Young,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.