Reflexão e refração
Rebata uma direção numa superfície, ou curve-a através dela, a partir da normal e do índice de refração.
Como funciona
A luz que chega numa superfície ou volta ou atravessa, curvando-se ao entrar. O GLSL tem uma função embutida para
cada caso: Normal Um vetor unitário perpendicular a uma superfície, apontando para onde ela está virada. Iluminação e efeitos Fresnel o comparam com outras direções.reflect e refract. As duas recebem uma direção de chegada e a normalUm vetor unitário perpendicular a uma superfície, apontando para onde ela está virada. Iluminação e efeitos Fresnel o comparam com outras direções.
reflect
reflect(I, N) espelha a direção incidente I em relação à normal N. A definição é:
vec3 r = I - 2.0 * dot(N, I) * N; // o que reflect(I, N) devolve
O produto escalarMultiplica dois vetores componente a componente e soma os resultados. Para vetores unitários é o cosseno do ângulo entre eles, de 1 (mesma direção) a -1 (opostos). Produto escalar Multiplica dois vetores componente a componente e soma os resultados. Para vetores unitários é o cosseno do ângulo entre eles, de 1 (mesma direção) a -1 (opostos).dot(a, b) = |a| |b| cos θI aponta para dentro da superfície; subtrair duas vezes essa
parte a inverte, e a parte ao longo da superfície fica igual. N precisa estar normalizada para o resultado manter o
comprimento de I.
O erro comum é o sentido de I. Ele aponta para a superfície, no sentido em que a luz viaja. O vetor de visão que a
maioria dos shaders carrega (i.view nos cards deste site) aponta para o outro lado, da superfície para a câmera,
então inverta o sinal:
vec3 n = normalize(i.normal);
vec3 v = normalize(i.view); // superfície → câmera
vec3 r = reflect(-v, n); // a direção que um espelho mostraria neste pixel
Passe r para um mapa de ambiente ou uma função de céu e você tem um espelho. Compare a direção refletida da luz com
a direção de visão e você tem um brilho de Phong, forte onde a luz rebate direto para a câmera:
vec3 l = normalize(vec3(0.4, 1.0, 0.3)); // em direção à luz
float spec = pow(max(dot(reflect(-l, n), v), 0.0), 32.0); // expoente maior, brilho menor e mais nítido
O Blinn-Phong troca o vetor refletido pelo vetor intermediário normalize(l + v) comparado com n, o que é mais
barato e se comporta melhor em ângulos rasantes; a técnica de brilhos especulares parte dos dois.
refract
Quando a luz passa de um material para outro, ela muda de velocidade e se curva. Cada material tem um
índice de refraçãoO quanto um material desacelera a luz em relação ao vácuo, o que define o quanto ela se curva ao entrar nele. Cerca de 1,0 no ar, 1,33 na água e 1,5 no vidro. Índice de refração (IOR) O quanto um material desacelera a luz em relação ao vácuo, o que define o quanto ela se curva ao entrar nele. Cerca de 1,0 no ar, 1,33 na água e 1,5 no vidro.n₁ sin θ₁ = n₂ sin θ₂refract(I, N, eta) a aplica:
Ié a direção incidente normalizada, em direção à superfície, como noreflect.Né a normal normalizada do lado de ondeIvem.etaé a razão entre os índices: o do meio que a luz deixa dividido pelo do meio em que ela entra.
float eta = 1.0 / 1.5; // do ar para o vidro
vec3 t = refract(-v, n, eta); // o raio curvado dentro do vidro
Para sair, tudo se inverte: a normal vira para encarar o raio, e eta passa a ser 1.5 / 1.0. Indo de um índice
maior para um menor, como do vidro para o ar, um raio num ângulo raso o bastante não consegue sair e reflete de volta
para dentro. Isso é a reflexão interna total, e o refract a sinaliza devolvendo um vetor nulo, que você deve conferir
antes de usar o resultado:
vec3 t = refract(dir, -n, 1.5); // do vidro para o ar
if (dot(t, t) == 0.0) {
t = reflect(dir, -n); // reflexão interna total
}
Refração na tela
Um shader em tempo real raramente traça o raio refratado pela cena. A aproximação comum desloca a consulta a uma imagem do que está atrás da superfície pela direção curvada:
vec3 tv = mat3(uView) * t; // mundo → câmera, para x e y seguirem a tela
vec2 offset = tv.xy * 0.05; // intensidade em unidades de tela, ajustada no olho
vec3 behind = texture(uScene, screenUv + offset).rgb;
Não é fisicamente exato, mas o olho aceita. Refratar cada canal de cor com um eta um pouco diferente separa as
cores como um prisma, um efeito de dispersão barato. Quanto da luz reflete e quanto refrata também depende do ângulo;
o efeito Fresnel descreve essa divisão.
Leitura complementar
- reflect (abre em uma nova aba)Referência
- refract (abre em uma nova aba)Referência
- Snell's law (abre em uma nova aba)Referência