Formule GLSL Light (atténuation, couleur et intensité)

17

J'implémente des lumières ponctuelles dans mon moteur Voxel et j'ai vraiment du mal à obtenir un bon flux de lumière, de 100% près de la source lumineuse à 0% au rayon lumineux.

J'ai 5 arguments pour la fonction:

  1. Couleur claire (Vec3)
  2. Intensité lumineuse (distance de la lumière jusqu'à la distance où la chute est de 100%)
  3. Distance de la lumière au fragment
  4. L'angle du fragment normal à la lumière
  5. La position de la lumière

Quelqu'un peut-il me pousser dans la bonne direction pour créer une fonction pour le calcul de la couleur du fragment?

Image d'une de mes expériences:

Test d'éclairage par fragment du moteur Voxel

Edit (code actuel demandé par Byte) Notez que ce n'est qu'un code d'expérience de mon côté. J'ai obtenu le float att sur un site Web, et ça marche, mais loin d'être parfait. :

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
la source
6
Tu es toujours dire des choses comme « luttent pour obtenir une bonne recherche , les lumières naturelles » et « œuvres, mais loin d' être parfait ». Vous devez inclure un langage précis et précis. Nous ne savons pas ce qu'est beau pour vous, ni à quoi ressemble la lumière naturelle, ni ce qui est parfait.
MichaelHouse
2
Avez-vous essayé de supprimer if (dist < 9)? Alternativement, vous pouvez calculer attavec une fonction qui renvoie 1 lorsque la distance est 0 et 0 lorsque la distance est 9. Par exemplemix(1.0, 0.0, dist / 9.0)
msell

Réponses:

39

La fonction d'atténuation que vous avez,

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

est assez courant en infographie - ou, plus généralement, 1.0 / (1.0 + a*dist + b*dist*dist))pour certains paramètres modifiables aet b. Pour comprendre le fonctionnement de cette courbe, il est utile de jouer avec les paramètres de manière interactive . Cette courbe est agréable car elle s'approche de la loi du carré inverse physiquement correcte à grande distance, mais elle ne tire pas à l'infini à courte distance. En fait, avec a = 0c'est un assez bon modèle de lumière sphérique.

Cependant, un inconvénient est que la lumière n'atteint jamais complètement à zéro à une distance finie. Pour des raisons pratiques de CG en temps réel, nous devons généralement couper les lumières à une distance finie, comme vous le faites avec leif (dist < 9) clause. Cependant, le rayon de 9 est trop court - avec vos paramètres dans la fonction d'atténuation, la lumière ne se rapproche de zéro que lorsque la distance est d'environ 100.

Vous pouvez calculer le rayon de la lumière à partir du bparamètre dans la fonction d'atténuation (puisque le terme quadratique domine à de grandes distances). Disons que vous voulez couper la lumière lorsque l'atténuation atteint une valeur minLight, comme 0,01. Puis réglez

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

Cela donne un rayon de 100 pour b = 0.01et minLight = 0.01. Alternativement, vous pouvez définir le rayon et calculer bpour correspondre:

b = 1.0 / (radius*radius * minLight)

Pour radius = 9et minLight = 0.01, cela donne b = 1.23. Vous pouvez le configurer dans les deux sens, mais la clé est de faire correspondre le rayon et la fonction d'atténuation afin de ne pas couper la lumière jusqu'à ce que la fonction d'atténuation soit déjà très faible, de sorte que vous ne verrez pas d'arête vive.


Cela dit, il existe d'autres fonctions d'atténuation que vous pouvez utiliser. Un autre assez courant est:

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

ou le légèrement plus chic:

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

Jouez avec les paramètres de ceux-ci aussi. Ces courbes ont l'avantage d'aller exactement à zéro au rayon donné, tout en ressemblant un peu à la loi carrée inverse naturelle.

Nathan Reed
la source
Génial! Cependant, j'utiliserais maxover clamppour des raisons de performances uniquement.
Mike Weir
4
@MikeWeir Le serrage à [0, 1] est gratuit sur de nombreux GPU, en fait. C'est une opération tellement courante qu'ils l'ont comme modificateur d'instructions.
Nathan Reed