Lição 3.4: Iluminação e Vetores Normais: Versão GLUT
Muito do código deste tutorial foi escrito originalmente por Jeff Molofee e Fredric Echols e é uma tradução do OpenGL Tutorial de Jeff Molofee & Neon Helium. Jeff o reescreveu totalmente em Janeiro de 2000. Durante a tradução ao português, a formatação original, com muitas tabelas e figuras mostrando bordas de tabelas para o texto explicativo, foi modificada de forma a facilitar a carga das páginas e a torná-las mais leves.
Muitas partes do código foram adaptadas para utilizarem a API GLUT. Para isso foi muitas vezes utilizado material proveniente do Livro OpenGL SuperBible, que foi programado por Michael Sweet.
O objetivo geral desta lição é ensiná-lo a determinar vetores normais a facetas de objetos, para permitir uma iluminação realista.
O objetivo desta lição é ensiná-lo a calcular os efeitos de iluminação causados por ângulos entre a luz incidente sobre um objeto e o que você enxerga.
Quando aluz incide sobre um objeto, como foi visto na aula sobre cor, há dois tipos de luz refletida: a luz reflexa difusa, que dá a cor do objeto e a luz especular, que é refletida na integridade de seu espectro e que dá o "brilho" de um objeto.
Para ambos os tipos de luz, a intensidade com que ela é refletida depende do ângulo com que a luz inside sobre a superfície refletora e também do ângulo realizado com a superfície pelos raios refletidos par aque atinjam o observador.
Isto pode ser mostrado pela figura 1:
OpenGL não calcula estes ângulos por default. Isto significa, que se você não prover informação para o cálculo destes ângulos, OpenGL vai gerar uma iluminação simplificada da cena, pouco realista. Para que OpenGL possa calcular os ângulos de reflexão de forma eficiente e gerar intensidades de iluminação (luz difusa) e reflexos (luz especular) realistas, é necessário que você o ajude.
Para isso, você vai prover para cada superfície de seu objeto, um vetor normal a esta superfície. Este vetor facilita o cálculo do reflexo, pois reduz a tarefa de OpenGL a calcular em tempo real os ângulos alfa e beta e a partir daí, a intensidade de luz refletida, o que é bem mais simples e rápido do que fazer tudo em tempo real.
Figura
2: Um vetor normal à superfíce.
Para implementar isto, vamos construir duas rotinas que calculam e normalizam um vetor normal a uma superfície representada por três pontos quaisquer que estejam em cima da mesma.
Este enfoque baseado em três pontos vai facilitar a sua vida também no quesito aplicar esta rotina, já que na nossa reconstrução 3D do modelo de cabeça baseado em pontos, você trabalha sempe com triângulos.
Depois disso vamos mostrar a você onde inserir isto no contexto do programa FacetaArame que você já fez na aula passada.
Podemos dividir o cálculo da normal em duas etapas:
#include <math.h>
// Nao esqueca de incluir este por
// causa da definicao de sqrt()
#include <gl/glut.h>
#include "triangulos.h"
Vamos iniciar a descrição das rotinas com a função de normalização, que poderia ser copiada de um livro de segundo grau:
void
reduzParaUnitario(
TipoPontos *vector )
{
GLfloat length;
// Calculate the length of the vector
length = (GLfloat) sqrt((vector->x * vector->x) +
(vector->y * vector->y) +
(vector->z * vector->z));
// Keep the program from blowing up by providing an exceptable
// value for vectors that may calculated too close to zero.
if (length == 0.0f)
length = 1.0f;
// Dividing each element by the length will result in a
// unit normal vector.
vector->x = vector->x / length;
vector->y = vector->y / length;
vector->z = vector->z / length;
}
O cálculo da normal a um plano você também já aprendeu em Álgebra Linear no início de seu curso de informática. Temos aqui uma rotina que se utiliza de nosso modelo de pontos para facilitar a passagem de parâmetros:
/* ===================================================== */ void calculaNormal( TipoPontos p1, TipoPontos p2, TipoPontos p3, TipoPontos *retorno ) /* Assume que p1, p2, & p3 estão especificados em */ /* sentido anti-horario. Se voce passar os parametros em */ /* outra ordem, o vetor vai apontar para o lado errado. */ /* ===================================================== */ { TipoPontos v1,v2; // Calcula dois vetores a partir dos tres pontos. v1.x = p1.x - p2.x; v1.y = p1.y - p2.y; v1.z = p1.z - p2.z; v2.x = p2.x - p3.x; v2.y = p2.y - p3.y; v2.z = p2.z - p3.z; // Take the cross product of the two vectors to get // the normal vector which will be stored in out retorno->x = v1.y * v2.z - v1.z * v2.y; retorno->y = v1.z * v2.x - v1.x * v2.z; retorno->z = v1.x * v2.y - v1.y * v2.x; // Normaliza o vector, reduzindo comprimento para um reduzParaUnitario(retorno); }
No laço para-faça, entre glBegin() e glEnd(), onde você plota os triângulos, inclua uma chamada ao cálculo da normal e, a seguir, uma chamada à rotina glNormal3F(), que passa essa normal para OpenGL antes de criar o triângulo. A normal passada por glNormal3F() será neste momento a normal válida para OpenGL e vai ser asociada a todos os objetos que forem criado. Como você calcula uma normal para cada faceta, a cada iteração, cada uma das faces do objeto vai ser associada com a sua própria normal.
Assim
você consegui um efeito como o mostrado aqui. Observe os rflexos
decorrentes da luz especular que havíamos definido.
O
código do laço fica então como abaixo:
for (trianguloAtual=1; trianguloAtual <= numTriangulos; trianguloAtual++) { /* (NOVO) Calcula a normal aos Pontos que vao definir a proxima faceta */ calculaNormal( meusPontos[meusTriangulos[trianguloAtual].v3], meusPontos[meusTriangulos[trianguloAtual].v2], meusPontos[meusTriangulos[trianguloAtual].v1], &normal ); /* (NOVO) Vou setar normal... */ glNormal3f(normal.x, normal.y, normal.z); glVertex3f( meusPontos[meusTriangulos[trianguloAtual].v3].x, meusPontos[meusTriangulos[trianguloAtual].v3].y, meusPontos[meusTriangulos[trianguloAtual].v3].z); glVertex3f( meusPontos[meusTriangulos[trianguloAtual].v2].x, meusPontos[meusTriangulos[trianguloAtual].v2].y, meusPontos[meusTriangulos[trianguloAtual].v2].z); glVertex3f( meusPontos[meusTriangulos[trianguloAtual].v1].x, meusPontos[meusTriangulos[trianguloAtual].v1].y, meusPontos[meusTriangulos[trianguloAtual].v1].z); }
Aldo von Wangenheim (UFSC)