Capítulo 6. Modelos de iluminação
- Índice
- 6.1. Descrição
do programa iluminacao.c- 6.2. Outros programas relacionados
- 6.3. Exercícios
- 6.1. Descrição
O propósito desta lição é ilustrar para o usuário os principais conceitos
envolvidos na iluminação de uma cena e na definição das propriedades luminosas
de um material, geralmente representadas pelos coeficientes de reflexão difusa e
especular.
Será mostrado como definir características básicas de uma fonte de luz, tais
como a intensidade da luz gerada, para as componentes de luz ambiente, difusa e
especular (no OpenGL, é possível separar as contribuições). Além disso, será
mostrado também o uso do canal alfa para geração de transparências.
A cena gerada pelo programa utilizado nesta lição é mostrado na Figura
6-1. Consiste de duas esferas (nomeadas A e B) sob um plano. A esfera “A” é
sólida, e os valores das suas constantes de reflexão difusa e especular pode ser
modificados através de seleção de opções em um menu. A esfera “B” é translúcida,
e sua translucidez pode ser ajustada através do canal alfa.
Até o momento, o canal alfa tem sido ignorado (alfa é o A em RGBA). Os
valores do canal alfa variam de 0 a 1, e são especificados com as funções glColor*(). Quando os efeitos de composição (blending) são utilizados, o valor de alfa é utilizado
para combinar a cor do fragmento que está sendo processado com a cor já presente
no framebuffer. De modo geral, a composição de
imagens utilando o canal alfa é dada pela seguinte expressão:
IT = IP * alfa + (1-alfa)* IFB |
onde: IT é a imagem total resultante, IP é a imagem que está sendo
processada, e IFB é a imagem anteriormente presente no framebuffer.
O programa usado para modelar esta cena é mostrado no Exemplo
6-1. As teclas LEFT e RIGHT servem
para rotacionar a posição do observador em torno do eixo y contra e a favor do
sentido dos ponteiros do relógio, respectivamente. A distância entre o
observador e o centro de rotação (raio de observação) é alterado pelas teclas r e R, que aumentam o diminuem o seu valor,
respectivamente. As teclas UP e DOWN,
controlam a altitude do observador (no eixo y). É possível modificar os graus de
reflexão difusa e especular da esfera maciça através do menu popup disponível pelo pressionamento do botão direito do
mouse. A tecla t habilita ou desabilita o uso de texturas.
Para finalizar o programa, basta digitar ESC.
Exemplo 6-1. programa iluminacao.c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <GL/glut.h>
#define PI 3.1415
GLint WIDTH =320;
GLint HEIGHT=240;
GLint fatias=30;
GLint pilhas=30;
GLint raioEsfera=1.5;
GLfloat obs[3]={0.0,7.0,0.0};
GLfloat olho[3]={0.0,3.0,0.0};
GLfloat plano_difusa[] = { 0.5, 0.5, 0.0, 1.0 };
GLfloat plano_especular[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat plano_brilho[] = { 50.0 };
GLfloat mat_a_difusa[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat mat_a_especular[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat mat_a_brilho[] = { 50.0 };
GLfloat mat_b_difusa[] = { 0.7, 0.7, 0.7, 0.5 };
GLfloat mat_b_especular[] = { 1.0, 1.0, 1.0, 0.5 };
GLfloat mat_b_brilho[] = { 50.0 };
GLfloat posicao_luz0[] = { 0.0, 10.0, 0.0, 1.0};
GLfloat cor_luz0[] = { 1.0, 1.0, 1.0, 1.0};
GLfloat cor_luz0_amb[] = { 0.3, 0.3, 0.3, 1.0};
GLfloat sem_cor[] = { 0.0, 0.0, 0.0, 1.0};
GLint gouraud=0;
GLfloat tetaxz=0;
GLfloat raioxz=6;
void reshape(int width, int height){
WIDTH=width;
HEIGHT=height;
glViewport(0,0,(GLint)width,(GLint)height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(70.0,width/(float)height,0.1,30.0);
glMatrixMode(GL_MODELVIEW);
}
void display(void){
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glDepthMask(GL_TRUE);
glClearColor(1.0,1.0,1.0,1.0);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
if(gouraud){
glShadeModel(GL_SMOOTH);
}
else{
glShadeModel(GL_FLAT);
}
glPushMatrix();
/* calcula a posicao do observador */
obs[0]=raioxz*cos(2*PI*tetaxz/360);
obs[2]=raioxz*sin(2*PI*tetaxz/360);
gluLookAt(obs[0],obs[1],obs[2],olho[0],olho[1],olho[2],0.0,1.0,0.0);
/* propriedades do material do plano */
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, plano_difusa);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, plano_especular);
glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, plano_brilho);
/* desenha o plano */
glNormal3f(0,1,0);
glBegin(GL_QUADS);
glVertex3f(-10,0,10);
glVertex3f(10,0,10);
glVertex3f(10,0,-10);
glVertex3f(-10,0,-10);
glEnd();
glPushMatrix();
glTranslatef(posicao_luz0[0],posicao_luz0[1],posicao_luz0[2]);
glColor3f(1,0,0);
glMaterialfv(GL_FRONT, GL_EMISSION, cor_luz0);
glutSolidSphere(0.3,5,5);
glPopMatrix();
glMaterialfv(GL_FRONT, GL_EMISSION, sem_cor);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_a_difusa);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_a_especular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_a_brilho);
glPushMatrix();
glTranslatef(0.0,3.0,-3.0);
glutSolidSphere(raioEsfera,fatias,pilhas);
glPopMatrix();
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_b_difusa);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_b_especular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_b_brilho);
glTranslatef(0.0,+3.0,+3.0);
glutSolidSphere(raioEsfera,fatias,pilhas);
glPopMatrix();
glutSwapBuffers();
}
void special(int key, int x, int y){
switch (key) {
case GLUT_KEY_UP:
obs[1]=obs[1]+1;
glutPostRedisplay();
break;
case GLUT_KEY_DOWN:
obs[1] =obs[1]-1;
glutPostRedisplay();
break;
case GLUT_KEY_LEFT:
tetaxz=tetaxz+2;
glutPostRedisplay();
break;
case GLUT_KEY_RIGHT:
tetaxz=tetaxz-2;
glutPostRedisplay();
break;
}
}
void keyboard(unsigned char key, int x, int y){
switch (key) {
case 27:
exit(0);
break;
case 'g':
gouraud = !gouraud;
glutPostRedisplay();
break;
case 'r':
raioxz=raioxz+1;
glutPostRedisplay();
break;
case 'R':
if(raioxz>1){
raioxz=raioxz-1;
glutPostRedisplay();
}
break;
}
}
void init(){
gouraud=1;
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glLightfv(GL_LIGHT0, GL_DIFFUSE, cor_luz0);
glLightfv(GL_LIGHT0, GL_SPECULAR, cor_luz0);
glLightfv(GL_LIGHT0, GL_AMBIENT, cor_luz0_amb);
glLightfv(GL_LIGHT0, GL_POSITION, posicao_luz0);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_AUTO_NORMAL);
glEnable(GL_NORMALIZE);
}
void menu(int value){
switch (value) {
case 0:
mat_a_especular[0]=mat_a_especular[1]=mat_a_especular[2]=0.0;
break;
case 1:
mat_a_especular[0]=mat_a_especular[1]=mat_a_especular[2]=0.5;
break;
case 2:
mat_a_especular[0]=mat_a_especular[1]=mat_a_especular[2]=1.0;
break;
case 3:
mat_a_difusa[0]=mat_a_difusa[1]=mat_a_difusa[2]=0.0;
break;
case 4:
mat_a_difusa[0]=mat_a_difusa[1]=mat_a_difusa[2]=0.5;
break;
case 5:
mat_a_difusa[0]=mat_a_difusa[1]=mat_a_difusa[2]=1.0;
break;
}
glutPostRedisplay();
}
int main(int argc,char **argv){
glutInitWindowPosition(0,0);
glutInitWindowSize(WIDTH,HEIGHT);
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_RGB|GLUT_DEPTH|GLUT_DOUBLE);
if(!glutCreateWindow("Modelos de iluminacao")) {
fprintf(stderr,"Error opening a window.n");
exit(-1);
}
init();
glutKeyboardFunc(keyboard);
glutSpecialFunc(special);
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutCreateMenu(menu);
glutAddMenuEntry("-sem spec", 0);
glutAddMenuEntry("-spec média", 1);
glutAddMenuEntry("-spec alta", 2);
glutAddMenuEntry("-sem difusa", 3);
glutAddMenuEntry("-difusa média", 4);
glutAddMenuEntry("-difusa alta", 5);
glutAttachMenu(GLUT_RIGHT_BUTTON);
glutMainLoop();
return(0);
}
|
Para compilar e executar o programa iluminacao.c, salve-o juntamente com o
arquivo Makefile em um diretório e execute a seguinte seqüência de
comandos:
$ make iluminacao
$ iluminacao
|
6.1. Descrição do programa
iluminacao.c
GLint fatias=30; GLint pilhas=30; GLint raioEsfera=1.5; |
Define o número de fatias (longitude), o número de pilhas (latitude) e o raio
das esferas da cena.
GLfloat plano_difusa[] = { 0.5, 0.5, 0.0, 1.0 };
GLfloat plano_especular[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat plano_brilho[] = { 50.0 };
GLfloat mat_a_difusa[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat mat_a_especular[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat mat_a_brilho[] = { 50.0 };
GLfloat mat_b_difusa[] = { 0.7, 0.7, 0.7, 0.5 };
GLfloat mat_b_especular[] = { 1.0, 1.0, 1.0, 0.5 };
GLfloat mat_b_brilho[] = { 50.0 };
|
Define os coeficientes de reflexão difusa (*_difusa) e
especular (*_especular) para os três objetos da cena. As
componentes destes vetores são do tipo R,G,B e A(lfa) e serão utilizados para as
compor cores do plano, da esfera A e da esfera B. Observe que a esfera B possui
valor de alfa igual a 0.5, proporcionando assim a sua aparência translúcida.
O brilho do material, que pode assumir valores entre 1 e 128, é um expoente
que modela a função de distribuição espacial da componente de luz refletida
especularmente. À medida em que o valor do brilho aumenta, diminui o
espalhamento da luz refletida.
GLfloat posicao_luz0[] = { 0.0, 10.0, 0.0, 1.0};
GLfloat cor_luz0[] = { 1.0, 1.0, 1.0, 1.0};
GLfloat cor_luz0_amb[] = { 0.3, 0.3, 0.3, 1.0};
|
Indica a posição (posicao_luz0) e as componentes de luz para reflexão difusa
e especular (cor_luz0), e a componente de luz para
reflexão ambiente (cor_luz0_amb).
if(gouraud){
glShadeModel(GL_SMOOTH);
}
else{
glShadeModel(GL_FLAT);
}
|
Através da variável gouraud a função display decide se o modelo de sombreamento (shading) para os objetos será o de Gouraud (GL_SMOOTH)
ou flat shading, tornando os objetos com aparência
faceteada.
/* propriedades do material do plano */ glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, plano_difusa); glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, plano_especular); glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, plano_brilho); /* desenha o plano */ glNormal3f(0,1,0); glBegin(GL_QUADS); glVertex3f(-10,0,10); glVertex3f(10,0,10); glVertex3f(10,0,-10); glVertex3f(-10,0,-10); glEnd(); |
A função glMaterialfv() define as propriedades de
reflexão difusa e especular, e brilho do material que será utilizado para compor
o objeto imediatamente desenhado, neste caso um plano, via GL_QUADS. Observer a
chamada à função glNormal3f() antes de o plano ser
desenhado. Isto é necessário porque, por default, o
vetor normal encontra-se na direção (x,y,z)=(0,0,1) e para que a normal à
superfície encontra-se orientada com eixo y, ou seja, na direção
(x,y,z)=(0,1,0).
Um procedimento semelhante a esse é utilizado para definir as características
do material das esferas presentes na cena.
glPushMatrix(); glTranslatef(posicao_luz0[0],posicao_luz0[1],posicao_luz0[2]); glColor3f(1,0,0); glMaterialfv(GL_FRONT, GL_EMISSION, cor_luz0); glutSolidSphere(0.3,5,5); glPopMatrix(); |
Para facilitar a identificação da fonte de luz, é desenhada uma esfera
na posição desta fonte. Na chamada à função glMaterialfv(), o parâmetro GL_EMISSION define a intensidade luminosa emitida
pelo material como sendo a própria cor da fonte de luz, dando a aparência de que
a esfera brilha, assim como brilharia uma lâmpada.
glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); |
Habilita a composição (blending) de imagens dos
valores RGBA correntes com aqueles presentes no framebuffer. A função glBlendFunc() define os pesos para a imagem que está sendo
processada (IP) e a presente no framebuffer. Neste
caso, os pesos são o próprio valor do canal alfa (GL_SRC_ALPHA) e 1-alfa (GL_ONE_MINUS_SRC_ALPHA).
glLightfv(GL_LIGHT0, GL_DIFFUSE, cor_luz0); glLightfv(GL_LIGHT0, GL_SPECULAR, cor_luz0); glLightfv(GL_LIGHT0, GL_AMBIENT, cor_luz0_amb); glLightfv(GL_LIGHT0, GL_POSITION, posicao_luz0); |
Define a posição e as componentes de cor da fonte de luz GL_LIGHT0.
void menu(int value){
switch (value) {
case 0:
mat_a_especular[0]=mat_a_especular[1]=mat_a_especular[2]=0.0;
break;
case 1:
mat_a_especular[0]=mat_a_especular[1]=mat_a_especular[2]=0.5;
break;
case 2:
mat_a_especular[0]=mat_a_especular[1]=mat_a_especular[2]=1.0;
break;
case 3:
mat_a_difusa[0]=mat_a_difusa[1]=mat_a_difusa[2]=0.0;
break;
case 4:
mat_a_difusa[0]=mat_a_difusa[1]=mat_a_difusa[2]=0.5;
break;
case 5:
mat_a_difusa[0]=mat_a_difusa[1]=mat_a_difusa[2]=1.0;
break;
}
glutPostRedisplay();
}
|
A função menu é ativada quando o botão direito do mouse é pressionado. De
acordo com o valor da variável value, determinadas
propriedades do material que compõe a esfera A são modificadas.
glutCreateMenu(menu);
glutAddMenuEntry("-sem spec", 0);
glutAddMenuEntry("-spec média", 1);
glutAddMenuEntry("-spec alta", 2);
glutAddMenuEntry("-sem difusa", 3);
glutAddMenuEntry("-difusa média", 4);
glutAddMenuEntry("-difusa alta", 5);
glutAttachMenu(GLUT_RIGHT_BUTTON);
|
Aqui, a função glutCreateMenu(), em conjunto com a
função glutAddMenuEntry(), habilita um menu popup com seis opções que permitirão ativar entradas
presentes na função menu, de acordo com os valores
passados nos segundos argumentos das funções glutAddMenuEntry(). Finalmente, a função glutAttachMenu() associa o aparecimento deste menu ao
pressionamento do botão direito do mouse.
