Home arrow Artigos arrow Introdução ao OpenGL
Introdução ao OpenGL

A OC-Zone decidiu aprender um pouco de OpenGl e divertir-se as custas desta biblioteca que a maioria das placas gráficas suportam. Contudo criamos este Artigo para que os nossos leitores e principiantes da programação em C++, aprendam a utilizar e a criar pequenos cenários utilizando algumas figuras geométricas.

O Software utilizado para programar C++ foi o Borlan C++ Builder 6, no entanto, também funciona em Visual Studio C++. O processo é o mesmo portanto fica ao inquérito dos nossos leitores.

Introdução ao OpenGL

A OpenGL (Open Graphics Library), é uma biblioteca padrão de funções gráficas para o desenvolvimento de aplicações gráficas interactivas 2D e 3D com qualidade profissional e de alta performance, tendo sido criada em 1992 pela Silicon Graphics (actual SGI – www.sgi.com), na forma de uma API (Application Programming Interface), gráfica, independente dos dispositivos de exibição e dos sistemas operativos.
A OpenGL foi escrita em C e segue a convenção de chamada da linguagem C.

Existem diferentes implementações da OpenGL para diferentes sistemas operativos, por exemplo para Windows e Linux. As várias implementações usam os mesmos tipos de dados e os mesmos nomes de funções. Desse modo um programa escrito para Windows por exemplo, pode também ser compilado para Linux.

A OpenGL não contem funções especificas para interacção com os modernos sistemas de janelas (Windows, X Window etc), pelo que as tarefas comuns de uma aplicação, como criar janelas, gerir eventos provenientes do rato e do teclado, assim como apresentação de menus, ficam a cargo de bibliotecas próprias de cada ambiente de programação. Existe no entanto uma extensão da biblioteca OpenGL conhecida por GLUT (OpenGL ToolKit), que contem funções para a gestão de janelas e eventos e pode ser usada para o desenvolvimento de aplicações gráfica sem OpenGL, independentes do sistema de gestão de janelas dos sistemas operativos.

A OpenGL transformou-se num padrão extensamente utilizado pela indústria. O desenvolvimento desta biblioteca está hoje em dia a cargo de um consórcio formado pelas principais empresas de software e hardware do mundo (www.opengl.org).

A OpenGL promove a inovação e acelera o desenvolvimento de aplicações incorporando um grande conjunto de funções de renderização, de texturas, de efeitos especiais, e de outras poderosas funções de visualização.

1.1 Máquina de estados

A OpenGL é uma máquina de estados porque é possível colocá-la em vários estados (ou modos) que não são alterados, a menos que uma função seja chamada para isso. Por exemplo, a cor de desenho é uma variável de estado que pode ser definida como branca. Todos os objectos são então desenhados com a cor branca, até ao momento em que outra cor de desenho seja especificada. A OpenGL mantém uma série de variáveis de estado, tais como estilo de linhas, posições e características das luzes, propriedades do material dos objectos a desenhar, etc.

Muitas delas referem-se a modos que podem ser activados ou desactivados com os comandos glEnable() e glDisable().

1.2 O Pipeline da OpenGL

Os comandos da biblioteca OpenGL seguem uma série de estágios de processos ao serem executados a que chamamos Pipeline de Renderização da OpenGL. A sequencia dos processos desse pipeline é mostrada no diagrama da figura 1.

Figura 1 – Pipeline de Renderização da OpenGL

1.3 Funções gráficas da OpenGL

As funções gráficas da biblioteca OpenGL podem ser agrupadas de acordo com a função que desempenham. Segue-se a descrição genérica desses grupos de funções.

Buffer de acumulação: Trata-se de um buffer no qual múltiplos frames renderizados podem ser compostos para produzir uma única imagem. Este buffer é usado para efeitos tais como a profundidade de campo, “blur” de movimento, e de anti-aliasing da cena.

Alfa Blending: Proporciona mecanismos para criar objectos transparentes. Usando

a informação alfa, é possível escolher se um objecto é definido como algo totalmente transparente até algo totalmente opaco.

Anti-aliasing: Um método de renderização utilizado para suavizar linhas e curvas.

