Formula GLSL Light (Atenuasi, Warna dan intensitas)

17

Saya menerapkan lampu titik di mesin Voxel saya, dan saya benar-benar berjuang untuk mendapatkan aliran cahaya yang baik, dari 100% di dekat sumber cahaya hingga 0% di radius cahaya.

Saya punya 5 argumen untuk fungsi ini:

  1. Warna terang (Vec3)
  2. Intensitas cahaya (jarak dari cahaya sampai jarak di mana falloff adalah 100%)
  3. Jarak dari cahaya ke fragmen
  4. Sudut dari fragmen normal ke cahaya
  5. Posisi cahaya

Adakah yang bisa mendorong saya ke arah yang benar untuk membuat fungsi untuk menghitung warna fragmen?

Gambar salah satu eksperimen saya:

Uji Pencahayaan Per-Fragmen Mesin Voxel

Edit (kode saat ini diminta oleh Byte) Perhatikan bahwa ini hanya beberapa kode eksperimen dari pihak saya. Saya mendapat att float dari sebuah situs web, dan itu berfungsi, tetapi jauh dari sempurna. :

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
sumber
6
Anda masih sedang mengatakan hal-hal seperti "berjuang untuk mendapatkan tampan , lampu alami " dan "bekerja, tapi jauh dari sempurna ". Anda harus memasukkan bahasa yang spesifik dan tepat. Kami tidak tahu apa yang tampak menarik bagi Anda, atau seperti apa lampu alami bagi Anda, atau apa yang sempurna.
MichaelHouse
2
Sudahkah Anda mencoba menghapus if (dist < 9)? Atau Anda dapat menghitung attdengan fungsi yang mengembalikan 1 ketika jarak adalah 0 dan 0 ketika jarak adalah 9. Misalnyamix(1.0, 0.0, dist / 9.0)
msell

Jawaban:

39

Fungsi atenuasi yang Anda miliki,

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

adalah salah satu yang cukup umum dalam grafik komputer - atau, lebih umum, 1.0 / (1.0 + a*dist + b*dist*dist))untuk beberapa parameter adan tweakable b. Untuk memahami cara kerja kurva ini, sebaiknya Anda memainkan parameter secara interaktif . Kurva ini bagus karena mendekati hukum kuadrat terbalik yang benar secara fisik pada jarak yang jauh, tetapi tidak melesat hingga tak terbatas pada jarak pendek. Bahkan, dengana = 0 model cahaya area bola yang cukup bagus.

Namun, satu kelemahannya adalah bahwa cahaya tidak pernah benar-benar pergi ke nol pada jarak berapapun. Untuk tujuan praktis dalam CG waktu nyata, kami biasanya harus mematikan lampu pada jarak yang terbatas, seperti yang Anda lakukan denganif (dist < 9) klausa. Namun, jari-jari 9 terlalu pendek - dengan pengaturan Anda pada fungsi atenuasi, cahayanya tidak mendekati nol sampai dist berada di sekitar 100.

Anda dapat menghitung jari-jari cahaya dari bparameter dalam fungsi atenuasi (karena istilah kuadrat mendominasi pada jarak yang jauh). Katakanlah Anda ingin memotong lampu ketika redaman mencapai beberapa nilai minLight, seperti 0,01. Kemudian atur

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

Itu memberi radius 100 untuk b = 0.01dan minLight = 0.01. Atau, Anda dapat mengatur radius dan menghitung buntuk mencocokkan:

b = 1.0 / (radius*radius * minLight)

Untuk radius = 9dan minLight = 0.01, itu memberi b = 1.23. Anda dapat mengaturnya dengan baik, tetapi kuncinya adalah untuk membuat jari-jari dan fungsi atenuasi cocok sehingga Anda tidak memotong lampu sampai fungsi atenuasi sudah sangat rendah, sehingga Anda tidak akan melihat ujung yang tajam.


Semua yang dikatakan, ada fungsi atenuasi alternatif yang dapat Anda gunakan. Satu lagi yang cukup umum adalah:

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

atau yang sedikit lebih keren:

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

Mainkan dengan parameter untuk parameter itu juga. Kurva-kurva ini memiliki keuntungan untuk pergi tepat ke nol pada radius yang diberikan, sambil tetap terlihat seperti hukum kuadrat terbalik alami.

Nathan Reed
sumber
Bagus! Meskipun, saya akan menggunakan maxlebih clampkarena alasan kinerja saja.
Mike Weir
4
@MikeWeir Menjepit ke [0, 1] sebenarnya gratis di banyak GPU. Ini adalah operasi yang umum sehingga mereka memilikinya sebagai pengubah instruksi.
Nathan Reed