Formula GLSL Light (attenuazione, colore e intensità)


17

Sto implementando le luci puntiformi nel mio motore Voxel e sto davvero lottando per ottenere un buon flusso di luce, dal 100% vicino alla sorgente luminosa allo 0% nel raggio di luce.

Ho 5 argomenti per la funzione:

  1. Colore chiaro (Vec3)
  2. Intensità della luce (distanza dalla luce fino alla distanza in cui la caduta è del 100%)
  3. Distanza dalla luce al frammento
  4. L'angolo dal frammento normale alla luce
  5. La posizione della luce

Qualcuno può spingermi nella giusta direzione per creare una funzione per il calcolo del colore del frammento?

Immagine di uno dei miei esperimenti:

Test di illuminazione per frammento del motore Voxel

Modifica (codice corrente richiesto da Byte) Nota che questo è solo un codice dell'esperimento da parte mia. Ho preso il float att da un sito Web, e in genere funziona, ma tutt'altro che perfetto. :

void main()
{
// Light color
vec3 torchColor = vec3(1.0f, 1.0f, 1.0f);

float lightAdd = 0.0f;
for (int i=0; i<5; i++) {
    vec3 pos = lights[i];
    if (pos.x == 0.0f) continue;

    float dist = distance(vertex_pos, pos);
    if (dist < 9) {
        float att=1.0/(1.0+0.1*dist+0.01*dist*dist);
        vec3 surf2light = normalize(pos - vertex_pos);
        vec3 norm = normalize(normal);
        float dcont=max(0.0,dot(norm,surf2light));
        lightAdd += att*(dcont+0.4);
    }
}

vec3 textureColor = texture2D(texture, texture_coordinate).rgb;
vec3 torch_output = lightAdd * torchColor;

vec3 final_color = ((0.1+torch_output) * textureColor);

gl_FragColor = vec4(final_color, 1.0f); 
}

6
Stai ancora dicendo cose come "che lottano per ottenere un bell'aspetto , luci naturali " e "opere, ma lungi dall'essere perfetto ". Devi includere un linguaggio specifico e preciso. Non sappiamo quale sia il tuo bell'aspetto, né come ti appaiano le luci naturali o cosa sia perfetto.
MichaelHouse

2
Hai provato a rimuovere if (dist < 9)? In alternativa, è possibile calcolare attcon una funzione che restituisce 1 quando la distanza è 0 e 0 quando la distanza è 9. Ad esempiomix(1.0, 0.0, dist / 9.0)
msell

Risposte:


39

La funzione di attenuazione che hai,

att = 1.0 / (1.0 + 0.1*dist + 0.01*dist*dist)

è abbastanza comune nella computer grafica - o, più in generale, 1.0 / (1.0 + a*dist + b*dist*dist))per alcuni parametri modificabili ae b. Per capire come funziona questa curva è utile giocare con i parametri in modo interattivo . Questa curva è piacevole perché si avvicina alla legge quadrata inversa fisicamente corretta a grandi distanze, ma non scatta all'infinito a brevi distanze. In effetti, con a = 0esso è un modello abbastanza buono di una luce sferica di area.

Tuttavia, uno svantaggio di ciò è che la luce non va mai completamente a zero a qualsiasi distanza finita. Per scopi pratici nella CG in tempo reale, generalmente dobbiamo tagliare le luci a una distanza finita, come stai facendo con la if (dist < 9)clausola. Tuttavia, il raggio di 9 è troppo corto - con le tue impostazioni nella funzione di attenuazione, la luce non si avvicina allo zero fino a quando dist è circa 100.

È possibile calcolare il raggio della luce dal bparametro nella funzione di attenuazione (poiché il termine quadratico domina a grandi distanze). Supponiamo che tu voglia tagliare la luce quando l'attenuazione raggiunge un valore minLight, come 0,01. Quindi impostare

radius = sqrt(1.0 / (b * minLight))

Ciò fornisce un raggio di 100 per b = 0.01e minLight = 0.01. In alternativa, puoi impostare il raggio e calcolare la bcorrispondenza:

b = 1.0 / (radius*radius * minLight)

Per radius = 9e minLight = 0.01, questo dà b = 1.23. Puoi impostarlo in entrambi i modi, ma il tasto è far combaciare il raggio e la funzione di attenuazione in modo da non tagliare la luce fino a quando la funzione di attenuazione non è già molto bassa, quindi non vedrai un bordo nitido.


Detto questo, ci sono funzioni di attenuazione alternative che puoi usare. Un altro abbastanza comune è:

att = clamp(1.0 - dist/radius, 0.0, 1.0); att *= att

o leggermente più elaborato:

att = clamp(1.0 - dist*dist/(radius*radius), 0.0, 1.0); att *= att

Gioca con i parametri anche per quelli. Queste curve hanno il vantaggio di andare esattamente a zero al raggio dato, pur sembrando un po 'come la legge del quadrato inverso naturale.


Grande! Anche se, userei maxsopra clampsolo per motivi di prestazioni.
Mike Weir,

4
@MikeWeir Clamping su [0, 1] è gratuito su molte GPU, in realtà. È un'operazione così comune che ce l'hanno come modificatore di istruzioni.
Nathan Reed,
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.