GLSL Light (Dämpfung, Farbe und Intensität) Formel

17

Ich implementiere Punktlichter in meiner Voxel-Engine und habe wirklich Mühe, einen guten Lichtfluss zu erzielen, von 100% in der Nähe der Lichtquelle bis 0% im Lichtradius.

Ich habe 5 Argumente für die Funktion:

  1. Lichtfarbe (Vec3)
  2. Lichtintensität (Abstand vom Licht bis zu einem Abstand von 100%)
  3. Entfernung vom Licht zum Fragment
  4. Der Winkel vom Fragment normal zum Licht
  5. Die Position des Lichts

Kann jemand mich in die richtige Richtung treiben, um eine Funktion für die Berechnung der Fragmentfarbe zu erstellen?

Bild von einem meiner Experimente:

Voxel Engine Pro-Fragment-Beleuchtungstest

Bearbeiten (aktueller Code wird von Byte angefordert) Beachten Sie, dass dies nur ein experimenteller Code von meiner Seite ist. Ich habe das float att von einer Website bekommen, und es funktioniert, aber alles andere als perfekt. :

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); 
}
Basaa
quelle
6
Du sagst immer noch Dinge wie "Mühe, ein gut aussehendes , natürliches Licht zu bekommen " und "funktioniert, aber alles andere als perfekt ". Sie müssen eine bestimmte, genaue Sprache angeben. Wir wissen nicht, was gut für Sie aussieht, wie natürliches Licht für Sie aussieht oder wie perfekt es ist.
MichaelHouse
2
Haben Sie versucht, es zu entfernen if (dist < 9)? Alternativ können Sie berechnen attmit einer Funktion , die den Wert 1 zurück , wenn der Abstand 0 und 0 , wenn der Abstand 9. Egmix(1.0, 0.0, dist / 9.0)
msell

Antworten:

39

Die Dämpfungsfunktion, die Sie haben,

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

ist in der Computergrafik ziemlich verbreitet - oder allgemeiner 1.0 / (1.0 + a*dist + b*dist*dist))für einige einstellbare Parameter aund b. Um zu verstehen, wie diese Kurve funktioniert, ist es hilfreich, interaktiv mit den Parametern zu spielen . Diese Kurve ist schön, weil sie sich bei großen Entfernungen dem physikalisch korrekten Gesetz des umgekehrten Quadrats annähert, aber bei kurzen Entfernungen nicht bis ins Unendliche schießt. Tatsächlich ist a = 0es ein ziemlich gutes Modell einer Kugelflächenleuchte.

Ein Nachteil ist jedoch, dass das Licht in keiner endlichen Entfernung ganz gegen Null geht. Für praktische Zwecke in Echtzeit-CG müssen wir im Allgemeinen die Lichter in einem begrenzten Abstand abschalten, wie Sie es mit der if (dist < 9)Klausel tun . Der Radius von 9 ist jedoch zu kurz - mit Ihren Einstellungen in der Dämpfungsfunktion kommt das Licht erst bei einem Abstand von etwa 100 gegen Null.

Sie können den Radius des Lichts aus dem bParameter in der Dämpfungsfunktion berechnen (da der quadratische Term bei großen Entfernungen dominiert). Angenommen, Sie möchten das Licht ausschalten, wenn die Dämpfung einen Wert minLightwie 0,01 erreicht. Dann setzen

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

Das ergibt einen Radius von 100 für b = 0.01und minLight = 0.01. Alternativ können Sie den Radius festlegen und entsprechend berechnen b:

b = 1.0 / (radius*radius * minLight)

Für radius = 9und minLight = 0.01, das gibt b = 1.23. Sie können es so oder so einstellen, aber der Schlüssel ist, dass der Radius und die Dämpfungsfunktion übereinstimmen, damit Sie das Licht nicht ausschalten, bis die Dämpfungsfunktion bereits sehr niedrig ist, sodass Sie keine scharfe Kante sehen.


Trotzdem gibt es alternative Dämpfungsfunktionen, die Sie verwenden können. Ein weiteres ziemlich verbreitetes ist:

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

oder der etwas schickere:

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

Spielen Sie auch bei diesen mit den Parametern . Diese Kurven haben den Vorteil, dass sie beim gegebenen Radius genau auf Null gehen und dennoch dem natürlichen Gesetz des umgekehrten Quadrats ähneln.

Nathan Reed
quelle
Groß! Obwohl, würde ich maxüber clampnur aus Leistungsgründen.
Mike Weir
4
@MikeWeir Das Clamping auf [0, 1] ist auf vielen GPUs tatsächlich kostenlos. Es ist so eine übliche Operation, dass sie es als Befehlsmodifikator hat.
Nathan Reed