Jonh Edson Ribeiro de Carvalho | IC-UFF
Computação Visual e Interfaces
 
 
Tutorial OpenGL

Capítulo 6. Modelagem de Sólidos

O propósito desta lição é introduzir o uso de listas de apresentação (display lists), mapeamento de texturas e o uso de teclas especiais.

As listas de apresentação permitem que vários comandos sejam executados de uma vez só apenas com uma chamada da lista. Cada lista de apresentação precisa ser armazenada apenas uma vez, de modo que quando uma chamada é realizada, todos os comandos declarados nesta lista já estarão presentes máquina interna do OpenGL, tornando o processo de desenho mais fácil e com performance melhorada.

O mapeamento de texturas nos objetos torna a cena mais realística, principalmente quando texturas de objetos reais são utilizadas. A maioria das texturas existentes não podem ser geradas de forma artificial. Quando isto é possível, os algoritmos utilizados são complexos e computacionamente custosos. Assim, geramente as texturas são lidas de arquivos de imagens digitais capturadas com dispositivos de arquisição de dados.

Diversos formatos podem ser usados para armazenar uma imagem digital: bmp, jpeg, gif, tif, tga etc. Neste curso usaremos o formato IRIS RGB, criado pela Silicon Graphics. As funções que permitem a leitura de arquivos RGB e seus respectivos protótipos estão implementados nos arquivos image.c e image.h.

O objeto utilizado nesta lição será um modelo simples de uma avião a jato com fuselagem texturizada, imóvel sobre um plano com textura montanhosa, como mostra a Figura 6-1. As vistas laterais, superior e frontal estão esquematizadas na Figura 6-2.

Neste exemplo, o olhar do observador é fixo aproximadamente no centro do avião, de modo que apenas a posição do observador é alterada para o usuário poder visualizar diversas tomadas da cena.

O programa usado para modelar o avião é 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). A tecla t habilita ou desabilita o uso de texturas. Para finalizar o programa, basta digitar ESC. As teclas e suas respectivas ações estão definidas nas funções keyboard() e special().

Exemplo 6-1. programa jato.c

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <GL/glut.h>
#include "image.h"

#define PI 3.1415

#define COORD_TEXTURA_PLANO 1.0
#define COORD_TEXTURA_AVIAO 1.0
#define COR_DO_PLANO 0.52,0.52,0.78,1.0
#define COR_DO_AVIAO 0.3,0.52,0.18,1.0
#define TEXTURA_DO_PLANO "montanhas.rgb"
#define TEXTURA_DO_AVIAO "camuflagem.rgb"

GLint WIDTH =800;
GLint HEIGHT=600;

GLfloat obs[3]={0.0,7.0,0.0};
GLfloat look[3]={0.0,3.0,0.0};
GLuint  textura_plano;
GLuint  textura_aviao;

GLshort texturas=1;
GLfloat tetaxz=0;
GLfloat raioxz=6;
GLuint  jato;

GLfloat ctp[4][2]={
  {-COORD_TEXTURA_PLANO,-COORD_TEXTURA_PLANO},
  {+COORD_TEXTURA_PLANO,-COORD_TEXTURA_PLANO},
  {+COORD_TEXTURA_PLANO,+COORD_TEXTURA_PLANO},
  {-COORD_TEXTURA_PLANO,+COORD_TEXTURA_PLANO}
};

