Ricerca…


Phong Lighting Model

NOTA: questo esempio è WIP, verrà aggiornato con diagrammi, immagini, altri esempi, ecc.

Cos'è Phong?

Phong è un modello di luce molto semplice, ma dall'aspetto reale per le superfici che ha tre parti: illuminazione ambientale, diffusa e speculare.

Illuminazione dell'ambiente:

L'illuminazione ambientale è la più semplice delle tre parti per capire e calcolare. L'illuminazione ambientale è luce che inonda la scena e illumina l'oggetto in modo uniforme in tutte le direzioni.

Le due variabili nell'illuminazione ambientale sono la forza dell'ambiente e il colore dell'ambiente. Nel tuo frammento shader, quanto segue funzionerà per ambient:

in vec3 objColor;

out vec3 finalColor;

uniform vec3 lightColor;

void main() {
   float ambientStrength = 0.3f;
   vec3 ambient = lightColor * ambientStrength;
   finalColor = ambient * objColor;
}

Illuminazione diffusa:

L'illuminazione diffusa è leggermente più complessa di quella ambientale. L'illuminazione diffusa è luce direzionale, in sostanza significa che i volti rivolti verso la fonte di luce saranno meglio illuminati e le facce rivolte verso l'esterno saranno più scure a causa di come la luce le colpisce.

Nota: l'illuminazione diffusa richiederà l'uso di normali per ogni faccia che non mostrerò come calcolare qui. Se vuoi imparare come fare, dai un'occhiata alla pagina matematica 3D.

Per modellare il riflesso della luce nella computer grafica viene utilizzata una funzione di distribuzione della riflettanza bidirezionale (BRDF). BRDF è una funzione che fornisce la relazione tra la luce riflessa lungo una direzione di uscita e l'incidente di luce da una direzione in entrata.

Una superficie diffusa perfetta ha un BRDF che ha lo stesso valore per tutte le direzioni incidenti e uscenti. Questo riduce sostanzialmente i calcoli e quindi è comunemente usato per modellare superfici diffuse in quanto è fisicamente plausibile, anche se non ci sono materiali puramente diffusi nel mondo reale. Questo BRDF è chiamato riflesso lambertiano perché obbedisce alla legge cosma di Lambert.

La riflessione lambertiana viene spesso utilizzata come modello per la riflessione diffusa. Questa tecnica fa sì che tutti i poligoni chiusi (come un triangolo all'interno di una mesh 3D) riflettano la luce in modo uguale in tutte le direzioni durante il rendering. Il coefficiente di diffusione viene calcolato dall'angolo tra il vettore normale e il vettore di luce.

f_Lambertian = max( 0.0, dot( N, L )

dove N è il vettore normale della superficie, e L è il vettore verso la fonte di luce.

Come funziona

In generale Il prodotto punto di 2 vettori è uguale al coseno dell'angolo tra i 2 vettori moltiplicato per la grandezza (lunghezza) di entrambi i vettori.

dot( A, B ) == length( A ) * length( B ) * cos( angle_A_B ) 

Ne consegue che il prodotto punto di 2 vettori unitari è uguale al coseno dell'angolo tra i 2 vettori, poiché la lunghezza di un vettore unitario è 1.

uA = normalize( A )
uB = normalize( B )
cos( angle_A_B ) == dot( uA, uB )

inserisci la descrizione dell'immagine qui

Se osserviamo la funzione cos (x) tra gli angoli -90 ° e 90 ° allora possiamo vedere che ha un massimo di 1 ad un angolo di 0 ° e scende a 0 agli angoli di 90 ° e -90 °.

inserisci la descrizione dell'immagine qui

Questo comportamento è esattamente ciò che vogliamo per il modello di riflessione. Quando il vetore nromale della superficie e la direzione verso la sorgente di luce sono nella stessa direzione (l'angolo tra 0 °), allora vogliamo un massimo di riflessione. Al contrario, se i vettori sono ortonormalizzati (l'angolo nel mezzo è di 90 °), vogliamo un minimo di riflessione e vogliamo un funzionamento regolare e continuo tra i due bordi di 0 ° e 90 °.

inserisci la descrizione dell'immagine qui

Se la luce viene calcolata per vertice, la riflessione viene calcolata per ciascun angolo della primitiva. Tra i primitivi le riflessioni sono interpolate secondo le sue coordinate baricentriche. Vedi le riflessioni risultanti su una superficie sferica:

inserisci la descrizione dell'immagine qui

Ok, quindi per iniziare con il nostro framment shader, avremo bisogno di quattro input.

  • Vertex normals (dovrebbe essere nel buffer e specificato dai puntatori degli attributi dei vertici)
  • Posizione del frammento (dovrebbe essere emessa dal vertex shader in frag shader)
  • Posizione della sorgente di luce (uniforme)
  • Colore chiaro (uniforme)
in vec3 normal;
in vec3 fragPos;
    
out vec3 finalColor;
    
uniform vec3 lightColor;
uniform vec3 lightPos;
uniform vec3 objColor;

All'interno del main è dove dobbiamo fare un po 'di matematica. L'intero concetto di illuminazione diffusa si basa sull'angolo tra la normale e la direzione della luce. Maggiore è l'angolo, minore è la luce fino a 90 ° dove non c'è luce.

Prima di poter calcolare la quantità di luce, abbiamo bisogno del vettore di direzione della luce. Questo può essere recuperato semplicemente sottraendo la posizione della luce dalla posizione del frammento che restituisce un vettore dalla posizione della luce che punta alla posizione del frammento.

vec3 lightDir = lightPos-fragPos;

Inoltre, vai avanti e normalizza i vettori normal e lightDir modo che abbiano la stessa lunghezza con cui lavorare.

normal  = normalize(normal);
lightDir = normalize(lightDir);

Ora che abbiamo i nostri vettori, possiamo calcolare la differenza tra loro. Per fare questo, useremo la funzione del prodotto con punti. Fondamentalmente, questo prende 2 vettori e restituisce il cos () dell'angolo formato. Questo è perfetto perché a 90 gradi produrrà 0 ea 0 gradi produrrà 1. Di conseguenza, quando la luce è puntata direttamente sull'oggetto, sarà completamente illuminata e viceversa.

float diff = dot(normal, lightDir);

C'è ancora una cosa che dobbiamo fare al numero calcolato, dobbiamo assicurarci che sia sempre positivo. Se ci pensi, un numero negativo non ha senso nel contesto perché significa che la luce è dietro la faccia. Potremmo usare un'istruzione if, oppure possiamo usare la funzione max() che restituisce il massimo di due input.

diff = max(diff, 0.0);

Fatto ciò, siamo ora pronti per calcolare il colore di output finale per il frammento.

vec3 diffuse = diff * lightColor;
finalColor = diffuse * objColor;

Dovrebbe sembrare come questo: inserisci la descrizione dell'immagine qui

Illuminazione speculare:

Lavora in produzione, ricontrolla più tardi.

Combinato

Lavori in corso, ricontrolla più tardi.

Il codice e l'immagine qui sotto mostrano questi tre concetti di illuminazione combinati.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow