Estudo Dirigido - Iluminação em OpenGL

Introdução

O objetivo desta aula é aprender a trabalhar com os recursos de iluminação da biblioteca OpenGL. Para isto, será utilizada a API JOGL e a linguagem de programação Java, tendo como resultado um programa que pode ser executado tanto no ambiente Windows como no ambiente Linux.

O primeiro passo é ler com atenção alguns slides sobre iluminação, a saber:

Antes de seguir adiante, tenha certeza que você compreende os conceitos de tipos de fontes de luz, reflexão ambiente, difusa e especular. A tarefa solicitada abaixo exige esse conhecimento.

Este arquivo contém os programas fonte que serão usados nesta aula. Esta aplicação corresponde à implementação da hierarquia de planetas já feita na aula prática de transformações.

Inicialmente verifique o conteúdo do programa fonte fornecido para relembrar como foi implementada a hierarquia e a manipulação de câmera, e quais são as teclas usadas para interação. Depois, compile e execute o programa que mostrará a animação dos planetas.

Parte I - Aplicando Iluminação em Primitivas GLUT

Tarefa 1: Desenhando Sólidos

O primeiro passo para usar iluminação é desenhar objetos sólidos: para tanto, troque as chamadas glutWire... por glutSolid....

Repare que a qualidade final não é nada boa, já que todas as superfícies de um mesmo objeto são desenhadas com a mesma cor - os objetos parecem ser planos, e não tridimensionais.

Tarefa 2: Ativando a Iluminação

Para corrigir o problema, é necessário ativar a iluminação. Isso é feito através de duas etapas: primeiro ativamos as fontes de luz desejadas, e depois ativamos a iluminação como um todo. Para tanto, inclua as seguintes linhas no final do método init:

gl.glEnable(GL.GL_LIGHT0);
gl.glEnable(GL.GL_LIGHTING);

Observe que OpenGL suporta até 8 fontes de luz simultâneas, de GL_LIGHT0 a GL_LIGHT7. Porém, a cor dos objetos desapareceu! Por que ?

Tarefa 3: Ajustando os Parâmetros da Iluminação

A explicação para a ausência de cores é simples: para que a iluminação funcione, é preciso criar um material, e especificar suas propriedades (ambiente, difusa e especular) através de chamadas aos métodos glMaterial... O problema é que esse processo é trabalhoso, então OpenGL suporta um recurso chamado COLOR MATERIAL, que faz com que a cor do material seja alterada de acordo com a cor corrente de desenho. Dessa forma, basta usar glColor... para modificar o material.

Para que isso funcione, é preciso habilitar esse estado e indicar de que forma a cor do material deve ser modificada. Inclua então as seguinte linhas no final de init:

gl.glEnable(GL.GL_COLOR_MATERIAL);
gl.glColorMaterial(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT_AND_DIFFUSE);

A segunda linha informa que devem ser alteradas as propriedades ambiente e difusa do material.

Também é necessário ajustar as propriedades da(s) fonte(s) de luz desejada(s). Para isso, crie um método defineIluminacao com o seguinte conteúdo, e insira uma chamada a ele no método display, antes de chamar especificaParametrosVisualizacao.

// Define os parâmetros através de vetores RGBA - o último valor deve ser
// sempre 1.0f (transparência, não usada neste exemplo)
float luzAmbiente[] = {0.2f, 0.2f, 0.2f, 1.0f};
float luzDifusa[] = {0.7f, 0.7f, 0.7f, 1.0f};
float luzEspecular[] = {1.0f, 1.0f, 1.0f, 1.0f};
float posicaoLuz[] = {0.0f, 50.0f, 50.0f, 1.0f};

// Ativa o uso da luz ambiente 
gl.glLightModelfv(GL.GL_LIGHT_MODEL_AMBIENT, luzAmbiente, 0);

