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> #include "image.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]); 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]); 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.