Formuła GLSL Light (tłumienie, kolor i intensywność)

17

Wdrażam światła punktowe w silniku Voxel i naprawdę bardzo staram się uzyskać dobry przepływ światła, od 100% w pobliżu źródła światła do 0% w promieniu światła.

Mam 5 argumentów dla funkcji:

  1. Barwa światła (Vec3)
  2. Intensywność światła (odległość od światła do odległości, w której spadek wynosi 100%)
  3. Odległość od światła do fragmentu
  4. Kąt od fragmentu normalny do światła
  5. Pozycja światła

Czy ktoś może popchnąć mnie we właściwym kierunku, aby stworzyć funkcję do obliczania koloru fragmentu?

Zdjęcie jednego z moich eksperymentów:

Test oświetlenia dla poszczególnych fragmentów silnika Voxel

Edycja (aktualny kod wymagany przez Byte) Zauważ, że to tylko część kodu eksperymentu z mojej strony. Mam float att ze strony internetowej i to działa, ale dalekie od ideału. :

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
źródło
6
Nadal mówisz takie rzeczy, jak „walka o dobre , naturalne oświetlenie ” i „działa, ale dalekie od ideału ”. Musisz podać konkretny, dokładny język. Nie wiemy, jak dobrze dla ciebie wyglądają, jak wyglądają dla ciebie naturalne światła, ani co jest idealne.
MichaelHouse
2
Czy próbowałeś usunąć if (dist < 9)? Alternatywnie można obliczyć attza pomocą funkcji, która zwraca 1, gdy odległość wynosi 0, a 0, gdy odległość wynosi 9. Np.mix(1.0, 0.0, dist / 9.0)
sprzedaj

Odpowiedzi:

39

Masz funkcję tłumienia,

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

jest dość powszechny w grafice komputerowej - lub, bardziej ogólnie, w 1.0 / (1.0 + a*dist + b*dist*dist))przypadku niektórych modyfikowalnych parametrów ai b. Aby zrozumieć, jak działa ta krzywa, pomocne jest interaktywne granie z parametrami . Ta krzywa jest dobra, ponieważ zbliża się do fizycznie poprawnego odwrotnego prawa kwadratu na dużych odległościach, ale nie strzela do nieskończoności na krótkich odległościach. W rzeczywistości a = 0jest to całkiem dobry model sferycznego światła obszarowego.

Jednak jedną z jego wad jest to, że światło nigdy nie osiąga zera w żadnej skończonej odległości. Ze względów praktycznych w grze komputerowej w czasie rzeczywistym zazwyczaj musimy odcinać światła w ograniczonej odległości, tak jak robisz to z if (dist < 9)klauzulą. Jednak promień 9 jest zbyt krótki - przy ustawieniach funkcji tłumienia światło nie zbliża się do zera, dopóki dist nie osiągnie około 100.

Promień światła można obliczyć na podstawie bparametru w funkcji tłumienia (ponieważ element kwadratowy dominuje na dużych odległościach). Powiedzmy, że chcesz odciąć światło, gdy tłumienie osiągnie pewną wartość minLight, na przykład 0,01. Następnie ustaw

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

To daje promień 100 dla b = 0.01i minLight = 0.01. Alternatywnie możesz ustawić promień i obliczyć, baby dopasować:

b = 1.0 / (radius*radius * minLight)

Za radius = 9i minLight = 0.01to daje b = 1.23. Możesz to ustawić w dowolny sposób, ale kluczem jest dopasowanie promienia i funkcji tłumienia, aby nie odcinać światła, dopóki funkcja tłumienia nie będzie już bardzo niska, aby nie zobaczyć ostrej krawędzi.


To powiedziawszy, istnieją alternatywne funkcje tłumienia, których można użyć. Innym dość powszechnym jest:

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

lub nieco bardziej fantazyjny:

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

Graj też z parametrami dla tych. Krzywe te mają tę zaletę, że idą dokładnie do zera przy danym promieniu, a jednocześnie wyglądają jak naturalne odwrotne prawo kwadratowe.

Nathan Reed
źródło
Świetny! Choć użyję maxciągu clamptylko ze względu na wydajność.
Mike Weir
4
@MikeWeir Clamping do [0, 1] jest w rzeczywistości bezpłatny na wielu procesorach graficznych. Jest to tak powszechna operacja, że ​​mają ją jako modyfikator instrukcji.
Nathan Reed