Fórmula GLSL Light (atenuação, cor e intensidade)

17

Estou implementando luzes pontuais no meu mecanismo Voxel e estou realmente lutando para obter um bom fluxo de luz, de 100% perto da fonte de luz a 0% no raio da luz.

Eu tenho 5 argumentos para a função:

  1. Cor clara (Vec3)
  2. Intensidade da luz (distância da luz até a distância em que a queda é de 100%)
  3. Distância da luz ao fragmento
  4. O ângulo do fragmento normal para a luz
  5. A posição da luz

Alguém pode me empurrar na direção certa para criar uma função para o cálculo da cor do fragmento?

Imagem de uma das minhas experiências:

Teste de iluminação por fragmento do motor Voxel

Editar (código atual solicitado pelo Byte) Observe que este é apenas um código experimental do meu lado. Eu peguei o float att de um site e ele funciona, mas está longe de ser perfeito. :

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
fonte
6
Você ainda está dizendo coisas como "esforçar-se para obter uma boa aparência , luzes naturais " e "funciona, mas longe de ser perfeito ". Você precisa incluir um idioma exato e específico. Não sabemos o que é bonito para você, nem como são as luzes naturais, nem o que é perfeito.
MichaelHouse
2
Você já tentou remover if (dist < 9)? Alternativamente, você pode calcular attcom uma função que retorna 1 quando a distância é 0 e 0 quando a distância é 9. Por exemplomix(1.0, 0.0, dist / 9.0)
msell

Respostas:

39

A função de atenuação que você tem,

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

é bastante comum em computação gráfica - ou, de maneira mais geral, 1.0 / (1.0 + a*dist + b*dist*dist))para alguns parâmetros ajustáveis ae b. Para entender como essa curva funciona, é útil brincar com os parâmetros interativamente . Essa curva é agradável porque se aproxima da lei do quadrado inverso fisicamente correta a grandes distâncias, mas não atinge o infinito em distâncias curtas. De fato, a = 0é um modelo bastante bom de luz de área esférica.

No entanto, uma desvantagem disso é que a luz nunca chega a zero a qualquer distância finita. Para propósitos práticos em CG em tempo real, geralmente precisamos cortar as luzes a uma distância finita, como você faz com a if (dist < 9)cláusula. No entanto, o raio de 9 é muito curto - com suas configurações na função de atenuação, a luz não chega perto de zero até que dist esteja em torno de 100.

Você pode calcular o raio da luz a partir do bparâmetro na função de atenuação (uma vez que o termo quadrático domina grandes distâncias). Digamos que você queira reduzir a luz quando a atenuação atingir algum valor minLight, como 0,01. Então defina

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

Isso fornece um raio de 100 para b = 0.01e minLight = 0.01. Como alternativa, você pode definir o raio e calcular bpara corresponder:

b = 1.0 / (radius*radius * minLight)

Para radius = 9e minLight = 0.01, isso dá b = 1.23. Você pode configurá-lo de qualquer maneira, mas a chave é fazer com que o raio e a função de atenuação coincidam para que você não apague a luz até que a função de atenuação já esteja muito baixa, para que você não veja uma borda nítida.


Tudo isso dito, existem funções de atenuação alternativas que você pode usar. Outro bastante comum é:

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

ou o ligeiramente mais extravagante:

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

Brinque com os parâmetros para esses também. Essas curvas têm a vantagem de ir exatamente para zero no raio especificado, enquanto ainda se parecem com a lei quadrada inversa natural.

Nathan Reed
fonte
Ótimo! Embora, eu uso maxmais clampapenas para motivos de desempenho.
Mike Weir
4
O @MikeWeir Clampping para [0, 1] é gratuito em muitas GPUs, na verdade. É uma operação tão comum que eles a possuem como um modificador de instruções.
22617 Nathan Reed