GLfloat cta[4][2]={
  {-COORD_TEXTURA_AVIAO,-COORD_TEXTURA_AVIAO},
  {+COORD_TEXTURA_AVIAO,-COORD_TEXTURA_AVIAO},
  {+COORD_TEXTURA_AVIAO,+COORD_TEXTURA_AVIAO},
  {-COORD_TEXTURA_AVIAO,+COORD_TEXTURA_AVIAO}
};


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 compoe_jato(void){
  GLUquadricObj *quadric;

  GLfloat asa[][3]={
    {-4.0,0.0,0.0},
    {+4.0,0.0,0.0},
    {0.0,0.0,3.0}
  };

  GLfloat cauda[][3]={
    {0.0,0.0,0.0},
    {0.0,2.0,-1.0},
    {0.0,2.0,0.0},
    {0.0,0.0,2.0}
  };
  /* inicia a composicao do jato */
  jato = glGenLists(1);
  glNewList(jato, GL_COMPILE);

  /* asas */
  glBegin(GL_TRIANGLES); 
  glTexCoord2fv(cta[0]); glVertex3fv(asa[0]);
  glTexCoord2fv(cta[1]); glVertex3fv(asa[1]);
  glTexCoord2fv(cta[3]); glVertex3fv(asa[2]);
  glEnd();
  
  /* corpo */
  quadric = gluNewQuadric();
  gluQuadricTexture(quadric, GL_TRUE);
  gluCylinder(quadric, 0.5, 0.5, 4, 12, 3);
  
  /* nariz */
  quadric = gluNewQuadric();
  gluQuadricTexture(quadric, GL_TRUE);
  glPushMatrix();
  glTranslatef(0,0,4);
  gluCylinder(quadric, 0.5, 0.0, 1.5, 12, 3);
  glPopMatrix();

  /* cauda */
  glBegin(GL_POLYGON); 
  glTexCoord2fv(cta[0]); glVertex3fv(cauda[0]);
  glTexCoord2fv(cta[1]); glVertex3fv(cauda[1]);
  glTexCoord2fv(cta[2]); glVertex3fv(cauda[2]);
  glTexCoord2fv(cta[3]); glVertex3fv(cauda[3]);
  glEnd();

  /* cabine do piloto */
  glTranslatef(0,0.3,3.5);
  glPushMatrix();
  glScalef(0.7,0.7,2.0);
  quadric=gluNewQuadric();
  glColor3f(0.3,0.5,1);
  glDisable(GL_TEXTURE_2D);
  gluSphere(quadric,0.5,12,12);
  glPopMatrix();

  /* termina a composicao do jato*/
  glEndList();
}

void display(void){
  glEnable(GL_DEPTH_TEST);
  
  glDepthMask(GL_TRUE);
  glClearColor(1.0,1.0,1.0,1.0);
  glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
  
  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],look[0],look[1],look[2],0.0,1.0,0.0);

  /* habilita/desabilita uso de texturas*/
  if(texturas){
    glEnable(GL_TEXTURE_2D);  
  }
  else{
    glDisable(GL_TEXTURE_2D);
  }

  glColor4f(COR_DO_PLANO);
  glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL);
  glBindTexture(GL_TEXTURE_2D,textura_plano);
   
  glBegin(GL_QUADS);
  glTexCoord2fv(ctp[0]);  glVertex3f(-10,0,10);
  glTexCoord2fv(ctp[1]);  glVertex3f(10,0,10);
  glTexCoord2fv(ctp[2]);  glVertex3f(10,0,-10);
  glTexCoord2fv(ctp[3]);  glVertex3f(-10,0,-10);
  glEnd();
  glTranslatef(0.0,2.0,-3.0);

  glColor4f(COR_DO_AVIAO);
  glBindTexture(GL_TEXTURE_2D,textura_aviao);
  glCallList(jato);

  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 't':
    texturas = !texturas;
    glutPostRedisplay();
    break;
  case 'r':
    raioxz=raioxz+1;
    glutPostRedisplay();
    break;
  case 'R':
    raioxz=raioxz-1;
    if(raioxz==0){
      raioxz=1;
    }
    glutPostRedisplay();
    break;
  }
}

void carregar_texturas(void){
  IMAGE *img;
  GLenum gluerr;

  /* textura do plano */
  glGenTextures(1, &textura_plano);
  glBindTexture(GL_TEXTURE_2D, textura_plano);
  
  if(!(img=ImageLoad(TEXTURA_DO_PLANO))) {
    fprintf(stderr,"Error reading a texture.\n");
    exit(-1);
  }

  gluerr=gluBuild2DMipmaps(GL_TEXTURE_2D, 3, 
			   img->sizeX, img->sizeY, 
			   GL_RGB, GL_UNSIGNED_BYTE, 
			   (GLvoid *)(img->data));
  if(gluerr){
    fprintf(stderr,"GLULib%s\n",gluErrorString(gluerr));
    exit(-1);
  }

  glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
  glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
  glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
  glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
  glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL);

  /* textura do aviao */
  glGenTextures(1, &textura_aviao);
  glBindTexture(GL_TEXTURE_2D, textura_aviao);

  
  if(!(img=ImageLoad(TEXTURA_DO_AVIAO))) {
    fprintf(stderr,"Error reading a texture.\n");
    exit(-1);
  }

  gluerr=gluBuild2DMipmaps(GL_TEXTURE_2D, 3, 
			   img->sizeX, img->sizeY, 
			   GL_RGB, GL_UNSIGNED_BYTE, 
			   (GLvoid *)(img->data));
  if(gluerr){
    fprintf(stderr,"GLULib%s\n",gluErrorString(gluerr));
    exit(-1);
  }

  glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
  glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
  glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
  glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
  glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL);
  
}

