5. Texturização
Neste exercício aprenderemos como mapear texturas em objetos usando OpenGL.
Uma textura é uma imagem que eé mapeada sobre polígonos. Associando vértices (no espaço da tela) a pontos de controle chamados coordenadas de textura u e v (no espaço da textura), se obtém uma interpolação na tela. Texturas são essencialmente usadas para adicionar características a superfícies que seriam muito custosas para produzir usando polígonos.
Fig. 1. Transformações do espaço de textura para o espaço da tela.
Existe uma consideração importante a se fazer ao usar texturas em OpenGL: por razões de performance, texturas devem ser imagens quadradas com o tamanho do lado sendo uma potência de 2. Portanto, bons tamanhos de textura são: 2, 4, 8, 16, 32, 64, 128, 256, 512, ...
a. Um tutorial
O tutorial de textura (mostrado abaixo) demonstra como funciona a texturização em OpenGL. Especificamente, as coordenadas de textura para um polígono são fornecidas da mesma maneira que as próprias coordenadas cartesianas dos vértices dos polígonos. Os parâmetros de textura e atributos do ambiente são configuráveis. Um painel de comando separado permite a manipulação da matriz da textura. (fonte: http://www.xmission.com/~nate/tutors.html)
Baixem o tutorial e vejam como funcionam os diferentes parâmetros e atributos.
Podem usar a página de recursos neste site para entender os diferentes parâmetros:
glTexParameter, glTexEnv and glTexCoord
Fig. 2. Tutorial para texturização (by Nate Robins)
b. Usando texturas sobre uma malha
Façamos algo mais prático. Vamos mapear uma testura sobre uma malha.
Para isso, precisamos uma malha, uma imagem de textura e coordenadas de textura. Conforme visto anteriormente, coordenadas de textura associam cada vértice v em 3D com uma coordenada uv sobre o plano da imagem de textura (2D).
Na aula anterior já havíamos incluído algumas mudanças à classe Mesh para que ela possa lidar com texturas.
Agora, explicaremos estas mudanças.
1. Na declaração da classe Mesh (mesh.h), adicionamos as seguintes linhas:
// mesh.h
|
2. E na implementação da classe Mesh
(mesh.cpp) estas:
|
3. No programa principal, adicionamos a função textureMagic() e modificamos a drawMesh():
void textureMagic() |
Esta função é usada para
carregar arquivos de imagens em formato de mapa de bits bruto (.raw) e
usa a classe DCImage que já vimos quando carregamos arquivos
3ds.
Para usar esta estratégia, a classe precisa ser incluída
no seu project.
4. A função drawMesh() foi modificada para que incluísse as coordenadas de textura.
void drawMesh(Mesh* m) |
5. Finalmente, a malha pode ser carregada de uma malha 3ds na função init():
|
Exercício 1:
- Use o seu programa para carregar o seguinte personagem do jogo Counter-Strike(TM).
![]() ![]() |
Fig. 3. Um personagem do Counter-Strike(TM) com sua textura.
Arquivos necessários:
- Malha: man.h
- Textura: man.raw
Exercício 2:
Uma possibilidade em relação à imagem que
será usada é a função abaixo, que carrega
arquivos bitmap
padrão (.bmp). Experimentem.
Depois, sintam-se livres para pesquisar na Internet e encontrar
funções similares para carregar arquivos de outros
formatos (.jpg, .gif, etc.).
int |
Atenção para as mudanças ao carregar arquivos .bmp na textureMagic():
typedef struct { // give here the
image file containing the
texture glTexImage2D(GL_TEXTURE_2D, 0, 3, img.sizeX, img.sizeY, 0,
GL_RGB, GL_UNSIGNED_BYTE, img.data); |
Comentário importante:
Se vocês quiserem usar uma malha com indexação separada de vértices e uvs, só poderão usar a maneira glBegin()/glEnd() de desenhar triângulos. Por que isso?
Na chamada da drawElements se assume que cada vértice é acompanhado por uma uv, mas se existem mais uvs que vértices surge um problema. Nesse caso, os vértices teriam que ser duplicados em um vertex array estendido e a lista de triângulos teria que ser reindexada para se adequar ao novo array.
Assim, a maneira mais fácil é mesmo
usar chamadas glBegin/glEnd, especialmente se uma performance
máxima não é requerida.