Texturas e display lists

5.1. Textura simples

O propósito desta lição é introduzir o uso de texturas.

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 armário como mostra
a Figura
5-1
. O armário com textura ao chão é ilustrado na Figura
5-2
.

O programa usado para modelar o armário é mostrado no Exemplo
5-1
.

Exemplo 5-1. programa armario_simples.c

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

#define COORD_TEXTURA_PLANO 1.0
#define COR_DO_PLANO 0.52,0.52,0.78,1.0
#define TEXTURA_DO_PLANO "montanhas.rgb"

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}
};

GLuint  textura_plano;
static int shoulder = 0, elbow = 0, elbow2 = 0;



void display(void){

  glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    /* habilita/desabilita uso de texturas*/
  glEnable(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(-5,-3.2,5);
  glTexCoord2fv(ctp[1]);  glVertex3f(5,-3.2,5);
  glTexCoord2fv(ctp[2]);  glVertex3f(5,-3.2,-5);
  glTexCoord2fv(ctp[3]);  glVertex3f(-5,-3.2,-5);
  glEnd();

  glDisable(GL_TEXTURE_2D);

  glPushMatrix();


  glRotatef ((GLfloat) shoulder, 0.0, 1.0, 0.0);

  /* Chao*/
  glColor3f(0.0,0.0,1.0);
  glPushMatrix();
  glTranslatef (0.0, -3.4, 0.0);
  glScalef (10.0, 0.2, 10.0);
  glutSolidCube (1.0);
  glPopMatrix();

  /* armario */
  glColor3f(1.0,0.0,1.0);
  glPushMatrix();
  glScalef (4.0, 6.0, 2.0);
  glutSolidCube (1.0);
  glPopMatrix();

  /* porta 1*/

  glColor3f(1.0,1.0,0.0);
  glPushMatrix();
  glTranslatef (-2.0, 0.0, 1.1);
  glRotatef ((GLfloat) elbow, 0.0, 1.0, 0.0);
  glTranslatef (2.0, 0.0, -1.1);
  glTranslatef (-1.0, 0.0, 1.0);
  glScalef (2.0, 6.0, 0.2);
  glutSolidCube (1.0);
  glColor3f(1.0,0.0,0.0);
  glPushMatrix();    /* desenha macaneta 1*/
  glTranslatef (0.4, 0.0, 1.1);
  glScalef (0.2, 0.2, 0.2);
  glutSolidCube (1.0);
  glPopMatrix();
  glPopMatrix();

/* porta 2*/

  glColor3f(1.0,1.0,0.0);
  glPushMatrix();
  glTranslatef (2.0, 0.0, 1.1);
  glRotatef ((GLfloat) elbow2, 0.0, 1.0, 0.0);
  glTranslatef (-2.0, 0.0, -1.1);
  glTranslatef (1.0, 0.0, 1.0);
  glScalef (2.0, 6.0, 0.2);
  glutSolidCube (1.0);
  glColor3f(1.0,0.0,0.0);
  glPushMatrix();    /* desenha macaneta 2*/
  glTranslatef (-0.4, 0.0, 1.1);
  glScalef (0.2, 0.2, 0.2);
  glutSolidCube (1.0);
  glPopMatrix();
  glPopMatrix();

  /* origem volta para o sistema de coordenadas original */
  glPopMatrix();
  glutSwapBuffers();
}

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%sn",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 reshape (int w, int h){
  glViewport (0, 0, (GLsizei) w, (GLsizei) h);
  glMatrixMode (GL_PROJECTION);
  glLoadIdentity ();
  gluPerspective(65.0, (GLfloat) w/(GLfloat) h, 1.0, 30.0);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glTranslatef (0.0, 0.0, -15.0);
}

void keyboard (unsigned char key, int x, int y){
  switch (key) {
  case 's':
    shoulder = (shoulder + 5) % 360;
    glutPostRedisplay();
    break;
  case 'S':
    shoulder = (shoulder - 5) % 360;
    glutPostRedisplay();
    break;
  case 'e':
    elbow = (elbow + 5) % 360;
    glutPostRedisplay();
    break;
  case 'E':
    elbow = (elbow - 5) % 360;
    glutPostRedisplay();
    break;
  case 'p':
    elbow2 = (elbow2 + 5) % 360;
    glutPostRedisplay();
    break;
  case 'P':
    elbow2 = (elbow2 - 5) % 360;
    glutPostRedisplay();
    break;
  case 27:
    exit(0);
    break;
  default:
    break;
  }
}

void init(void){

  glClearColor (1.0, 1.0, 1.0, 0.0);
  glEnable(GL_DEPTH_TEST);
  carregar_texturas();
  //glEnable(GL_TEXTURE_2D);
}

int main(int argc, char** argv){
  glutInit(&argc, argv);
  glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
  //glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
  glutInitWindowSize (500, 500);
  glutInitWindowPosition (100, 100);
  glutCreateWindow (argv[0]);
  init ();
  glutDisplayFunc(display);
  glutReshapeFunc(reshape);
  glutKeyboardFunc(keyboard);
  glutMainLoop();
  return 0; 
}

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

$ make -f Makefile.modelagem armario_simples
$ ./armario_simples

5.1.1 Descrição do programa armario_simples.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 TEXTURA_DO_PLANO "montanhas.rgb"

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

GLuint  textura_plano;

A variável textura_plano armazena o identificador da textura do
plano.

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}
};

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á usada para criar o carimbo do plano é ctp[].

 
    glEnable(GL_TEXTURE_2D);  
    glDisable(GL_TEXTURE_2D);
  

Aqui o mapeamento de texturas é habilitado ou desabilitado, quando se utilizam
as funções acima. 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(-5,-3.2,5);
  glTexCoord2fv(ctp[1]);  glVertex3f(5,-3.2,5);
  glTexCoord2fv(ctp[2]);  glVertex3f(5,-3.2,-5);
  glTexCoord2fv(ctp[3]);  glVertex3f(-5,-3.2,-5);
  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 da textura é 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, a textura é uma imagem 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.