void init(){
  carregar_texturas();
  compoe_jato();
  glShadeModel(GL_FLAT);
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
  glEnable(GL_TEXTURE_2D);
}

int main(int argc,char **argv){
  glutInitWindowPosition(0,0);
  glutInitWindowSize(WIDTH,HEIGHT);
  glutInit(&argc,argv);
  glutInitDisplayMode(GLUT_RGB|GLUT_DEPTH|GLUT_DOUBLE);

  if(!glutCreateWindow("Avião a jato")) {
    fprintf(stderr,"Error opening a window.\n");
    exit(-1);
  }

  init();
  
  glutKeyboardFunc(keyboard);
  glutSpecialFunc(special);
  glutDisplayFunc(display);
  glutReshapeFunc(reshape);
  glutMainLoop();

  return(0);
}

Para compilar e executar o programa jato.c, salve-o juntamente com os arquivo Makefile.modelagem, image.c, image.h, montanhas.rgb e camuflagem.rgb em um diretório e execute a seguinte seqüência de comandos:

$ make -f Makefile.modelagem jato
$ jato

6.1. Descrição do programa jato.c

#define COORD_TEXTURA_PLANO 1.0
#define COORD_TEXTURA_AVIAO 1.0
#define COR_DO_PLANO 0.52,0.52,0.78,1.0
#define COR_DO_AVIAO 0.3,0.52,0.18,1.0
#define TEXTURA_DO_PLANO "montanhas.rgb"
#define TEXTURA_DO_AVIAO "camuflagem.rgb"

Define as cores e coordenadas das texturas do plano e do avião, além dos nomes dos arquivos que contém as imagens das texturas.

GLint WIDTH =320;
GLint HEIGHT=240;

Especifica a largura (WIDTH) e a altura (HEIGHT) iniciais da janela de desenho.

GLfloat obs[3]={0.0,7.0,0.0};
GLfloat look[3]={0.0,3.0,0.0};

As coordenadas da posição do observador é armazenada no vetor obs[] e as do ponto para onde o observador olha é armazenado no vetor look[]. De acordo com estes vetores, o observador encontra-se no ponto (x,y,z)=(0,7,0) e está olhando para o ponto (x,y,z)=(0,3,0).

GLuint  textura_plano;
GLuint  textura_aviao;

As variáveis textura_plano e textura_aviao armazenam os identificadores das texturas do plano e do avião.

GLshort texturas=1;
GLfloat tetaxz=0;
GLfloat raioxz=6;
GLuint  jato;

A variável texturas é usada para habilitar/desabilitar o uso de texturas nos objetos. As variáveis tetaxz e raioxz armazenam o ângulo de rotação do ponto de observação e a distância entre o observador e a origem, respectivamente. A variável jato amazena o identificador para a lista de apresentação do avião a jato.

GLfloat ctp[4][2]={
  {-COORD_TEXTURA_PLANO,-COORD_TEXTURA_PLANO},
  {+COORD_TEXTURA_PLANO,-COORD_TEXTURA_PLANO},
  {+COORD_TEXTURA_PLANO,+COORD_TEXTURA_PLANO},
  {-COORD_TEXTURA_PLANO,+COORD_TEXTURA_PLANO}
};

GLfloat cta[4][2]={
  {-COORD_TEXTURA_AVIAO,-COORD_TEXTURA_AVIAO},
  {+COORD_TEXTURA_AVIAO,-COORD_TEXTURA_AVIAO},
  {+COORD_TEXTURA_AVIAO,+COORD_TEXTURA_AVIAO},
  {-COORD_TEXTURA_AVIAO,+COORD_TEXTURA_AVIAO}
};