Esta técnica calcula a média da cor dos pixels junto à linha. Tem o efeito visual de suavizar a transição dos pixels na linha e daqueles junto à linha, gerando assim uma aparência mais suave.
Modo “Color-Index
: Buffer de Cores que armazena índices de cores das componentes vermelhas, verdes, azuis, e alfa das cores (RGBA), de modo a criar uma paleta de cores especifica.

Display Lists: Uma lista nomeada de comandos de OpenGL. As funções inseridas numa Display list podem ser pré-processadas de modo a serem executadas mais eficientemente do que se fossem executadas no modo imediato.

Double buffering: Usado para fornecer uma animação suave dos objectos. Cada cena sucessiva de um objecto em movimento pode ser construída em “background” ou no buffer “invisível” e então apresentada. Isto permite que somente as imagens completas sejam sempre apresentadas no ecrã.

FeedBack: Um modo onde OpenGL retornará a informação geométrica processada (cores, posições do pixel, e assim por diante), à aplicação. Gouraud Shading: Interpolação suave das cores através de um segmento de polígono ou de linha. As cores são atribuídas em vértices e linearmente interpoladas através da primitiva para produzir uma variação relativamente suave na cor.

Modo Imediato: A execução de comandos OpenGL quando eles são chamados, possui resultado melhor do que os “Display Lists”. Iluminação e sombreamento de materiais: A habilidade de computar exactamente a cor de algum ponto da superfície de um objecto dado as propriedades materiais para essa superfície.

Operações de pixel: Armazena, transforma, traça e processa aumento e redução de imagens.

Executores polinomiais: Para suportar as NURBS (non-uniform rational Bsplines). Primitivas: Um ponto, uma linha, um polígono, um bitmap, ou uma imagem. Primitivas da rasterização : bitmaps e retângulos de pixels.

Modo RGBA: Buffers de cores armazenam componentes vermelhos, verdes, azuis, e alfa da cor.

Selecção e colheita: Trata-se de um modo no qual a OpenGL determina se certa primitiva identificada do gráfico é renderizada numa dada região no buffer de frame.

Planos do stencil: Um buffer que é usado para mascarar pixels individuais no buffer de frame de cores.

Mapeamento de Texturas: O processo de aplicar uma imagem a uma primitiva gráfica. Esta técnica é usada para gerar imagens com realismo.

Transformações: A habilidade de mudar a rotação, o tamanho, e a perspectiva de um objecto no espaço 3D.

Z-buffering: O Z-buffer é usado remover as superfícies escondidas.

1.4 Sintaxe de comandos da OpenGL

Os comandos da biblioteca OpenGL obedecem a um padrão bem definido para especificação dos nomes de funções e constantes. A grande maioria dos nomes das funções contêm o prefixo glem letras minúsculas e todos os nomes de constantes começam com as iniciais “GL_”, em letras maiúsculas, e usam um “underscore” para separar as palavras que formam o nome

(Ex. GL_COLOR_BUFFER_BIT ). Existem algumas funções para a plataforma Windows (evidenciados pelo prefixo "wgl") que facilitam a criação de um espaço de renderização numa janela comum Windows.

Os nomes de algumas funções contêm um sufixo formado por um algarismo e uma ou duas letras (Ex. glColor3f e glVertex3fv). O algarismo indica o número de argumentos da função e a letra indica o tipo desses argumentos. Este formato para o nome das funções ajuda a lidar com o grande número de variantes que uma dada função pode ter. A figura 2 ilustra o formato do nome das variantes para a função glVertex…().

Figura 2 - Variantes do nome da função glVertex…().

Para tornar o código “portátil”, foram definidos tipos de dados próprios para OpenGL. Estes tipos de dados são mapeados dos tipos de dados comuns do C, que também podem ser utilizados.

Como os vários compiladores e ambientes possuem regras diferentes para determinar o tamanho das variáveis em C, usando os tipos OpenGL é possível “isolar” o código das aplicações, destas alterações. A tabela 1 apresenta os sufixos das funções e os tipos de dados para os argumentos das funções.

Tab. 1 - Sufixos e tipos de dados para os argumentos das funções da OpenGL.

2 Instalação da OpenGL no Windows XP