// Define os parâmetros da luz de número 0
gl.glLightfv(GL.GL_LIGHT0, GL.GL_AMBIENT, luzAmbiente, 0);
gl.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, luzDifusa, 0);
gl.glLightfv(GL.GL_LIGHT0, GL.GL_SPECULAR, luzEspecular, 0);
gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, posicaoLuz, 0);

Agora experimente fazer as seguintes alterações no código:

  • Troque a cor difusa da luz (luzDifusa) para vermelho (1.0f,0.0f,0.0f,1.0f) - O que acontece com a cor dos objetos ? Explique o porquê ?

  • Agora troque a cor ambiente (luzAmbiente) também para vermelho escuro (0.2f,0.0f,0.0f,1.0f) - Qual é a diferença visual ? Por que isso ocorre ?

  • Insira as seguinte linhas no final do método defineIluminacao, para especificar a propriedade especular do material e o coeficiente especular (que define o tamanho e concentração do ponto de brilho). O que acontece com a aparência dos objetos ? Explique.
    // Brilho do material
    float especularidade[] = {1.0f, 1.0f, 1.0f, 1.0f};
    int especMaterial = 60;
    
    // Define a reflectância do material 
    gl.glMaterialfv(GL.GL_FRONT, GL.GL_SPECULAR, especularidade, 0);
    
    // Define a concentração do brilho
    gl.glMateriali(GL.GL_FRONT, GL.GL_SHININESS, especMaterial);
    
  • Agora troque o valor de especMaterial para 128 - O que acontece com os pontos de brilho ? Depois experimente trocar o valor para 10. Por que acontece esse efeito ?

    Na prática, para esse exemplo o ideal é nem haver componente especular, pois os planetas ficam mais realistas sem ela. Há duas formas de fazer isso: basta remover as linhas que definem a especularidade do material (glMaterialfv) ou a da luz (glLightfv).

  • Troque agora o último parâmetro na linha que define a posição da fonte de luz para 0. Isso significa que esse vetor agora será interpretado como uma direção, e não mais como uma posição. Ou seja, estamos convertendo a luz pontual para direcional. E agora, o que acontece com a iluminação ? Explique.

  • No método display, inverta a ordem de chamada dos métodos defineIluminacao e especificaParametrosVisualizacao(). O que muda na iluminação ? Tente imaginar por que esse efeito acontece. Dica: gire o observador para ver melhor a diferença entre as duas formas.

  • Experimente criar e habilitar uma segunda fonte de luz. Utilize outra cor diferente de branco para que seja mais fácil ver o efeito, e tente posicioná-la diretamente acima da cena.

Parte II - Aplicando Iluminação em Objetos Modelados por Faces

Para trabalhar nesta aula, primeiramente leia os seguintes slides:

Tarefa: Especificando os Vetores Normais

A vantagem das primitivas que utilizamos até agora é que elas já especificam corretamente o vetor normal de cada face desenhada, o que vimos ser fundamental para o resultado correto da iluminação. Porém, frequentemente é necessário modelar objetos manualmente, através de primitivas como GL_QUADS ou GL_TRIANGLES. Nesse caso, será preciso especificar o vetor normal de cada face ou vértice explicitamente, antes do vértice correspondente.

Para realizar a segunda parte desta aula, faça o download de um programa que desenha uma casinha e um chão. O primeiro passo agora é colocar o suporte básico à iluminação neste programa, ou seja, da mesma forma que você realizou nas etapas anteriores: o método defineIluminação, etc.

Após fazer isso, você deverá especificar o vetor normal de cada face, usando:

...
gl.glBegin(GL_QUADS);
gl.glNormal3f(nx,ny,nz); // nx,ny,nz devem corresponder à normal desta face
gl.glVerter3f(x0,y0,z0);
...
gl.glEnd();

É fundamental observar que os vetores normais devem ser unitários, ou a iluminação ficará incorreta.

Mais informações sobre o uso de iluminação em JOGL podem ser encontradas em diversos sites, como por exemplo: http://jerome.jouvie.free.fr/OpenGl/Tutorials11-15.php.