Quando uma textura é carregada, o OpenGL guarda em uma matriz e assume coordenadas (0,0), (1,0), (1,1) e (0,1) para os quatro cantos da textura. Neste exemplo, é assumido a repetição das texturas, de modo a poder cobrir todo o objeto. As coordenadas especificadas neste trecho de código indicam que o objeto será carimbado com sua respectiva textura e que as coordenadas das texturas que serão usadas para criar os carimbos do plano e do avião serão ctp[] e cta[], respectivamente.

  GLUquadricObj *quadric;

Quádricas são superfícies definidas pela seguinte equação a1x2 + a2y2 +a3z2 + a4xy + a5yz + a6xz + a7x + a8y +a9z + a10. Os tipos de quádricas mais conhecidos são os cones, os cilindros, as esferas e os discos, modelados pelo ajuste adequado dos parâmetros ai. Estes objetos são guardados em uma estrutura de dados do tipo GLUquadricObj.

  GLfloat asa[][3]={
    {-4.0,0.0,0.0},
    {+4.0,0.0,0.0},
    {0.0,0.0,3.0}
  };

  GLfloat cauda[][3]={
    {0.0,0.0,0.0},
    {0.0,2.0,-1.0},
    {0.0,2.0,0.0},
    {0.0,0.0,2.0}
  };

As variáveis asa[][] e cauda[][] armazenam as coordenadas dos polígonos que compõem a asa e a cauda do avião, respectivamente.

  jato = glGenLists(1);
  glNewList(jato, GL_COMPILE);

A função glGenLists() aloca um conjunto contínuo de listas de apresentação vazias e retorna o identificador utilizado para a lista alocada. Neste exemplo, o conjunto contém apenas uma lista de apresentação. Mais listas poderão ser alocadas com subseqüentes chamadas à mesma função.

A função glNewList() especifica o início de uma lista de apresentação. Todos os comandos executados até a chamada de glEndList() ficarão armazenados nesta lista. A função glNewList() possui o seguinte protótipo:

void glNewList(GLUint list, GLenum mode);

list é um número inteiro maior que zero que identifica de forma única a lista de apresentação. O parâmetro mode pode assumir os valores GL_COMPILE e GL_COMPILE_AND_EXECUTE. O primeiro apenas armazena os comandos na lista; o segundo executa os comandos enquanto são armazenados na lista.

  quadric = gluNewQuadric();
  gluQuadricTexture(quadric, GL_TRUE);
  gluCylinder(quadric, 0.5, 0.5, 4, 12, 3);

Inicia, usando a função gluNewQuadric(), a primeira das quádricas do exemplo: o corpo do avião. Para esta quádrica, o mapeamento de textura está habilitado. A função gluCylinder() possui o seguinte protótipo:

void gluCylinder(GLUquadric* quad, GLdouble base, GLdouble top, GLdouble height, GLint slices, GLint stacks);

O parâmetro quad é o objeto de quádrica; base, top e height especificam o raio da base, o raio do topo e a altura do cilindro, respectivamente; slices stacks especificam o número de subdivisões ao redor do eixo z e ao longo do mesmo.

  obs[0]=raioxz*cos(2*PI*tetaxz/360);
  obs[2]=raioxz*sin(2*PI*tetaxz/360);
  gluLookAt(obs[0],obs[1],obs[2],look[0],look[1],look[2],0.0,1.0,0.0);

Na função display() as coordenadas x e z da posição do observador são calculadas e a função gluLookAt() é chamada para mudar a posição do observador.

  if(texturas){
    glEnable(GL_TEXTURE_2D);  
  }
  else{
    glDisable(GL_TEXTURE_2D);
  }

Aqui o mapeamento de texturas é habilitado ou desabilitado, de acordo com o estado da variável texturas. Quando o mapeamento de texturas é desabilitado, o objeto é desenhado utilizando as cores especificadas pela função glColor*().

  glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL);