2.1 C++ Builder e no Visual Studio C++

A implementação da biblioteca para uma dada plataforma e sistema operativo é composta por dois grupos de ficheiros: os ficheiros que formam a biblioteca propriamente dita e os ficheiros de interface com a biblioteca. No caso da implementação da biblioteca para o sistema operativo Windows, os ficheiros que forma a biblioteca são opengl32.dll (kernel OpenGL) e glu32.dll (biblioteca GLU) e fazem parte da distribuição desse sistema operativo (encontram-se na pasta System32). Os ficheiros de interface com a biblioteca vêm normalmente com o ambiente de desenvolvimento da linguagem de programação que quisermos usar. No caso do C++Builder, os ficheiros de interface são opengl32.lib (interface para o kernel OpenGL) e glu32.lib (interface para a biblioteca GLU) e os ficheiros de cabeçalho gl.h (com os protótipos para o kernel OpenGL) e glu.h (com os protótipos para a biblioteca GLU). Estes ficheiros encontram-se respectivamente nas pastas $(BCB)LIBPSDK e $(BCB)INCLUDEGL.

No caso do Visual C++, tanto os ficheiros de interface como os ficheiros de cabeçalho são os mesmos mencionados anteriormente, no entanto esses encontra-se respectivamente nas pastas $(VC++)LIBPSDK e $(VC++)INCLUDEGL.

Existem versões da biblioteca OpenGL para outros sistemas operativos e ambientes de programação. Os ficheiros correspondentes podem ser obtidos no site oficial da OpenGL (www.opengl.org).

2.1 Primeiros passos

Segue-se o desenvolvimento de uma pequena aplicação que exemplifica a instalação e o uso da biblioteca OpenGL em C++Builder. Os gráficos produzidos pelos comandos da biblioteca OpenGL podem ser visualizados em qualquer componente para o qual se possa obter um device context. No exemplo que se segue, usaremos um componente TPanel para esse efeito.

Crie um novo projecto e adicione um componente TPanel ao formulário principal. Altere o nome do painel para PanelGL e elimine o texto na sua propriedade Caption. Altere as dimensões do painel para que fique apenas um pouco menor do que o formulário e centre-o em relação a este. Declare as seguintes variáveis na secção private da classe do formulário, no ficheiro Unit.h:

private:
HDC hdc;
HGLRC hrc;

int
pixel_format;

A primeira variável é o handle para o device context do componente TPanel. A segunda guardará um handle para a janela de renderização OpenGL e a terceira é um identificador do “formato de pixel” que teremos que configurar. Declare as seguintes funções na secção public da classe do formulário:

void __fastcall Configurar_Pixel_Format();
void
__fastcall Idle_Loop(TObject*, bool &Terminar);
void
__fastcall Desenhar_Cena();

Inclua no ficheiro Unit.cpp os ficheiros cabeçalho necessários para usar a biblioteca OpenGL:

#include <gl/gl.h>
#include <gl/glu.h>

Crie a função Configurar_Pixel_Format() que configura o device context do componente PanelGL para que possa ser usado para renderizar as imagens geradas pela OpenGL:

void __fastcall TForm1::Configurar_Pixel_Format()
{

//Configuração da estrutura Pixel Format Descriptor com
//suporte para OpenGL, uso de Double Buffer e formato RGBA
.
PIXELFORMATDESCRIPTOR pfd = {

sizeof
(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
24,
0,0,0,0,0,0,
0,0,
0,0,0,0,0,
32,
0,
0,
PFD_MAIN_PLANE,

0,
0,0,0
};

//Cria o device context do painel com as características
//configuradas no Pixel Format Descriptor.

pixel_format = ChoosePixelFormat(hdc, &pfd);
SetPixelFormat(hdc, pixel_format, &pfd);
}

Crie a função Idle_Loop() que será chamada automaticamente no evento OnIdle da aplicação e que implementa o ciclo de renderização da OpenGL:

void __fastcall TForm1::Idle_Loop(TObject*, bool &Terminar)
{
Terminar = false;

Desenhar_Cena
();
SwapBuffers
(hdc);
}

Acrescente o seguinte código ao construtor do formulário para configurar o evento OnIdle:


__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
{

Application-
>OnIdle = Idle_Loop;
}

Acrescente o seguinte código ao evento OnCreate para configurar o device context do painel e o rendering context da OpenGL:

void __fastcall TForm1::FormCreate(TObject *Sender)
{
hdc = GetDC(PanelGL->Handle);
Configurar_Pixel_Format
();
hrc
= wglCreateContext(hdc);
wglMakeCurrent
(hdc, hrc);
//Configuração inicial do rendering context da OpenGL

glClearColor
(0.0f, 0.0f, 0.0f, 1.0f);
glClear
(GL_COLOR_BUFFER_BIT);
glFlush();
}

Acrescente o seguinte código ao evento OnDestroy para eliminar o device context e o rendering context:

void __fastcall TForm1::FormDestroy(TObject *Sender)
{

ReleaseDC
(Handle, hdc);
wglMakeCurrent
(hdc, NULL);
wglDeleteContext
(hrc);
}

Acrescente o seguinte código ao evento OnRecize para configurar a viewport e a projecção a usar na renderização:

void __fastcall TForm1::FormResize(TObject *Sender)
{

glViewport
(0, 0, PanelGL->Width, PanelGL->Height);
glMatrixMode
(GL_PROJECTION);
glLoadIdentity
();
glOrtho
(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
glMatrixMode
(GL_MODELVIEW);
glLoadIdentity
();
}

A função glViewport(0, 0, PanelGL->Width, PanelGL->Height) define a viewport de visualização, isto é, a área rectangular dentro do componente TPanel onde as imagens serão renderizadas. O protótipo desta função é: void glViewport(GLint x, GLint y, GLsizei width, GLsizei height).

Os seus parâmetros especificam o canto inferior esquerdo da viewport (x e y), dentro da área do painel, e a sua largura e altura em pixels (width e height). Neste caso os parâmetros usados fazem com que a viewport coincida com a própria área do painel, mas é possível usar outros valores e definir uma área mais pequena (ou mais grande), do que a área do componente. Também é possível definir mais do que uma viewport permitindo assim renderizar mais do que uma cena, ou mais doq que uma vista da mesma cena.

A função glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0) é usada para definir o volume de visualização e em consequência o tipo de projecção, que será usada para renderizar as imagens.

O protótipo desta função é: void glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GL double zNear, GLdouble zFar).

Os parâmetros left e right especificam os limites mínimo e máximo no eixo X; analogamente, bottom e top especificam os limites mínimo e máximo no eixo Y; zNear e zFar, por sua vez, especificam a coordenada mais próxima e mais distante do observador, respectivamente, no eixo de profundidade Z. Estes parâmetros definem um volume de visualização com a forma de um paralelepípedo rectangular como o mostrado na figura 1. Este volume de visualização é também designado por volume de recorte, pois só será renderizado aquilo que estiver dentro deste volume. A projecção definida pela função é uma projecção ortográfica ou paralela.

Figura 3 – Volume de visualização da projecção ortográfica.

As funções glMatrixMode(GL_PROJECTION) e glMatrixMode(GL_MODELVIEW), servem para indicar qual é a matriz que será afectada pelas operações de transformações que possam ser executadas a seguir. A biblioteca OpenGL mantém internamente duas pilhas de matrizes distintas. Uma delas diz respeito às transformações da câmara (ou observador) e é representadapelo identificador GL_PROJECTION, a outra diz respeito às transformações geométricas dos modelos que formam a cena e é representada pelo identificador GL_MODELVIEW. A função glMatrixMode() permite indicar qual destas matrizes deve passar a estar activa num dado momento e portanto qual será afectada pelas transformações que possam ser executadas a seguir. A função glLoadIdentity() serve para inicializar (igualar à matriz identidade), a matriz que estiver activa no momento.

Por fim crie A função Desenhar_Cena().

void __fastcall TForm1::Desenhar_Cena()
{

//local onde vai desenhar o cenário propriamente dito

}

Contudo se compilamos o código podemos visualizar algo parecido a isto:

Como vê-mos criamos um modelo de aplicação predefinida para depois poder-mos desenhar os cenários e brincar um bocado com o OpenGL.