A função glTexEnvf() define os parâmetros do ambiente de textura. O primeiro parâmetro é sempre GL_TEXTURE_ENV; o segundo parâmetro é sempre GL_TEXTURE_ENV_MODE e o terceiro especifica como a textura será combinada com a cor para formar a superfície do objeto, neste caso substituindo completamente a cor do objeto pela textura corrente.

  glBindTexture(GL_TEXTURE_2D,textura_plano);
   
  glBegin(GL_QUADS);
  glTexCoord2fv(ctp[0]);  glVertex3f(-10,0,10);
  glTexCoord2fv(ctp[1]);  glVertex3f(10,0,10);
  glTexCoord2fv(ctp[2]);  glVertex3f(10,0,-10);
  glTexCoord2fv(ctp[3]);  glVertex3f(-10,0,-10);
  glEnd();

glBindTexture() carrega a textura bidimensional associada com a variável textura_plano. Em seguida, um plano é desenhado usando GL_QUADS. Observe que, antes de desenhar cada vértice, a função glTexCoord2fv() é chamada para definir as coordenadas de textura correntes.

void carregar_texturas(void){
  IMAGE *img;
  GLenum gluerr;

  /* textura do plano */
  glGenTextures(1, &textura_plano);
  glBindTexture(GL_TEXTURE_2D, textura_plano);
  
  if(!(img=ImageLoad(TEXTURA_DO_PLANO))) {
    fprintf(stderr,"Error reading a texture.\n");
    exit(-1);
  }

A carga das texturas é feita com uso da função ImageLoad(), implementada em image.c e definida em image.h. Esta função recebe como parâmetro o nome do arquivo com a imagem da textura e retorna um ponteiro para uma estrutura de dados do tipo IMAGE.

A função glGenTextures() gera 1 nome de textura em textura_plano. Um nome de textura é qualquer inteiro diferente de zero que identifique de forma única a textura. Assim, texturas diferentes possuem nomes diferentes. Caso o segundo parâmetro desta função seja um vetor de elementos GLuint, mais nomes de textura podem ser gerados, um para cada elemento deste vetor.

  gluerr=gluBuild2DMipmaps(GL_TEXTURE_2D, 3, 
			   img->sizeX, img->sizeY, 
			   GL_RGB, GL_UNSIGNED_BYTE, 
			   (GLvoid *)(img->data));

Mipmaps são séries de versões em baixa resolução de um mapa de textura. Geralmente é utilizado para texturizar um objeto cuja resolução na tela difere da resolução no mapa de textura. Por exemplo, um objeto próximo da tela pode ser desenhado utilizando uma resolução de textura maior que um objeto distante da tela. O uso de mipmaps evita o efeito de serrilhamento (aliasing) e outros distúrbios de exibição quando um objeto é aproximado ou afastado da tela.

Mipmaps bidimensionais são construídos com a função gluBuild2dMipmaps(), que possuiu seguinte protótipo:

void gluBuild2dMipmaps(GLenum target, GLint internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *data);

target indica o tipo de mipmaps que se deseja construir - neste caso uma textura bidimensional. internalFormat indica o tipo de armazenagem interna do arquivo de textura. Neste exemplo, as texturas são imagens RGB, incluindo 3 (três) componentes de cor. Os parâmetros width e height especificam a largura e a altura do dado. Ambas estas dimensões devem ser potências de 2. format especifica o formato dos pixels do dado (RGB). type especifica o tipo de dado representado no vetor de dados. data especifica o ponteiro para a posição de memória onde os dados de textura residem.

  glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
  glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);

A função glTexParameterf() define uma série de parâmetros que controlam como uma textura é tratada e aplicada a um fragmento de um dado objeto. Aqui, através dos parâmetros GL_TEXTURE_WRAP_S e GL_TEXTURE_WRAP_T, a função define que nas direções s e t (coordenadas) a textura deverá será repetida no objeto.

  glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
  glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

Neste trecho do código, as funções glTexParameterf() define os tipos de filtro usados quando a textura for minimizada ou maximizada. Pelo parâmetro GL_LINEAR_MIPMAP_LINEAR, o OpenGL escolhe dois mipmaps que mais aproximam o tamanho do pixel a ser texturizado calcula a média dos quatro elementos de textura mais próximos do centro do pixel. O valor da textura para o pixel será a média desses dois valores. O parâmetro GL_LINEAR, por sua vez não utiliza mipmaps: associa ao pixel a ser texturizado a média dos quatro elementos de textura mais próximos do centro do pixel.

CopyLeft Webmaster |Atualizado em 22/11/2003
Contato   Pesquisas e Projetos  Publicações   Home