Portanto vamos lá então criar o nosso primeiro cenário. Iremos desenhar 2 sólidos, um cubo e uma esfera. Depois de desenhados os sólidos iremos aplicar as texturas para dar vida e para visualizarem o poder do OpenGl. Contudo existem muitos factores que fazem uma pessoa visualizar os sólidos, e um desses factores é a Luz.

A criação, configuração e posicionamento das luzes em OpenGL é feita com as

funções:

glLightf(GLenum light, GLenum pname, GLfloat param);
glLighti
(GLenum light, GLenum pname, GLint param);
glLightfv
(GLenum
light, GLenum pname, const GLfloat *params);
glLightiv
(GLenum
light, GLenum pname, const GLint *params);

light: especifica qual fonte de luz que está a ser configurada (varia de GL_LIGHT0 a GL_LIGHT7)

pname: especifica o parâmetro de luz que está a ser configurado pela chamada desta função.

param ou params: é um valor ou um vector de valores a usar na configuração.

Os parâmetros globais de iluminação são configurados pelas funções:

glLightModelf(GLenum pname, GLfloat param);
glLightModeli
(GLenum
pname, GLint param);
glLightModelfv
(GLenum
pname, const GLfloat *params);
glLightModeliv
(GLenum
pname, const GLint *params);

pname: especifica o parâmetro que está a ser configurado pela chamada desta função.

param ou params: é um valor ou um vector de valores a usar na configuração.

Nome do parâmetro

Valor Padrão

Significado

GL_LIGHT_MODEL_AMBIENT

(0.2, 0.2, 0.2, 1.0)

Intensidade RGBA; ambiente de toda a cena

GL_LIGHT_MODEL_LOCAL_VIEWER

0.0 ou GL_FALSE

Como o ângulo de reflexão especular e calculado

GL_LIGHT_MODEL_TWO_SIDE

0.0 ou GL_FALSE

Define entre a iluminação de um ou dois lados

Outros factores é o material de que é feito os objectos, as sombras que podemos dar aos objectos e as reflexões que os objectos podem criar perante outras superfícies como por exemplo um cubo a reflectir na água. Esses factores são importantes, mas no entanto não serão mencionados no nosso Artigo, embora estejam presentes na programação.

3 Criação da Cena.

Crie uma nova aplicação a partir do modelo de aplicações OpenGL definido em aulas anteriores. Altere o painel de modo a que fique com a dimensão de 500x500 pixels

Altere a função FormResize() de modo a que fique com o seguinte código:

void __fastcall TForm1::FormResize(TObject *Sender)
{

glViewport
(0, 0, PanelGL->Width, PanelGL->Height);
glMatrixMode
(GL_PROJECTION);
glLoadIdentity
();
gluPerspective
(65.0, PanelGL->Width/PanelGL->Height, 1.0, 50.0);
glMatrixMode
(GL_MODELVIEW);
}

Acrescente o seguinte código à função FormCreate() para escolher o modelo de sombreamento e ligar o Z-buffer para remoção das superfícies ocultas:

.
.
.

glShadeModel
(GL_SMOOTH);
glClearDepth
(1.0);
glEnable
(GL_DEPTH_TEST);
glDepthFunc
(GL_LESS);
glHint
(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
.
.
.

O cubo será construído usando as primitivas básicas da OpenGL, isto é, através da definição de 8 polígonos (quadrados), a esfera será desenhada usando objectos quadráticos da biblioteca GLU (as texturas não podem ser aplicadas directamente nas primitivas da biblioteca GLUT).

As coordenadas do cubo são mostradas na figura 4.

Figura 4 – Coordenadas do cubo.

Na função Desenhar_Cena() coloque o seguinte código:

void __fastcall TForm1::Desenhar_Cena()
{

// Limpa o ecrã e o buffer de profundidade

glClear
(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(1.0, 0.0, -8.0);
glPushMatrix();

Ang
= Ang + 5;
if(
Ang >= 360) Ang = Ang - 360;
glTranslatef(0.5, 0.5, -0.5);

glRotatef
(Ang, 1.0, 1.0, 1.0);
glTranslatef
(-0.5, -0.5, 0.5);
glBegin
(GL_QUADS);
//frente

glColor3b(127, 0, 0);
glVertex3f(0, 0, 0);
glVertex3f(1, 0, 0);
glVertex3f(1, 1, 0);
glVertex3f(0, 1, 0);

//direita

glColor3b(0, 127, 0);
glVertex3f(1, 0, 0);
glVertex3f(1, 0, -1);
glVertex3f(1, 1, -1);

glVertex3f
(1, 1, 0);
//trás

glColor3b
(0, 0, 127);
glVertex3f
(1, 0, -1);
glVertex3f
(0, 0, -1);
glVertex3f
(0, 1, -1);
glVertex3f
(1, 1, -1);
//direita

glColor3b
(127, 127, 0);
glVertex3f
(0, 0, -1);
glVertex3f
(0, 0, 0);
glVertex3f
(0, 1, 0);
glVertex3f
(0, 1, -1);
//superior

glColor3b
(0, 127, 127);
glVertex3f
(0, 1, 0);
glVertex3f
(1, 1, 0);
glVertex3f
(1, 1, -1);
glVertex3f
(0, 1, -1);
//inferior

glColor3b
(127, 0, 127);
glVertex3f
(0, 0, -1);
glVertex3f
(1, 0, -1);
glVertex3f
(1, 0, 0);
glVertex3f
(0, 0, 0);
glEnd
();
glPopMatrix
();
glTranslatef
(-5.0, 0.0, -5.0);
glRotatef
(Ang, 0.0, 1.0, 0.0);
glRotatef
(-90, 1.0, 0.0, 0.0);
glColor3b(
127, 127, 127);
GLUquadricObj
* q = gluNewQuadric ( );
gluQuadricDrawStyle
( q, GLU_FILL );
gluQuadricNormals
( q, GLU_SMOOTH );
gluQuadricTexture
( q, GL_TRUE );
gluSphere
(q, 2.0, 20, 20 );
gluDeleteQuadric
( q );
glFlush
(); //Processar os comandos
}

Execute o programa. Verá os dois objectos em rotação algo idênticos a Figura 5

Figura 5 – Esfera e Cubo em Rotação

4 Aplicar texturas aos objectos.

O uso de texturas em OpenGL é um assunto complexo e bastante extenso. Neste texto faremos apenas uma breve introdução ao tema.

Na sua forma mais simples, uma textura é uma imagem 2D e a aplicação de uma textura consiste no mapeamento dos pontos que formam essa imagem, nos pontos que forma a superfícies de um polígono 3D.

Na figura 6 as letras A, B, C e D definem os vértices de uma textura e os vértices A1, B1, C1 e D1 os vértices de um polígono 3D onde deve ser mapeada essa textura. O processo de mapeamento de texturas em OpenGL consiste em aplicar a imagem 2D sobre o polígono 3D de forma que os pontos A, B, C e D correspondam aos pontos A1, B1, C1 e D1.

Figura 6 – Correspondência entre os vértices de uma textura e os vértices de um polígono.

Acrescentando o seguinte código à função FormCreate() para criar duas texturas:

//Criação das texturas
glEnable
(GL_TEXTURE_2D);
glGenTextures
(2, Texturas);
//Criação da textura do cubo

Imagem = auxDIBImageLoadA( (const char*) "textura1.bmp");

glBindTexture
(GL_TEXTURE_2D, Texturas[0]);
glTexImage2D
(GL_TEXTURE_2D, 0, GL_RGB, Imagem->sizeX,
Imagem-
>sizeY, 0,GL_RGB,GL_UNSIGNED_BYTE,Imagem->data);
glTexParameteri
(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri
(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
delete
Imagem;
//Criação da textura da esfera

Imagem = auxDIBImageLoadA( (const char*) "Terra.bmp");

glBindTexture
(GL_TEXTURE_2D, Texturas[1]);
glTexImage2D
(GL_TEXTURE_2D, 0, GL_RGB, Imagem->sizeX,
Imagem-
>sizeY, 0,GL_RGB,GL_UNSIGNED_BYTE,Imagem->data);
glTexParameteri
(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri
(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
delete
Imagem;

A função glEnable(GL_TEXTURE_2D) habilita o uso de texturas na aplicação. A função glGenTextures(2, Texturas) gera automaticamente identificadores para as texturas que vamos usar. O primeiro parâmetro indica quantos identificadores de texturas vamos gerar e o segundo parâmetro é um ponteiro para uma lista (array), que armazena os identificadores gerados.

Declare o array Texturas na classe do formulário como sendo do tipo unsigned int. Declare também a variável Imagem como sendo um ponteiro do tipo _AUX_RGBImageRec (este tipo está declarado no ficheiro glaux.h).

Exemplo: no Unit.h declare o seguinte:

class TForm1 : public TForm
{
__published: // IDE-managed Components
TPanel *PanelGL;
void __fastcall FormCreate(TObject *Sender);
void __fastcall FormDestroy(TObject *Sender);
void __fastcall FormResize(TObject *Sender);

private
: // User declarations
HDC hdc;

HGLRC hrc;

int pixel_format
;
public
: // User declarations
__fastcall TForm1(TComponent* Owner);
void
__fastcall Configurar_Pixel_Format();

void __fastcall Idle_Loop(TObject*, bool &Terminar);

void
__fastcall Desenhar_Cena();
unsigned int Texturas[2];
_AUX_RGBImageRec *Imagem;

};

Voltando à criação das texturas, criamos os identificadores necessários (um para a textura do cubo, um para a textura da esfera), lê-mos as imagens a partir de ficheiros do tipo .BMP usando a função auxDIBImageLoadA() da biblioteca glaux.lib, ligamos o identificador de textura no qual iremos trabalhar e criamos a textura. A função glTexImage2D() recebe como parâmetros o tipo de textura a ser criada (uma textura bidimensional, portanto 2D), o nível de detalhe da imagem (utilizado na criação de BMP’s), o formato interno da imagem (quantos bytes cada pixel gastará), a largura e altura da imagem, se haverá ou não bordas na imagem, o formato da imagem a carregar, como os dados estão agrupados na imagem e finalmente o ponteiro para o local da memória onde está localizado o bitmap.

Tendo criado a textura, acertamos os filtros que serão usados quando o objecto estiver muito perto (GL_TEXTURE_MIN_FILTER) ou muito longe (GL_TEXTURE_MAG_FILTER) da câmara. Os valores mais comuns são GL_NEAREST (mais rápido, porém com menor qualidade) e GL_LINEAR (maior qualidade na produção do ponto da textura). Finalmente, após a imagem ter sido carregada e os parâmetros ajustados, liberamos a memória que foi alojada para armazenar a imagem (pois a OpenGL encarrega-se de manipular o conteúdo das texturas). As texturas estão criadas e prontas para serem usadas. Na função Desenhar_Cena(), imediatamente antes do desenho do cubo, executamos a função glBindTexture(GL_TEXTURE_2D, Texturas[0]) que liga a textura desejada para que todos os vértices definidos daquele ponto em diante usem a textura identificada por Texturas[0]. Após isso, para cada vértice que enviarmos à OpenGL, precisamos também enviar um ponto de mapeamento de texturas, que indica o ponto na imagem da textura que o vértice actual utiliza.

Para permitir a construção desta correspondência entre a imagem 2D e o polígono 3D, usa-se a função glTexCoord2f() antes da definição do ponto 3D.

Por exemplo, considerando a figura 61, o código:

glTexCoord2f(0.0f, 0.0f);

glVertex3f(1.0f, -1.0f, 1.0f);

define que o ponto (0.0, 0.0) da textura 2D corresponde ao ponto (1.0, -1.0,1.0) do polígono 3D.

O sistema de coordenadas da textura tem como (0,0) o ponto inferior esquerdo da

imagem e como (1,1) o ponto superior direito. Ou seja, na imagem acima temos

as seguintes coordenadas para os pontos A, B, C e D:

Vértice da Textura Coordenada
A (0,1)
B (1,1)
C (1,0)
D (0,0)

Supondo que o polígono 3D é a face lateral direita de um cubo de aresta 2 com o centro no ponto (0,0,0), teremos as seguintes coordenadas:

Vértice do Polígono 3D Coordenada
A1 1.0, 1.0, 1.0
B1 1.0, 1.0, -1.0
C1 1.0, -1.0, -1.0
D1 1.0, -1.0, 1.0

Assim, substitua o código que desenha o cubo pelo seguinte:

glBegin(GL_QUADS);
//frente

glColor3b(
127, 0, 0);
glTexCoord2f(
0, 0); glVertex3f(0, 0, 0);
glTexCoord2f(
1, 0); glVertex3f(1, 0, 0);
glTexCoord2f(
1, 1); glVertex3f(1, 1, 0);
glTexCoord2f(
0, 1); glVertex3f(0, 1, 0);
//direita

glColor3b(
0, 127, 0);
glTexCoord2f(
0, 0); glVertex3f(1, 0, 0);
glTexCoord2f(
1, 0); glVertex3f(1, 0, -1);
glTexCoord2f(
1, 1); glVertex3f(1, 1, -1);
glTexCoord2f(
0, 1); glVertex3f(1, 1, 0);
//trás

glColor3b(
0, 0, 127);
glTexCoord2f(
0, 0); glVertex3f(1, 0, -1);
glTexCoord2f(
1, 0); glVertex3f(0, 0, -1);
glTexCoord2f(
1, 1); glVertex3f(0, 1, -1);
glTexCoord2f(
0, 1); glVertex3f(1, 1, -1);
//direita

glColor3b(
127, 127, 0);
glTexCoord2f(
0, 0); glVertex3f(0, 0, -1);
glTexCoord2f(
1, 0); glVertex3f(0, 0, 0);
glTexCoord2f(
1, 1); glVertex3f(0, 1, 0);
glTexCoord2f(
0, 1); glVertex3f(0, 1, -1);
//superior

glColor3b(
0, 127, 127);
glTexCoord2f(
0, 0); glVertex3f(0, 1, 0);
glTexCoord2f(
1, 0); glVertex3f(1, 1, 0);
glTexCoord2f(
1, 1); glVertex3f(1, 1, -1);
glTexCoord2f(
0, 1); glVertex3f(0, 1, -1);
//inferior

glColor3b(
127, 0, 127);
glTexCoord2f(
0, 0); glVertex3f(0, 0, -1);
glTexCoord2f(
1, 0); glVertex3f(1, 0, -1);
glTexCoord2f(
1, 1); glVertex3f(1, 0, 0);
glTexCoord2f(
0, 1); glVertex3f(0, 0, 0);
glEnd
();

Para a esfera o processo é o mesmo. Ligamos a textura que queremos usar com a chamada da função glBindTexture(GL_TEXTURE_2D, Texturas[1]), notar que indicámos o outro identificador de textura. Como a esfera é desenhada usando primitivas de objectos do tipo quadrático, não é necessário indicar os pontos de mapeamento, pois são determinados automaticamente.

Por último referir que a OpenGL aceita apenas texturas com largura e altura como potências de 2 (2, 4, 8, 16, 32, 64, 128, 256, etc) portanto é necessário cuidado na criação de imagens para usar como texturas.

As texturas utilizadas no nosso programa estão no formato BMP e vez das actuais Jpeg.

As includes utilizadas foram as seguintes:

#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#include <gl/glaux.h>

As Texturas aplicadas no nosso exemplo foram as seguintes:

Para a Esfera é:

E para o Cubo:

Execute novamente o programa, Deverá ver os objectos com as respectivas texturas a rodar.

Uma pequena Optimização que podemos fazer, é a seguinte:

Em vez de usar-mos a função Idle_Loop(), podemos substitui-la pelo evento Timer e programar o timer para 1000 milissegundos, assim liberta-se mais o processador para ele poder executar outras tarefas deixando este de funcionar a 100% Load.

Esperamos com este artigo ter respondido a muitas questões acerca do OpenGL bem como de outros assuntos com ele relacionado, já sabe que se possuir alguma dúvida tem à disposição o nosso fórum.

Actualizado em ( 14-Ago-2007 )
 

AlphaContent © 2005-2010 - visualclinic.fr and modified by OC-Zone

Pesquisa

 

Publicidade