13.07.2015 Views

Manual de Apoio 2010

Manual de Apoio 2010

Manual de Apoio 2010

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

Computação Gráfica<strong>Manual</strong> <strong>de</strong> <strong>Apoio</strong><strong>2010</strong>Luís Filipe Lobo


Conteúdo1 Introdução 51.1 A caminho do 3D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51.2 Efeitos 3D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61.3 Transformações Geométricas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71.3.1 Eye Coordinates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71.3.2 Viweing Transformations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71.3.3 Mo<strong>de</strong>ling Transformations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81.3.4 Projection Transformations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81.3.5 Viewport Transformations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 Princípios Básicos <strong>de</strong> Programação 3D 112.1 Sistemas <strong>de</strong> Coor<strong>de</strong>nadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112.1.1 Coor<strong>de</strong>nadas Cartesianas - 2D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112.1.2 Viewport - Mapeamento <strong>de</strong> coor<strong>de</strong>nadas para pixels . . . . . . . . . . . . . . . . . . . . . 112.1.3 O Vertex (vértice) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132.1.4 Coor<strong>de</strong>nadas Cartesianas 3D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132.2 Projecções: 3D → 2D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132.2.1 Projecções Ortogonais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132.2.2 Projecções em Perspectiva . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143 Introdução ao OpenGL 153.1 O pipeline OpenGL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153.2 Tipos <strong>de</strong> Dados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153.2.1 Nomes <strong>de</strong> Funções - Convenção . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153.3 JOGL - Java OpenGL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163.4 The State Machine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173.5 O pipeline <strong>de</strong> Transformações . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173.5.1 Matriz Mo<strong>de</strong>lview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183.5.2 A Matriz I<strong>de</strong>ntida<strong>de</strong> (I<strong>de</strong>ntity Matrix) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193.6 Projecções . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213.7 Câmaras e Actores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224 Primitivas Geométricas e Buffers 244.1 O ponto 3D - (Vertex) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244.2 Primitivas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244.2.1 Pontos: GL POINTS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254.2.2 Linhas: GL LINES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264.2.3 Tiras <strong>de</strong> Linha: GL LINE STRIP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274.2.4 Loops <strong>de</strong> Linha: GL LINE LOOP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274.2.5 Padrões <strong>de</strong> Linha:GL LINE STIPPLE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304.2.6 Triângulos: GL TRIANGLES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314.2.7 Tiras <strong>de</strong> Triângulos:GL TRIANGLE STRIP . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324.2.8 Triangle Fans:GL TRIANGLE FANS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334.3 Outras Primitivas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334.3.1 Quadriláteros: GL QUADS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334.3.2 Tiras <strong>de</strong> Quadrados:GL QUAD STRIP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344.3.3 Polígonos: GL POLYGON . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344.3.4 Preenchimento <strong>de</strong> Polígonos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341


4.3.5 Regras na Criação <strong>de</strong> Polígonos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364.3.6 Sub-divisão <strong>de</strong> Arestas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364.4 Buffers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364.4.1 Buffer <strong>de</strong> Profundida<strong>de</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374.4.2 Scissors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375 Cores, Materiais e Luzes 395.1 Cor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395.1.1 Modos <strong>de</strong> Display . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405.2 Utilização <strong>de</strong> Cores no OpenGL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 425.2.1 Shading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 425.3 Cores no Mundo Real . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445.3.1 Luz Ambiente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445.3.2 Luz Difusa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445.3.3 Luz Especular . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455.3.4 Luz <strong>de</strong> Modo Geral . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455.4 Materiais no Mundo Real . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455.5 Adição <strong>de</strong> Luzes ao Cenário . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465.6 Fontes <strong>de</strong> Luz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 485.6.1 Configurando uma Fonte <strong>de</strong> luz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 535.7 Efeitos <strong>de</strong> Iluminação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 545.7.1 Luz Especular . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 545.7.2 Normal Averaging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 555.7.3 Especificando um Spotlight . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 565.7.4 Tesselation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 576 Cores e Materiais (Continuação) 586.1 Blending . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 586.1.1 Combinação <strong>de</strong> Cores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 586.1.2 Alteração da Equação <strong>de</strong> Blending . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 616.2 Antialiasing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 626.2.1 Multisampling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 636.3 Nevoeiro (Fog) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 636.4 Accumulation Buffer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 657 Imagens no OpenGL 687.1 Bitmaps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 687.2 Pixmaps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 707.3 Operações com Pixels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 717.4 Outras Operações com Imagens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 748 Mapeamento <strong>de</strong> Texturas 788.1 Carregamento <strong>de</strong> Texturas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 788.1.1 Pipeline do Carregamento <strong>de</strong> Imagens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 798.1.2 Utilizando o Color Buffer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 798.1.3 Actualização <strong>de</strong> Texturas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 808.2 Mapeamento <strong>de</strong> Texturas à Geometria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 818.3 Exemplo <strong>de</strong> Textura 2D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 828.4 Texture Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 848.5 Parametrização <strong>de</strong> Texturas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 858.5.1 Filtering Básico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 858.5.2 Texture Wrap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 868.5.3 Mipmapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 868.5.4 Mipmap Filtering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 878.5.5 Geração <strong>de</strong> níveis <strong>de</strong> Mipmapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 878.5.6 Geração <strong>de</strong> Mipmaps por Hardware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 888.5.7 Level Of Detail (LOD) Bias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 898.6 Texture Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 892


9 Mapeamento <strong>de</strong> Texturas: Conceitos Avançados 909.1 Cor Secundária . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 909.2 Anisotropic Filtering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 909.3 Compressão <strong>de</strong> Texturas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 929.4 Geração <strong>de</strong> Coor<strong>de</strong>nadas <strong>de</strong> Texturas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 939.4.1 Oject Linear Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 939.4.2 Eye Linear Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 949.4.3 Sphere Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 959.5 Cube Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 959.5.1 Carregamento do Cube Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 959.5.2 Utilização dos Cube Maps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9810 Curvas e Superfícies 10010.1 Superfícies Incorporadas no OpenGL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10010.1.1 Configuração <strong>de</strong> Superfícies Quádricas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10110.1.2 Desenho <strong>de</strong> Superfícies Quádricas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10210.2 Curvas <strong>de</strong> Bézier e Superfícies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10410.2.1 Representação Paramétrica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10510.2.2 Pontos <strong>de</strong> Control (control points) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10510.2.3 Continuida<strong>de</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10610.2.4 Evaluators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10610.2.5 Superfícies 3D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10810.2.6 Luzes e Vectores Normais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10911 Conceitos Avançados 11111.1 Display Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11111.1.1 Prós e Contras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112A Exemplos 113A.1 Exemplo Simples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113B Tabelas 114B.1 Formato <strong>de</strong> Imagem (pixmap) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114B.2 Tipo <strong>de</strong> Dados (pixmap) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115C Código Fonte 116C.1 Example0.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116C.2 Imaging.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118C.3 Pyramid.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122C.4 TexGen.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126C.5 CubeMap.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131C.6 SnowMan.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136C.7 Bezier2D.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139C.8 Bezier3D.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141C.9 BezierLighting.java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1443


ConvençõesNa medida dos possíveis foram traduzidos os nomes <strong>de</strong> inglês para português. No entanto, para o <strong>de</strong>senvolvimentocom a API OpenGL, algumas palavras chave são <strong>de</strong> gran<strong>de</strong> relevo na língua original, o inglês.Para facilitar a localização <strong>de</strong> métodos nas classes JOGL, estes são apresentados sob a forma:Classe . metodo ( Tipo1 arg1 , ... , TipoN argN );ReferênciasOs apontamentos foram elaborados com base nas referências:• OpenGL Superbible [?]4


Capítulo 1Introdução1.1 A caminho do 3DOs objectos tri-dimensionais po<strong>de</strong>m ser mostrados ou <strong>de</strong>scritos com recurso a três medidas: largura, altura eprofundida<strong>de</strong>. Nos dispositivos gráficos, superfícies inevitavelmente 2D, a forma <strong>de</strong> dar a sensação <strong>de</strong> profundida<strong>de</strong>é conseguida pela introdução <strong>de</strong> perspectiva (Figura 1.1).Figura 1.1: Wireframe <strong>de</strong> um cubo 3DA visão humana obtém a percepção tridimensional pela utilização <strong>de</strong> dois olhos. O nosso cérebro recebeduas imagens bidimensionais ligeiramente diferentes por serem recebidas em ângulos diferentes (Figura 1.2). Asduas imagens são então combinadas numa só com o objectivo <strong>de</strong> produzir uma única imagem, mas <strong>de</strong>sta veztridimensional.Figura 1.2: Visão tridimensionalÀ medida que um <strong>de</strong>terminado objecto se afasta, o ângulo (θ) entre as duas imagens vai diminuindo.Normalmente os dispositivos gráficos que simulam o efeito <strong>de</strong> realismo 3D (estereoscópicos) utilizam duas imagensdiferentes para cada olho.Os dispositivos gráficos flat utilizam o mesmo mecanismo que a natureza provi<strong>de</strong>ncia para que pessoas comum único olho possam ver a três dimensões. O “artefacto” utilizado para conseguir este efeito é, com efeito, aperspectiva. Para além <strong>de</strong>ste artefacto, mesmo com um único olho, o nosso cérebro continua a conseguir discernirprofundida<strong>de</strong>, em gran<strong>de</strong> parte <strong>de</strong>vido aos efeitos presentes nos objectos.5


1.2 Efeitos 3DUma palavra a ter em mente na computação gráfica é ren<strong>de</strong>ring. Ren<strong>de</strong>ring consiste na conversão da <strong>de</strong>scrição<strong>de</strong> um objecto tridimensional numa imagem no ecrã. Os efeitos seguintes são aplicados aquando do ren<strong>de</strong>ring <strong>de</strong>uma imagem.Perspective : A perspectiva refere-se aos ângulos entre as linhas que levam à ilusão <strong>de</strong> estarmos napresença <strong>de</strong> três dimensões.Color and Shading: A colocação <strong>de</strong> cores nas várias faces do cubo, permite replicar níveis <strong>de</strong> escuridão(shading), reafirmando o aspecto <strong>de</strong> um objecto sólido.(a)Light & Shadows: Ainda com a utilização <strong>de</strong> cores é possível dar às superfícies a sensação <strong>de</strong> iluminação.Adicionando sombra avançamos um passo no caminho do realismo.(b)Texture Mapping: A maneira brute-force <strong>de</strong> alcançar realismo, po<strong>de</strong> passar pela adição <strong>de</strong> mais polígonos(mais <strong>de</strong>talhe) à imagem, no entanto, á custa <strong>de</strong> processamento adicional. Outra forma <strong>de</strong> obter o mesmoefeito, passa pela utilização <strong>de</strong> imagens nas faces dos objectos - texture mapping.– A imagem fornecida para utilizar é chamada <strong>de</strong> texture– Cada elemento da imagem que é aplicado numa face é chamado texel– O processo esticar a imagem à face do objecto é <strong>de</strong>nominada <strong>de</strong> filteringFog: A adição <strong>de</strong> nevoeiro po<strong>de</strong>rá contribuir para a credibilida<strong>de</strong> da imagem.6


Blenging & Transparency: Blending consiste na mistura <strong>de</strong> objectos ou cores. Através da sobreposição<strong>de</strong> imagens ou objectos, é possível dar a sensação <strong>de</strong> transparência ou reflexão.Antialiasing: O Aliasing é provocado pelo processo <strong>de</strong> rastering nos monitores. Um processo semelhanteao blenging dos pixels com a cor <strong>de</strong> fundo confere-lhe um aspecto suave.1.3 Transformações GeométricasAs transformações geométricas em OpenGL são efectuadas por intermédio <strong>de</strong> operações matriciais. Transformaçõesconsecutivas resultam na multiplicação <strong>de</strong> várias matrizes. Deste modo, duas transformações consecutivas sãoacumulativas.As transformações geométricas po<strong>de</strong>m ser <strong>de</strong> três tipos: viewing, mo<strong>de</strong>ling e projection.A terminologia utilizada no OpenGL para as transformações é:ViewingEspecificação da Localização do Observador (ou câmara)Mo<strong>de</strong>lingLocalização <strong>de</strong> Objectos na CenaMo<strong>de</strong>lViewDualida<strong>de</strong> <strong>de</strong> transformações Viewing e Mo<strong>de</strong>lingProjectionModificações no clip e size do volume (clipping volume) da cenaViewportRelacionado com a escala do output final para o utilizador1.3.1 Eye CoordinatesAs coor<strong>de</strong>nadas do olho representam um ponto virtual absoluto (in<strong>de</strong>pen<strong>de</strong>nte <strong>de</strong> qualquer transformada), on<strong>de</strong>está localizado o observador.Na Figura 1.3a, olhamos o nosso sistema cartesiano 3D <strong>de</strong> frente (o eixo-z não é visível). Na Figura 1.3b oeixo-x e eixo-y foram rodados. À medida que o valor <strong>de</strong> z aumenta, os objectos ficam mais perto do observador.1.3.2 Viweing TransformationsAs primeiras transformações a serem executadas, são as relacionadas com o ponto para on<strong>de</strong> o observador está aolhar.Por omissão, a nossa cena contém um observador localizado no ponto O 0 = [x = 0, y = 0, z = 0] e a direcçãodo olhar aponta para a parte negativa do eixo z <strong>de</strong> tal forma que os objectos <strong>de</strong>senhados com z > 0 ficamsituados nas costas do utilizador.7


Figura 1.3: Duas perspectivas diferentes (eye coordinates)1.3.3 Mo<strong>de</strong>ling TransformationsEste tipo <strong>de</strong> transformações (Figura 1.4), são aplicadas aos objectos no nosso mo<strong>de</strong>l (mo<strong>de</strong>lo 3D).Figura 1.4: Transformações do Mo<strong>de</strong>lo (mo<strong>de</strong>ling)A or<strong>de</strong>m das transformações dita o aspecto final da nossa cena uma vez que as transformações são acumulativas.Na Figura 1.5 é mostrada a diferença na or<strong>de</strong>m da aplicação <strong>de</strong> duas transformações, ainda que com os mesmosvalores.Dualida<strong>de</strong> Mo<strong>de</strong>l/ViewingA dualida<strong>de</strong> é utilizada porque no fim <strong>de</strong> contas, transformações efectuadas no mo<strong>de</strong>l ou no viewing têm umefeito semelhante. A diferença é entre mover a referência das coor<strong>de</strong>nadas na nossa direcção (Figura 1.6a) ouaproximarmos-nos da referência (Figura 1.6b), o que representa o mesmo resultado.Basicamente, uma transformação viewing é semelhante a uma transformação mo<strong>de</strong>ling que aplicamos a umobjecto virtual - o observador.1.3.4 Projection TransformationsAs transformações feitas ao sistema <strong>de</strong> projecções configuram o volume da área visível da nossa cena.Os principais tipos <strong>de</strong> projecções são:Orthographic ProjectionNeste tipo <strong>de</strong> projecção os objectos são <strong>de</strong>senhados no ecrã usando linhas paralelas para a <strong>de</strong>finiçãoda perspectiva, o que implica que os objectos apresentam todos tamanho semelhante in<strong>de</strong>pen<strong>de</strong>ntemente dadistância.Perspective ProjectionA aparência dos objectos é mais semelhante à realida<strong>de</strong>. Objectos mais distantes aparecem mais pequenosque objectos mais próximo. Neste perspectiva, linhas paralelas convergem num ponto distante.8


Figura 1.5: Transformações: rotação/translação e translação/rotaçãoFigura 1.6: Dualida<strong>de</strong> Mo<strong>de</strong>l/ViewingFigura 1.7: esquerda: Ortogonal, direita: Perspectiva9


1.3.5 Viewport TransformationsNo final do pipeline das transformações, obtemos uma imagem 2D que terá que ser mostrada na nossa janela.As transformações <strong>de</strong> viewport <strong>de</strong>finem a porção do nosso “universo” 3D que é <strong>de</strong>senhada na janela e on<strong>de</strong>.10


Capítulo 2Princípios Básicos <strong>de</strong> Programação 3D2.1 Sistemas <strong>de</strong> Coor<strong>de</strong>nadasAntes <strong>de</strong> ser possível colocar um objecto numa cena é preciso um quadro <strong>de</strong> referência. Num dispositivo flat alocalização é feita com recurso a dois valores, um <strong>de</strong>les normalmente refere-se à distância <strong>de</strong>s<strong>de</strong> o lado esquerdo eo outro refere-se à distância <strong>de</strong>s<strong>de</strong> o topo do ecrã.Nas APIs 3D, existem vários sistemas <strong>de</strong> coor<strong>de</strong>nadas. Ao inicializar a nossa cena o sistema <strong>de</strong> coor<strong>de</strong>nadasescolhido, <strong>de</strong>fine a forma como os valores fornecidos são mapeados em pixels no ecrã.2.1.1 Coor<strong>de</strong>nadas Cartesianas - 2DO sistema <strong>de</strong> coor<strong>de</strong>nadas 2D mais utilizado é o sistema cartesiano. O sistema é composto por uma coor<strong>de</strong>nadasx que <strong>de</strong>fine a distância horizontal à origem ([x = 0, y = 0]) e uma coor<strong>de</strong>nada y que <strong>de</strong>fine a distância vertical àorigem (Figura 2.1).Figura 2.1: Plano CartesianoClipping <strong>de</strong> Coor<strong>de</strong>nadasAntes <strong>de</strong> começarmos a <strong>de</strong>senhar pontos e linhas é necessário dizer à API que zona do sistema cartesiano <strong>de</strong>veráaparecer na janela. Esta zona é normalmente <strong>de</strong>nominada <strong>de</strong> clipping region (Figura 2.2).2.1.2 Viewport - Mapeamento <strong>de</strong> coor<strong>de</strong>nadas para pixelsRaramente as coor<strong>de</strong>nadas cartesianas do nosso sistema, correspon<strong>de</strong>m ao tamanho em pixels da janelas. Énecessário fazer um mapeamento da zona visível do nosso sistema <strong>de</strong> coor<strong>de</strong>nadas para o espaço mostrado empixels na janela. Este mapeamento é chamado <strong>de</strong> viewport.Na Figura 2.3 o tamanho do viewport é o dobro da área <strong>de</strong> clipping. Na Figura 2.4 o clipping volume, ocupaapenas uma zona da janela.11


Figura 2.2: Duas zonas <strong>de</strong> clipping diferentesFigura 2.3: Viewport com o dobro da área <strong>de</strong> clippingFigura 2.4: Viewport com a mesma dimensão da área <strong>de</strong> clipping12


2.1.3 O Vertex (vértice)Um objecto numa imagem, po<strong>de</strong> ser <strong>de</strong>composto em formas mais pequenas normalmente <strong>de</strong>nominadas <strong>de</strong>primitives. As primitives po<strong>de</strong>m ser entida<strong>de</strong>s ou superfícies (pontos, linhas, planos ou polígonos) no espaço. Ocubo nas imagens da introdução, são compostos <strong>de</strong> seis faces quadradas. Cada canto <strong>de</strong>stes quadrados, ou <strong>de</strong>qualquer outra primitive é <strong>de</strong>nominado <strong>de</strong> vertex. Cada vertex é representado por uma coor<strong>de</strong>nada. O processo<strong>de</strong> construir geometria em 3D, consiste num jogo <strong>de</strong> liga-os-pontos.2.1.4 Coor<strong>de</strong>nadas Cartesianas 3DNas coor<strong>de</strong>nadas cartesianas 3D, ao sistema bidimensional, adicionamos profundida<strong>de</strong>. Um terceiro eixo (z) éperpendicular aos eixos x e y. A Figura 2.5 mostra o nosso sistema cartesiano com uma rotação (y é rodadopara a esquerda, x rodado para baixo), caso contrário o eixo z não estaria visível.Figura 2.5: Coor<strong>de</strong>nadas Cartesianas 3D2.2 Projecções: 3D → 2DPor mais que tentemos convencer o olho da sensação <strong>de</strong> imagem 3D, o nosso display será 2D. O nosso sistema3D é mostrado em 2D com ajuda <strong>de</strong> trigonometria e operações matriciais simples.As coor<strong>de</strong>nadas 3D são “achatadas” ou projectadas numa superfície 2D (o fundo da janela). O resultadoobtido é semelhante ao <strong>de</strong> pintar os contornos <strong>de</strong> um objecto num vidro <strong>de</strong> uma janela (Figura 2.6).O tipo <strong>de</strong> projecção especifica como o volume <strong>de</strong>ve ser transformado na janela.Figura 2.6: Projecção 2D <strong>de</strong> uma imagem 3D2.2.1 Projecções OrtogonaisNuma projecção ortogonal, o volume visível é <strong>de</strong>finido com recurso a uma superfície rectangular. Qualquerobjecto fora do volume não é <strong>de</strong>senhado. Os objectos aparentam todos o mesmo aspecto, in<strong>de</strong>pen<strong>de</strong>ntemente daprofundida<strong>de</strong> em que se encontram.A Figura 2.7 ilustra o volume visível (viewing volume) <strong>de</strong>finido especificando os clipping planes - far, near,left, right, top e bottom. O conjunto das projecções dos planos são então projectados numa imagem 2D.13


Figura 2.7: O clipping volume <strong>de</strong> uma projecção ortogonal2.2.2 Projecções em PerspectivaUma projecção em perspectiva é em tudo semelhante à ortogonal. Os objectos mais distantes são, no entantomais pequenos, dando a sensação <strong>de</strong> profundida<strong>de</strong>. O viewing volume é parecido com uma pirâmi<strong>de</strong> com a partesuperior cortada. A forma resultante é <strong>de</strong>nominada frustrum (Figura 2.8). Esta projecção é a que confere ummaior realismo.Figura 2.8: O clipping volume (frustrum) <strong>de</strong> uma projecção em perspectiva14


Capítulo 3Introdução ao OpenGLO OpenGL po<strong>de</strong> ser <strong>de</strong>finido como uma interface <strong>de</strong> software para dispositivos gráficos. Consiste <strong>de</strong> uma bibliotecagráfica <strong>de</strong> 3D e mo<strong>de</strong>ling. É altamente portable (suportada em vários sistemas operativos e dispositivos). Tirapartido da aceleração hardware (on<strong>de</strong> disponível), conseguindo níveis <strong>de</strong> <strong>de</strong>sempenho elevados.Mais do que uma linguagem ou uma simples API, <strong>de</strong>fine um standard.3.1 O pipeline OpenGLO processo <strong>de</strong> <strong>de</strong>senho com recurso a OpenGL, é feito com recurso a um conjunto <strong>de</strong> passos (stages). O nomepipeline sintetiza numa palavra esta topologia (Figura 3.1).Figura 3.1: Versão simplificada do pipeline OpenGLOs comandos feitos com recurso à API são colocados num fila <strong>de</strong>nominada command buffer (dados dosvértices (vertex), texturas, . . . ). Quando é dada uma or<strong>de</strong>m <strong>de</strong> processamento ao buffer (flush), quer <strong>de</strong> formaprogramática ou pelo driver, os dados são passados ao passo seguinte na pipeline.No passo transform and lighting, são efectuadas as transformações matemáticas e geométricas que permitemo cálculo da localização e orientação dos nossos pontos e objectos.Na fase <strong>de</strong> rasterização a imagem é criada a partir dos dados <strong>de</strong> geometria, textura e cor do passo anterior.A imagem resultante é então colocada no frame buffer que po<strong>de</strong> ser consi<strong>de</strong>rado como a memória do nossodispositivo gráfico que finalmente a coloca no ecrã.3.2 Tipos <strong>de</strong> DadosA fim <strong>de</strong> garantir portabilida<strong>de</strong>, o OpenGL <strong>de</strong>fine os seus próprios tipos <strong>de</strong> dados (data types). Ao fazê-lo,permite ao <strong>de</strong>veloper, abstrair-se dos pormenores relacionados com os compiladores <strong>de</strong> cada sistema operativo.A cada tipo <strong>de</strong> dados correspon<strong>de</strong> um sufixo. Este sufixo será utilizado a fim <strong>de</strong> i<strong>de</strong>ntificar o tipo <strong>de</strong> dados<strong>de</strong> cada função da API. A Tabela 3.1 mostra os tipos <strong>de</strong> dados, a sua representação em C e o sufixo utilizado nanomenclatura OpenGL.3.2.1 Nomes <strong>de</strong> Funções - ConvençãoAs funções OpenGL seguem uma nomenclatura que ajuda na i<strong>de</strong>ntificação da biblioteca, comando, número <strong>de</strong>argumentos e tipo <strong>de</strong> dados a utilizar nesses argumentos (Figura 3.2), segundo a sintaxe:< biblioteca >< comando >< argumentos >< tipo >O exemplo em cima é uma função da biblioteca gl, o comando Color, que recebe 3 argumentos do tipo float(f).15


Tipo Interno OpenGL Tipo em C SufixoGLbyte signed char bGLshort short sGLint, GLsizei long lGLfloat, GLclampf float fGLdouble, GLclampd double dGLubyte, GLboolean unsigned char ubGLushort unsigned short usGLuint, GLenum, GLbitfield unsigned long uiTabela 3.1: Tipos <strong>de</strong> dados OpenGLFigura 3.2: Função OpenGL3.3 JOGL - Java OpenGLO JOGL é um binding para a API OpenGL <strong>de</strong>senvolvido em Java. Um binding representa uma forma <strong>de</strong> utilizarfuncionalida<strong>de</strong>s <strong>de</strong> uma biblioteca (nativa ou não) do sistema operativo com base numa linguagem diferente daque foi utilizada nessa biblioteca. Para o caso do JOGL, a biblioteca OpenGL (ficheiro .dll no Windows, .so noLinux) do sistema é disponibilizada na linguagem <strong>de</strong> programação Java.Uma das formas mais comuns <strong>de</strong> utilizar a API consiste na criação <strong>de</strong> uma classe que implementa a interfaceGLEventListener. Esta interface <strong>de</strong>fine os métodos:void init ( GLAutoDrawable drawable );void dispose ( GLAutoDrawable drawable );void display ( GLAutoDrawable drawable );void reshape ( GLAutoDrawable drawable ,int x, int y,int width , int height );init(GLAutoDrawable drawable)Este método será invocado imediatamente <strong>de</strong>pois <strong>de</strong> o contexto OpenGL ser inicializado. Normalmente éutilizado para inicializar a nossa cena, tal como configurar luzes.dispose(GLAutoDrawable drawable)Este método é invocado aquando da libertação <strong>de</strong> recursos no contexto OpenGL. A chamada tem lugarimediatamente antes <strong>de</strong> o contexto OpenGL ser <strong>de</strong>struído.display(GLAutoDrawable drawable)Invocado pelo contexto OpenGL para inicializar o processo <strong>de</strong> ren<strong>de</strong>ring. No caso <strong>de</strong> double buffering (ver . . . ),a chamada a este método troca automaticamente os buffers.16


eshape(GLAutoDrawable drawable, int x, int y, int width, int height)Chamada sempre que a janela (ou componente) contendo o viewport, são redimensionados. Permite a actualizaçãoe ajuste do viewport ou da visualização em geral, <strong>de</strong> forma a a<strong>de</strong>quá-lo ao novo tamanho da janela.3.4 The State MachineO <strong>de</strong>senho em OpenGL é conseguido com a ajuda <strong>de</strong> um conjunto <strong>de</strong> variáveis e atributos que vão sendo <strong>de</strong>finidosao longo da execução. No entanto, não é fácil ao <strong>de</strong>veloper estar consciente <strong>de</strong> todos esses elementos aquando daadição <strong>de</strong> nova geometria.Estes elementos fazem parte estado no pipeline. O OpenGL funciona como uma máquina <strong>de</strong> estados quepermite gerir o estado <strong>de</strong>stes elementos.Utilizando a máquina <strong>de</strong> estados, o OpenGL permite que no mesmo <strong>de</strong>senho possamos ter, por exemplo,objectos <strong>de</strong>senhados com lighting e outros sem lighting activando e <strong>de</strong>sactivando uma simples flag.gl. glEnable (GL. GL_LIGHTING );// ...// draw geometry with lightinggl. glDisable (GL. GL_LIGHTING );// ...// draw geometry without lightingA verificação do estado <strong>de</strong> uma <strong>de</strong>terminada flag po<strong>de</strong> ser feito com recurso a gl.glIsEnabled(flag).3.5 O pipeline <strong>de</strong> TransformaçõesAs transformações mais comuns são feitas na projection matrix e na mo<strong>de</strong>lview matrix.O caminho <strong>de</strong>s<strong>de</strong> as coor<strong>de</strong>nadas <strong>de</strong> simples vertex até à imagem no ecrã está <strong>de</strong>scrito na Figura 3.3.Figura 3.3: Pipeline <strong>de</strong> transformações <strong>de</strong> vérticesNo primeiro passo, cada vector é convertido numa matrix 1 × 4, contento as coor<strong>de</strong>nadas [x, y, z] e um factor<strong>de</strong> escala 1 . O vertex é então multiplicado pela matriz <strong>de</strong> transformações Mo<strong>de</strong>lview, que contém as transformadasrelacionadas com o observador. O resultado é multiplicado pela matriz <strong>de</strong> transformações Projection, o queelimina todos os objectos fora do nosso volume <strong>de</strong> clipping 2 . No passo seguinte, as coor<strong>de</strong>nadas resultantes sãodivididas pelo factor w do vertex, com o objectivo <strong>de</strong> normalizar as coor<strong>de</strong>nadas.1 Utilizado nas funções <strong>de</strong> vectores com 4 argumentos, ex.: glVertex4f(x, y, z, w)2 O que representa uma mais-valia em termos <strong>de</strong> processamento17


O passo final consiste no mapeamento do nosso sistema <strong>de</strong> coor<strong>de</strong>nadas a um plano 2D pelas transformaçõesna matriz <strong>de</strong> transformações Viewport.3.5.1 Matriz Mo<strong>de</strong>lviewFelizmente para os utilizadores da API OpenGL, uma gran<strong>de</strong> parte das transformadas é disponibilizada sob aforma <strong>de</strong> um conjunto <strong>de</strong> funções <strong>de</strong> alto nível. Ainda que estas funções <strong>de</strong> alto nível permitam ao utilizadorabstrair-se das operações matemáticas, existe a possibilida<strong>de</strong> <strong>de</strong> efectuar operações avançadas com matrizesespecíficas 1TranslationFunção <strong>de</strong> Translação:GL. glTranslatef (float x, float y, float z)A função recebe como argumentos os valores da translação no x, y e z. O pseudo-código seguinte é ilustradona Figura 3.4.GL2 gl = drawable . getGL (). getGL2 ();GLUT glut = new GLUT ();// Translacao <strong>de</strong> y =10gl. glTranslatef (0f, 10f, 0f);// Desenhar o cubo ...glut . glutWireCube (10 f);Figura 3.4: TranslationRotationFunção <strong>de</strong> Rotação:GL. glRotatef (float angle ,float x, float y, float z)A função recebe como argumentos, o ângulo <strong>de</strong> rotação. Os valores x, y e z, representam um eixo arbitráriosobre o qual <strong>de</strong>ve ser feita a rotação 2 . O pseudo-código seguinte é ilustrado na Figura 3.5.GL2 gl = drawable . getGL (). getGL2 ();GLUT glut = new GLUT ();// Rodar 45 em Torno do vector Vr =[1 ,1 ,1]gl. glRotatef (45f, 1f, 1f, 1f);1 Consultar na referência da API - glLoadMatrix*2 Por exemplo: para obter um eixo perpendicular ao eixo − y basta fornecer o vector V y =[0f, 1f, 0f]18


Desenhar o cubo ...glut . glutWireCube (10 f);Figura 3.5: RotationScalingFunção <strong>de</strong> Escala:GL. glScalef (float x, float y, float z)A função recebe como argumentos os factores <strong>de</strong> escala em cada um dos eixos x, y e z. O pseudo-códigoseguinte é ilustrado na Figura 3.6.GL2 gl = drawable . getGL (). getGL2 ();GLUT glut = new GLUT ();// Escalar x =2*x, y =1* x e z =2* zgl. glScalef (2f, 1f, 2f);// Desenhar o cubo ...glut . glutWireCube (10 f);Figura 3.6: Scale3.5.2 A Matriz I<strong>de</strong>ntida<strong>de</strong> (I<strong>de</strong>ntity Matrix)Ao tentar conseguir o resultado na Figura 3.7, po<strong>de</strong>mos ser tentados a escrever o código:GL2 gl = drawable . getGL (). getGL2 ();GLUT glut = new GLUT ();// Translate y =1019


Figura 3.7: Duas Esferasgl. glTranslatef (0f, 10f, 0f);// Desenhar a primeira esferaglut . glutSolidSphere (1f, 15 , 15);// Translate x =10gl. glTranslatef (10f, 0f, 0f);// Desenhar a segunda esferaglut . glutSolidSphere (1f, 15 , 15);No entanto, o resultado final será mais parecido com o da Figura 3.8.Figura 3.8: Duas Esferas (continuação)Isto <strong>de</strong>ve-se ao facto <strong>de</strong> as transformações no pipeline OpenGL serem acumulativas. O que aconteceu <strong>de</strong> facto foiuma translação <strong>de</strong> 10 no eixo-y seguida do <strong>de</strong>senho da primeira esfera (tudo correcto), <strong>de</strong>pois movemos-nos 10 noeixo-x sem voltar à origem, o que provocou que a esfera final aparecesse com uma translação <strong>de</strong> [x = 10, y = 10].Para obter o efeito da Figura 3.7, é preciso repor o sistema <strong>de</strong> coor<strong>de</strong>nadas original (uma espécie <strong>de</strong> reset).Isto po<strong>de</strong> ser obtido pelo carregamento da in<strong>de</strong>ntity matrix. O nosso código ficaria então mais parecido com:GL2 gl = drawable . getGL (). getGL2 ();GLUT glut = new GLUT ();// Escolher a Matriz <strong>de</strong> Transformacoes// MODELVIEW e fazer um reset inicialgl. glMatrixMo<strong>de</strong> ( GL2 . GL_MODELVIEW );gl. glLoadI<strong>de</strong>ntity ();// Translate y =10gl. glTranslatef (0f, 10f, 0f);// Desenhar a primeira esferaglut . glutSolidSphere (1f, 15 , 15);20


eset !gl. glLoadI<strong>de</strong>ntity ();// Translate x =10gl. glTranslatef (10f, 0f, 0f);// Desenhar a segunda esferaglut . glutSolidSphere (1f, 15 , 15);3.6 ProjecçõesOrthographic ProjectionsA perspectiva ortogonal consiste <strong>de</strong> um volume com os lados todos iguais (Figura 2.7). Este tipo <strong>de</strong> projecção éutilizada normalmente em software CAD, on<strong>de</strong> é necessária uma percepção das dimensões reais dos objectos. Noentanto, a sua aproximação à realida<strong>de</strong> é um pouco vaga (Figura 3.9).(a)Figura 3.9: Ortogonal- Normal/Lado/FrenteA função que permite configurar uma projecção ortogonal, tem o seguinte protótipo:GL2 . glOrtho (double left , double right ,double bottom , double top ,double near , double far)Os argumentos representam as distância até à origem do sistema <strong>de</strong> coor<strong>de</strong>nadas das faces - esquerda (left),direita (right), fundo (bottom), topo (top), a face mais próxima (near) e mais distante (far) do observador(Figura 3.10).Figura 3.10: Projecção OrtogonalO volume <strong>de</strong> <strong>de</strong>senho (viweing volume), correspon<strong>de</strong> ao volume compreendido entre os 6 planos do cubo.Perspective ProjectionsAo contrário da perspectiva ortogonal, numa projecção <strong>de</strong> perspectiva, os objectos mais distantes são “encolhidos”.O viewing volume é agora uma espécie <strong>de</strong> pirâmi<strong>de</strong> com a parte superior cortada - <strong>de</strong>nominada frustrum (Figura3.11).21


Figura 3.11: FrustrumA <strong>de</strong>finição <strong>de</strong>ste frustrum po<strong>de</strong> ser feita com recurso à função glPerspective 1 :GL. glPerspective (double fovy , fouble aspect ,double zNear , doule zFar);Os parâmetros da função são o campo <strong>de</strong> visão (field of view) expresso em graus (ângulo vertical), o aspectratio da altura e largura e as distâncias ao plano mais próximo e mais afastado do volume <strong>de</strong> visualização(viewing volume), como mostra a Figura 3.12.Figura 3.12: Frustrum utilizando glPerspectivePo<strong>de</strong>mos comparar a diferença em relação à perspectiva ortogonal, analisando a Figura 3.13.(a)Figura 3.13: Perspectiva - Normal/Lado/Frente3.7 Câmaras e ActoresAgora que temos o nosso cenário configurado, seria interessante conseguirmos movimentar-nos, nós os observadores.O conceito <strong>de</strong> câmara ou actor não existe realmente no OpenGL, o nome serve apenas como metáfora parafacilitar a compreensão do conceito.A forma <strong>de</strong> mimetizarmos esta funcionalida<strong>de</strong> passa pela utilização da função glLookAt:GLU . gluLookAt (float eyex , float eyey , float eyez ,float atx , float aty , float atz ,float upx , float upy , float upz);1 Existe a função glFrustrum, no entanto a função glPerspective é mais intuitiva22


Figura 3.14: glLookAtOs argumentos <strong>de</strong>sta função são, a posição do observador - Eye(x, y, z), o ponto para o qual o observadorestá a olhar At(x, y, z) e o vector que <strong>de</strong>termina qual a direcção vertical do observador V UP (x, y, z) (Figura3.14).23


Capítulo 4Primitivas Geométricas e BuffersUtilizando ferramentas ou bibliotecas <strong>de</strong> <strong>de</strong>senho gráfico convencionais, o elemento mais básico é o pixel.Basicamente colocamos um <strong>de</strong>terminado pixel na tela com uma <strong>de</strong>terminada cor e uma posição específica.Desenhar em OpenGL é um pouco diferente, não <strong>de</strong>senhamos numa <strong>de</strong>terminada específica no ecrã, mas simem coor<strong>de</strong>nadas no nosso viewing volume. A forma como a nossa cena aparece no ecrã está a cargo do OpenGL.As secções seguintes <strong>de</strong>screvem como são <strong>de</strong>senhadas as varias primitivas geométricas com recurso à APIOpenGL.4.1 O ponto 3D - (Vertex)A função utilizada para <strong>de</strong>senhar um ponto 3D na nossa cena é a função glVertex. O ponto <strong>de</strong>senhado <strong>de</strong>pen<strong>de</strong>do número <strong>de</strong> argumentos e tipo utilizados na função.Figura 4.1: Ponto P (50,50,50)Por exemplo, para obter o ponto na Figura 4.1, po<strong>de</strong>mos utilizar:gl. glVertex3f (50f, 50f, 0f);Ou ainda:gl. glVertex2f (50f, 50f);Po<strong>de</strong>mos ainda especificar o factor <strong>de</strong> escala w (por omissão 1f) utilizando o código:gl. glVertex4f (50f, 50f, 0f, 1f);4.2 PrimitivasUm vertex por si só no espaço, não tem gran<strong>de</strong> significado. Um ponto po<strong>de</strong> representar a intersecção <strong>de</strong> duaslinhas ou curvas ou então o canto <strong>de</strong> um polígono ou sólido.24


A forma como um conjunto <strong>de</strong> pontos é interpretado no nosso espaço 3D é chamada <strong>de</strong> primitiva. O OpenGLtem 10 tipos <strong>de</strong> primitivas.Para <strong>de</strong>senhar com uma <strong>de</strong>terminada primitiva, utilizamos a função glBegin para iniciar, <strong>de</strong>senhamos osnossos pontos e terminamos utilizando o glEnd para terminar a utilização da primitiva.Para <strong>de</strong>senhar vários tipos <strong>de</strong> primitivas, teremos que utilizar vários blocos glBegin/glEnd, por exemplo:gl. glBegin ( GL_POINTS );gl. glVertex3f (0.0f, 0.0f, 0.0 f);gl. glVertex3f (50.0f, 50.0f, 50.0 f);gl. glEnd ();4.2.1 Pontos: GL POINTSEsta primitiva é a mais simples <strong>de</strong> todas: pontos. Os vértices <strong>de</strong>senhados <strong>de</strong>ntro <strong>de</strong> um bloco GL POINTS,aparecem como pontos.Figura 4.2: Desenho utilizando a primitiva GL POINTSO código utilizado para obter a Figura 4.2 será:float GL_PI = 3.1415 f;// Comecar a Desenhar Pontosgl. glBegin ( GL2 . GL_POINTS );float angle , x, y, z = -50.0 f;for ( angle =0f; angle


Figura 4.3: Explicação da espiral <strong>de</strong> pontosfloat [] step = new float [1];gl. glGetFloatv ( GL2 . GL_POINT_SIZE_GRANULARITY , step , 0);O array size (float[] <strong>de</strong> tamanho 2) irá conter os valores mínimo (P S min = size[0]) e máximo (P S max =size[1]) possíveis para o tamanho <strong>de</strong> um ponto, o array step (float[] <strong>de</strong> tamanho 1) irá conter o valor dadiferença mínima (P S step =step[0]) possível entre tamanhos do ponto.Os valor passado à função glPointSize, <strong>de</strong>verá estar compreendido entre P S min e P S max , sendo a diferençamínima entre eles <strong>de</strong> P S step .Assim ao utilizar o código em baixo em conjunto com os arrays do exemplo anterior, obtemos a Figura 4.4.float GL_PI = 3.1415 f;float angle , x, y, z = -50.0 f;float psize = sizes [0];for ( angle =0f; angle


Figura 4.4: Pontos com vários tamanhosgl. glVertex3f (50.0f, 50.0f, 50.0 f);gl. glEnd ();Porque as linhas são só <strong>de</strong>senhadas entre 2 pontos, ao utilizar um número ímpar <strong>de</strong> pontos, o último éignorado. O código em baixo ilustra o <strong>de</strong>senho com o uso da primitiva GL LINES:float z = 0.0f, angle , x, y;// Comecar Linhasgl. glBegin ( GL2 . GL_LINES );for ( angle = 0.0 f; angle


Figura 4.5: Desenho com GL LINESFigura 4.6: Tiras <strong>de</strong> Linhas: GL LINE STRIPFigura 4.7: Loops <strong>de</strong> Linhas: GL LINE LOOP28


A Figura 4.7 mostra os mesmos vértices da Figura 4.6 <strong>de</strong>senhados com a primitiva GL LINE LOOP.Exemplo: Aproximação a Curvas com recurso a Tiras <strong>de</strong> LinhaUtilizando o exemplo da Figura 4.2, on<strong>de</strong> <strong>de</strong>senhamos uma espiral <strong>de</strong> pontos e substituindo a primitiva GL POINTSpela primitiva GL LINE STRIP, po<strong>de</strong>mos fazer uma aproximação a uma espiral. Ao diminuir o espaço entre ospontos po<strong>de</strong>mos tornar a sensação <strong>de</strong> curva ainda mais real.float GL_PI = 3.1415 f;// Comecar a Tiras <strong>de</strong> Linhagl. glBegin ( GL2 . GL_LINE_STRIP );float angle , x, y, z = -50.0 f;for ( angle =0f; angle


float y, curwidth = widths [0];gl. glColor3f (1f, 1f, 1f);for (y= -90f; y


Figura 4.10: Padrão Stipple}gl. glLineStipple ( factor ++ , pattern );// Comecar Linhasgl. glBegin ( GL2 . GL_LINE_STRIP );gl. glVertex2f ( -80f, y);gl. glVertex2f (80f, y);// Terminar Linhasgl. glEnd ();O pattern 0x5555 é um valor hexa<strong>de</strong>cimal, que em binário representa 0101010101010101, o que resultaránuma linha dotted. À medida que o ciclo avança vamos aumentando o factor, pelo que a linha passa <strong>de</strong> dottedpara dashed <strong>de</strong> forma progressiva, como mostra a Figura 4.11.Figura 4.11: Desenho com recurso a stippling4.2.6 Triângulos: GL TRIANGLESAté aqui as formas criadas com recurso às primitivas <strong>de</strong> pontos e linhas, não contém qualquer preenchimento.Para <strong>de</strong>senhar superfícies sólidas com preenchimento é necessário utilizar polígonos.Um polígono é uma forma fechada que po<strong>de</strong> estar ou não preenchida com a cor actual <strong>de</strong> preenchimento. EmOpenGL, todo o <strong>de</strong>senho <strong>de</strong> sólidos assenta na composição baseada em polígonos.O polígono mais simples possível é o triângulo e po<strong>de</strong> ser <strong>de</strong>senhado com recurso à primitiva GL TRIANGLES.gl. glBegin ( GL2 . GL_TRIANGLES );31


gl. glVertex2f (0f, 0f); // V 0gl. glVertex2f (25f, 25f); // V 1gl. glVertex2f (50f, 0f); // V 2gl. glVertex2f ( -50f, 0f); // V 3gl. glVertex2f ( -75f, 50f); // V 4gl. glVertex2f ( -25f, 0f); // V 5gl. glEnd ();O código acima <strong>de</strong>senha dois triângulos preenchidos com a cor <strong>de</strong> preenchimento actual (<strong>de</strong>finida por glColor)no plano x, y (Figura 4.12).Figura 4.12: Desenho <strong>de</strong> TriângulosWindingA or<strong>de</strong>m pela qual os vértices são <strong>de</strong>senhados, <strong>de</strong>fine o lado para que está virado o polígono por eles <strong>de</strong>finido.Assim o primeiro triângulo (V 0 , V 1 , V 2 ) é <strong>de</strong>senhado no sentido dos ponteiros do relógio (clockwise) está <strong>de</strong>costas para o observador, enquanto que o segundo triângulo (V 3 , V 4 , V 5 ) foi <strong>de</strong>senhado no sentido inverso(counterclockwise), está <strong>de</strong> frente para o observador (Figura 4.13).Figura 4.13: WindingO conceito <strong>de</strong> winding é importante quando estamos a preencher um <strong>de</strong>terminado polígono com característicasdiferentes à frente e atrás.Para alterar o comportamento do winding po<strong>de</strong>mos forçar o caminho clockwise a representar a face <strong>de</strong> umpolígono activando a flag GL CW:gl. glEnable ( GL2 . GL_CW );Para restaurar o comportamento por omissão activamos a flag GL CCW.4.2.7 Tiras <strong>de</strong> Triângulos:GL TRIANGLE STRIPO <strong>de</strong>senho da maioria das superfícies consiste num conjunto <strong>de</strong> triângulos. O <strong>de</strong>senho <strong>de</strong>ssas superfícies po<strong>de</strong> serfacilitado pela utilização da primitiva <strong>de</strong> <strong>de</strong>senho <strong>de</strong> triângulos GL TRIANGLE STRIP. Os triângulos são <strong>de</strong>senhadosentre os vértices à medida que eles são especificados (Figura 4.14).32


Figura 4.14: Tiras <strong>de</strong> TriângulosNo caso do GL TRIANGLE STRIP, o winding é forçado para counterclockwise in<strong>de</strong>pen<strong>de</strong>ntemente da or<strong>de</strong>m <strong>de</strong><strong>de</strong>senho.Uma vantagem evi<strong>de</strong>nte <strong>de</strong>ste método <strong>de</strong> <strong>de</strong>senho é o facto <strong>de</strong>, <strong>de</strong>pois dos três primeiros vértices, o <strong>de</strong>senho<strong>de</strong> triângulos adicionais só precisa <strong>de</strong> mais um vértice por triângulo.4.2.8 Triangle Fans:GL TRIANGLE FANSNesta primitiva, os triângulos são <strong>de</strong>senhados em volta <strong>de</strong> um ponto central, <strong>de</strong>finido pelo primeiro vértice aser <strong>de</strong>senhado (V 0 ). Depois dos primeiros três vértices <strong>de</strong>finirem o primeiro triângulo, todos os restantes são<strong>de</strong>finidos com apenas um vértice (Figura 4.15).Figura 4.15: Triangle Fans4.3 Outras PrimitivasA utilização <strong>de</strong> triângulos na construção <strong>de</strong> objectos em OpenGL é preferida pelo facto da maior parte do hardwareacelerar o seu <strong>de</strong>senho. No entanto a utilização <strong>de</strong> outras formas que não o triângulo po<strong>de</strong>rá ser pertinente.4.3.1 Quadriláteros: GL QUADSA primitiva GL QUADS permite <strong>de</strong>senhar um polígono quadrilátero com recurso a quatro vértices. Os vérticesteriam que ser complanares (Figura 4.16).Figura 4.16: Quadriláteros33


4.3.2 Tiras <strong>de</strong> Quadrados:GL QUAD STRIPDa mesma forma que a primitiva GL TRIANGLE SRTIP, a primitiva GL QUAD STRIP permite <strong>de</strong>senhar uma tira <strong>de</strong>quadriláteros pela adição <strong>de</strong> vértices aos quatro primeiros (Figura 4.17).Figura 4.17: Tiras <strong>de</strong> Quadriláteros4.3.3 Polígonos: GL POLYGONEsta primitiva permite o <strong>de</strong>senho <strong>de</strong> polígonos com um número variado <strong>de</strong> lados. No entanto, a restrição dosvértices serem complanares mantém-se (Figura 4.18).Figura 4.18: Desenho <strong>de</strong> PolígonosEsta restrição po<strong>de</strong> ser evitada pela utilização da primitiva GL TRIANGLE FAN em lugar da utilização daprimitiva GL POLYGON.4.3.4 Preenchimento <strong>de</strong> PolígonosAs duas formas mais utilizadas para preenchimento <strong>de</strong> polígonos são, a utilização <strong>de</strong> cores sólidas ou a aplicação<strong>de</strong> texturas. No entanto, da mesma forma que utilizámos os padrões <strong>de</strong> preenchimento <strong>de</strong> linhas, po<strong>de</strong>mosutilizar padrões <strong>de</strong> preenchimento (stippling) <strong>de</strong> polígonos.O padrão é agora <strong>de</strong>finido por uma matriz on/off <strong>de</strong> 32 × 32 bits (Figura 4.19).Para construir a máscara que vai ser utilizada para fazer o stippling utilizamos um array <strong>de</strong> 32 × 4 bytes 1 .Em vez <strong>de</strong> um array bidimensional é utilizado um array simples.O padrão po<strong>de</strong> ser aplicado activando o stippling <strong>de</strong> polígonos e utilizando um array do tipo <strong>de</strong>scrito emcima:byte [] pattern = {0x00 , 0x00 , 0x00 , 0 x00...0x00 , 0x00 , 0x10 , 0 x00};gl. glEnable ( GL2 . GL_POLYGON_STIPPLE );gl. glPolygonStipple ( pattern );Mais uma vez o array é <strong>de</strong>finido ao contrário, sendo o bit menos significativo, o primeiro bit da imagem, ouseja, a primeira linha correspon<strong>de</strong> à última linha da imagem.1 1byte = 8bit =⇒ 32 × 4byte =⇒ 32 × 32bit34


Figura 4.19: Padrão <strong>de</strong> Preenchimento <strong>de</strong> PolígonosFigura 4.20: Exemplo <strong>de</strong> Stippling <strong>de</strong> Polígonos35


4.3.5 Regras na Criação <strong>de</strong> Polígonos1. A primeira regra a ter em mente na construção <strong>de</strong> polígonos é que, todos os seus vértices <strong>de</strong>vem residir nomesmo plano. Um polígono não po<strong>de</strong> “dobrar-se” no espaço (Figura 4.21).Figura 4.21: Polígonos Planos/Não-planosEsta primeira regra pen<strong>de</strong> a favor da utilização <strong>de</strong> triângulos na <strong>de</strong>finição dos nossos polígonos, uma vezque matematicamente um plano é <strong>de</strong>finido por três pontos.2. Outra regra diz que os segmentos do polígono não <strong>de</strong>vem intersectar-se e o polígono <strong>de</strong>ve ser convexo. Opolígono é convexo quando qualquer linha que o atravesse sai e entra apenas uma vez no polígono (Figura4.22).Figura 4.22: Polígonos válidos e inválidos4.3.6 Sub-divisão <strong>de</strong> ArestasApesar <strong>de</strong> o OpenGL só conseguir <strong>de</strong>senhar polígonos convexos, existem situações em que polígonos não-convexosfazem sentido, por exemplo ao <strong>de</strong>senhar uma estrela. A primeira estrela da Figura 4.23 não é convexa, mas aodividirmos o polígono em vários triângulos obtemos uma figura convexa válida.Figura 4.23: Polígonos não ConvexosA fim <strong>de</strong> evitar que as linhas (edges) no interior da segunda estrela sejam visíveis po<strong>de</strong>mos a edge flag. Estaflag é activa/inactiva pela função glEdgeFlag(boolean enable). Esta função diz ao OpenGL se os segmentosseguintes são consi<strong>de</strong>rados arestas do polígono.4.4 BuffersO OpenGL não <strong>de</strong>senha realmente as primitivas no ecrã, utiliza antes um buffer buffer <strong>de</strong> cor, inicialmenteinvisível, que posteriormente é colocado no ecrã. Chamamos aos dois buffers <strong>de</strong> cor, o front buffer (o que seencontra actualmente no ecrã) e o back buffer (o que está a ser <strong>de</strong>senhado).36


No JOGL a configuração do display é feita alterando as capacida<strong>de</strong>s na classe GLDrawable 1 . As capacida<strong>de</strong>sactuais do display po<strong>de</strong>m ser consultadas utilizando:public class MyGl implements GLEventListener {...@Overri<strong>de</strong>public void init ( GLAutoDrawable drawable ) {GLCapabilities cap = drawable . getChosenGLCapabilities ();boolean db = cap . getDoubleBuffered ();...}...}O double buffering encontra-se activo por omissão. Po<strong>de</strong> no entanto ser activado ou <strong>de</strong>sactivado utilizando ocódigo:GLCapabilities cap = drawable . getChosenGLCapabilities ();// Desactivar o double buffercap . setDoubleBuffered ( false );A troca <strong>de</strong> buffers po<strong>de</strong> ser conseguida utilizando a função GLDrawable.swapBuffers() 2 .Utilizando Buffer TargetsÉ possível <strong>de</strong>senhar directamente no front buffer dizendo ao OpenGL qual o buffer <strong>de</strong> <strong>de</strong>stino (target) para asprimitivas que estamos actualmente a <strong>de</strong>senhar, utilizando:gl. glDrawBuffer ( int mo<strong>de</strong> );Especificando GL2.GL FRONT, <strong>de</strong>senhamos directamente no front buffer, utilizando GL2.GL BACK, remetemoso <strong>de</strong>senho para o back buffer. Outra forma <strong>de</strong> <strong>de</strong>senhar directamente no front buffer passa pela <strong>de</strong>sactivação dodouble buffering.Ao <strong>de</strong>senhar com recurso a um único buffer é aconselhável a utilização da função glFlush ou glFinish paraforçar o pipeline a processar a lista <strong>de</strong> comandos no buffer.4.4.1 Buffer <strong>de</strong> Profundida<strong>de</strong>O OpenGL suporta mais buffers para além dos buffers <strong>de</strong> cor. Um <strong>de</strong>les é o <strong>de</strong>pth buffer que em vez <strong>de</strong> armazenarvalores <strong>de</strong> cor, armazena valores <strong>de</strong> profundida<strong>de</strong>.O teste <strong>de</strong> profundida<strong>de</strong> faz correspon<strong>de</strong>r a cada elemento <strong>de</strong> cor no color buffer, um valor <strong>de</strong> profundida<strong>de</strong>no <strong>de</strong>pth buffer.Para utilizar o buffer <strong>de</strong> profundida<strong>de</strong> usamos o código:gl. glEnable ( GL2 . GL_DEPTH_TEST );Para <strong>de</strong>sactivar temporariamente o teste <strong>de</strong> profundida<strong>de</strong> sem <strong>de</strong>scartar os valores actualmente no buffer,po<strong>de</strong>mos utilizar:gl. glDepthMask ( false );Utilizando true em vez <strong>de</strong> false, po<strong>de</strong>mos reactivar o teste <strong>de</strong> profundida<strong>de</strong>.4.4.2 ScissorsAfim <strong>de</strong> optimizar recursos, po<strong>de</strong> ser pertinente <strong>de</strong>senhar apenas uma porção do ecrã. O OpenGL permite <strong>de</strong>finira zona rectangular do ecrã que é ren<strong>de</strong>rizada.O rectângulo po<strong>de</strong> ser <strong>de</strong>finido activando o scissor test com recurso à função glEnable(GL2.GL SCISSOR TEST)e <strong>de</strong>finindo a o seu rectângulo, utilizando a função:GL2 . glScissor ( int x, int y, int width , int height );Os valores x e y especificam o canto inferior direito do rectângulo. Os valores width e height <strong>de</strong>finem alargura e altura do rectângulo.1 No caso da interface GLEventListener, os métodos recebem uma instância <strong>de</strong> um GLAutoDrawable que é do tipo GLDrawablepor herança2 Quando o double buffering está activo, isto é feito <strong>de</strong> forma automática37


GL2 gl = drawable . getGL (). getGL2 ();// Activar o Scissor Testgl. glEnable ( GL2 . GL_SCISSOR_TEST );gl. glClearColor (1f, 0f, 0f, 0f);gl. glScissor (50 , 50 , 200 , 200));gl. glClear ( GL2 . GL_COLOR_BUFFER_BIT );gl. glClearColor (1f, 1f, 0f, 0f);gl. glScissor (120 , 120 , 60 , 60);gl. glClear ( GL2 . GL_COLOR_BUFFER_BIT );// Desactivar o Scissor Testgl. glDisable ( GL2 . GL_SCISSOR_TEST );gl. glFlush ();O código em cima, <strong>de</strong>fine uma scissor box inicial com o canto inferior direito [x, y] = [50, 50] e com a largurae altura <strong>de</strong> 200. Seguidamente a cor <strong>de</strong> fundo é “limpa” <strong>de</strong> vermelho. Uma scissor box mais pequena é então<strong>de</strong>finida e pintada <strong>de</strong> amarelo (Figura 4.24).Figura 4.24: Scissor Test38


Capítulo 5Cores, Materiais e Luzes5.1 CorA cor é simplesmente um comprimento <strong>de</strong> onda da luz visível aos nossos olhos. A luz é uma onda que viaja peloespaço como uma onda num lago, no entanto é mo<strong>de</strong>lada, na física, como uma partícula, como uma gota <strong>de</strong>água que cai no chão.A luz vista como uma ondaA luz visível ao nosso olho é realmente uma mistura <strong>de</strong> diferentes tipos <strong>de</strong> luz. O que caracteriza cada tipo <strong>de</strong>luz, é o seu comprimento <strong>de</strong> onda (Figura 5.1).Figura 5.1: Medida do Comprimento <strong>de</strong> OndaO espectro visível ao olho humano varia <strong>de</strong>s<strong>de</strong> os 390 manómetros (luz violeta) e os 720 nanómetros (luzvermelha) (Figura 5.2). Os termos ultra-violeta e infra-vermelho referem-se ao espectro fora do espectro visível.Figura 5.2: Espectro <strong>de</strong> Luz Visível39


A luz vista como uma partículaA cor que vemos nos objectos é realmente a soma <strong>de</strong> todas os comprimentos <strong>de</strong> onda <strong>de</strong> luz que o objectoconsegue reflectir. Por exemplo, o branco é a soma <strong>de</strong> todos os comprimentos <strong>de</strong> onda do espectro, enquanto queo preto é a ausência <strong>de</strong> luz (pelo menos da luz visível).Depen<strong>de</strong>ndo do tipo e quantida<strong>de</strong> <strong>de</strong> átomos <strong>de</strong> uma superfície, esta reflecte um <strong>de</strong>terminado comprimento<strong>de</strong> onda dos fotões <strong>de</strong> luz que a bombar<strong>de</strong>iam e absorve os restantes. Cada um <strong>de</strong>stes fotões tem também umcomprimento <strong>de</strong> onda associado (dai a dualida<strong>de</strong> onda/partícula), o somatório dos comprimentos <strong>de</strong> onda daspartículas reflectidas confere a cor visível aos nossos olhos (Figura 5.3).Figura 5.3: Partículas (fotões) reflectidos dos materiaisA retina do olho humano é “excitada” pelo conjunto <strong>de</strong> fotões reflectidos pelas superfícies e o cérebrointerpreta-os como luz. As células fotossensíveis da nossa retina são sensíveis a três tipos <strong>de</strong> comprimento <strong>de</strong>onda - o vermelho, o ver<strong>de</strong> e o azul. A mistura <strong>de</strong> quantida<strong>de</strong>s diferentes das três componentes resulta na corinterpretada pelo nosso cérebro (Figura 5.4).Figura 5.4: Interpretação da cor pelo olho humanoComputador → Gerador <strong>de</strong> FotõesA geração <strong>de</strong> cores num computador utilizando várias intensida<strong>de</strong> <strong>de</strong> Red, Green e Blue (RGB), faz agora algumsentido. Os nossos monitores são compostos <strong>de</strong> pequenos elementos contendo as três componentes <strong>de</strong> cor (RGB).Ao mudar as intensida<strong>de</strong>s das várias cores, obtemos a palete <strong>de</strong> cores visível (Figura 5.5).5.1.1 Modos <strong>de</strong> DisplayHoje em dia, graças aos fabricantes dos sistemas operativos, o <strong>de</strong>senho nos dispositivos gráficos é feito comrecurso a uma camada <strong>de</strong> virtualização, chamada <strong>de</strong> driver gráfico, que permite, por intermédio <strong>de</strong> uma API (talcomo o OpenGL), <strong>de</strong>senhar no ecrã sem ter que conhecer os pormenores do hardware.No entanto, <strong>de</strong>vemos ter presentes algumas palavras chaves na utilização <strong>de</strong>stas APIs.40


Figura 5.5: Computador → Gerador <strong>de</strong> FotõesResoluçãoAs resoluções mais usadas nos monitores actuais varia entre os 640x480 e os 1920x1080 (full hd). Devemoster sempre em conta o tamanho da janela e ajustar o nosso clipping volume e viewport <strong>de</strong> forma a manter aimagem que o utilizador visualiza coerente. É <strong>de</strong> esperar que à medida que a janela é aumentada, a resoluçãodos objectos será maior.Profundida<strong>de</strong> <strong>de</strong> CorDa mesma forma que mais resolução significa uma imagem mais <strong>de</strong>talhada, o aumento nos níveis <strong>de</strong> cor disponíveis(color <strong>de</strong>pth) <strong>de</strong>verá aumentar a clarida<strong>de</strong> da nossa imagem.Ao utilizar a API OpenGL <strong>de</strong>vemos ter em mente três profundida<strong>de</strong>s <strong>de</strong> cor:• 4-bit: 16 níveis <strong>de</strong> cor• 8-bit: 256 níveis <strong>de</strong> cor• 24-bit: A cor <strong>de</strong> cada pixel é <strong>de</strong>finida com recurso a 3 componentes <strong>de</strong> 8-bit (8 + 8 + 8 = 25), o queproporciona mais <strong>de</strong> 16 milhões <strong>de</strong> coresA maioria do hardware gráfico actual tem aceleração para os 24-bit <strong>de</strong> profundida<strong>de</strong> <strong>de</strong> cor.Os 16-bit e 32-bit <strong>de</strong> profundida<strong>de</strong> foram em tempos utilizados como uma forma <strong>de</strong> optimizar o <strong>de</strong>sempenho,uma vez que permitem o alinhamento mais eficiente em memória (en<strong>de</strong>reço <strong>de</strong> 32-bit). No entanto, 32-bit nãosignifica um aumento do número <strong>de</strong> cores, mas sim um <strong>de</strong>sperdício <strong>de</strong> 8-bit <strong>de</strong> memória.41


5.2 Utilização <strong>de</strong> Cores no OpenGLPo<strong>de</strong>mos representar todas as cores possíveis com a utilização <strong>de</strong> um cubo, mapeando as componentes Red,Green e Blue nos eixos x, y e z respectivamente (Figura 5.6).Figura 5.6: RGB ColorspaceNa origem ([x, y, z] = [0, 0, 0]) encontra-se o preto, no ponto [x, y, z] = [255, 255, 255] encontra-se o branco enos extremos do cubo em cada um dos eixos encontram-se as cores vermelho (x), ver<strong>de</strong> (y) e azul (z).Alterando a Cor utilizada para <strong>de</strong>senharA função utilizada para alterar a cor utilizada para <strong>de</strong>senho é a função:GL2 . glColor ( red , green , blue [, alpha ]);Na nome da função, o representa o número <strong>de</strong> argumentos. No caso <strong>de</strong> ser 3, a cor será <strong>de</strong>finida com astrês componentes RGB, no caso <strong>de</strong> ser 4, é especificado o valor adicional alpha que <strong>de</strong>fine o nível <strong>de</strong> transparência.O no nome na função especifica o tipo <strong>de</strong> argumento, que po<strong>de</strong>rá ser b, d, f, i, s, ub, ui, or us, querepresentam respectivamente, byte, double, float, integer, short, unsigned byte, unsigned integer, andunsigned short.A gran<strong>de</strong> maioria dos programas em OpenGL utilizam o glColor3f que permite especificar a intensida<strong>de</strong><strong>de</strong> cor entre 0f e 1f (1f = intensida<strong>de</strong> máxima <strong>de</strong> cor). No entanto é possível representar, por uma questão <strong>de</strong>conveniência, o valor da cor entre 0 e 255, utilizando por exemplo:gl. glColor3ub (0 , 255 , 128);No entanto, o seu uso é <strong>de</strong>saconselhado, uma vez que, a tendência no futuro aponta para adição <strong>de</strong> maisníveis <strong>de</strong> intensida<strong>de</strong> (> 255). O OpenGL na realida<strong>de</strong> representa os níveis <strong>de</strong> cor internamente utilizando umfloat, pelo que forçar a utilização <strong>de</strong> glColor3ub, implica um passo adicional para a conversão.5.2.1 ShadingFigura 5.7: Shading <strong>de</strong> uma linha42


Ao utilizar a função glColor estamos a dizer ao OpenGL que cor <strong>de</strong>ve ser utilizada para os vértices <strong>de</strong>senhadosa seguir. No entanto, todas as primitivas para além do ponto contém mais do que um vértice. A forma comosão <strong>de</strong>senhados os pontos intermédios dos vértices, nessas primitivas, por exemplo para uma linha, <strong>de</strong>pen<strong>de</strong> domo<strong>de</strong>lo <strong>de</strong> shading escolhido.A Figura 5.7 mostra como as cores são seleccionadas no caminho entre dois vértices <strong>de</strong> uma linha, on<strong>de</strong> oprimeiro é preto e o segundo é branco. Pela análise da figura, po<strong>de</strong>mos concluir que a linha aparecerá como umgradiente <strong>de</strong> preto para branco.O shading <strong>de</strong> polígonos é um pouco mais complexo. Um triângulo po<strong>de</strong> ser também <strong>de</strong>senhado <strong>de</strong>ntro docubo RGB Colorspace, como mostra a Figura 5.8(a).(a) Cores dos vértices nos eixos <strong>de</strong> cor(b) Triângulo RGBA imagem 5.8(b), po<strong>de</strong> ser obtida com o código:GL2 gl = drawable . getGL (). getGL2 ();...// Utilizar smooth shadinggl. glSha<strong>de</strong>Mo<strong>de</strong>l ( GL2 . GL_SMOOTH );gl. glBegin ( GL2 . GL_TRIANGLE_STRIP );// Vermelhogl. glColor3f (1f, 0f, 0f);gl. glVertex3f (40f, 0f, 0f);// Ver<strong>de</strong>gl. glColor3f (0f, 1f, 0f);gl. glVertex3f (0f, 60f, 0f);// Azulgl. glColor3f (0f, 0f, 1f);gl. glVertex3f ( -40f, 0f, 0f);gl. glEnd ();Figura 5.8: Triângulo no RGB ColorspaceO mo<strong>de</strong>lo por omissão <strong>de</strong> shading no OpenGL é o GL2.GL SMOOTH, no qual a transição entre as cores é a<strong>de</strong>scrita em cima. O outro mo<strong>de</strong>lo é o GL2.GL FLAT, no qual a cor da superfície utilizada correspon<strong>de</strong> à cor doultimo vértice, com a excepção das primitivas GL POLYGON, on<strong>de</strong> a cor utilizada é do primeiro vértice.Para alterar o shading mo<strong>de</strong>l utilizado pelo OpenGL, po<strong>de</strong>mos utilizar a função:gl. glSha<strong>de</strong>Mo<strong>de</strong>l ( int mo<strong>de</strong>l );Utilizando como argumento uma das constantes GL2.GL SMOOTH ou GL2.GL FLAT.43


5.3 Cores no Mundo RealO shading dos objectos no mundo real não se resume apenas aos valores RGB das cores. Ao preenchermossuperfícies com cores, os nossos mo<strong>de</strong>los estão longe <strong>de</strong> parecer reais (Figura 5.9).Figura 5.9: Objecto preenchido com cores diferentes em cada polígonoO OpenGL simula <strong>de</strong> forma bastante aproximada as condições <strong>de</strong> luz num ambiente real. Com a excepção dosobjectos que emitem luz própria, todos eles são afectados por três tipos <strong>de</strong> luz: ambiente, difusa e especular.5.3.1 Luz AmbienteA luz ambiente não tem direccionalida<strong>de</strong>. As superfícies dos objectos <strong>de</strong> uma cena são afectadas <strong>de</strong> forma igual euniforme (Figura 5.10). O resultado <strong>de</strong> uma cena com uma luz ambiente é semelhante aos exemplos ilustradosaté agora on<strong>de</strong> a cor é uniforme.Figura 5.10: Objecto iluminado com luz ambiente5.3.2 Luz DifusaA luz difusa provém <strong>de</strong> uma direcção específica mas é reflectida <strong>de</strong> maneira uniforme pelas superfícies em váriosângulos (Figura 5.11). No entanto, as superfícies iluminadas directamente, são mais claras que as ilmunidadascom um <strong>de</strong>terminado ângulo. Um bom exemplo <strong>de</strong> luz difusa é uma lâmpada fluorescente.Figura 5.11: Objecto iluminado com Luz Difusa44


5.3.3 Luz EspecularA luz especular vem <strong>de</strong> uma direcção em especifico, é reflectida <strong>de</strong> maneira uniforme, mas o feixe da sua reflexãocontinua a ser direccional (Figura 5.12). Os objectos afectados por luz especular costumam mostrar um pontomais iluminado, on<strong>de</strong> a luz inci<strong>de</strong> com mais intensida<strong>de</strong>.Figura 5.12: Objecto iluminado com Luz Especular5.3.4 Luz <strong>de</strong> Modo GeralNenhuma cena é composta <strong>de</strong> um único tipo <strong>de</strong> luz, é antes composta pelo conjunto das vaŕias componentes comvárias intensida<strong>de</strong>s. A titulo <strong>de</strong> exemplo, imaginemos um feixe <strong>de</strong> laser vermelho num quarto escuro. O feixepo<strong>de</strong>rá ser comparado com a luz especular, as partículas <strong>de</strong> pó que reflectem parte da luz do laser po<strong>de</strong>m serconsi<strong>de</strong>radas luz difusa e o matizado vermelho nas pare<strong>de</strong>s do quarto po<strong>de</strong> ser comparado com a luz ambiente.Da mesma forma que a cor, os vários tipos <strong>de</strong> luz têm um valor RGBA associado 1 . Assim por exemplo parao laser vermelho po<strong>de</strong>ríamos ter os valores na Tabela 5.1.Red Green Blue AlphaSpecular 0.99 0.0 0.0 1.0Diffuse 0.10 0.0 0.0 1.0Ambient 0.05 0.0 0.0 1.0Tabela 5.1: Distribuição <strong>de</strong> Luz e Cor <strong>de</strong> um Laser VermelhoPela interpretação da tabela, po<strong>de</strong>mos concluir que o laser vermelho tem uma forte componente especular,uma pequena componente difusa e pouquíssima componente <strong>de</strong> luz ambiente. O mais certo é que, a presença <strong>de</strong>partículas, gere uma componente difusa <strong>de</strong> luz, induzindo luz ambiente no compartimento.As componentes difusa e ambiente são muito semelhantes na natureza e são normalmente combinadas.5.4 Materiais no Mundo RealA forma como os materiais aparecem no mundo real <strong>de</strong>pen<strong>de</strong> dum conjunto <strong>de</strong> factores, no entanto, os mais<strong>de</strong>terminantes tem a ver com a sua cor, e as condições <strong>de</strong> iluminação que o ro<strong>de</strong>iam. Assim, por exemplo, umabola azul reflecte a maior parte dos comprimentos <strong>de</strong> onda azuis e absorve gran<strong>de</strong> parte dos outros.Na maioria dos cenários, a luz é branca, no entanto mudando a luz para amarelo, por exemplo, a nossa bolateria uma cor escura (ou mesmo preto) uma vez que absorve tudo que não seja azul.Proprieda<strong>de</strong>s dos MateriaisQuando utilizamos luzes, não <strong>de</strong>finimos o material <strong>de</strong> um <strong>de</strong>terminado polígono só pela cor, dizemos antes, qual acor da luz que eles reflectem. Os materiais po<strong>de</strong>m reflectir luz especular <strong>de</strong> uma <strong>de</strong>terminada cor e absorver todaa luz difusa da mesma cor, ou ao contrário. Outra proprieda<strong>de</strong> que é <strong>de</strong>finida nos materiais, é a sua capacida<strong>de</strong><strong>de</strong> gerar luz própria.Adição <strong>de</strong> Luz aos MateriaisNão existe nenhuma regra (como a do RGB Colorspace) para ajudar a <strong>de</strong>finir as proprieda<strong>de</strong>s i<strong>de</strong>ais <strong>de</strong> luzes emateriais. Normalmente os resultados são conseguidos com alguma prática e análise.1 Para efeitos <strong>de</strong> luz o alpha é ignorado45


Ao <strong>de</strong>senhar objectos, o OpenGL <strong>de</strong>ci<strong>de</strong> qual a cor para cada pixel. Cada vértice das nossas primitivas temum valor RGB diferente baseado no efeito conjunto das 3 componentes <strong>de</strong> luz ambiente, difusa e especularmultiplicado pela reflectivida<strong>de</strong> ambiente, difusa e especular do material.Calculo do Efeito das luzesO cálculo do RGB das três componentes <strong>de</strong> luz é feito <strong>de</strong> forma similar. No entanto, a direccionalida<strong>de</strong> das luzesdifusa e especular <strong>de</strong>pen<strong>de</strong> do ângulo <strong>de</strong> incidência da luz.O cálculo do RGB obtido a partir da incidência da luz (ambiente neste caso) com o RGB (0.5, 0.5, 0.5)numa superfície com proprieda<strong>de</strong>s reflectivas da luz ambiente (0.5, 1.0, 0.5) po<strong>de</strong> ser calculado da seguinteforma;Ou seja,RGB reflected = RGB light × RGB materialRGB reflected = (0.5, 0.5, 0.5) × (0.5, 1.0, 0.5) = (0.25, 0.5, 0.25)Figura 5.13: Compontente <strong>de</strong> luz ambiente <strong>de</strong> um Objecto5.5 Adição <strong>de</strong> Luzes ao CenárioA fim <strong>de</strong> consolidar os conceitos teóricos nas secções anteriores, e antes <strong>de</strong> avançarmos com mais conceitos, épertinente contextualizá-los no OpenGL.Activação a LuzPara utilizar a luz no OpenGL activamos a flag GL LIGHTING:gl. glEnable ( GL2 . GL_LIGHTING );gl. glColor3f (0.75f, 0.75f, 0.75 f);gl. glBegin ( GL2 . GL_TRIANGLES );gl. glVertex3f (60f, -40, 0f);gl. glVertex3f ( -60f, -40f, 0f);gl. glVertex3f (0f, 40f, 0f);gl. glEnd ();Ao activar a flag GL LIGHTING, o OpenGL <strong>de</strong>termina a cor <strong>de</strong> cada vértice com base nos parâmetros dosmateriais. Como ainda não foram <strong>de</strong>finidos quaisquer parâmetros dos materiais, os objectos no nosso cenárioaparecem escuros e sem qualquer luz (Figura 5.14).Configuração do Mo<strong>de</strong>lo <strong>de</strong> LightingO primeiro passo <strong>de</strong>pois <strong>de</strong> activar o cálculo <strong>de</strong> luzes é configurar o mo<strong>de</strong>lo <strong>de</strong> lighting. As três componentes <strong>de</strong>luz que afectam o nosso mo<strong>de</strong>lo <strong>de</strong> lighting são <strong>de</strong>finidos com recurso à função:glLightMo<strong>de</strong>lfv ( int mo<strong>de</strong> , float [] params , int offset );46


Figura 5.14: Polígono sem parâmetros <strong>de</strong> materiais <strong>de</strong>finidosO argumento mo<strong>de</strong> permite-nos seleccionar qual a(s) componente(s) que estamos a configurar. O segundoargumento é um array contendo os valores RGBA da luz 1 .Para <strong>de</strong>finir a componente <strong>de</strong> luz ambiente utilizamos a variável GL LIGHT MODEL AMBIENT, como mostra oexemplo:gl. glEnable ( GL2 . GL_LIGHTING );float [] ambientLight = new float [] {1f, 1f, 1f, 1f};gl. glLightMo<strong>de</strong>lfv ( GL2 . GL_LIGHT_MODEL_AMBIENT , ambientLight , 0);Configuração das Proprieda<strong>de</strong>s dos MateriaisA configuração das proprieda<strong>de</strong>s dos materiais é feita antes <strong>de</strong> os <strong>de</strong>senhar, <strong>de</strong>finindo as cores que estes reflectempara cada componente. Isto po<strong>de</strong> ser feito com recurso à função:glMaterialfv ( int face , int pname , float [] params , int offset )O parâmetro face <strong>de</strong>fine qual a face que toma os valores que estamos a <strong>de</strong>finir:• GL FRONT: Frente• GL BACK: Trás• GL FRONT AND BACK: Ambas as facesO parâmetro pname <strong>de</strong>fine qual a proprieda<strong>de</strong> que estamos a configurar e po<strong>de</strong> tomar os valores:• GL AMBIENT, GL DIFFUSE, GL SPECULAR: Luz ambiente, difusa e especular• GL AMBIENT AND DIFFUSE: Componentes ambiente e difusa (tomarão os mesmos valores).• GL EMISSION: Luz própria do objecto.O seguinte exemplo mostra uma possível utilização da função glMaterial:float gray [] = { 0.75f, 0.75f, 0.75f, 1.0 f };gl. glMaterialfv ( GL2 . GL_FRONT , GL2 . GL_AMBIENT_AND_DIFFUSE , gray , 0);Na maior parte das vezes, a luz ambiente e difusa tomam valores semelhantes, para tal a flag GL AMBIENT AND DIFFUSEé utilizada. A nossa imagem terá agora o aspecto da Figura 5.15.A segunda forma <strong>de</strong> atribuir proprieda<strong>de</strong>s aos materiais é activando o color tracking. Com o color tracking,dizemos ao OpenGL para utilizar as cores <strong>de</strong>finidas com recurso a glColor.Para activar o color tracking, activamos a flag GL COLOR MATERIAL:glEnable ( GL2 . GL_COLOR_MATERIAL );Da mesma forma utilizamos a função glColorMaterial para <strong>de</strong>finir a face e a componente <strong>de</strong> luz afectada:glColorMaterial ( GL2 . GL_FRONT , GL2 . GL_AMBIENT_AND_DIFFUSE );1 O parâmetro offset é específico do binding JOGL. No contexto <strong>de</strong>ste documento tomará sempre o valor 0.47


Figura 5.15: Polígono após <strong>de</strong>finição com glMaterialJuntando tudo no mesmo bloco <strong>de</strong> código obtemos:...// No metodo init (...)gl. glEnable ( GL2 . GL_LIGHTING );float [] ambientLight = new float [] {1f, 1f, 1f, 1f};gl. glLightMo<strong>de</strong>lfv ( GL2 . GL_LIGHT_MODEL_AMBIENT , ambientLight , 0);gl. glEnable ( GL2 . GL_COLOR_MATERIAL );...// No metodo display (...)gl. glColorMaterial ( GL2 . GL_FRONT , GL2 . GL_AMBIENT_AND_DIFFUSE );gl. glColor3f (0.75f, 0.75f, 0.75 f);gl. glBegin ( GL2 . GL_TRIANGLES );gl. glVertex3f (60f, -40, 0f);gl. glVertex3f ( -60f, -40f, 0f);gl. glVertex3f (0f, 40f, 0f);gl. glEnd ();O resultado final da utilização do GL COLOR MATERIAL, será semelhante ao obtido <strong>de</strong>finindo o material paracada polígono individual (Figura 5.15).A Figura 5.16 mostra os resultados para vários níveis da luz ambiente num objecto mais complexo.5.6 Fontes <strong>de</strong> LuzNum cenário real, as fontes <strong>de</strong> luz po<strong>de</strong>m ser variadas. Para além da cor e intensida<strong>de</strong>, são providas <strong>de</strong> umalocalização e direccionalida<strong>de</strong>. Com o OpenGL é possível <strong>de</strong>finir 8 fontes <strong>de</strong> luzes in<strong>de</strong>pen<strong>de</strong>ntes com umalocalização no nosso viewing volume.Ao especificar uma luz, dizemos ao OpenGL a sua localização e para on<strong>de</strong> se dirige. Ao especificar umaluz direccional, esta inci<strong>de</strong> nos objectos com um <strong>de</strong>terminado ângulo, que <strong>de</strong>termina a forma como o objectoé iluminado. A fim <strong>de</strong> conseguir calcular o shading (as sombras) numa superfície, o OpenGL <strong>de</strong>ve conhecer oângulo <strong>de</strong> incidência da luz.A luz inci<strong>de</strong> numa superfície com um <strong>de</strong>terminado ângulo (A) e é reflectida com outro ângulo (B) em direcçãoao observador (Figura 5.17). Estes ângulos são utilizados em conjunto com as proprieda<strong>de</strong>s dos materiais, para<strong>de</strong>terminar a cor aparente <strong>de</strong> cada vértice. Posteriormente pela utilização <strong>de</strong> smooth shading (GL SMOOTH), édada a sensação <strong>de</strong> iluminação do polígono.Se tivermos em consi<strong>de</strong>ração que cada polígono é <strong>de</strong>finido por um conjunto <strong>de</strong> pontos, <strong>de</strong>terminar o ânguloque a luz faz com um ponto po<strong>de</strong> representar uma dificulda<strong>de</strong> 1 . Para resolver esse problema, cada ponto teráque conter informação acerca do vector que <strong>de</strong>fine a sua orientação vertical (upward).1 O segundo ângulo (B) po<strong>de</strong> tomar uma infinida<strong>de</strong> <strong>de</strong> valores possíveis48


(a) ambientList = .8, .8, .8 (b) ambientList = .4, .4, .4Figura 5.16: Vários níveis <strong>de</strong> Luz AmbienteFigura 5.17: Reflexão da Luz nos Objectos49


Vectores Normais às SuperfíciesO vector normal (normal vector) <strong>de</strong> uma <strong>de</strong>terminada superfície, é um vector que começa num ponto imaginárioalgures na superfície fazendo um ângulo recto com esta (Figura 5.18).Figura 5.18: Vectores Normais (2D e 3D)É possível especificar normais para os polígonos gerados pelas primitivas OpenGL. No entanto, po<strong>de</strong> fazersentido especificar normais para pontos em particular, por exemplo, no caso em que a normal não é totalmenteperpendicular ao polígono.Especificação da NormalA Figura 5.19 mostra um plano paralelo ao plano xy no espaço 3D. O vector normal po<strong>de</strong>rá será <strong>de</strong>finido pelovector entre o ponto (1, 1, 0) e qualquer outro ponto (acima <strong>de</strong>ste) na recta que o atravessa, como por exemplo oponto (1, 10, 0).Figura 5.19: Exemplo <strong>de</strong> um vector normal a uma superfíciePara especificar o vector normal do exemplo seriam necessários 2 pontos. No OpenGL é possível especificaro vector normal utilizando apenas um ponto, para tal, movemos o vector para a origem (0, 0, 0), subtraindo oprimeiro ponto ao segundo, obtendo:V normal = V (1,10,10) − V (1,1,0) = V (0,9,0)O ângulo do vector normal inicial mantém-se no vector entre a origem e nosso novo ponto (Figura 5.20).O vector normal po<strong>de</strong> ser associado a uma superfície utilizando a função glNormal, como mostra o exemplo:gl. glBegin ( GL_TRIANGLES );gl. glNormal3f (0.0f, -1.0f, 0.0 f);gl. glVertex3f (0.0f, 0.0f, 60.0 f);gl. glVertex3f ( -15.0f, 0.0f, 30.0 f);gl. glVertex3f (15.0f ,0.0f ,30.0 f);gl. glEnd ();50


Figura 5.20: Vector Normal movido para a OrigemNo exemplo, a função glNormal3f especifica o vector normal à superfície <strong>de</strong>finida pelos três vectoressubsequentes (neste caso um triângulo), na direcção do eixo-y negativo. Os vectores são <strong>de</strong>senhados emcounterclock-wise quando vistos do lado para que aponta o vector, <strong>de</strong> forma a manter o winding coerente.Vectores Normais UnitáriosTodos os vectores normais no OpenGL, <strong>de</strong>vem ser vectores unitários (<strong>de</strong> comprimento 1). Po<strong>de</strong>mos converterqualquer vector para um vector unitário obtendo o seu cumprimento e dividindo as componentes nos 3 eixos poresse cumprimento:x 1 =Length = √ x 2 + y 2 + z 2xLength , y y1 =Length , z z1 =LengthV 1 = (x 1 , y 1 , z 1 )O processo <strong>de</strong> tornar um vector unitários <strong>de</strong>nomina-se <strong>de</strong> normalização <strong>de</strong> vectores.A fim <strong>de</strong> evitar os cálculos, po<strong>de</strong>mos dizer ao OpenGL para converter automaticamente as nossas normaispara vectores unitários, activando a flag:glEnable ( GL2 . GL_NORMALIZE );No entanto, isto tem um custo no <strong>de</strong>sempenho. A melhor solução será efectuar o pré-cálculo dos vectoresnormais já normalizados.Sem mais configuração, a transformação efectuada pela função glScale, altera, junto com a geometria, otamanho das normais <strong>de</strong>finidas para os nossos polígonos. Para evitar esse efeito po<strong>de</strong>mos activar a flag:glEnable ( GL2 . GL_RESCALE_NORMALS );Junto com este documento, foi criado um projecto do eclipse contendo classes <strong>de</strong> auxílio à programaçãoOpenGL. Uma <strong>de</strong>ssas classes é a classe GlTools que contém um conjunto <strong>de</strong> funções úteis. Uma <strong>de</strong>ssas funções éa função:GLVector GlTools . normalizeVector ( GLVector v);Esta função recebe como argumento um vector (classe GLVector) e <strong>de</strong>volve uma instância normalizada dovector fornecido.51


Figura 5.21: Um vector normal não trivialFigura 5.22: Produto Vectorial52


Determinando a NormalNo caso da Figura 5.21 o vector normal não é tão óbvio.A <strong>de</strong>terminação <strong>de</strong> um vector normal po<strong>de</strong> ser feita utilizando 2 vectores (ou 3 pontos) do plano que representaa superfície (Figura 5.22). O produto vectorial <strong>de</strong>sses vectores é um vector perpendicular à superfície.Tomando o vector V 1 ( P ⃗ 1 P 2 ) e V 2 ( P ⃗ 1 P 3 ), o produto vectorial (V P = V 1 × V 2 ) po<strong>de</strong> ser obtido utilizando afórmula:x P = y 1 × z 2 − y 2 × z 1y P = x 1 × z 2 − x 2 × z 1z P = x 1 × y 2 − x 2 × y 1Mais uma vez, com o fim <strong>de</strong> facilitar o cálculo, foi criado código para facilitar esta tarefa:GLVector v1 = new GLVector (1f, 2f, 3f);GLVector v2 = new GLVector (4f, 5f, 6f);GLVector vp = v1. crossProduct (v2 );O exemplo acima, cria dois vectores (v1 e v2) e utiliza a função crossProduct da instância v1 para criar ovector vp com o resultado do produto vectorial.5.6.1 Configurando uma Fonte <strong>de</strong> luzConhecidos que estão os requisitos para a iluminação <strong>de</strong> polígonos, po<strong>de</strong>mos adicionar luzes ao nosso cenário. Aconfiguração <strong>de</strong> luzes é feita utilizando a função:glLightfv ( int light , int pname , float [] params , int offset );A criação <strong>de</strong> um ponto <strong>de</strong> luz po<strong>de</strong> ser feita da seguinte forma:// na funcao init (...)float [] ambientLight = { 0.1f, 0.1f, 0.1f, 1.0 f };float [] diffuseLight = { 0.4f, 0.4f, 0.4f, 1.0 f };// Configurar e Activar a Luz 0gl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_AMBIENT , ambientLight , 0);gl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_DIFFUSE , diffuseLight , 0);gl. glEnable ( GL2 . GL_LIGHT0 );// na funcao display (...)// Posicionar a luz no cenariofloat lightPos [] = { -50.f, 50.0f, 100.0f, 1.0 f };gl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_POSITION , lightPos );Os arrays ambientLight[] e diffuseLight[] contém o RGBA das componentes ambiente e difusa da luz.O array lightPos[] <strong>de</strong>fine a posição da luz. A posição da luz é <strong>de</strong>finida na Mo<strong>de</strong>lView Matrix uma vez queé um objecto geométrico, daí ser posicionada normalmente na função display(). Quando o último valor daposição da luz é 1f, o array <strong>de</strong>fine a posição da luz, quando o seu valor é 0, a luz encontra-se na direcção dovector lightPos[], mas no infinito.Ao <strong>de</strong>senhar os polígonos dos nossos objectos é necessário <strong>de</strong>finir os vectores normais. Para facilitar o cálculodos vectores normais foi criada uma função na classe GlTools, que po<strong>de</strong> ser utilizada da seguinte forma:// Criar uma lista <strong>de</strong> vectoresGLTVectorList vPoints = new GLTVectorList ();vPoints . add (15.0f, 0.0f, 30.0 f);vPoints . add (0.0f, 15.0f, 30.0 f);vPoints . add (0.0f, 0.0f, 60.0 f);// Obter o vector normal ao planoGLVector vNormal = GlTools . getNormalVector ( vPoints );gl. glNormal3fv ( vNormal . toArray () , 0); // 0 -> ignorarvPoints . draw ();A classe GLTVectorList fornece uma forma simples <strong>de</strong> criar listas <strong>de</strong> vectores. O vector normal po<strong>de</strong> serobtido com a função GlTools.getNormalVector que recebe como argumento a lista <strong>de</strong> vectores criada. Ocálculo é feito com base nos primeiros 3 vectores adicionados na lista. Para utilizar nas funções do OpenGL,53


classe GLVector fornece o método toArray como forma simples <strong>de</strong> converter a instância actual para um array<strong>de</strong> float.Depois <strong>de</strong> aplicada a nossa fonte <strong>de</strong> luz, a imagem da Figura 5.16(b) toma o aspecto da Figura 5.23.Figura 5.23: Imagem com Fonte <strong>de</strong> LuzSugestão A fim <strong>de</strong> aumentar o <strong>de</strong>sempenho da nossa aplicação e uma vez que a geometria normalmente éconhecida à partida, seria boa i<strong>de</strong>ia guardar a lista <strong>de</strong> vectores (polígonos) e suas normais à priori e apenas<strong>de</strong>senhá-los no momento oportuno.5.7 Efeitos <strong>de</strong> IluminaçãoA luz ambiente e difusa são suficientes no caso <strong>de</strong> estarmos a mo<strong>de</strong>lar superfícies que não têm componenteespecular da luz, tais como, ma<strong>de</strong>ira, papel, carvão, ou semelhantes.No entanto, em superfícies tais como a pele e metálicas em geral, é <strong>de</strong>sejável a componente especular. Aoadicionar esta componente po<strong>de</strong>mos dar brilho às superfícies, tal como o brilho numa esfera colorida <strong>de</strong> vidro.5.7.1 Luz EspecularPo<strong>de</strong>mos adicionar luz especular à fonte <strong>de</strong> luz <strong>de</strong>finida anteriormente da seguinte forma:// metodo init (...)float [] specularLight = { 1f, 1f, 1f, 1f };...gl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_SPECULAR , specularLight , 0);O array specularLight[] especifica uma componente especular muito brilhante na nossa fonte <strong>de</strong> luz (luzbranca RBG=[255, 255, 255]). Seguidamente, utilizando a função glLightfv adicionamos à luz GL LIGHT0 acomponente especular.A seguir é necessário <strong>de</strong>finir a forma como o nosso objecto reflecte a luz especular, o que po<strong>de</strong> ser feito com ocódigo:// metodo display (...)// Definir a componente especular dos objectos ...float [] specref = { 1.0f, 1.0f, 1.0f, 1.0 f };gl. glMaterialfv ( GL2 . GL_FRONT , GL2 . GL_SPECULAR , specref , 0);// .. com um valor alto <strong>de</strong> brilhogl. glMateriali ( GL2 . GL_FRONT , GL2 . GL_SHININESS , 128);...Mantendo o color tracking activo, as cores utilizadas são as <strong>de</strong>finidas pela função glColor. O array specref[]<strong>de</strong>fine os valores RGBA para a reflexão especular. Utilizando a cor branca, dizemos que as superfícies reflectem54


praticamente todas as cores da componente especular. Utilizamos <strong>de</strong>pois este array na proprieda<strong>de</strong> GL SPECULARna face GL FRONT dos nossos objectos (Figura 5.24).Figura 5.24: Adição <strong>de</strong> Luz EspecularExpoente EspecularPara obter o efeito <strong>de</strong> brilho nas zonas on<strong>de</strong> a luz especular inci<strong>de</strong> e sombra à medida que nos afastamos,alteramos a proprieda<strong>de</strong> GL SHININESS na face do objecto:gl. glMateriali ( GL2 . GL_FRONT , GL2 . GL_SHININESS , 128);Esta função <strong>de</strong>fine o chamado expoente especular dos materiais. Basicamente <strong>de</strong>fine o quão pequeno é o focoda luz. Com o valor próximo <strong>de</strong> 0, o foco é gran<strong>de</strong> e provocando uma incidência semelhante à difusa/ambiente.À medida que aumentamos o valor, a nossa luz vai ficando mais “focada” provocando um ponto <strong>de</strong> luz mais<strong>de</strong>finido. O valor do GL SHININESS po<strong>de</strong> variar entre 1 e 128.5.7.2 Normal AveragingA técnica <strong>de</strong> normal averaging é um tweaking que permite dar a ilusão <strong>de</strong> uma superfície suave (smooth) aindaque seja composta por polígonos planos, como o exemplo da Figura 5.25.Figura 5.25: Esfera composta <strong>de</strong> quads e triângulosSe especificarmos as normais aos polígonos, o que vamos obter é uma superfície parecida com um diamante.No entanto, se especificarmos as “verda<strong>de</strong>iras” normais, ou seja, nos vértices, o OpenGL interpola <strong>de</strong> forma suaveatravés da superfície dos polígonos. O resultado é a ilusão <strong>de</strong> uma superfície suave.A Figura 5.26 ilustra as diferenças entre as normais aos polígonos e aos vértices. No segundo caso as normaissão perpendiculares à superfície real do circulo.55


(a) Normais perpendiculares a cada face(b) Normais perpendiculares a cada vérticeFigura 5.26: Normal AveragingCalcular este tipo <strong>de</strong> normal para uma esfera é relativamente simples, uma vez que todas as normais passampelo seu centro. No entanto, para superfícies mais complexas o cálculo é feito com base nas normais <strong>de</strong> váriospolígonos que partilham o vértice em questão. A normal resultante é a média <strong>de</strong>stas normais, daí o nome normalaveraging.5.7.3 Especificando um SpotlightPara transformarmos uma fonte <strong>de</strong> luz num Spotlight basta adicionar-lhe direccionalida<strong>de</strong> e <strong>de</strong>finir a sua abertura.Po<strong>de</strong>mos conseguir esse efeito utilizando o código:// algures no init (...)gl. glLightf ( GL2 . GL_LIGHT0 , GL2 . GL_SPOT_CUTOFF , 30f);// algures no display (...)float [] spotDir = { -1f, -1f, 0f};gl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_SPOT_DIRECTION , spotDir , 0);O efeito conseguido com a adição <strong>de</strong> um spotlight será semelhante ao da Figura 5.27.Figura 5.27: Adição <strong>de</strong> um Spotlight56


A proprieda<strong>de</strong> GL SPOT CUTOFF <strong>de</strong>fine o ângulo <strong>de</strong> abertura do nosso spotlight, conforme mostra a Figura5.28.Figura 5.28: Ângulo do cone do SpotlightA proprieda<strong>de</strong> GL SPOT DIRECTION <strong>de</strong>fine a direcção do spot segundo um vector normalizado. Neste caso ovector (−1, −1, 0) diz que o nosso spot aponta no sentido negativo dos eixos x e y e faz um ângulo <strong>de</strong> 45 o com oeixo-y para esquerda.5.7.4 TesselationO conjunto <strong>de</strong> polígonos que <strong>de</strong>screvem uma superfície <strong>de</strong>nomina-se <strong>de</strong> tesselation. Quanto maior for o nível <strong>de</strong>tesselation mais realistas parecem as nossas curvas no caso da esfera. A Figura 5.29 mostra duas esferas comnúmeros diferentes <strong>de</strong> polígonos.Figura 5.29: Tesselation57


Capítulo 6Cores e Materiais (Continuação)Os exemplos constantes daqui para a frente, serão baseados no código fornecido junto com este item, na classeExample0 (Apêndice C.1). O exemplo contém um torus, <strong>de</strong>finições básicas <strong>de</strong> luz e um chão como mostra aFigura 6.1.Figura 6.1: Example06.1 BlendingJá vimos que o OpenGL armazena os valores <strong>de</strong> cor no buffer <strong>de</strong> cor (em circustâncias normais) na altura <strong>de</strong>ren<strong>de</strong>rizar. Já vimos também que o mesmo acontece à informação da profundida<strong>de</strong> <strong>de</strong> cada um dos elementos(<strong>de</strong>pth buffer). Quando activamos o teste <strong>de</strong> profundida<strong>de</strong> (<strong>de</strong>pth test), um elemento substitui outro, apenas seestiverem mais perto no clipping volume ao utilizador.No caso do blending estas regras não se aplicam:glEnable ( GL2 . GL_BLEND );Quando activo, novas cores são combinadas com as que existem actualmente no buffer <strong>de</strong> cor. Esta combinaçãoé feita com base num conjunto <strong>de</strong> critérios.6.1.1 Combinação <strong>de</strong> CoresA terminologia utilizada para as cores é:• <strong>de</strong>stination color: Valores RGBA da cor que se encontra actualmente no buffer <strong>de</strong> cor• source color: Valores RGBA da cor do elemento que vai interagir com a <strong>de</strong>stination color58


A forma como estas duas componentes interagem é controlada pela equação <strong>de</strong> blending:C f = (C S ∗ S) + (C D ∗ D)Em que C f é a cor resultante, C S é a source color, C D é a cor <strong>de</strong> <strong>de</strong>stino, S e D são os factores <strong>de</strong> blending<strong>de</strong> origem e <strong>de</strong>stino. Estes factores são configurados com a função:glBlendFunc ( int source_function , int <strong>de</strong>stination_function );Os valores possíveis para os factores estão enumerados na Tabela 6.1.Function RGB Blend Factors Alpha Blend FactorGL ZERO (0, 0, 0) 0GL ONE (1, 1, 1) 1GL SRC COLOR (R S , G S , B S ) A SGL ONE MINUS SRC COLOR (1, 1, 1)–(R S , G S , B S ) 1–AsGL DST COLOR (R D , G D , B D ) A DGL ONE MINUS DST COLOR (1, 1, 1)–(Rd, Gd, Bd) 1–A DGL SRC ALPHA (A S , A S , A S ) A SGL ONE MINUS SRC ALPHA (1, 1, 1)–(As, As, As) 1–A SGL DST ALPHA (Ad, Ad, Ad) AdGL ONE MINUS DST ALPHA (1, 1, 1)–(Ad, Ad, Ad) 1–AdGL CONSTANT COLOR (Rc, Gc, Bc) AcGL ONE MINUS CONSTANT COLOR (1, 1, 1)–(Rc, Gc, Bc) 1–AcGL CONSTANT ALPHA (Ac, Ac, Ac) AcGL ONE MINUS CONSTANT ALPHA (1, 1, 1)–(Ac, Ac, Ac) 1–AcGL SRC ALPHA SATURATE (f, f, f) f=min(AS ,1−A D ) 1Tabela 6.1: Factores <strong>de</strong> Blending do OpenGLComo os valores RGBA são floats (0 ❀ 1) as operações <strong>de</strong> soma e subtracção continuam a gerar valoresválidos.Ao seleccionar uma das opções GL CONSTANT COLOR, GL ONE MINUS CONSTANT COLOR, GL CONSTANT ALPHA, eGL ONE MINUS CONSTANT ALPHA, po<strong>de</strong>mos introduzir uma cor constante no blending. A cor inicialmente é black(rgba(0, 0, 0, 0)), para alterar essa cor utilizamos:glBlendColor ( float red , float green , float blue , float alpha );Um exemplo comum <strong>de</strong> configuração da função <strong>de</strong> blending po<strong>de</strong>ria ser:glBlendFunc ( GL2 . GL_SRC_ALPHA , GL2 . GL_ONE_MINUS_SRC_ALPHA );Neste caso a source color (RGBA) seria multiplicada pelo seu valor alpha (GL SRC ALPHA). A <strong>de</strong>stinationcolor seria multiplicada por 1 menos o valor do seu alpha (GL ONE MINUS SRC ALPHA). O resultado final seria asoma das duas cores. Por exemplo:C S = rgba (0 , 0, 1, .5) ⇒ GL_SRC_ALPHA ⇒ S = 0.5C D = rgba (1 , 0, 0, 0) ⇒ GL_ONE_MINUS_SRC_ALPHA ⇒ D = 1 - 0.5Como C f = (C S ∗ S) + (C D ∗ D) temos:C f = ( Blue * 0.5) + ( Red * 0.5)Para o exemplo a cor final será uma mistura das duas cores (vermelho + azul). Quanto maior for o valor S,maior será o valor da source color na mistura.O blending é normalmente utilizado para conseguir a ilusão <strong>de</strong> transparência. Tal ilusão po<strong>de</strong> ser conseguida,ligando o blending ao <strong>de</strong>senhar o objecto transparente. O código em baixo utiliza a estrutura do Example0adicionando um segundo torus com blending:// Limpar o fundo da janela com a cor <strong>de</strong>finidagl. glClear ( GL2 . GL_COLOR_BUFFER_BIT | GL2 . GL_DEPTH_BUFFER_BIT );gl. glMaterialfv ( GL2 . GL_FRONT , GL2 . GL_SPECULAR , fBrightLight , 0);gl. glPushMatrix ();gl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_POSITION , fLightPos . toArray () , 0);59


Desenhar um Torus Ver<strong>de</strong>gl. glColor4f (0 , 1f, 0, .5f);drawWorld (gl );// Desenhar um Torus Vermelho com Blendinggl. glPushMatrix ();gl. glTranslatef (.1f, 0, .3f);gl. glEnable ( GL2 . GL_BLEND );gl. glBlendFunc ( GL2 . GL_SRC_ALPHA , GL2 . GL_ONE_MINUS_SRC_ALPHA );gl. glColor4f (1f, 0, 0,drawWorld (gl );.5f);gl. glDisable ( GL2 . GL_BLEND );gl. glPopMatrix ();gl. glColor4f (0.60f, .40f, .10f, .5f);GlUtil . drawGround ( FloorSize , 1f);gl. glPopMatrix ();O resultado do código em cima será o da Figura 6.2.Figura 6.2: Exemplo <strong>de</strong> BlendingModificando ligeiramente o código anterior, po<strong>de</strong>mos obter a sensação <strong>de</strong> reflexão:gl. glPushMatrix ();// Desenhar a luz Invertida ( mirror Y)gl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_POSITION , fLightPos . mirrorY (). toArray () , 0);// Desenhar a reflexao do Torusgl. glPushMatrix ();// Ao utiliza y=-1 estamos a inverter a imagemgl. glScalef (1f, -1f, 1f);gl. glFrontFace ( GL2 . GL_CW ); // Estamos InvertidosdrawWorld (gl );gl. glFrontFace ( GL2 . GL_CCW ); // Reporgl. glPopMatrix ();// Desenhar o Nosso chao , mas agora transparentegl. glDisable ( GL2 . GL_LIGHTING );gl. glEnable ( GL2 . GL_BLEND );gl. glBlendFunc ( GL2 . GL_SRC_ALPHA , GL2 . GL_ONE_MINUS_SRC_ALPHA );gl. glColor4f (.5f, .5f, .5f, .5f);GlUtil . drawGround ( FloorSize , 1f);60


gl. glDisable ( GL2 . GL_BLEND );gl. glEnable ( GL2 . GL_LIGHTING );// Desenhar a luz no local correctogl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_POSITION , fLightPos . toArray () , 0);// Desenhar um Torus Ver<strong>de</strong>gl. glColor4f (0 , 1f, 0, .5f);drawWorld (gl );gl. glPopMatrix ();O código acima <strong>de</strong>senha inicialmente o torus <strong>de</strong>baixo do chão com a geometria invertida sobre o eixo-y(glScalef(0, -1f, 0)). Porque a geometria está invertida é preciso re<strong>de</strong>finir a forma como é <strong>de</strong>terinhada aface dos polígonos, utilizando glFrontFace(GL CW). Da mesma forma o nosso ponto <strong>de</strong> luz tem que ser colocadona posição inversa, <strong>de</strong> forma a manter a iluminação dos objectos coerentes. Seguidamente activamos o blendinge <strong>de</strong>senhamos o nosso chão com um alpha <strong>de</strong> .5f. Finalmente repomos a posição da luz e <strong>de</strong>senhamos o toruscom a geometria em posição. O resultado obtido é o da Figura 6.3.Figura 6.3: Ilusão <strong>de</strong> Reflexão utilizando Blending6.1.2 Alteração da Equação <strong>de</strong> BlendingA equação mostrada anteriormente:C f = (C S ∗ S) + (C D ∗ D)é a queação por omissão. Po<strong>de</strong>mos, no entanto, escolher <strong>de</strong> entre um conjunto <strong>de</strong> equações possíveis para a<strong>de</strong>terminação do blending, utilizando o seguinte código:glBlendEquation ( int equation );As equações possíveis para a <strong>de</strong>terminação do blending são as <strong>de</strong>scritas na Tabela 6.2.Mo<strong>de</strong>FunctionGL FUNC ADD (<strong>de</strong>fault) C f = (C S × S) + (C D × D)GL FUNC SUBTRACT C f = (C S × S) − (C D × D)GL FUNC REVERSE SUBTRACT C f = (C S × D) − (C D × S)GL MIN C f = min(C S , C D )GL MAX C f = max(C S , C D )Tabela 6.2: Factores <strong>de</strong> Blending do OpenGL61


Da mesma forma que <strong>de</strong>ifinimos a função <strong>de</strong> blending para os valores RGBA source e <strong>de</strong>stination utilizandoglBlendFunc, po<strong>de</strong>mos utilizar a função glBlendFuncSeparate para especificar funções diferentes também parao cálculo dos factores alpha:glBlendFuncSeparate ( int srcRGB , int dstRGB , int srcAlpha , int dstAlpha );Sendo o srcRGB a função para o source color, o dstRGB a função para o <strong>de</strong>stination color. Os valoressrcAlpha e dstAlpha permitem <strong>de</strong>finir as funções para o source e <strong>de</strong>stination alpha respectivamente.6.2 AntialiasingNa maior parte dos casos, fragmentos individuais da nossa ren<strong>de</strong>rização, consistem <strong>de</strong> pixeis no ecrã. Estespixeis são quadrados (ou quase), po<strong>de</strong>ndo quebrar a sensação <strong>de</strong> realismo. O OpenGL permite <strong>de</strong>finir flags quemisturam as bordas <strong>de</strong> pontos, linhas e polígonos com a vizinhança, suavizando os contornos dos objectos.Para tal, é preciso activar primeiro o blending e <strong>de</strong>finir as funções para o source e <strong>de</strong>stination color. Depoispo<strong>de</strong>mos activar as flags: GL POINT SMOOTH (pontos), GL LINE SMOOTH (linhas) e GL POLYGON SMOOTH (polígonos).A Figura 6.4 mostra dois circulos, sendo o <strong>de</strong> fora <strong>de</strong>senhado sem antialiasing e o <strong>de</strong> <strong>de</strong>ntro <strong>de</strong>senhado comantialiasing.(a) Circulos Com/Sem Antialiasing(b) ZoomFigura 6.4: AntialiasingO código para conseguir o resultado da Figura 6.4(a) po<strong>de</strong>ria ser:gl. glClear ( GL2 . GL_COLOR_BUFFER_BIT );float x, y;gl. glLineWidth (5f);// Desenhar o Primeiro Circulo Sem Antialiasinggl. glBegin ( GL2 . GL_LINE_LOOP );for ( float a =0; a < 2f * GlTools . GL_PI ; a += .1f) {x = ( float ) Math . sin (a) / 2f;y = ( float ) Math . cos (a) / 2f;gl. glVertex3f (x, y, 0);}gl. glEnd ();// Activar o Blending / Antialiasing e Desenhar o Circulo <strong>de</strong> Dentrogl. glEnable ( GL2 . GL_BLEND );gl. glBlendFunc ( GL2 . GL_SRC_ALPHA , GL2 . GL_ONE_MINUS_SRC_ALPHA );// Suavizar as linhasgl. glEnable ( GL2 . GL_LINE_SMOOTH );62


gl. glHint ( GL2 . GL_LINE_SMOOTH_HINT , GL2 . GL_NICEST );// Desenhar o Circulo <strong>de</strong> Dentro com Antialiasinggl. glBegin ( GL2 . GL_LINE_LOOP );for ( float a =0; a < 2f * GlTools . GL_PI ; a += .1f) {x = ( float ) Math . sin (a) / 2.2 f;y = ( float ) Math . cos (a) / 2.2 f;gl. glVertex3f (x, y, 0);}gl. glEnd ();gl. glDisable ( GL2 . GL_LINE_SMOOTH );gl. glDisable ( GL2 . GL_BLEND );O código em cima <strong>de</strong>senha um circulo sem antialiasing, <strong>de</strong>pois activa o blending e activa o antialiasing paraas linhas (GL LINE SMOOTH) e <strong>de</strong>senha o circulo mais pequeno. Adicionalmente o algoritmo escolhido para oantialiasing foi o que tem melhor aspecto final utilizando glHint(GL NICEST). Os algoritmos disponíveis para ocálculo do antialiasing são GL NICEST e GL FASTEST, sendo que o último tem um resultado visual mais fracoainda que seja mais rápido.6.2.1 MultisamplingApesar do aspecto visual ser melhorado pela utilização <strong>de</strong> antialiasing, esta não é a melhor escolha para tornaros objectos na cena mais realísticos. O GL POLYGON SMOOTH não é suportado em todas as plataformas e comoutiliza o blending termiamos que <strong>de</strong>senhar todas as nossa primitivas da frente para trás.Uma adição ao OpenGL 1 permite en<strong>de</strong>reçar esta “problema”. O multisampling consiste num buffer adicional.Neste buffer os pixeis <strong>de</strong> cada primitiva são amostrados 2 várias vezes, sendo o resultado mostrado como umúnico pixel.Se o nosso contexto OpenGL suportar multisampling, po<strong>de</strong>mos activá-lo utilizando:glEnable ( GL2 . GL_MULTISAMPLE );Figura 6.5: Normal vs Multisampling6.3 Nevoeiro (Fog)O OpenGL suporta a adição <strong>de</strong> nevoeiro ao cenário. A técnica utilizada pelo OpenGL para dar a sensação <strong>de</strong>fog consiste no blending <strong>de</strong> uma cor específica com a geometria no final <strong>de</strong> toda a computação. A quantida<strong>de</strong><strong>de</strong> blending <strong>de</strong>ssa cor (alias: nevoeiro) com cada objecto do cenário <strong>de</strong>pen<strong>de</strong> da distância <strong>de</strong>sse objecto aoobservador.A introdução <strong>de</strong> nevoeiro po<strong>de</strong> tornar ainda mais real a sensação <strong>de</strong> profundida<strong>de</strong>, uma vez que a visibilida<strong>de</strong>dos objectos é menor à medida que se afastam, o que acontece no mundo real. Po<strong>de</strong>mos activar o nevoeiroutilizando:glEnable ( GL2 . GL_FOG );1 Para versões > 1.32 sampled63


Figura 6.6: NevoeiroA configuração do nevoeiro é feita utilizando uma das versões da função glFog:glFogi ( int pname , int param );glFogf ( int pname , float param );glFogiv ( int pname , int [] params , int offset );glFogfv ( int pname , float [] params , int offset );On<strong>de</strong> o primeiro argumento (pname) escolhe a proprieda<strong>de</strong> que estamos a alterar e o segundo argumento<strong>de</strong>fine o novo valor para a proprieda<strong>de</strong>.Para configurar o nevoeiro utilizando uma <strong>de</strong>terminada cor, que começa a afectar a geometria à distância 1fdo observador, terminando em 30f e utilizando uma variação linear utilizamos:float [] fLowLight = { 0.25f, 0.25f, 0.25f, 1.0 f };...// Definir a cor do Nevoeirogl. glFogfv ( GL2 . GL_FOG_COLOR , fLowLight , 0);// A que distancia os objectos comecao a ser afectadosgl. glFogf ( GL2 . GL_FOG_START , 1.0 f);// Ponto on<strong>de</strong> o nevoeiro toma conta completamentegl. glFogf ( GL2 . GL_FOG_END , 30.0 f);// Equacao do calculo do Nevoeirogl. glFogi ( GL2 . GL_FOG_MODE , GL2 . GL_LINEAR )Analisando em mais <strong>de</strong>talhe o código temos:gl. glFogfv ( GL2 . GL_FOG_COLOR , fLowLight , 0);No exemplo (Figura 6.6) po<strong>de</strong>mos observar que o nevoeiro toma a cor do fundo. A cor do nevoeiro é <strong>de</strong>finidaalterando o parâmetro GL FOG COLOR e fornecendo um array contendo os valores RGB.// A que distancia os objectos comecao a ser afectadosgl. glFogf ( GL2 . GL_FOG_START , 1.0 f);// Ate on<strong>de</strong> vai o nevoeirogl. glFogf ( GL2 . GL_FOG_END , 30.0 f);O parâmetro GL FOG START especifica a que distância do observador o nevoeiro começa a ter efeito sobre osobjectos. O parâmetro GL FOG END <strong>de</strong>fine o ponto a partir do qual o nevoeiro toma completamente a cor dosobjectos.// Equacao do calculo do Nevoeirogl. glFogi ( GL2 . GL_FOG_MODE , GL2 . GL_LINEAR )A transição entre o GL FOG START e o GL FOG END é controlada pelo GL FOG MODE, que no exemplo estáconfigurada para GL LINEAR. A fog equation <strong>de</strong>fine o factor <strong>de</strong> nevoeiro entre 0 e 1, à medida que o objecto se<strong>de</strong>sloca entre o inicio e o fim do nevoeiro.64


Fog Mo<strong>de</strong> Fog EquationGL LINEAR f = end–cend–startGL EXP f = e –d×cGL EXP2 f = e –(d×c)2Tabela 6.3: Fog EquationsA Tabela 6.4 <strong>de</strong>screve as equações para as várias equações possíveis.On<strong>de</strong> c é a distância ao observador, start é o valor <strong>de</strong> GL FOG START e end é o valor <strong>de</strong> GL FOG END. O valord é a <strong>de</strong>nsida<strong>de</strong> do nevoeiro, que po<strong>de</strong> ser configurada alterando o parâmetro GL FOG DENSITY:glFogf ( GL2 . GL_FOG_DENSITY ,0.5 f);Para GL LINEAR o valor da <strong>de</strong>nsida<strong>de</strong> não tem qualquer efeito, no entanto, para GL EXP e GL EXP2 este valoraltera o resultado final do nevoeiro.A Figura 6.7 mostra a forma como cada equação faz variar o nevoeiro entre o inicio e o fim para uma<strong>de</strong>nsida<strong>de</strong> <strong>de</strong> 0.5.Figura 6.7: Equações da Densida<strong>de</strong> do NevoeiroO modo como o OpenGL <strong>de</strong>termina a distância a profundida<strong>de</strong> do nevoeiro para cada elemento no cenáriopo<strong>de</strong> ser alterada utilizando o parâmetro GL FOG COORD SRC. Dois valores po<strong>de</strong>m ser utilizados:• GL FRAGMENT DEPTH: Utiliza a profundida<strong>de</strong> do objecto como valor da distância. Permite obter melhoresresultados.• GL FOG COORD (valor por omissão): Faz a interpolação do nevoeiro entre vértices, o que permite um cálculomais rápido.A alteração <strong>de</strong>ste parâmetro po<strong>de</strong> ser feita utilizando:glFogi ( GL2 . GL_FOG_COORD_SRC , GL2 . GL_FRAGMENT_DEPTH );// ouglFogi ( GL2 . GL_FOG_COORD_SRC , GL2 . GL_FOG_COORD );6.4 Accumulation BufferEm adição aos buffers <strong>de</strong> cor, profundida<strong>de</strong> e stencil 1 , o OpenGL contém um buffer especial que nos permitearmazenar os valores do buffer <strong>de</strong> cor em vez <strong>de</strong> os enviar para o ecrã. Um conjunto <strong>de</strong> operações permitemacumular várias iterações do buffer <strong>de</strong> cor, que po<strong>de</strong> então ser mostrado no ecrã.O comportamento do buffer <strong>de</strong> acumulação po<strong>de</strong> ser controlado pela função:glAccum ( int operation , float value );O primeiro argumento especifica a operação <strong>de</strong> acumulação a realizar e o segundo valor especifica um factor<strong>de</strong> escala para a operação. A operações possíveis estão enumeradas na Tabela ??.Devido à quantida<strong>de</strong> <strong>de</strong> operações <strong>de</strong> memória e processador necessários para realizar este tipo <strong>de</strong> operações,poucas aplicações em tempo-real utilizam este buffer. No entanto, os efeitos visuais conseguidos po<strong>de</strong>m ser <strong>de</strong>um realismo surpreen<strong>de</strong>nte. Por exemplo, é possível recriar vários tipos <strong>de</strong> blur que po<strong>de</strong>m simular os efeitos <strong>de</strong>profundida<strong>de</strong> encontrados nas câmaras <strong>de</strong> ví<strong>de</strong>o e fotográficas.O efeito na Figura 6.8 po<strong>de</strong> ser conseguido utilizando:1 A ser analisado futuramente65


OperationGL ACCUMGL LOADGL MULTGL ADDGL RETURNDescriptionEscala os valores actuais do buffer <strong>de</strong> cor e adiciona-os ao buffer <strong>de</strong> acumulaçãoEscala os valores actuais do buffer <strong>de</strong> cor e substitui os valores actuais do buffer <strong>de</strong> acumulaçãoEscala os valores actuais do buffer e armazena-os no buffer <strong>de</strong> acumulaçãoEscala os valores actuais do buffer e adiciona os aos valores actualmente no buffer <strong>de</strong> acumulaçãoEscala os valores actuais do buffer e armazena-os no buffer <strong>de</strong> corTabela 6.4: Fog EquationsFigura 6.8: Efeito blur utilizando buffer <strong>de</strong> acumulação66


float xoff = 0f;// Desenhar os objectos 10x movendo -os progressivamente para a esquerdafor ( int pass = 0; pass < 10; pass ++) {}xoff += .05 f;gl. glPushMatrix ();gl. glTranslatef (xoff , 0, 0);gl. glColor3f (0 , 1f, 0);drawWorld (gl );gl. glPopMatrix ();// Desenhar o chaogl. glColor4f (0.60f, .40f, .10f, .5f);GlUtil . drawGround ( FloorSize , 1f);if( pass == 0)gl. glAccum ( GL2 . GL_LOAD , 0.5 f); // Copiar buffer <strong>de</strong> corelse// Acumular o buffer <strong>de</strong> cor actual com o accum buffergl. glAccum ( GL2 . GL_ACCUM , 0.5 f * (.5 f / pass ));// Despejar o buffer <strong>de</strong> acumulacao no buffer <strong>de</strong> corgl. glAccum ( GL2 . GL_RETURN , 1.0 f);67


Capítulo 7Imagens no OpenGL7.1 BitmapsO aspecto visual <strong>de</strong> um bitmap não é o i<strong>de</strong>al, ainda que seja perfeitamente possível i<strong>de</strong>ntificar os objectos nelecontidos (Figura 7.1(a)). No OpenGL os bitmaps po<strong>de</strong>m ser utilizados para máscaras (polygon stippling), fontes ecaracteres e dithering a duas cores.(a) Bitmap(b) PixmapFigura 7.1: Imagem BitmapA imagem da Figura 7.1(b), contém 256 níveis <strong>de</strong> cinzento, sendo <strong>de</strong>nominada <strong>de</strong> pixmap.Figura 7.2: Imagem CampfirePo<strong>de</strong>mos <strong>de</strong>screver a imagem utilizada anteriormente para o stippling <strong>de</strong> polígonos (Figura 7.2), sob a forma<strong>de</strong> um array <strong>de</strong> 4 bytes × 32 (32 × 32 bit). Os valores no array <strong>de</strong> bytes estão em hexa<strong>de</strong>cimal. Como po<strong>de</strong>mosobservar, os valores no array estão invertidos, ou seja, a primeira linha do array representa a última linha dobitmap.68


ByteBuffer fire = ByteBuffer . wrap ( new byte [] {( byte ) 0x00 , ( byte ) 0x00 , ( byte ) 0x00 , ( byte ) 0x00 ,( byte ) 0x00 , ( byte ) 0x00 , ( byte ) 0x00 , ( byte ) 0x00 ,( byte ) 0x00 , ( byte ) 0x00 , ( byte ) 0x00 , ( byte ) 0x00 ,( byte ) 0x00 , ( byte ) 0x00 , ( byte ) 0x00 , ( byte ) 0x00 ,( byte ) 0x00 , ( byte ) 0x00 , ( byte ) 0x00 , ( byte ) 0x00 ,( byte ) 0x00 , ( byte ) 0x00 , ( byte ) 0x00 , ( byte ) 0x00 ,( byte ) 0x00 , ( byte ) 0x00 , ( byte ) 0x00 , ( byte ) 0xc0 ,( byte ) 0x00 , ( byte ) 0x00 , ( byte ) 0x01 , ( byte ) 0xf0 ,( byte ) 0x00 , ( byte ) 0x00 , ( byte ) 0x07 , ( byte ) 0xf0 ,( byte ) 0x0f , ( byte ) 0x00 , ( byte ) 0x1f , ( byte ) 0xe0 ,( byte ) 0x1f , ( byte ) 0x80 , ( byte ) 0x1f , ( byte ) 0xc0 ,( byte ) 0x0f , ( byte ) 0xc0 , ( byte ) 0x3f , ( byte ) 0x80 ,( byte ) 0x07 , ( byte ) 0xe0 , ( byte ) 0x7e , ( byte ) 0x00 ,( byte ) 0x03 , ( byte ) 0xf0 , ( byte ) 0xff , ( byte ) 0x80 ,( byte ) 0x03 , ( byte ) 0xf5 , ( byte ) 0xff , ( byte ) 0xe0 ,( byte ) 0x07 , ( byte ) 0xfd , ( byte ) 0xff , ( byte ) 0xf8 ,( byte ) 0x1f , ( byte ) 0xfc , ( byte ) 0xff , ( byte ) 0xe8 ,( byte ) 0xff , ( byte ) 0xe3 , ( byte ) 0xbf , ( byte ) 0x70 ,( byte ) 0x<strong>de</strong> , ( byte ) 0x80 , ( byte ) 0xb7 , ( byte ) 0x00 ,( byte ) 0x71 , ( byte ) 0x10 , ( byte ) 0x4a , ( byte ) 0x80 ,( byte ) 0x03 , ( byte ) 0x10 , ( byte ) 0x4e , ( byte ) 0x40 ,( byte ) 0x02 , ( byte ) 0x88 , ( byte ) 0x8c , ( byte ) 0x20 ,( byte ) 0x05 , ( byte ) 0x05 , ( byte ) 0x04 , ( byte ) 0x40 ,( byte ) 0x02 , ( byte ) 0x82 , ( byte ) 0x14 , ( byte ) 0x40 ,( byte ) 0x02 , ( byte ) 0x40 , ( byte ) 0x10 , ( byte ) 0x80 ,( byte ) 0x02 , ( byte ) 0x64 , ( byte ) 0x1a , ( byte ) 0x80 ,( byte ) 0x00 , ( byte ) 0x92 , ( byte ) 0x29 , ( byte ) 0x00 ,( byte ) 0x00 , ( byte ) 0xb0 , ( byte ) 0x48 , ( byte ) 0x00 ,( byte ) 0x00 , ( byte ) 0xc8 , ( byte ) 0x90 , ( byte ) 0x00 ,( byte ) 0x00 , ( byte ) 0x85 , ( byte ) 0x10 , ( byte ) 0x00 ,( byte ) 0x00 , ( byte ) 0x03 , ( byte ) 0x00 , ( byte ) 0x00 ,( byte ) 0x00 , ( byte ) 0x00 , ( byte ) 0x10 , ( byte ) 0 x00});Os bitmaps são <strong>de</strong>senhados no ecrã recorrendo ao raster dos seus bits. Precisamos então <strong>de</strong> dizer ao OpenGLqual será a posição em que <strong>de</strong>ve ser feito o raster da nossa imagem, com recurso à função glRasterPos. A funçãoglRasterPos é po<strong>de</strong> receber 2 ou 3 argumentos, sendo que na primeira o eixo z toma o valor 0. O exemplomostra uma possível utilização da função:gl. glClear ( GL2 . GL_COLOR_BUFFER_BIT | GL2 . GL_DEPTH_BUFFER_BIT );// Bitmap <strong>de</strong> cor Ver<strong>de</strong>gl. glColor3f (0.0f, 1.0f, 0.0 f);for ( int i =0; i < 16; i ++) {// Definir coor<strong>de</strong>nadas do Rastergl. glRasterPos3d (i - 5, 1, i - 5);// Desenhar o bitmap 32 x32 no array <strong>de</strong> bytesgl. glBitmap (32 , 32 , 0f, 0f, 0, 0, fire );}gl. glColor3f (.5f, .5f, .5f);GlUtil . drawGround ( GL2 . GL_LINE_STRIP ,20f, 1f);No exemplo em cima, são <strong>de</strong>senhados 16 bitmaps percorrendo uma diagonal que atravessa o ponto (0, 0, 0).O nosso bitmap é <strong>de</strong>senhado utilizando a função:glBitmap (int width , int height ,float xorig , float yorig ,float xmove , float ymove ,ByteBuffer bitmap );O width e height referem-se ao tamanho em bits do bitmap, no nosso caso 32×32, o xorig e o yorigreferem-se ao offset x e y em relação à posição raster, o xmove e o ymove <strong>de</strong>finem a posição <strong>de</strong> raster quandoo <strong>de</strong>senho terminar e o parâmetro bitmap contém os dados a <strong>de</strong>senhar. Os bits a 1 no nosso bitmap serão69


transformados em fragmentos (pixel) com a cor actualmente <strong>de</strong>finida, os 0 não afectaram as cores actualmenteno color buffer.O resultado do código em cima po<strong>de</strong> ser visualizado na Figura 7.3.Figura 7.3: Utilização <strong>de</strong> glBitmap/glRasterPosAs coor<strong>de</strong>nadas da função glRasterPos são afectadas pela projecção e posição do observador. Se quisermosque o nosso bitmap seja mostrado sempre na mesma posição no ecrã, in<strong>de</strong>pen<strong>de</strong>ntemente do projecção eobservador, <strong>de</strong>vemos utilizar a função glWindowPos, por exemplo:gl. glColor3f (1.0f, 0.0f, 0.0 f);// colocar o raster o canto inferior esquerdo do ecragl. glWindowPos2i (10 , 10);gl. glBitmap (32 , 32 , 0f, 0f, 0, 0, fire );7.2 PixmapsOs pixmaps são <strong>de</strong> maior utilida<strong>de</strong> nos sistemas full-color. O seu layout na memória é parecido ao dos bitmaps,no entanto, cada pixel po<strong>de</strong> conter mais do que um bit <strong>de</strong> informação. Cada pixel po<strong>de</strong> conter informação acercada intensida<strong>de</strong> (referida normalmente como luminance) ou acerca dos componentes <strong>de</strong> cor.Um pixmap po<strong>de</strong> ser <strong>de</strong>senhado a partir da posição actual do raster utilizando a função:void glDrawPixels (int width , int height ,int format , int type ,Buffer pixels )Os primeiros dois argumentos (width/height) referem-se ao tamanho do pixmap em pixels. O argumentoformat refere-se à forma como a informação está disposta, normalmente referido como formato da imagem (verTabela B.1). O argumento type <strong>de</strong>fine o tipo <strong>de</strong> dados na informação e o último argumento contém a informaçãoda imagem 1 . Ao contrário da função glBitmap, a posição <strong>de</strong> raster não é alterada.Os formatos GL STENCIL INDEX e GL DEPTH COMPONENT, são utilizados para ler e escrever informação nosstencil e <strong>de</strong>pth buffers.O argumento type <strong>de</strong>fine o tipo <strong>de</strong> dados armazenados no nosso pixmap. Os valores possíveis para o tipo <strong>de</strong>dados estão <strong>de</strong>scritos na Tabela B.2 2 .1 No JOGL, em vez <strong>de</strong> arrays é recomendada a utilização <strong>de</strong> buffers - http://java.sun.com/<strong>de</strong>veloper/technicalArticles/releases/nio/2 Na tabela estão omissos os packed RGB value, para mais informação consultar - http://www.opengl.org/registry/specs/EXT/packed_pixels.txt70


O exemplo em baixo utiliza numa primeira instância o ClassLoa<strong>de</strong>r 1 do Java para ler um ficheiro e a classeTGAImage para utilizar o ficheiro como uma imagem (targa neste exemplo). Posteriormente o método getDatada classe TGAImage permite-nos obter uma referência para a informação (pixmap) contida na imagem sob aforma <strong>de</strong> um ByteBuffer.class Pixmap implements GLEventListener {}private TGAImage img ;private ByteBuffer bb;// ...@Overri<strong>de</strong>public void init ( GLAutoDrawable drawable ) {GL2 gl = drawable . getGL (). getGL2 ();// ...try {InputStream stream = getClass (). getResourceAsStream (" fire . tga ");img = TGAImage . read ( stream );bb = img . getData ();stream . close ();} catch ( IOException e) {// Tratar do erro ...}}// ...O bloco try { ... } catch(Exception e) { ... } permite ao nosso programa estar preparado no casoda leitura do ficheiro não ser bem sucedida. Este bloco é <strong>de</strong>nominado <strong>de</strong> exception handling no Java 2 .Dentro do try o nosso programa lê o ficheiro fire.tga que <strong>de</strong>verá estar contido no mesmo package da classePixmap.Basta agora no método display() da nossa classe mostrar o pixmap <strong>de</strong> acordo com a posição do raster.// Definir a posicao do rastergl. glRasterPos2i ( img . getWidth ()*3/4 , img . getHeight ()*3/4);// Redimensionar e inverter a imagemgl. glPixelZoom ( -.5f, -.5f);// Desenhar a Imagem no ByteBuffergl. glDrawPixels (img . getWidth () , img . getHeight () , // Tamanho da imagemimg . getGLFormat () , // Pixel FormatGL2 . GL_UNSIGNED_BYTE , bb );// Restaurargl. glPixelZoom (1f, 1f);O resultado do código em cima, po<strong>de</strong> ser visualizado na Figura 7.4. A função glPixelZoom(float xzoom,float yzoom) permite alterar a escala utilizada para <strong>de</strong>senhar os pixels. Utilizando xzoom/yzoom negativos,estamos a inverter a or<strong>de</strong>m utilizada para <strong>de</strong>senhar os pixels, pelo que a posição do raster tem que ser reajustada.7.3 Operações com PixelsAté agora temos estado a escrever para os buffers (color, <strong>de</strong>pth, . . . ), no entanto po<strong>de</strong> ser útil ler a informaçãocontida nestes buffers ou mesmo copiá-la entre eles. A informação para ler a informação dos pixels funcionacomo a função glDrawPixels só que no sentido inverso.glReadPixels (int x, int y,int width , int height ,int format , int type ,Buffer pixels )1 Ver <strong>de</strong>talhes em http://download.oracle.com/javase/6/docs/technotes/gui<strong>de</strong>s/lang/resources.html2 Ver mais <strong>de</strong>talhes em http://download.oracle.com/javase/tutorial/essential/exceptions/71


Figura 7.4: Utilização <strong>de</strong> glDrawPixels/glPixelZoomOs argumentos x, y, width e height, <strong>de</strong>finem o rectângulo a copiar 1 . O format e type são o formato e otipo <strong>de</strong> dados utilizados para armazenar o conteúdo do buffer na variável <strong>de</strong> saída pixels. In<strong>de</strong>pen<strong>de</strong>ntementeda forma como a informação está armazenada no color buffer, o OpenGL encarrega-se <strong>de</strong> fazer as conversõesnecessárias.Po<strong>de</strong> ser útil ainda copiar informação <strong>de</strong>ntro do mesmo buffer. Para tal <strong>de</strong>finimos o canto inferior esquerdo(com glRasterPos/glWindowPos) da zona a copiar. A função para efectuar a cópia é:glCopyPixels (int x, int y,int width , int height ,int type);Mais uma vez, o x, y, width e height, <strong>de</strong>finem o rectângulo <strong>de</strong> origem. O argumento type diz o que estamosa copiar, po<strong>de</strong>ndo tomar o valor GL COLOR para copiar do color buffer, GL DEPTH para copiar do <strong>de</strong>pth buffer ouGL STENCIL para copiar do stencil buffer.Por omissão estas operações <strong>de</strong> cópia realizam-se no back buffer em contextos com suporte para doublebuffering, ou no front buffer em contextos com single buffer. Para alterar a fonte ou <strong>de</strong>stino para a cópia,utilizamos:glDrawBuffer ( int <strong>de</strong>stination );glReadBuffer ( int source );O argumento <strong>de</strong>stination da função glDrawBuffer, especifica on<strong>de</strong> os pixels são escritos pelas operaçõesglDrawPixels/glCopyPixels. O argumento source da função glReadBuffer especifica a fonte para os pixelslidos nas operações glReadPixels/glCopyPixels. Os valores possíveis para os argumentos source/<strong>de</strong>stinationpo<strong>de</strong>rão ser: GL NONE, GL FRONT, GL BACK, GL FRONT AND BACK, GL FRONT LEFT, GL FRONT RIGHT, . . .Um caso prático <strong>de</strong> utilização das operações com pixels seria, por exemplo, armazenar o conteúdo do colorbuffer actual num ficheiro <strong>de</strong> imagem. A fim <strong>de</strong> ilustrar os conceitos explicados nesta secção, exemplificamoscomo isso po<strong>de</strong>ria ser feito:// Ler o valor READ BUFFER actual// ( para repor mais tar<strong>de</strong> )IntBuffer lastBuffer = IntBuffer . allocate (1);gl. glGetIntegerv ( GL2 . GL_READ_BUFFER , lastBuffer );// Vamos ler o FRONT BUFFER1 sendo x e y o canto inferior esquerdo72


gl. glReadBuffer ( GL2 . GL_FRONT );// O ByteBuffer output ira armazenar temporariamente// a nossa imagemByteBuffer output = ByteBuffer . allocate ( width * height * 3); // RGBgl. glReadPixels (0, 0, // Canto inferior esquerdowidth , height , // Tamanho a lerGL2 . GL_BGR , // Formato Blue , Green , Red ( targa )GL2 . GL_UNSIGNED_BYTE , // Tipo <strong>de</strong> dados unsigned byteoutput);// Criar um TGA a partir do ByteBufferTGAImage img = TGAImage . createFromData ( width , height , false , false , output );try {img . write ("c:/ my.tga "); // Guardar a imagem no disco} catch ( IOException e) {// Falha ao escrever o ficheiro}// Restaurar o read buffer anteriorgl. glReadBuffer ( lastBuffer . get (0));A função glGet(int pname, ...) 1 permite-nos obter informação das variáveis do OpenGL (nestecaso o buffer <strong>de</strong> leitura actual - GL READ BUFFER). No JOGL a maior parte das funções que retornam valores,utilizam subclasses <strong>de</strong> Buffer (neste caso IntBuffer) para os armazenar. É importante no nosso códigoOpenGL, restaurarmos, na medida do possível, o estado anterior das coisas, daí obtermos o lastBuffer e serfeita a sua reposição no fim do bloco <strong>de</strong> código, utilizando glReadBuffer(lastBuffer.get(0)). Seguidamenteespecificamos que o buffer para leitura será o que se encontra visível (GL FRONT):gl. glReadBuffer ( GL2 . GL_FRONT );Inicializamos um ByteBuffer com o tamanho da janela (width×height) multiplicado por 3, uma vez quevamos armazenar as três componentes <strong>de</strong> cor RGB, ficando então width×height×3.ByteBuffer output = ByteBuffer . allocate ( width * height * 3);Os ficheiros targa armazenam a informação da cor na or<strong>de</strong>m BGR (Blue, Green, Red), pelo que o formatodos dadosm especificado é GL BGR. O seguinte código copia o conteúdo do front buffer e coloca-o no nossoByteBuffer.gl. glReadPixels (0, 0, // Canto inferior esquerdowidth , height , // Tamanho a lerGL2 . GL_BGR , // Formato Blue , Green , Red ( targa )GL2 . GL_UNSIGNED_BYTE , // Tipo <strong>de</strong> dados unsigned byteoutput);Finalmente, criamos uma instância <strong>de</strong> TGAImage com base no ByteBuffer output, utilizando a função:TGAImage . createFromData (int width , int height , // Tamanho da imagemboolean hasAlpha , // Formato contem alphaboolean topToBottom , // A imagem esta armazenada "ao contrario "ByteBuffer output // ByteBuffer com os dados);Esta função é utilizada da seguinte forma:TGAImage img = TGAImage . createFromData ( width , height , false , false , output );Finalmente para armazenar a nossa imagem no disco fazemos:img . write ("c:/ my.tga ")A imagem my.tga irá conter o conteúdo do nosso front buffer.1 http://www.opengl.org/sdk/docs/man/xhtml/glGet.xml73


7.4 Outras Operações com ImagensPara além das operações <strong>de</strong>scritas, o OpenGL suporta ainda um conjunto <strong>de</strong> operações especiais aquando datransferência da informação dos pixels para e do color buffer.Abaixo discutimos algumas das operações possíveis, bem como o código necessário para consegui-las (códigocompleto no Apêndice C.2). O progama inicial para a <strong>de</strong>monstração <strong>de</strong>sta funcionalida<strong>de</strong> utiliza uma projecçãoortogonal configurada com a função:glOrtho2D (double left , double right ,double bottom , double top);Em que left/right são as coor<strong>de</strong>nadas dos clipping planes verticais, e bottom/top são as coor<strong>de</strong>nadasdos clipping planes horizontais. Os valores <strong>de</strong> zNear/zFar especificados na função glOrtho tomam os valores0 e 1 respectivamente.A nossa projecção ortogonal irá coincidir com o tamanho da janela e do viewport. O método reshape(...)terá o seguinte aspecto:public void reshape ( GLAutoDrawable drawable , int x, int y, int width , int height ) {// Viewport coinci<strong>de</strong> com a janelagl. glViewport (0 , 0, width , height );gl. glMatrixMo<strong>de</strong> ( GL2 . GL_PROJECTION );gl. glLoadI<strong>de</strong>ntity ();// Configurar a projeccao ortogonalglu . gluOrtho2D (0 , width , 0, height );}// ...À semelhança do que aconteceu com o exemplo na secção anterior, será carregado o ficheiro horse.tga comrecurso ao ClassLoa<strong>de</strong>r do Java e à função TGAImage.read(...) para a variável <strong>de</strong> classe img e lidos os bytespara um ByteBuffer (bb).A imagem (Figura 7.5) será mostrada no método display(...) utilizando:gl. glDrawPixels (img . getWidth () , img . getHeight () ,img . getGLFormat () ,GL2 . GL_UNSIGNED_BYTE , bb );Figura 7.5: Imaging.java mostranto horse.tgaOs blocos <strong>de</strong> código seguinte mostram como po<strong>de</strong>mos implementar os efetios GaussianBlur, Sharpen, Emboss,Invert e Brighten. Alguns dos efeitos são aplicados utilizando matrizes <strong>de</strong> convolução, que permitem obter ovalor <strong>de</strong> cor <strong>de</strong> um pixel com base nos que se encontram nas suas imediações 1 .O OpenGL permite aplicar este tipo <strong>de</strong> matrizes às imagens utilizando a função:1 Mais <strong>de</strong>talhes e exemplos em http://manual.gimp.org/en/plug-in-convmatrix.html74


glConvolutionFilter2D (int target , int internalFormat ,int width , int height ,int format , int type ,Buffer image );Em que o target toma o valor GL2.GL CONVOLUTION 2D, o internalFormat indica o formato interno danossa matriz <strong>de</strong> convolução (para o nosso caso GL2.GL RGB), width/height especificam o tamanho da matriz,format indica o formato dos pixels na imagem <strong>de</strong> <strong>de</strong>stino, type especifica o tipo <strong>de</strong> dados da imagem e imagecontém a informação da imagem.Gaussian BlurPara aplicar o efeito <strong>de</strong> gaussian blur (Figura 7.6) po<strong>de</strong>mos utilizar a seguinte matriz <strong>de</strong> convolução:⎛1 2 1⎞⎝ 2 4 2 ⎠1 2 1Para tal utilizamos o seguinte código:// Matriz ( convolution kernel ) para efectuar gaussian blurfloat norm = 16f; // normalizacao : somatorio dos valores da matrizFloatBuffer mGaussian = FloatBuffer . wrap ( new float [] {1f/norm , 2f/norm , 1f/norm ,2f/norm , 4f/norm , 1f/norm ,1f/norm , 2f/norm , 1f/ norm});// Aplicar a matrizgl. glConvolutionFilter2D (GL2 . GL_CONVOLUTION_2D , GL2 . GL_RGB , 3, 3,GL2 . GL_LUMINANCE , GL2 . GL_FLOAT , mGaussian);// Activar convolucao 2Dgl. glEnable ( GL2 . GL_CONVOLUTION_2D );// Mostrargl. glDrawPixels (img . getWidth () , img . getHeight () ,img . getGLFormat () , GL2 . GL_UNSIGNED_BYTE , bb );// Desactivar convolucao 2Dgl. glDisable ( GL2 . GL_CONVOLUTION_2D );Figura 7.6: Gaussian BlurA forma <strong>de</strong> aplicar outras matrizes é semelhante. Apenas mudamos o último argumento da funçãoglConvolutionFilter2D <strong>de</strong> mGaussian para a matriz criada. Como tal interessa apenas referir quais asmatrizes utilizadas e como foram inicializadas.75


EmbossA matriz utilizada para o efeito emboss (Figura 7.7) po<strong>de</strong>rá ser:⎛2 0 0⎞⎝ 0 −1 0 ⎠0 0 −1Inicializada com:// Matriz ( convolution kernel ) para efectuar embossFloatBuffer mEmboss = FloatBuffer . wrap ( new float [] {2.0f, 0.0f, 0.0f,0.0f, -1.0f, 0.0f,0.0f, 0.0f, -1.0f});Figura 7.7: EmbossSharpenA matriz utilizada para o efeito sharpen (Figura 7.8) po<strong>de</strong>rá ser:⎛0 −1 0⎞⎝ −1 5 −1 ⎠0 −1 0Inicializada com:// Matriz ( convolution kernel ) para efectuar o sharpenFloatBuffer mSharpen = FloatBuffer . wrap ( new float [] {0.0f, -1.0f, 0.0f,-1.0f, 5.0f, -1.0f,0.0f, -1.0f, 0.0 f});BrightenA operação <strong>de</strong> brighten na imagem po<strong>de</strong> ser implementada <strong>de</strong> forma simples. Mudando para a matriz <strong>de</strong> corutilizando:gl. glMatrixMo<strong>de</strong> ( GL2 . GL_COLOR );... po<strong>de</strong>mos utilizar a função glScale uma vez que as transformações são agora operadas na matriz <strong>de</strong> cor,como mostra o exemplo:76


Figura 7.8: Sharpen// Mudar para o matrix mo<strong>de</strong> Colorgl. glMatrixMo<strong>de</strong> ( GL2 . GL_COLOR );// Aumentar todas as cores em 50%gl. glScalef (2f, 2f, 2f);// Voltar ao Mo<strong>de</strong>lViewgl. glMatrixMo<strong>de</strong> ( GL2 . GL_MODELVIEW );77


Capítulo 8Mapeamento <strong>de</strong> TexturasNa computação gráfica convencional, cada elemento <strong>de</strong> um pixmaps correspon<strong>de</strong> a um elemento no ecrã, daí onome <strong>de</strong> picture element (alias pixel). No entanto, quando mapeados a primitivas geométricas, raramente estespixmaps têm uma proporção <strong>de</strong> 1 para 1.Figura 8.1: Mapeamento <strong>de</strong> TexturasA aplicação <strong>de</strong> imagens à geometria é referida como texture ou texture mapping. A utilização <strong>de</strong> texturaspo<strong>de</strong> aumentar dramaticamente o realismo e a qualida<strong>de</strong> dos nossos cenários.8.1 Carregamento <strong>de</strong> TexturasO primeiro passo na aplicação <strong>de</strong> texturas à nossa geometria, consiste no carregamento da imagem correspon<strong>de</strong>ntepara a memória. Esta imagem passa a ser consi<strong>de</strong>rada o “estado actual” da textura. O carregamento <strong>de</strong> imagenssob a forma <strong>de</strong> texturas po<strong>de</strong> ser feito utilizando uma das três versões da função glTextImage:glTexImage1D (int target , int level , int internalFormat ,int width , int bor<strong>de</strong>r ,int format , int type ,Buffer pixels);glTexImage2D (int target , int level , int internalformat ,int width , int height , int bor<strong>de</strong>r ,int format , int type ,Buffer pixels);glTexImage3D (78


int target , int level , int internalformat ,int width , int height , int <strong>de</strong>pth , int bor<strong>de</strong>r ,int format , int type ,Buffer pixels);Estas três funções permitem-nos dizer ao OpenGL tudo aquilo que ele precisa <strong>de</strong> saber relativamente à imagemcontida no array pixels. A função glTextImage carrega para a memória, a imagem fornecida e torna-a atextura actual a ser aplicada na geometria. Este carregamento para a memória po<strong>de</strong> ser dispendioso (alternativasdiscutidas mais à frente na Secção 8.6).O valor do target para cada uma das funções é respectivamente : GL TEXTURE 1D, GL TEXTURE 2D eGL TEXTURE 3D ou GL PROXY TEXTURE 1D, GL PROXY TEXTURE 2D ou GL PROXY TEXTURE 3D. Ao utilizar proxies,o OpenGL faz uma verificação prévia do que o sistema suporta em termos <strong>de</strong> compatibilida<strong>de</strong>, no caso <strong>de</strong> nãohaver suporte, o estado da imagem é tornado inválido sem que qualquer tipo <strong>de</strong> erro seja reportado 1 .O argumento level <strong>de</strong>fine o nível <strong>de</strong> mipmapping (discutido na Secção 8.5.3), <strong>de</strong> momento utilizaremostexturas sem mipmapping, especificando o valor 0.O argumento internalFormat, especifica quantos componentes <strong>de</strong> cor são armazenados por texel, o espaçonecessário ao armazenamento dos componentes e/ou se a textura é comprimida. A Tabela 8.1 lista os valoresmais comuns para este parâmetro.ConstanteGL ALPHAGL LUMINANCEGL LUMINANCE ALPHAGL RGBGL RGBAArmazenar os texels como:Valores <strong>de</strong> alphaValores <strong>de</strong> luminânciaValores <strong>de</strong> luminância e alphaComponentes <strong>de</strong> red, green e blueComponentes <strong>de</strong> red, green, blue e alphaTabela 8.1: Factores <strong>de</strong> Blending do OpenGLOs valores <strong>de</strong> width, height e <strong>de</strong>pth (apropriadamente) especificam as dimensões da textura que estamosa carregar para a memória. Estas dimensões <strong>de</strong>vem ser inteiros com base 2 (2 x = 1, 2, 4, 8, . . . ), isto nãoimplica que o mapeamento da textura seja quadrado, mas um valor não 2 x provovará que o mapeamento sejaimplicitamente <strong>de</strong>sabilitado.O argumento bor<strong>de</strong>r especifica a largura da borda à volta da textura. Esta borda permite acrescentartexels adicionais à textura.Os argumentos format, type, têm o mesmo objectivo que quando utilizados na função glDrawPixels. Oargumento pixels contêm a informação da imagem propriamente dita.Para aplicarmos textura à nossa geometria temos que activar o seu estado apropriado. Para tal utilizamosas funções glEnable/glDisable com as constantes GL TEXTURE 1D, GL TEXTURE 2D e GL TEXTURE 3D 2 , comoilustra o exemplo:glDisable ( GL2 . GL_TEXTURE_1D );glEnable ( GL2 . GL_TEXTURE_2D );8.1.1 Pipeline do Carregamento <strong>de</strong> ImagensO carregamento <strong>de</strong> imagens no OpenGL (por ex.: utilizando glTexImage) obe<strong>de</strong>ce a um pipeline <strong>de</strong> processamentoà semelhança do pipeline <strong>de</strong> transformações do OpenGL.As operações <strong>de</strong> convolução, pixel zoom, pixel packing, . . . (discutidas no capítulo anterior) são aplicadassegundo a Figura 8.2.8.1.2 Utilizando o Color BufferÉ possível carregar texturas 1D e 2D a partir do color buffer, utilizando as seguintes funções:glCopyTexImage1D (int target , int level , int internalformat ,int x, int y, int width ,int bor<strong>de</strong>r);1 Para verificar se foi bem sucedido utiliza-se a função glGetTexParameter2 É boa i<strong>de</strong>ia <strong>de</strong>sactivar os estados não utilizados, uma vez que apenas um <strong>de</strong>stes estados po<strong>de</strong> existir em simultâneo79


Figura 8.2: Pipeline - Carregamento <strong>de</strong> ImagensglCopyTexImage2D (int target , int level , int internalformat ,int x, int y, int width , int height ,int bor<strong>de</strong>r);O significado dos argumentos é semelhante aos argumentos da função glTexImage, mas neste caso o x, y,width e height especificam a área no color buffer a ler. O source buffer po<strong>de</strong> ser <strong>de</strong>finido utilizando a funçãoglReadBuffer.8.1.3 Actualização <strong>de</strong> TexturasO carregamento repetido <strong>de</strong> texturas po<strong>de</strong> ser dispendioso em termos <strong>de</strong> processamento. Substituir uma<strong>de</strong>terminada textura po<strong>de</strong> ser mais rápido, na maior parte das vezes do que o carregamento utilizando glTexImage.Para tal utilizamos as funções:glTexSubImage1D (int target , int level ,int xoffset ,int width ,int format , int type , Buffer data);glTexSubImage2D (int target , int level ,int xoffset , int yoffset ,int width , int height ,int format , int type , Buffer data);glTexSubImage3D (int target , int level ,int xoffset , int yoffset , int zoffset ,int width , int height , int <strong>de</strong>pth ,int format , int type , Buffer data);Mais uma vez, os argumentos são semelhantes aos utilizados em glTexImage. O xoffset, yoffset e zoffsetespecificam o local na textura actual para começar a fazer a substituição. O tamanho da textura no argumentodata é especificado pelos argumentos width, height e <strong>de</strong>pth.Finalmente um conjunto final <strong>de</strong> funções permite-nos substituir uma parte <strong>de</strong> uma textura utilizandoinformação extraída directamente do color buffer:glCopyTexSubImage1D (int target , int level ,int xoffset ,int x, GLint y,int width);glCopyTexSubImage2D (int target , int level ,int xoffset , int yoffset ,80


int x, GLint y,int width , int height);glCopyTexSubImage3D (int target , int level ,int xoffset , int yoffset , int zoffset ,int x, GLint y,int width);Po<strong>de</strong>m ter reparado que não existe uma função glCopyTexImage3D, isto <strong>de</strong>ve-se ao facto do color bufferser bi-dimensional. No entanto po<strong>de</strong>mos querer copiar informação do color buffer para uma face (plano?) emespecífico da nossa textura 3D, isso po<strong>de</strong> ser conseguido utilizando glCopyTexSubImage3D.8.2 Mapeamento <strong>de</strong> Texturas à GeometriaAo activar o mapeamento <strong>de</strong> texturas, o OpenGL permite-nos mapear qualquer textura a qualquer uma dasprimitivas. No entanto, é necessário fornecer ao OpenGL informação acerca <strong>de</strong> como esse mapeamento é feito, oque é conseguido especificando uma coor<strong>de</strong>nada na textura (texture coordinate) para cada vértice. Os texels 1não são especificados utilizando posições <strong>de</strong> memória, tal como nos pixmaps, mas com um sistema <strong>de</strong> coor<strong>de</strong>nadasmais abstracto. As coor<strong>de</strong>nadas das texturas são normalmente especificadas utilizando valores entre 0.0 e 1.0.Os nomes das coor<strong>de</strong>nadas são s, t, r e q (semelhantes á coor<strong>de</strong>nadas vectoriais x, y, z e w). A Figura 8.3mostra como os texels são especificados com base em coor<strong>de</strong>nadas 1D, 2D e 3D.Figura 8.3: Coor<strong>de</strong>nadas dos texels numa texturaO factor q representa um factor <strong>de</strong> escala da textura, à semelhança do valor w dos vértices. Ou seja, osvalores das coor<strong>de</strong>nadas das texturas são realmente s/q, t/q e r/q. Por omissão q=1.0.A coor<strong>de</strong>nada na textura é especificada utilizando a função glTexCoord. Esta função tem um conjunto <strong>de</strong>variações para vários objectivos, à semelhança das funções em geral do OpenGL. Algumas formas simples <strong>de</strong>stasfunções são:glTexCoord1f ( float s);glTexCoord2f ( float s, float t);glTexCoord3f ( float s, float t, float r);A coor<strong>de</strong>nada da textura é aplicada utilizando estas funções para cada um dos vértices. O OpenGL estica ouencolhe (alias stretching) a textura <strong>de</strong> forma a aplicá-la na geometria especificada com base nas coor<strong>de</strong>nadasespecíficadas. A forma como o stretching é feito <strong>de</strong>pen<strong>de</strong> do filtro da textura (texture filter) actual, discutidomais à frente. A Figura 8.4(a) mostra o exemplo <strong>de</strong> ume textura bi-dimensional aplicada a um GL QUAD. Noscantos do quad temos as coor<strong>de</strong>nadas correspon<strong>de</strong>ntes da textura. A função glTexCoord tem que ser utilizadaantes <strong>de</strong> cada chamada à função glVertex.Esta coincidência entre geometria e textura é rara. Um exemplo mais comum será o da Figura 8.4(b). Ascoor<strong>de</strong>nadas da textura neste caso não coinici<strong>de</strong>m com a forma da geometria.1 Um texel é um fragmento da textura, tal como o pixel <strong>de</strong> um pixmap81


(a) Textura bi-dimensional aplicada a umquad(b) Textura bi-dimensional aplicada a umtriânguloFigura 8.4: Aplicação <strong>de</strong> textura bi-dimensionalTexture MatrixÀ semelhança da matriz MODELVIEW, PROJECTION e COLOR, é também possível operar transformações na matriz<strong>de</strong> coor<strong>de</strong>nadas das texturas, po<strong>de</strong>mos fazê-lo passando para a texture matrix:glMatrixMo<strong>de</strong> ( GL2 . GL_TEXTURE );Depois <strong>de</strong>sta chamada todas as transformações são operadas nas coor<strong>de</strong>nadas da textura. No entanto,a stack <strong>de</strong> tranformações só tem dois níveis <strong>de</strong> profundida<strong>de</strong>, o que implica que só po<strong>de</strong>mos utilizar doisglPushMatrix/glPopMatrix enca<strong>de</strong>ados.8.3 Exemplo <strong>de</strong> Textura 2DExiste um conjunto <strong>de</strong> conceitos ainda por adquirir relacionados com as texturas, tais como coordinate wrapping,texture filters e texture environment. A fim <strong>de</strong> os abordar utilizamos como plataforma um exemplo simples 2D,com configurações comuns <strong>de</strong>stes parâmetros. O código fonte do exemplo Pyramid.java po<strong>de</strong> ser consultado noApêndice C.3.Figura 8.5: Exemplo pyramidA Figura 8.5 ilustra a posição dos cantos da pirâmi<strong>de</strong>. A <strong>de</strong>claração será feita no método init() do nossoGLEventListener da seguinte forma:// Cantos da Pirami<strong>de</strong>cTop = new GLVector (0.0f, .80f, 0.0 f);cBackLeft = new GLVector ( -0.5f, 0.0f, -.50f);cBackRight = new GLVector (0.5f, 0.0f, -0.50 f);cFrontRight = new GLVector (0.5f, 0.0f, 0.5 f);cFrontLeft = new GLVector ( -0.5f, 0.0f, 0.5 f);Ainda no método init(), carregamos a imagem que irá servir para aplicar textura na nossa geometria, eactivamos o TEXTURE 2D:82


...try {InputStream stream = Imaging . class . getResourceAsStream (" stone . tga ");img = TGAImage . read ( stream );bb = img . getData ();stream . close ();} catch ( IOException e) {e. printStackTrace ();}gl. glTexImage2D ( GL2 . GL_TEXTURE_2D , 0, GL2 . GL_RGB ,img . getWidth () , img . getHeight () ,0, img . getGLFormat () , GL2 . GL_UNSIGNED_BYTE , bb );// Activar as texturasgl. glEnable ( GL2 . GL_TEXTURE_2D );// ...A face frontal tem que ser <strong>de</strong>senhada na or<strong>de</strong>m CCW para que a nossa textura seja aplicada correctamente.Assim a or<strong>de</strong>m <strong>de</strong> <strong>de</strong>senho correcta será - cTop-cFrontLeft-cFrontRight. As coor<strong>de</strong>nadas são especificadas <strong>de</strong>forma semelhante às da Figura 8.4(b).O código em baixo calcula o verctor normal à face frontal (vNormal), especifica as coor<strong>de</strong>nadas da texturapara cada vértice (glTexCoord) e <strong>de</strong>senha-o 1 :// Face da FrentevNormal = GlTools . getNormalVector (cTop , cFrontLeft , cFrontRight );gl. glNormal3fv ( vNormal . toArray () , 0);gl. glTexCoord2f (.5f, 1f);cTop . draw ();gl. glTexCoord2f (0f, 0f);cFrontLeft . draw ();gl. glTexCoord2f (1f, 0f);cFrontRight . draw ();O <strong>de</strong>senho das restantes faces do triângulo são feitas <strong>de</strong> forma semelhante. A Figura 8.6 mostra o output doficheiro Pyramid.java.Figura 8.6: Output do ficheiro Pyramid.java1 Por uma questão <strong>de</strong> conveniência a class GLVector tem um método que <strong>de</strong>senha o vector actual utilizando a função glVertex83


8.4 Texture EnvironmentNo exemplo Pyramid, a pirâmi<strong>de</strong> é <strong>de</strong>senhada utilizando material <strong>de</strong> cor branca e a textura é aplicada <strong>de</strong> formaque as suas cores são “escaladas” (scaled) segundo a cor da geometria (branca) (Figura 8.7).Figura 8.7: Geometria Branca + Texture = Sha<strong>de</strong>d TextureA forma como a cor dos texels é combinada com a geometria subjacente é controlada pelo texture environment.A sua configuração é feita utilizando a função glTexEnv:glTexEnvi ( int target , int pname , int param );glTexEnvf ( int target , int pname , float param );glTexEnviv ( int target , int pname , IntBuffer param );glTexEnvfv ( int target , int pname , FloatBuffer param );Esta função po<strong>de</strong> ser usada <strong>de</strong> várias formas, e permite configurar parâmetros avançados das texturas. Noexemplo Pyramid, o environment mo<strong>de</strong> é confiurado com o valor GL MODULATE:gl. glTexEnvi ( GL2 . GL_TEXTURE_ENV , GL2 . GL_TEXTURE_ENV_MODE , GL2 . GL_MODULATE );Com a opção GL MODULATE, a cor do texel é multiplicada pela cor da geometria (<strong>de</strong>pois do calculo das luzes).Ao especificar uma cor clara para a geometria (branca, no exemplo) a textura é afectada <strong>de</strong> forma igual àgeometria. Isto permite com a mesma textura obter várias colorizações.Utilizando GL REPLACE, po<strong>de</strong>mos substituir completamente a cor da geometria pela cor do texel. Isto eliminatodos os efeitos aplicados à geometria subjacente. Se a textura tiver um alpha channel po<strong>de</strong>mos utilizar estemodo para tornar zonas da geometria transparente se a imagem contiver blocos também transparentes, activandoo blending.A opção GL DECAL é semelhante à opção GL REPLACE se a imagem não tiver alpha channel. No caso <strong>de</strong> aimagem ter um alpha channel a cor da geometria transparece pela textura no valor do alpha, <strong>de</strong> forma que oobjecto parece ser blen<strong>de</strong>d com os fragmentos subjacentes.Po<strong>de</strong>mos também fazer o blending da geometria com uma cor fixa (Figura 8.8) utilizando:gl. glTexEnvi ( GL2 . GL_TEXTURE_ENV ,GL2 . GL_TEXTURE_ENV_MODE , GL2 . GL_BLEND );gl. glTexEnvfv ( GL2 . GL_TEXTURE_ENV ,GL2 . GL_TEXTURE_ENV_COLOR , new float [] {1f, 0f, 0f, 0f}, 0);Figura 8.8: Utilizando GL BLENDFinalmente po<strong>de</strong>mos fazer a adição do valor da cor dos texels aos fragmentos da geometria, utilizando GL ADDcomo argumento. Os valores que ultrapassem 1.0 são truncados, o que po<strong>de</strong>rá provocar saturação das cores.84


8.5 Parametrização <strong>de</strong> TexturasO texturing no OpenGL envolve mais do que o simples mapeamento <strong>de</strong> uma textura num dos lados <strong>de</strong> umtriângulo. Um conjunto <strong>de</strong> parâmetros afectam o compotamento e a ren<strong>de</strong>rização das texturas aplicadas. Estesparâmetros po<strong>de</strong>m ser configurados com a função glTexParameter, utilizando uma das variantes:glTexParameterf ( int target , int pname , float param );glTexParameteri ( int target , int pname , int param );glTexParameterfv ( int target , int pname , FloatBuffer params );glTexParameteriv ( int target , int pname , IntBuffer params );O argumento target, especifica o modo da textura para o parâmetro que estamos a configurar (GL TEXTURE 1D,GL TEXTURE 1D, GL TEXTURE 3D). O parâmetro a configurar é especificado em pname e o seu valor é <strong>de</strong>finido noúltimo argumento da função (param/params).8.5.1 Filtering BásicoProque raramente a geometria coinci<strong>de</strong> com a textura em termos <strong>de</strong> pixels (ao contrário dos pixmaps), asimagens <strong>de</strong> textura são normalmente “<strong>de</strong>formadas” para obe<strong>de</strong>cer às coor<strong>de</strong>nadas especificadas para a geometria.A imagem po<strong>de</strong> ser encolhida ou esticada, ou ambos na aplicação à geometria.O processo <strong>de</strong> cálculo dos fragmentos <strong>de</strong> cor, quando uma textura está a ser aplicada, é chamado <strong>de</strong>filtering. Po<strong>de</strong>mos especificar os filtros utilizados na altura <strong>de</strong> encolher (minification) ou <strong>de</strong> esticar (magnification).Os parâmetros para a configuração <strong>de</strong>stes valores são, respectivamente, GL TEXTURE MIN FILTER eGL TEXTURE MAG FILTER. Po<strong>de</strong>mos especificar dois valores para este parâmetro:(a) Filtering com GL NEAREST(b) Filtering com GL LINEARFigura 8.9: Aplicação <strong>de</strong> textura bi-dimensional• GL NEAREST: Simples e rápido. São apuradas as coor<strong>de</strong>nadas <strong>de</strong> cada fragmento para cada texel, a corcorrespon<strong>de</strong>nte a essa coor<strong>de</strong>nada é utilizada como a cor do fragmento da textura. A técnica <strong>de</strong> nearestneighbour é caracterizada pelo aparecimento <strong>de</strong> blocos <strong>de</strong> uma só cor, quando a geometria é aumantada(Figura 8.9(a)). Po<strong>de</strong> ser configurado utilizando:gl. glTexParameteri ( GL2 . GL_TEXTURE_2D ,GL2 . GL_TEXTURE_MAG_FILTER , GL2 . GL_NEAREST );gl. glTexParameteri ( GL2 . GL_TEXTURE_2D ,GL2 . GL_TEXTURE_MIN_FILTER , GL2 . GL_NEAREST );• GL LINEAR: Requer mais processamento, mas o resultado justifica. O custo <strong>de</strong> processamento do linearfiltering é, nos computadores actuais <strong>de</strong>spresível. O valor da cor <strong>de</strong> cada fragmento da textura é conseguidopela interpolação linear (média pon<strong>de</strong>rada) dos texels que ro<strong>de</strong>iam a sua coor<strong>de</strong>nada. Po<strong>de</strong> ser configuradoutilizando:gl. glTexParameteri ( GL2 . GL_TEXTURE_2D ,GL2 . GL_TEXTURE_MAG_FILTER , GL2 . GL_LINEAR );gl. glTexParameteri ( GL2 . GL_TEXTURE_2D ,GL2 . GL_TEXTURE_MIN_FILTER , GL2 . GL_LINEAR );85


8.5.2 Texture WrapNormalmente especificamos as coor<strong>de</strong>nadas das texturas com valores entre 0.0 e 1.0 relativamente ao texturemap. A forma como são tratados os valores que saem fora do intervalo [0.0, 1.0] é <strong>de</strong>finida no parâmetro texturewrap. O valor <strong>de</strong>ste parâmetro é <strong>de</strong>finido individualmente para as coor<strong>de</strong>nada s, t e r, utilizando a funçãoglTexParameteri com o pname - GL TEXTURE WRAP S, GL TEXTURE WRAP T, or GL TEXTURE WRAP R. Os modosdisponíveis para o wrapping são:• GL REPEAT: Neste modo a textura é repetida na direcção do valor que ultrapassou o intervalo. Isto permiteutilizar uma textura pequena para superfcícies gran<strong>de</strong>s. Se a imagem for bem escolhida po<strong>de</strong>mos dar asensação <strong>de</strong> uma textura muito maior à custa <strong>de</strong> uma porção mais pequena.• GL CLAMP: Os texels são obtidos a partir da cor borda da imagem. Esta cor é especificada no parâmetroGL TEXTURE BORDER COLOR (configurado com glTexParameterfv).• GL CLAMP TO EDGE: Faz com que os valores fora da borda não sejam utilizados no cálculo da interpolaçãolinear.• GL CLAMP TO BORDER: Utiliza os valores da borda quando as coor<strong>de</strong>nadas da textura caem fora do intervalo[0.0, 1.0].A escolha do tipo <strong>de</strong> wrapping influencia o modo como o filtering funciona, principalmente quando estamos ausar GL LINEAR. Como o valor do fragmento é calculado com base na vizinhança, o cálculo junto das bordas datextura po<strong>de</strong> representar um problema se nos limitarmos a fazer clamping. O problema po<strong>de</strong> ser resolvido <strong>de</strong>forma simples quando utilizamos GL REPEAT. Os novos texels são obtidos a partir da linha/coluna adjacentes, queno repeat mo<strong>de</strong> são tirados a partir do outro extremo da imagem (como se juntássemos as duas arestas opostasda imagem). Isto funciona especialmente bem quando usamos texturas em que um lado encaixa perfeitamenteno outro (por ex.: esferas).Uma aplicação comum para o clamping po<strong>de</strong> ser vista no mapping <strong>de</strong> texturas que não caberiam em memóriapelo seu tamanho. Em vez disso, são utilizados “mosaicos”. No entanto, a não utilização <strong>de</strong> GL CLAMP TO EDGEpo<strong>de</strong> levar ao aparecimento <strong>de</strong> artefactos in<strong>de</strong>sejados no texture mapping.8.5.3 MipmappingO mipmapping é uma técnica que permite melhorar a performance <strong>de</strong> ren<strong>de</strong>rização e a qualida<strong>de</strong> visual <strong>de</strong> umacena. O melhoramento é feito en<strong>de</strong>reçando dois problemas comuns na aplicação <strong>de</strong> textura à geometria:1. Scintillation: Consiste <strong>de</strong> artefactos <strong>de</strong> aliasing que aparecem nas superfcícies quando os objectos sãomuito ren<strong>de</strong>rizados muito pequenos, comparados com a textura que lhes é aplicada. Este efeito é maisnotório quando existe movimento na cena, ou quando o observador se encontra em movimento.2. Performance: Uma gran<strong>de</strong> quantida<strong>de</strong> <strong>de</strong> memória <strong>de</strong>ve ser carregada com texturas e processada (filtering)para ren<strong>de</strong>rizar um conjunto limitado (ou pequeno) <strong>de</strong> fragmentos no ecrã. O que provoca um <strong>de</strong>créscimona performance à medida que a quantida<strong>de</strong> e tamanho das texturas aumenta.Uma solução para estes dois problemas po<strong>de</strong>ria passar pela utilização <strong>de</strong> uma textura mais pequenta. Noentanto, quando nos aproximamos do objecto, essa textura será inevitavelmente escalada, tornando as nossasmenos nítidas ou cheias <strong>de</strong> artefactos.A solução <strong>de</strong>veras eficaz passa pela utilização <strong>de</strong> mipmapping 1 . O mipmapping consiste na utilização <strong>de</strong>várias imagens com diferentes tamanhos e resoluções para a mesma textura. O OpenGL utiliza então tiras dasimagens para aplicar à geometria <strong>de</strong> forma apropriada e com base num novo conjunto <strong>de</strong> filtros. Isto po<strong>de</strong>rá serfeito à custa <strong>de</strong> um pouco mais <strong>de</strong> memória e/ou processamento.Uma textura mipmapped consiste <strong>de</strong> um conjunto <strong>de</strong> imagens, on<strong>de</strong> cada uma é meta<strong>de</strong> da anterior até queatingem o tamanho 1x1 (Figura 8.10).As imagens não precisam <strong>de</strong> ser necessariamente quadradas, neste caso quando uma coor<strong>de</strong>nada chega a 1 aoutra é dividida repetidamente até que chega a 1x1.A utilização <strong>de</strong> mipmapping po<strong>de</strong>rá ocupar até 3× mais memória <strong>de</strong> textura que a não utilização.Os níveis <strong>de</strong> mipmap para cada imagem são especificados no segundo argumento (level) da função glTexImageaquando do seu carregamento. O primeiro nível será 0, <strong>de</strong>pois 1, 2, 4 e por aí fora numa base 2 x em que x é aiteração da imagem. Quando o mipmapping não está a ser utilizado, a imagem especificada no valor 0 é tomadopor omissão.1 Palavra proveniente <strong>de</strong> multum in parvo - muitas coisas num local pequeno86


Figura 8.10: Conjunto <strong>de</strong> imagens MipmappedPor omissão, para utilizar o mipmapping, temos que especificar uma imagem para cada nível <strong>de</strong> mip.Po<strong>de</strong>mos, no entanto, <strong>de</strong>finir qual o valor base e máximos utilizando os parâmetros GL TEXTURE BASE LEVEL eGL TEXTURE MAX LEVEL, como mostra o exemplo:gl. glTexParameteri ( GL2 . GL_TEXTURE_2D , GL2 . GL_TEXTURE_BASE_LEVEL , 0);gl. glTexParameteri ( GL2 . GL_TEXTURE_2D , GL2 . GL_TEXTURE_MAX_LEVEL , 4);Desta forma, serão apenas utilizados os níveis 0, 1, 2 e 4 <strong>de</strong> mipmapping para aplicar à nossa geometria.Po<strong>de</strong>mos ainda especificar os valores mínimos e máximos <strong>de</strong> <strong>de</strong>talhe a ser utilizados alterandos os valores <strong>de</strong>GL TEXTURE MIN LOD e GL TEXTURE MAX LOD.8.5.4 Mipmap FilteringCom o mipmapping ganhamos um novo conjunto <strong>de</strong> modos <strong>de</strong> filtering <strong>de</strong> texturas. Estes novos modos estão<strong>de</strong>scritos na Tabela 8.2.ConstanteGL NEARESTGL LINEARGL NEAREST MIPMAP NEARESTGL NEAREST MIPMAP LINEARGL LINEAR MIPMAP NEARESTGL LINEAR MIPMAP LINEARDescriçãoCalcular com base no “vizinho mais próximo” no nível<strong>de</strong> mip actualCalcular com base na interpolação linear da vizinhançano nível <strong>de</strong> mip actualSeleccionar o nível <strong>de</strong> mip mais próximo e operar filtronearestInterpolação linear entre níveis <strong>de</strong> mip e operar filtronearestSeleccionar o nível <strong>de</strong> mip mais próximo e operar filtragemlinearInterpolação linear entre níveis <strong>de</strong> mip e operar filtragemlinear (alias trilinear mipmapping)Tabela 8.2: Mipmap FilteringO simples facto <strong>de</strong> especificarmos o nível <strong>de</strong> mip na função glTexImage não activa o mipmapping. Se o filtroda textura estiver configurado para GL LINEAR ou GL NEAREST, os níveis <strong>de</strong> mip ≠ 0 são ignorados. Um dosfiltros <strong>de</strong> mipmapping <strong>de</strong>ve ser especificado para que o mipmapping seja usado.As constantes tomam a forma GL MIPMAP , on<strong>de</strong> o FILTRO especifica o tipo <strong>de</strong> filtragemoperado no nível <strong>de</strong> mip seleccionado. O SELECTOR, como o nível <strong>de</strong> mip é seleccionado. Se seleccionarmosum modo <strong>de</strong> filtering que activa o mipmapping sem carregar os níveis <strong>de</strong> mip correspon<strong>de</strong>nte, <strong>de</strong>sactivamos omapping <strong>de</strong> texturas.A escolha do filtro <strong>de</strong>pen<strong>de</strong> um pouco da aplicação e dos requisitos <strong>de</strong> performance. Utilizando o filtroGL NEAREST MIPMAP NEAREST, conseguimos um <strong>de</strong>sempenho melhor e um valor <strong>de</strong> scintillation reduzido, noentanto o aspecto visual final não é o mais agradável. O GL LINEAR MIPMAP NEAREST é utilizado normalmentepara acelerar jogos, mantendo um aspecto visual melhorado, ainda que a seleccção do nível <strong>de</strong> mip seja feita porproximida<strong>de</strong>.A utilização <strong>de</strong> nearest para a seleccção do nível <strong>de</strong> mip, po<strong>de</strong>rá também conduzir ao aparecimento <strong>de</strong>artefactos in<strong>de</strong>sejados. Quando vista <strong>de</strong> uma forma obliqua, é perceptível a transição <strong>de</strong> um nível para o outro<strong>de</strong> mip. A utilização <strong>de</strong> uma selecção LINEAR (GL * MIPMAP LINEAR) permite uma interpolação adicional entreníveis para eliminar esta zona <strong>de</strong> transição, ainda que à custa <strong>de</strong> processamento adicional.8.5.5 Geração <strong>de</strong> níveis <strong>de</strong> MipmappingPo<strong>de</strong>riamos chamar <strong>de</strong> forma reptida a função glTexImage para conseguirmos popular todos os níveis <strong>de</strong> mip.No entanto, isto po<strong>de</strong> ser uma tarefa ardua, sendo que nem sempre o <strong>de</strong>veloper tem acesso a todos tamanhos da87


imagem da textura. A biblioteca GLU fornece um conjunto <strong>de</strong> funções que permitem convenientemente gerar osvários níveis para cada imagem. Estas funções carregam ainda a imagem automaticamente utilizando a funçãoglTexImage. As variantes da função são:gluBuild1DMipmaps (int target , int internalFormat ,int width ,int format , int type ,Buffer data);gluBuild2DMipmaps (int target , int internalFormat ,int width , int height ,int format , int type ,Buffer data);gluBuild3DMipmaps (int target , int internalFormat ,int width , int height , int <strong>de</strong>pth ,int format , int type ,Buffer data);A utilização <strong>de</strong>stas funções é semelhante à utilização da função glTexImage, no entanto, não especificamos oargumento level uma vez que os níveis <strong>de</strong> mip são gerados <strong>de</strong> forma automática. A imagem é redimensionadautilizando um box filter, que po<strong>de</strong>rá não ter o mesmo efeito que utilizando um programa <strong>de</strong> <strong>de</strong>senho (como oPhotoshop).Para o caso <strong>de</strong> querermos especificar o nível <strong>de</strong> mip a que uma imagem se refere 1 aquando da geração dosníveis po<strong>de</strong>mos ainda utilizar:gluBuild1DMipmapLevels (int target , int internalFormat ,int width ,int format , int type , int level ,int base , int max , Buffer data);gluBuild2DMipmapLevels (int target , int internalFormat ,int width , int height ,int format , int type , int level ,int base , int max , Buffer data);gluBuild3DMipmapLevels (int target , int internalFormat ,int width , int height , int <strong>de</strong>pth ,int format , int type , int level ,int base , int max , Buffer data);Aqui o level especifica o nível <strong>de</strong> mip a que a imagem (data) se refere e o base e max, <strong>de</strong>finem o nívelmínimo e máximo <strong>de</strong> mip. Estas funções são utilizadas para gerar os níveis <strong>de</strong>ntre base e max.8.5.6 Geração <strong>de</strong> Mipmaps por HardwareSe soubermos que queremos utilizar todos os níveis <strong>de</strong> mipmapping, po<strong>de</strong>mos utilizar a aceleração <strong>de</strong> hardwaredo OpenGL para os gerar <strong>de</strong> forma rápida. Isto é conseguido fazendo:gl. glTexParameteri ( GL2 . GL_TEXTURE_2D , GL2 . GL_GENERATE_MIPMAP , GL2 . GL_TRUE );Quando o valor <strong>de</strong> GL GENERATE MIPMAP é GL TRUE, as chamadas subsequentes a glTexImage e glTexSubImageque actualizam o nível 0, actualizam <strong>de</strong> forma automática todos os outros níveis 2 . A utilização <strong>de</strong>sta funcionalida<strong>de</strong>permite um ganho em <strong>de</strong>sempenho relativamente à utilização <strong>de</strong> gluBuildMipmaps.1 Por exemplo: temos a imagem para o nível 4 <strong>de</strong> mip e estamos a gerar do 0 ao 82 Esta funcionalida<strong>de</strong> está disponível apenas para versões do OpenGL ≥ 1.488


8.5.7 Level Of Detail (LOD) BiasO OpenGL calcula o nível <strong>de</strong> mip a utilizar com base no número <strong>de</strong> níveis disponíveis no tamanho da geometriaa ren<strong>de</strong>rizar. Po<strong>de</strong>mos, no entanto, dizer ao OpenGL para “polarizar” o valor do seu critério (valores <strong>de</strong> mipmais baixos) ou diminuir essa polarização (valores mais altos <strong>de</strong> mip). O ajuste <strong>de</strong>ste valor po<strong>de</strong>rá aumentar aperformance (utilizando os níveis mais baixos <strong>de</strong> mip) ou aumentar a fi<strong>de</strong>lida<strong>de</strong> das nossas texturas (utilizandoníveis mais altos <strong>de</strong> mip). O valor <strong>de</strong>sta polarização po<strong>de</strong> ser configurado utilizando, por exemplo:gl. glTexEnvf ( GL2 . GL_TEXTURE_FILTER_CONTROL , GL2 . GL_TEXTURE_LOD_BIAS , -1.5);No exemplo em cima, o <strong>de</strong>talhe das texturas é aumentado ligeiramente (nível <strong>de</strong> mip mais baixo), resultandonum cenário com aspecto melhorado, ainda que à custa <strong>de</strong> processamento adicional.8.6 Texture ObjectsO carregamento e gestão das texturas representam uma porção importante no <strong>de</strong>senvolvimento <strong>de</strong> qualqueraplicação 3D, tal como os jogos, em particular.As funções glTexImage, glTexSubImage e gluBuildMipmaps, movem gran<strong>de</strong>s quantida<strong>de</strong>s <strong>de</strong> memória, peloque a troca entre texturas seria uma operação dispoendiosa, em termos <strong>de</strong> processamento.Os texture objects permitem-nos carregar mais do que um estado <strong>de</strong> textura <strong>de</strong> cada vez e trocar entre elas<strong>de</strong> forma rápida. O estado das texturas é armazenado e rotulado com um i<strong>de</strong>ntificador inteiro. A alocação <strong>de</strong>texturas com i<strong>de</strong>ntificadores é feita utilizando:glGenTextures ( int n, IntBuffer textures );O argumento n especifica o número <strong>de</strong> texturas que queremos armazenar e o argumento textures é umarray <strong>de</strong> inteiros (IntBuffer) que irá conter os i<strong>de</strong>ntificadores gerados. Para fazer o bind <strong>de</strong> uma textura a um<strong>de</strong>stes estados fazemos:glBindTexture ( int target , int texture );Mais uma vez o target po<strong>de</strong> tomar o valor GL TEXTURE 1D, GL TEXTURE 2D ou GL TEXTURE 3D. O argumentotexture é o i<strong>de</strong>ntificador ao qual vamos fazer o bind. O carregamentos <strong>de</strong> texturas e parametrizações subsequentesafectaram apenas o estado da textura representado pelo i<strong>de</strong>ntificador texture.Para apagar um <strong>de</strong>terminado estado utilizamos a função:glDeleteTextures ( int n, IntBuffer textures );O significado dos argumentos é o mesmo da função glBindTexture.Po<strong>de</strong>mos verificar se um <strong>de</strong>terminado i<strong>de</strong>ntificador contém um estado válido utilizando a função:boolean glIsTexture ( int texture );O valor <strong>de</strong> retorno da função será GL TRUE no caso <strong>de</strong> ter sido alocado um estado a este i<strong>de</strong>ntificador.89


Capítulo 9Mapeamento <strong>de</strong> Texturas: ConceitosAvançados9.1 Cor SecundáriaAntes <strong>de</strong> aplicar as texturas à geometria, o OpenGL calcula os valores individuais <strong>de</strong> cada fragmento, incluindo acomponente <strong>de</strong> LIGHTING. O valor <strong>de</strong> cada fragmento é mais tar<strong>de</strong> combinado com os texels (<strong>de</strong>pois do processo<strong>de</strong> filtering) dando origem à textura.Este processo diminui drásticamente a visibilida<strong>de</strong> da componente especular.A solução para este problema passa pela aplicação da componente especular da luz apenas após ter sidoaplicada a textura. Esta técnica é <strong>de</strong>signada <strong>de</strong> secondary specular color. Apesar <strong>de</strong> po<strong>de</strong>r ser aplicada <strong>de</strong> formamanual, o OpenGL contém já um mecanismo que faz esse trabalho <strong>de</strong> forma automática. Para tal basta-nosalterar o nosso light mo<strong>de</strong>l, da seguinte forma:glLightMo<strong>de</strong>li ( GL2 . GL_LIGHT_MODEL_COLOR_CONTROL , GL2 . GL_SEPARATE_SPECULAR_COLOR );As Figuras 9.1(a) e 9.1(b) mostram as diferenças sem e com a separação da componente especular.(a) Sem GL SEPARATE SPECULAR COLOR(b) Com GL SEPARATE SPECULAR COLORFigura 9.1: Utilização <strong>de</strong> Secondary ColorPara retomar o light mo<strong>de</strong>l anterior po<strong>de</strong>mos utilizar:glLightMo<strong>de</strong>li ( GL2 . GL_LIGHT_MODEL_COLOR_CONTROL , GL2 . GL_COLOR_SINGLE );9.2 Anisotropic FilteringApesar <strong>de</strong> não fazer parte da espeficicação do OpenGL, a técnica <strong>de</strong> Anisotropic Filtering é suportada pela maiorparte dos fabricantes. A sua utilização permite um melhoramento das operações <strong>de</strong> filtering das texturas.90


No processo <strong>de</strong> filtering, o OpenGL utiliza os texels na vizinhança <strong>de</strong> uma <strong>de</strong>terminada posição para fazero sampling da textura 1 . Com este processo obtemos resultados perfeitos quando a textura é vista <strong>de</strong> frente(perpendicularmente à geometria), no entanto, quando a geometria é vista <strong>de</strong> forma oblíqua po<strong>de</strong>mos observar aperda <strong>de</strong> informação da imagem (como que uma <strong>de</strong>sfocagem).Para evitar este efeito a área da vizinhança utilizada no filtering tem agora uma forma a<strong>de</strong>quada à posição doobservador (Figura 9.2). Isto implica que vários samples da imagem sejam feitos, relativamente ao observador,resultado numa imagem anisotrópica.Figura 9.2: Textura normal vs Anisotropic FilteringPo<strong>de</strong>mos aplicar o anisotropic filtering a texturas básicas e mipmapped. Para tal são efectuados 3 passos:1. Verificamos se a extensão para anisotropic filtering está disponível:GL2 gl = ...;gl. isExtensionAvailable (" GL_EXT_texture_filter_anisotropic ");2. Obtemos valor máximo <strong>de</strong> anisotpropia suportado:// Array temporario <strong>de</strong> saida para a funcao glGetFloatv (...)FloatBuffer out = FloatBuffer . allocate (1);gl. glGetFloatv ( GL2 . GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT , out );// Valor maximo <strong>de</strong> anisotropia esta na posicao 0 <strong>de</strong> outfloat largest = out . get (0);3. Finalmente <strong>de</strong>finimos a quantida<strong>de</strong> <strong>de</strong> anisotropia utilizando:gl. glTexParameterf (GL2 . GL_TEXTURE_2D ,GL2 . GL_TEXTURE_MAX_ANISOTROPY_EXT ,largest);Quando maior o valor <strong>de</strong> anisotropia <strong>de</strong>finido, maior será a quantida<strong>de</strong> <strong>de</strong> texels que são utilizados noprocesso <strong>de</strong> filtering.Pegando no exemplo Tunnel.java dos capítulos anteriores, as texturas serão carregadas da seguinte formano método init():// ...// Se afLargest se mantiver a 0 nao suporta anisotropic filteringfloat afLargest = 0f;// Se o anisotropic filtering for suportado obtemos o valor maximo// e armazenamos em afLargestif(gl. isExtensionAvailable (" GL_EXT_texture_filter_anisotropic ")) {FloatBuffer out = FloatBuffer . allocate (1);gl. glGetFloatv ( GL2 . GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT , out );afLargest = out . get (0);}for ( int i = 0; i < textureNames . size (); i ++) {1 Utilizando por exemplo GL NEAREST e GL LINEAR91


Fazer o bind do estado da textura ao i<strong>de</strong>ntificadorgl. glBindTexture ( GL2 . GL_TEXTURE_2D , textures . get (i ));// Carregar e Ler a imagem para a nossa texturaTGAImage img = loadTexture ( textureNames . get (i ));ByteBuffer bb = img . getData ();glu . gluBuild2DMipmaps ( GL2 . GL_TEXTURE_2D , GL2 . GL_RGBA ,img . getWidth () , img . getHeight () , img . getGLFormat () ,GL2 . GL_UNSIGNED_BYTE , bb );// Parametros da textura ( ignorar para ja)gl. glTexParameteri ( GL2 . GL_TEXTURE_2D , GL2 . GL_TEXTURE_MAG_FILTER ,GL2 . GL_LINEAR );gl. glTexParameteri ( GL2 . GL_TEXTURE_2D , GL2 . GL_TEXTURE_MIN_FILTER ,GL2 . GL_LINEAR_MIPMAP_LINEAR );gl. glTexParameteri ( GL2 . GL_TEXTURE_2D , GL2 . GL_TEXTURE_WRAP_S ,GL2 . GL_CLAMP_TO_EDGE );gl. glTexParameteri ( GL2 . GL_TEXTURE_2D , GL2 . GL_TEXTURE_WRAP_T ,GL2 . GL_CLAMP_TO_EDGE );// Se afLargest for > 1f o anisotropic filtering e suportadoif( afLargest > 1f)gl. glTexParameterf ( GL2 . GL_TEXTURE_2D ,GL2 . GL_TEXTURE_MAX_ANISOTROPY_EXT ,afLargest );}As Figuras 9.3(a) e 9.3(b) ilustram a diferença entre o filtering e sem e com utilização <strong>de</strong> anisotropic filtering.(a) Sem Anisotropic Filtering(b) Com Anisotropic FilteringFigura 9.3: Utilização <strong>de</strong> Anisotropic Filteing9.3 Compressão <strong>de</strong> TexturasA indiscutível melhoria introduzida pelas texturas é normalmente acompanhada por uma quantida<strong>de</strong> importante<strong>de</strong> memória ocupada e algum overhead <strong>de</strong> processamento. No entanto, o mais preocupante é realmente o espaçoocupado (normalmente na memória gráfica).O OpenGL permite ao utilizador fazer o carregamento <strong>de</strong> texturas comprimidas 1 . Os dados da texturamantém-se comprimidos na memória gráfica, o que po<strong>de</strong> representar um ganho no <strong>de</strong>sempenho aquando doswapping <strong>de</strong> texturas, bem como uma menor quantida<strong>de</strong> <strong>de</strong> memória ocupada.1 Na maior parte das implementações92


As texturas carregadas não precisam <strong>de</strong> estar inicialmente comprimidas. Na altura do carregamento <strong>de</strong>staspo<strong>de</strong>mos solicitar a compressão ao OpenGL, alterando o internalFormat para um dos valores na Tabela 9.1.Formato ComprimidoGL COMPRESSED ALPHAGL COMPRESSED LUMINANCEGL COMPRESSED LUMINANCE ALPHAGL COMPRESSED INTENSITYGL COMPRESSED RGBGL COMPRESSED RGBAFormato BaseGL ALPHAGL LUMINANCEGL LUMINANCE ALPHAGL INTENSITYGL RGBGL RGBATabela 9.1: Formatos <strong>de</strong> Texturas ComprimidasAo utilizar os formatos <strong>de</strong> texturas comprimidos, é adicionado um ligeiro overhead no carregamento inicialdas texturas. No entanto, este overhead é <strong>de</strong>spresível face as melhorias na utilização da memória.Se por algum motivo a textura não for carregada com o formato comprimido solicitado, o OpenGL alternapara o formato base correspon<strong>de</strong>nte (Tabela 9.1).Quando utilizamos estes formatos para a compressão <strong>de</strong> texturas, o OpenGL tenta <strong>de</strong>terminar a melhor forma<strong>de</strong> comprimir a textura. Po<strong>de</strong>mos dizer ao OpenGL para escolher um algortimo mais rápido ou com melhorqualida<strong>de</strong> utilizando a função glHint:• Método mais rápido:gl. glHint ( GL2 . GL_TEXTURE_COMPRESSION_HINT , GL2 . GL_FASTEST );• Método com melhor qualida<strong>de</strong>:gl. glHint ( GL2 . GL_TEXTURE_COMPRESSION_HINT , GL2 . GL_NICEST );• Deixar o OpenGL <strong>de</strong>cidir:gl. glHint ( GL2 . GL_TEXTURE_COMPRESSION_HINT , GL2 . GL_DONT_CARE );9.4 Geração <strong>de</strong> Coor<strong>de</strong>nadas <strong>de</strong> TexturasComo vimos no capítulo anterior, a atribuição das coor<strong>de</strong>nadas das texturas a geometria é uma tarefa simplesquando estamos a lidar com superfícies simples (triângulos, quadrados, etc. . . ). No entanto, para superfíciescomplexas, o cálculo das coor<strong>de</strong>nadas correctas po<strong>de</strong> ser uma tarefa complexa.Até certo ponto, o OpenGL consegue gerar automáticamente estas coor<strong>de</strong>nadas das texturas para cada vértice.Para activar/<strong>de</strong>sactivar a geração das coor<strong>de</strong>nadas (s, r, t e q), utilizamos as funções glEnable()/glDisable()com as flags - GL TEXTURE GEN S, GL TEXTURE GEN R, GL TEXTURE GEN T e GL TEXTURE GEN Q.Quando a geração <strong>de</strong> coor<strong>de</strong>nadas está activa, quaisquer chamadas subsequentes às funções glTextCoord sãoignoradas.A função utilizada pelo o OpenGL para o cálculo <strong>de</strong>stas coor<strong>de</strong>nadas é <strong>de</strong>finida utilizando uma das funções:glTexGenf ( int coord , int pname , float param );glTexGenfv ( int coord , int pname , FloatBuffer param );O argumento coord po<strong>de</strong> tomar um dos valores - GL S, GL T, GL R ou GL Q. O argumento pname <strong>de</strong>verá serum <strong>de</strong> - GL TEXTURE GEN MODE, GL OBJECT PLANE ou GL EYE PLANE. O argumento param <strong>de</strong>fine os valores paraa função utilizada no argumento pname.Para exemplificar a utilização das funções <strong>de</strong> cálculo das coor<strong>de</strong>nadas foi criado o programa TexGen.java(ver Apêndice C.4). O programa permite ao utilizador alterar a função <strong>de</strong> cálculo em runtime, utilizando asteclas 1 = Object Linear, 2 = Eye Linear e 3 = Sphere Map.9.4.1 Oject Linear MappingQuando a função para a geração das texturas é GL OBJECT LINEAR, as coor<strong>de</strong>nadas das texturas são geradascom base na seguinte função:Coord OLM = P 1 × x + P 2 × y + P 3 × z + P 4 × w93


On<strong>de</strong> x, y, z e w são as coor<strong>de</strong>nadas dos vértices do objecto on<strong>de</strong> está a ser aplicada a textura e os valoresP i (i = [1; 4]) são os coeficientes <strong>de</strong> uma equação do plano.Nesta caso, as coor<strong>de</strong>nadas da textura são projectadas sobre a geometria, sob o ponto <strong>de</strong> vista do plano<strong>de</strong>scrito pela equação. O excerto <strong>de</strong> código em baixo do programa TexGen.java, projecta as coor<strong>de</strong>nadas s e t,a partir do plano z = 0:FloatBuffer zPlane = FloatBuffer . wrap ( new float [] {0f, 0f, 1f, 0f });gl. glTexGeni ( GL2 .GL_S , GL2 . GL_TEXTURE_GEN_MODE ,GL2 . GL_OBJECT_LINEAR );gl. glTexGeni ( GL2 .GL_T , GL2 . GL_TEXTURE_GEN_MODE ,GL2 . GL_OBJECT_LINEAR );gl. glTexGenfv ( GL2 .GL_S , GL2 . GL_OBJECT_PLANE , zPlane );gl. glTexGenfv ( GL2 .GL_T , GL2 . GL_OBJECT_PLANE , zPlane );De notar que é ainda possível <strong>de</strong>finir funções diferentes para cada coor<strong>de</strong>nada (neste caso s e t). Esta técnicamapeia as coor<strong>de</strong>nadas da texturas em coor<strong>de</strong>nadas no objecto, in<strong>de</strong>pen<strong>de</strong>ntemente das tranformações em curso.O resultado final po<strong>de</strong> ser observado na Figura 9.4. Na Figura é possível observar o objecto com a texturaaplicada e a textura propriamente dita.Figura 9.4: Função Object Plange9.4.2 Eye Linear MappingQuando a função <strong>de</strong> geração das coor<strong>de</strong>nadas é GL EYE LINEAR, as coor<strong>de</strong>nadas são geradas <strong>de</strong> forma semelhantea GL OBJECT LINEAR, mas neste caso os valores <strong>de</strong> x, y, z e w referem-se agora à localização do observador. Aequação do plano têm também os seus coeficientes invertidos.Coord ELM = −P 1 × x eye − P 2 × y eye − P 3 × z eye − P 4 × w eyeA textura parece <strong>de</strong>slizar sobre a geometria à medida que é afectada pelas transformações geométricas. Oexcerto do programa TexGen.java abaixo mostra como po<strong>de</strong>mos utilizar o eye linear mapping para o mesmoplano z = 0:FloatBuffer zPlane = FloatBuffer . wrap ( new float [] {0f, 0f, 1f, 0f });gl. glTexGeni ( GL2 .GL_S , GL2 . GL_TEXTURE_GEN_MODE ,GL2 . GL_EYE_LINEAR );gl. glTexGeni ( GL2 .GL_T , GL2 . GL_TEXTURE_GEN_MODE ,GL2 . GL_EYE_LINEAR );gl. glTexGenfv ( GL2 .GL_S , GL2 . GL_EYE_PLANE , zPlane );gl. glTexGenfv ( GL2 .GL_T , GL2 . GL_EYE_PLANE , zPlane );O resultado po<strong>de</strong> ser observado nas Figuras 9.5(a) e 9.5(b).94


(a) GL EYE LINEAR(b) GL EYE LINEAR após ligeira rotaçãoFigura 9.5: Função GL EYE LINEAR9.4.3 Sphere MappingAo utilizar o GL SPHERE MAP o OpenGL calcula as coor<strong>de</strong>nadas das texturas <strong>de</strong> tal forma que ficamos coma impressão <strong>de</strong> que o objecto actual está a reflectir a textura que lhe aplicámos. O excerto do programaTexGen.java em baixo exemplifica a utilização da função sphere mapping:gl. glTexGeni ( GL2 .GL_S , GL2 . GL_TEXTURE_GEN_MODE , GL2 . GL_SPHERE_MAP );gl. glTexGeni ( GL2 .GL_T , GL2 . GL_TEXTURE_GEN_MODE , GL2 . GL_SPHERE_MAP );Para obter uma sensação <strong>de</strong> reflexão convincente, uma textura é normalmente adquirida utilizando uma lentefish-eye.Apesar <strong>de</strong> o sphere mapping ter sido substituído pelo cube mapping (Secção 9.5), ainda se encontram algunscasos <strong>de</strong> uso, na medida em que apenas uma textura é necessária, ao contrário do cube mapping on<strong>de</strong> sãonecessárias 6 !. Se apenas queremos dar a sensação <strong>de</strong> reflexão e não realismo, o sphere mapping é uma boaescolha.Um exemplo em que o sphere mapping faz um match perfeito, é o caso das superfícies que não reflectem oambiente <strong>de</strong> forma não espelhada, como por exemplo uma peça <strong>de</strong> metal não uniforme.No programa TexGen.java foi utilizada a imagem <strong>de</strong> fundo (constante em todos os exemplos) para aplicarna textura com a função sphere map. Quando aplicamos transformações, o torus parece reflectir o ambiente(Figura 9.6).9.5 Cube MappingOs últimos dois modos <strong>de</strong> geração <strong>de</strong> coor<strong>de</strong>nadas são o GL REFLECTCION MAP e o GL NORMAL MAP, ambos requerema utilização <strong>de</strong> um tipo especial <strong>de</strong> textura <strong>de</strong> ambiente (environment texture) - o cube map.A Figura 9.7 mostra a disposição das seis texturas nas faces do cube map utilizado no programa CubeMap.java(ver Apêndice C.5).Este mapa será uma aproximação das vistas (frente, trás, esquerda, direita, “tecto” e chão) <strong>de</strong> um cenárioreal. Utilizando o modo GL REFLECTION MAP, é possível criar uma reflexão bastante precisa.9.5.1 Carregamento do Cube MapO cube mapping adiciona 6 novos valores para o primeiro argumento da função glTexImage2D:• GL TEXTURE CUBE MAP POSITIVE X• GL TEXTURE CUBE MAP NEGATIVE X• GL TEXTURE CUBE MAP POSITIVE Y• GL TEXTURE CUBE MAP NEGATIVE Y95


Figura 9.6: Sphere MapFigura 9.7: Cube Map do programa CubeMap.java96


• GL TEXTURE CUBE MAP POSITIVE Z• GL TEXTURE CUBE MAP NEGATIVE ZAs constantes representam a direcção no ambiente real para a textura que estamos a carregar. Por exemplopara <strong>de</strong>finirmos a textura para a direcção x positiva, po<strong>de</strong>riamos utilizar a função glTexImage2D da seguinteforma:TGAImage img = loadImage (" facex . tga ");glTexImage2D ( GL2 . GL_TEXTURE_CUBE_MAP_POSITIVE_X , 0,img . getGLFormat () , img . getWidth () , img . getHeight () , 0,GL_RGBA , GL_UNSIGNED_BYTE , img . getData ());O excerto <strong>de</strong> código em baixo pertence ao programa CubeMap.java, e ilustra a forma como as texturaspo<strong>de</strong>riam carregadas para o cube map:// Obter os i<strong>de</strong>ntificadoresIntBuffer textures = IntBuffer . allocate ( NumTextures );gl. glGenTextures ( NumTextures , textures );// ...// Direccoes para as texuras do cuboList < String > textureNames = new ArrayList < String >();private static final int [] CubeDirection = {GL2 . GL_TEXTURE_CUBE_MAP_POSITIVE_X ,GL2 . GL_TEXTURE_CUBE_MAP_NEGATIVE_X ,GL2 . GL_TEXTURE_CUBE_MAP_POSITIVE_Y ,GL2 . GL_TEXTURE_CUBE_MAP_NEGATIVE_Y ,GL2 . GL_TEXTURE_CUBE_MAP_POSITIVE_Z ,GL2 . GL_TEXTURE_CUBE_MAP_NEGATIVE_Z};// ...// Texturas do Cube MaptextureNames . add (" cubemap / right . tga ");textureNames . add (" cubemap / left . tga ");textureNames . add (" cubemap /up.tga ");textureNames . add (" cubemap / down . tga ");textureNames . add (" cubemap / backward . tga ");textureNames . add (" cubemap / forward . tga ");// ...// Carregar as texturas do Cubogl. glBindTexture ( GL2 . GL_TEXTURE_CUBE_MAP , textures . get ( CubeStartIn<strong>de</strong>x ));// Parametros das texturas do cube mapgl. glTexParameteri ( GL2 . GL_TEXTURE_CUBE_MAP , GL2 . GL_TEXTURE_MAG_FILTER ,GL2 . GL_LINEAR );gl. glTexParameteri ( GL2 . GL_TEXTURE_CUBE_MAP , GL2 . GL_TEXTURE_MIN_FILTER ,GL2 . GL_LINEAR );gl. glTexParameteri ( GL2 . GL_TEXTURE_CUBE_MAP , GL2 . GL_TEXTURE_WRAP_S , GL2 . GL_REPEAT );gl. glTexParameteri ( GL2 . GL_TEXTURE_CUBE_MAP , GL2 . GL_TEXTURE_WRAP_T , GL2 . GL_REPEAT );gl. glTexParameteri ( GL2 . GL_TEXTURE_CUBE_MAP , GL2 . GL_TEXTURE_WRAP_R , GL2 . GL_REPEAT );// Restantes texturas preenchem o cubofor ( int i = CubeStartIn<strong>de</strong>x ; i < textureNames . size (); i ++) {// Carregar e Ler a imagem para a nossa texturaTGAImage img = loadTexture ( textureNames . get (i ));ByteBuffer bb = img . getData ();}gl. glTexImage2D ( CubeDirection [i - CubeStartIn<strong>de</strong>x ], 0, GL2 . GL_RGBA ,img . getWidth () , img . getHeight () , 0, img . getGLFormat () ,GL2 . GL_UNSIGNED_BYTE , bb );97


O valor utilizado no primeiro argumento das funções relacionadas com as texturas passa agora a serGL TEXTURE CUBE MAP. O mesmo valor é utilizado para activar o cube mapping:gl. glEnable ( GL2 . GL_TEXTURE_CUBE_MAP );No caso <strong>de</strong> GL TEXTURE CUBE MAP e GL TEXTURE 2D estarem activos simultâneamente, o cube map prevalece.De notar que a utilização do glTexParameter no cube map afecta as 6 texturas <strong>de</strong> uma só vez. Para todos osefeitos, os cube maps são tratados como texturas 3D, com coor<strong>de</strong>nadas s, t e r.9.5.2 Utilização dos Cube MapsA utilização mais comum para o cube map é a reflexão do ambiente. No exemplo CubeMap.java, a cena écapturada mudando o ponto <strong>de</strong> vista 6 vezes utilizando um field of view <strong>de</strong> 90 o .Para conseguir o efeito <strong>de</strong> reflexão com a composição das seis imagens, utilizamos:gl. glTexGeni ( GL2 .GL_S , GL2 . GL_TEXTURE_GEN_MODE , GL2 . GL_REFLECTION_MAP );gl. glTexGeni ( GL2 .GL_T , GL2 . GL_TEXTURE_GEN_MODE , GL2 . GL_REFLECTION_MAP );gl. glTexGeni ( GL2 .GL_R , GL2 . GL_TEXTURE_GEN_MODE , GL2 . GL_REFLECTION_MAP );Ao <strong>de</strong>senhar a nossa geometria (no exemplo um torus e uma sphere), <strong>de</strong>sactivamos temporáriamenteo GL TEXTURE 2D e activamos o GL TEXTURE CUBE MAP, activamos a geração das coor<strong>de</strong>nadas das texturas e<strong>de</strong>senhamos a geometria normalmente. Seguidamente restauramos o estado anterior reactivando o GL TEXTURE 2D:// Desactivar temporariamente TEXTURE_2Dgl. glDisable ( GL2 . GL_TEXTURE_2D );// Activar o Cube Mapgl. glEnable ( GL2 . GL_TEXTURE_CUBE_MAP );gl. glBindTexture ( GL2 . GL_TEXTURE_CUBE_MAP , textures . get ( CubeStartIn<strong>de</strong>x ));gl. glEnable ( GL2 . GL_TEXTURE_GEN_S );gl. glEnable ( GL2 . GL_TEXTURE_GEN_T );gl. glEnable ( GL2 . GL_TEXTURE_GEN_R );// Desenhar a Geometria que reflecte o cube mapgl. glTranslatef (0 , .5f, -.3f);glut . glutSolidSphere (.15 , 61 , 37);gl. glRotatef (( float ) Math . toDegrees ( torusRot ), 0, 1f, 0);glut . glutSolidTorus (.1f, .3f, 61 , 37);// Colocar tudo como estavagl. glDisable ( GL2 . GL_TEXTURE_GEN_S );gl. glDisable ( GL2 . GL_TEXTURE_GEN_T );gl. glDisable ( GL2 . GL_TEXTURE_GEN_R );gl. glDisable ( GL2 . GL_TEXTURE_CUBE_MAP );gl. glEnable ( GL2 . GL_TEXTURE_2D );O output do programa CubeMap.java po<strong>de</strong> ser analisado na Figura 9.8.98


Figura 9.8: CubeMap.java99


Capítulo 10Curvas e Superfícies10.1 Superfícies Incorporadas no OpenGLDisponíveis na biblioteca GLU 1 , estão disponíveis um conjunto <strong>de</strong> funções que ren<strong>de</strong>rizam superfícies quádricas 2 .São suportados três tipos <strong>de</strong> superfícies quádricas - esferas, cilindros e discos. Para <strong>de</strong>senhar um cone, porexemplo, po<strong>de</strong>mos utilizar um cilindro, tendo um dos lados raio=0. Os discos permitem especificar um buracono centro.A Figura 10.1 mostra algumas formas conseguidas com recurso a estas superfícies.Figura 10.1: Formas QuádricasPara conseguir formas mais complexas, po<strong>de</strong>mos utilizar composições contendo as várias superfícies quádricasdo OpenGL. Como por exemeplo, os eixos unitários 3D (Figura 10.2), que são construídos com a ajuda <strong>de</strong> 1 esfera,3 cilindros e 3 cones.Figura 10.2: Eixos unitários (x, y, z) <strong>de</strong>senhados com quádricas1 OpenGL Utility Library2 Conjunto dos pontos do espaço tridimensional cujas coor<strong>de</strong>nadas formam um polinómio <strong>de</strong> segundo grau, com um máximo trêsvariáveis → equação cartesiana da superfície100


Os eixos 3D servem <strong>de</strong> auxílio ao posicionamento e à visualização no sistema <strong>de</strong> coor<strong>de</strong>nadas <strong>de</strong> um modogeral. Para <strong>de</strong>senhar os eixos po<strong>de</strong> ser utilizada a função GlUtil.drawUnitAxes() fornecida na biblioteca <strong>de</strong>apoio à ca<strong>de</strong>ira <strong>de</strong> Computação gráfica.10.1.1 Configuração <strong>de</strong> Superfícies QuádricasO <strong>de</strong>senho <strong>de</strong> superfícies quádricas fornece alguma flexibilida<strong>de</strong> no que respeita a normais, coor<strong>de</strong>nadas <strong>de</strong>texturas e outros parâmetros. No entanto, a colocação <strong>de</strong> todos estes parâmetros numa única função tornariaa sua utilização pouco prática. Em vez disso, é utilizada uma abordagem orientada a objectos. Um objectoquádrico é criado e os parâmetros <strong>de</strong> ren<strong>de</strong>rização são <strong>de</strong>finidos com um conjunto <strong>de</strong> funções, como mostra oexemplo:GLU glu = new GLU ();// Criar o objecto para a superficie quadricaGLUquadric quad = glu . gluNewQuadric ();// Configurar parametros// Desenhar as superficies// Apagar o objectoglu . gluDeleteQuadric ( quad );A função GLU.gluNewQuadric() retorna um objecto do tipo GLUquadric sobre o qual vamos alterar aspreferências. Depois <strong>de</strong> o objecto ser <strong>de</strong>senhado, po<strong>de</strong>mos libertar o objecto anteriormente criado utilizando afunção GLU.gluDeleteQuadric().Existem quatro funções para alterar a forma com os quadrics são <strong>de</strong>senhados. A primeira função é:GLU . gluQuadricDrawStyle ( GLUquadric quad , int drawStyle );O primeiro parâmetro da função é o objecto criado previamente. O segundo parâmetro toma o valor <strong>de</strong> umadas constantes <strong>de</strong>scritas na Tabela 10.1.ConstanteGLU.GLU FILLGLU.GLU LINEGLU.GLU POINTGLU.GLU SILHOUETTEDescriçãoQuadrics <strong>de</strong>senhados como objectos sólidosQuadrics <strong>de</strong>senhados como wireframesQuadrics <strong>de</strong>senhados como pontosParecido com o wireframe, mas faces adjacentesdos polígonos não são <strong>de</strong>senhadasTabela 10.1: Estilos <strong>de</strong> Desenho dos QuadricsA função seguinte permite <strong>de</strong>finir a forma como as normais são criadas para a geometria:GLU . gluQuadricNormals ( GLUquadric quad , int normals );Os valores possíveis para o parâmetro normals são:• GLU NONE - superfície <strong>de</strong>senhadas sem normais• GLU SMOOTH - as normais são calculadas para cada vértice, dando uma aparência suave à superfície• GLU FLAT - as normais são calculadas com base no conjunto dos vértices <strong>de</strong> cada polígono (triângulo)A orientação das normais po<strong>de</strong> ser especificada com:GLU . gluQuadricOrientation ( GLUquadric quad , int orientation );Neste caso, o parâmetro orientation, po<strong>de</strong> tomar um dos valores GLU OUTSIDE ou GLU INSIDE. Por omissão,os quadrics são <strong>de</strong>senhados com a face para fora (CCW).Esta função é útil para o caso <strong>de</strong> querermos alterar a face sobre a qual a luz inci<strong>de</strong>, por exemplo, no interior<strong>de</strong> uma esfera especificaríamos GLU INSIDE.A última função permite activar a geração <strong>de</strong> coor<strong>de</strong>nadas para a superfície:GLU . gluQuadricTexture ( GLUquadric quad , boolean texture );Neste caso quanto texture é true, é activada a geração, caso contrário é <strong>de</strong>sactivada. As esferas e cilindrossão “embrulhados” na textura, como se a textura os envolvesse. No caso dos discos, o centro da textura coinciecomo centro do disco e as bordas da textura coinci<strong>de</strong>m com as bordas do disco.101


10.1.2 Desenho <strong>de</strong> Superfícies QuádricasDepois <strong>de</strong> um objecto ter sido configurado <strong>de</strong> forma satisfatória, po<strong>de</strong> ser <strong>de</strong>senhado com uma única função. Nocaso da esfera, utilizamos:GLU . gluSphere ( GLUQuadric quad , double radius , int slices , int stacks );O argumento quad representa o nosso objecto GLUquadric previamente criado. O argumento radius <strong>de</strong>fineo raio da esfera. A esfera é <strong>de</strong>senhada utilizando um conjunto <strong>de</strong> triângulos 1 dispostos numa stack (pilha) <strong>de</strong>s<strong>de</strong>o fundo até ao topo. O número <strong>de</strong> elementos da pilha é <strong>de</strong>finido no parâmetro stacks. O parâmetro slices,<strong>de</strong>fine o número <strong>de</strong> triângulos (ou quads) utilizados para <strong>de</strong>senhar à volta da esfera (Figura 10.3).Os valores são semelhantes, conceptualmente, à latitu<strong>de</strong> e longitu<strong>de</strong> do globo terrestre.Figura 10.3: Stacks e SlicesA esfera é <strong>de</strong>senhada com o centro na origem das coor<strong>de</strong>nadas e com o eixo-z positivo a “sair” do topo daesfera.Figura 10.4: Esfera <strong>de</strong>senhada com quadricsO resultado da Figura 10.4 po<strong>de</strong> ser obtido com o código:GlUtil . drawUnitAxes ();GLUquadric quad = glu . gluNewQuadric ();glu . gluQuadricDrawStyle (quad , GLU . GLU_LINE );glu . gluSphere (quad , .75 , 16 , 16);glu . gluDeleteQuadric ( quad );O cilindro é <strong>de</strong>senhado <strong>de</strong> forma semelhante à esfera. Um conjunto <strong>de</strong> stacks são <strong>de</strong>senhadas ao longo doeixo-z positivo. A função para <strong>de</strong>senhar o cilindro é:1 Ou quads <strong>de</strong>pen<strong>de</strong>ndo da implementação da biblioteca GLU102


GLU . gluCylin<strong>de</strong>r (GLUquadric quad , double base ,double top , double height ,int slices , int stacks);Com esta função especificamos o raio da base e topo (base e top), sendo a base localizada na origem. Oparâmetro height <strong>de</strong>fine o coprimento do cilindro (Figura 10.5).Figura 10.5: Cilindro <strong>de</strong>senhado com quadricsPara <strong>de</strong>senhar um cone basta colocar o raio do topo (top) a 0 (Figura 10.6).Figura 10.6: Cone <strong>de</strong>senhado com quadricsA última superfície é o disco. Os discos são <strong>de</strong>senhados com recurso a loops <strong>de</strong> trinângulos (ou quads)dispostos por vários slices. A função utilizada para <strong>de</strong>senhar um disco é:gluDisk (GLUquadric quad , double inner ,double outer ,int slices , int loops);Para <strong>de</strong>senhar o disco especificamos um raio interno (inner) e um raio exterior (outer) (Figura 10.7). Paraconseguir um disco sólido, especificamos inner=0. Com um valor <strong>de</strong> inner diferente <strong>de</strong> 0 obtemos um disco comum orificio no meio.103


(a) inner=0(b) inner=0.25Figura 10.7: Disco <strong>de</strong>senhado com quadricsO disco é <strong>de</strong>senhado no plano xy.A composição <strong>de</strong> diferentes quadrics permite a criação <strong>de</strong> objectos, tal como o exemplo Snowman.java daFigura 10.8 (ver Apêndice C.6).Figura 10.8: Snowman.java10.2 Curvas <strong>de</strong> Bézier e SuperfíciesOs quadrics fornecem um meio <strong>de</strong> <strong>de</strong>senhar superfícies facilmente mo<strong>de</strong>ladas com equações algébricas. Noentanto, para <strong>de</strong>senhar superfícies mais complexas, tais como curvas ou superfícies não lineares, on<strong>de</strong> não existeuma especificação matemática inicial, a tarefa não é trivial.Pierre Bézies, um <strong>de</strong>signer da industria automóvel, reconheceu esta dificulda<strong>de</strong> e criou uma forma <strong>de</strong> <strong>de</strong>senharcurvas com recurso a um conjunto <strong>de</strong> pontos <strong>de</strong> controlo (control points). Para além da facilida<strong>de</strong> narepresentação das curvas, os pontos <strong>de</strong> controlo representam uma forma interactiva <strong>de</strong> mudar a forma da curvaou superfície.104


10.2.1 Representação ParamétricaAntes <strong>de</strong> passar ao <strong>de</strong>senho <strong>de</strong> curvas, convém estabelecer algum vocabulário e relembrar alguns conceitosmatemáticos.Se estivermos a pensar em linha recta, a representação no plano-xy po<strong>de</strong> ser feita com recurso à equação:y = m · x + bO valor m representa a inclinação da recta, enquanto que o valor b <strong>de</strong>fine a coor<strong>de</strong>nada y on<strong>de</strong> a recta secruza com o eixo-y.Outra forma <strong>de</strong> representar uma curva (ou recta), consiste na parametrização das suas coor<strong>de</strong>nadas emfunção <strong>de</strong> uma outra variável que não tem necessáriamente a ver com o sistema <strong>de</strong> coor<strong>de</strong>nadas. Por exemplo,po<strong>de</strong>mos especificar a forma como as coor<strong>de</strong>nadas variam, ao longo do tempo (t). Assim para cada coor<strong>de</strong>nadateremos uma função que varia com o tempo:x = f(t)y = g(t)z = h(t)No OpenGL, as curvas são <strong>de</strong>finidas utilizando uma equação paramétrica. A curva é <strong>de</strong>finida por um parâmetrou e pelo conjunto dos seus valores no domínio da curva. Para a <strong>de</strong>finição <strong>de</strong> superfícies utilizamos um valoradicional v (Figura 10.9).É importante reter que os valores u e v não representam coor<strong>de</strong>nadas reais da curva, são antes valores <strong>de</strong>parametrização da mesma. Desta forma, uma curva será <strong>de</strong>finida com base nos seus domínios u e v 1 .Figura 10.9: Representação Paramétrica <strong>de</strong> Curvas e Superfícies10.2.2 Pontos <strong>de</strong> Control (control points)Uma curva po<strong>de</strong> ser representada por um conjunto <strong>de</strong> pontos <strong>de</strong> controlo que <strong>de</strong>terminam o comportamento dacurva. O primeiro e último pontos da curva pertencem às coor<strong>de</strong>nadas da curva, no entanto, os restantes pontosestão fora da curva e representam uma espécie <strong>de</strong> “pontos magnéticos” que atraém a curva no seu percurso(Figura 10.10).Figura 10.10: Pontos <strong>de</strong> Controlo1 Sendo um domínio, o conjunto dos valores possíveis para um parâmetro na curva em questão105


A curva da Figura 10.10b é uma curva quadrática (2 pontos <strong>de</strong> controlo) e a curva da Figura 10.10c é umacurva cúbica (3 pontos <strong>de</strong> controlo). Estas últimas são as mais utilizadas.10.2.3 Continuida<strong>de</strong>Quando duas curvas lado-a-lado partilham um ponto (breakpoint), formam uma secção curva (Figura 10.11). Aforma como as curvas se encontram <strong>de</strong>fine a continuida<strong>de</strong> da secção. As categorias <strong>de</strong> continuida<strong>de</strong> são:• none - Quando as curvas não se encontram <strong>de</strong> todo• positional (C0) - As curvas encontram-se num ponto• tangetial (C1) - As curvas partilham a mesma tangente no breakpoint• curvature (C2) - Para além <strong>de</strong> partilharem a mesma tangente, a aproximação à tangente é semelhante nasduas curvasFigura 10.11: Continuida<strong>de</strong> <strong>de</strong> CurvasOs tipos <strong>de</strong> continuida<strong>de</strong> mais utilizados, quando estamos a juntar um gran<strong>de</strong> conjunto <strong>de</strong> curvas, são os tipotangential e curvature.10.2.4 EvaluatorsO OpenGL fornece uma forma simples <strong>de</strong> <strong>de</strong>senhar curvas e superfícies <strong>de</strong> Bézier. Para tal, especificamos ospontos <strong>de</strong> controlo e o domínio dos valores paramétrics u e v. Finalmente ao invocar o evaluator apropriado, oOpenGL gera os pontos que <strong>de</strong>finem a curva ou superfície.Uma curva 2DO programa Bezier2D.java (ver Apêndice C.7), mostra como po<strong>de</strong> ser <strong>de</strong>senhada uma curva <strong>de</strong> Bézier 2D.A melhor forma <strong>de</strong> explicar o programa passa pela explicação <strong>de</strong> cada bloco <strong>de</strong> código.Começamos por <strong>de</strong>finir uma perspectiva ortogonal entre +10 e -10, a fim <strong>de</strong> facilitar a visualização:gl. glMatrixMo<strong>de</strong> ( GL2 . GL_PROJECTION );gl. glLoadI<strong>de</strong>ntity ();glu . gluOrtho2D ( -10f, 10f, -10f, 10f);Criamos os pontos <strong>de</strong> controlo com recurso à classe GLVectorList:// Definir Pontos <strong>de</strong> ControloctrlPoints = new GLVectorList ();ctrlPoints . add ( -4f, 0f, 0f); // StartctrlPoints . add ( -6f, 4f, 0f); // Control106


ctrlPoints . add (6f, -4f, 0f); // ControlctrlPoints . add (4f, 0f, 0f); // EndDepois <strong>de</strong> limpar o color buffer, criamos o mapping para a curva utilizando a função glMap1f:gl. glClear ( GL2 . GL_COLOR_BUFFER_BIT );float umin = 0f; // Valor minimo do ufloat umax = 100 f; // Valor maximo do ugl. glMap1f (GL2 . GL_MAP1_VERTEX_3 , // Tipo <strong>de</strong> dados geradosumin , // Valor minimo do uumax , // Valor maximo do u3, // Distancia entre pontosctrlPoints . size () , // Numero <strong>de</strong> pontos <strong>de</strong> controloctrlPoints . toFloatBuffer () // FloatBuffer contendo pontos <strong>de</strong> controlo);O primeiro argumento da função glMap1f <strong>de</strong>fine o tipo <strong>de</strong> evaluator a utilizar para gerar os vértices da linha.O segundo e terceiros argumentos <strong>de</strong>finem o valor <strong>de</strong> u para o ponto inicial da curve e o valor <strong>de</strong> u para o pontofinal da curva. Os pontos intermédios representam uma posição na curva 1 .O terceiro argumento representa o número <strong>de</strong> elementos em cada posição do array contendo os pontos <strong>de</strong>controlo (neste caso um FloatBuffer). Para o programa Bezier2D.java, estamos a utilizar as coor<strong>de</strong>nadas x,y e z, daí o valor ser 3. O quarto argumento <strong>de</strong>fine o número <strong>de</strong> pontos <strong>de</strong> controlo que preten<strong>de</strong>mos utilizar efinalmente o último argumento é o buffer (ou array) contendo os pontos <strong>de</strong> controlo.Por uma questão <strong>de</strong> conveniência, foi criado o método toFloatBuffer() na classe GLVectorList que <strong>de</strong>volveum FloatBuffer contendo as posições x, y e z dos vectores adicionados (via add(x, y, z)).No passo seguinte activamos o evaluator que utilizámos na função glMap1f e <strong>de</strong>senhamos os vértices geradoscom a ajuda da função glEvalCoord:// Activar o Evaluatorgl. glEnable ( GL2 . GL_MAP1_VERTEX_3 );// Desenhar os vertices da curvagl. glColor3f (0f, 0f, 0f);gl. glBegin ( GL2 . GL_LINE_STRIP );for ( float i= umin ; i< umax ; i ++)gl. glEvalCoord1f (i);gl. glEnd ();A fim <strong>de</strong> facilitar a compreensão dos pontos <strong>de</strong> controlo, <strong>de</strong>senhamo-los também:// Desenhar Pontos <strong>de</strong> Contrologl. glPointSize (5.0 f);gl. glColor3f (1f, 0f, 0f);gl. glBegin ( GL2 . GL_POINTS );for ( GLVector point : ctrlPoints )point . draw ();gl. glEnd ();O resultado final do nosso programa Bezier2D po<strong>de</strong> ser visualizado na Figura 10.12.Por uma questão <strong>de</strong> eficiência e a fim <strong>de</strong> tornar o código mais compacto, o OpenGL permite-nos <strong>de</strong>senhar acurva utilizando apenas as 2 funções glMapGrid e glEvalMesh, <strong>de</strong> forma que o código:gl. glBegin ( GL2 . GL_LINE_STRIP );for ( float i= umin ; i< umax ; i ++)gl. glEvalCoord1f (i);gl. glEnd ();Seria re-escrito da seguinte forma:// Mapear uma grelha <strong>de</strong> 100 pontos com u min=0 e u max =100gl. glMapGrid1d (100 , umin , umax );// Fazer o evaluate da grelha anterior utilizando linhas ( GL_LINE )gl. glEvalMesh1 ( GL2 . GL_LINE , ( int )umin , ( int ) umax );1 Neste caso, o valor 50 representa o ponto no meio da curva, o 25 a um quarto da curva, etc. . .107


Figura 10.12: Curva <strong>de</strong> Bézier 2D10.2.5 Superfícies 3DA forma <strong>de</strong> criar uma superfície <strong>de</strong> Bézier é muito semelhante à criação das curvas. Adicionamos apenas oparâmetro v que correspon<strong>de</strong> a um segundo domínio. O código completo do exemplo seguinte po<strong>de</strong> ser consultadono Apêndice C.8 referente ao programa Bezier3D.java.A diferença, à partida, em relação ao exemplo Bezier2D, é que neste utilizamos 3 conjuntos <strong>de</strong> 3 pontos <strong>de</strong>controlo cada.Começamos então por <strong>de</strong>finir os pontos <strong>de</strong> controlo, 3 na direcção <strong>de</strong> u por 3 na direcção <strong>de</strong> v:// Definir Pontos <strong>de</strong> ControloctrlPoints = new GLVectorList ();// v0ctrlPoints . add ( -4f, 0f, 4f); // Start (u0)ctrlPoints . add ( -2f, 4f, 4f); // Control (u0)ctrlPoints . add (4f, 0f, 4f); // End (u0)// v1ctrlPoints . add ( -4f, 0f, 0f); // Start (u1)ctrlPoints . add ( -2f, 4f, 0f); // Control (u1)ctrlPoints . add (4f, 0f, 0f); // End (u1)// v2ctrlPoints . add ( -4f, 0f, -4f); // Start (u2)ctrlPoints . add ( -2f, 4f, -4f); // Control (u2)ctrlPoints . add (4f, 0f, -4f); // End (u2)Seguidamente geramos o mapa bidmimensional <strong>de</strong> pontos para a superfície, utilizando a função glMap2f como primeiro argumento tendo o valor GL2.GL MAP2 VERTEX 3, em vez da função glMap1f, da seguinte forma:float umin = 0f; // Valor minimo do ufloat umax = 10f; // Valor maximo do ufloat vmin = 0f; // Valor minimo do vfloat vmax = 10f; // Valor maximo do vgl. glMap2f (GL2 . GL_MAP2_VERTEX_3 , // Tipo <strong>de</strong> dados geradosumin , // Valor minimo do uumax , // Valor maximo do u108


);3, // Distancia entre pontos ( dominio u)3, // Numero <strong>de</strong> pontos <strong>de</strong> controlo ( dominio u)vmin , // Valor minimo do vvmax , // Valor maximo do v9, // Distancia entre pontos ( dominio v)3, // Numero <strong>de</strong> pontos <strong>de</strong> controlo ( dominio v)ctrlPoints . toFloatBuffer () // FloatBuffer contendo pontos <strong>de</strong> controloO segundo e terceiro argumentos <strong>de</strong>finem os valores mínimo e máximo no domínio u. O quarto e quintoargumentos <strong>de</strong>finem, respectivamente, o intervalo entre os pontos do domínio u no buffer <strong>de</strong> dados e o número<strong>de</strong> pontos <strong>de</strong> controlo. O intervalo <strong>de</strong> dados para u é <strong>de</strong> 3 pela mesma razão do exemplo anterior. O número <strong>de</strong>pontos <strong>de</strong> controlo ao longo <strong>de</strong> u é <strong>de</strong> 3.O sexto e sétimo argumentos representam o valor mínimo e máximo do domínio v. O oitávo e nono argumentosrepresentam, respectivamente, o intervalo entre os pontos no domínio v e o número <strong>de</strong> pontos <strong>de</strong> controlo. Ointervalo no caso do domínio v é <strong>de</strong> 9, uma vez que para cada iteração <strong>de</strong> v existem 3 pontos em u (3 × 3 = 9).O número <strong>de</strong> pontos <strong>de</strong> controlo ao longo <strong>de</strong> v é igualmente <strong>de</strong> 3.O último argumento é o FloatBuffer contendo os pontos <strong>de</strong> controlo.Finalmente po<strong>de</strong>mos <strong>de</strong>senhar a nossa superfície <strong>de</strong> forma semelhante à utilizada para o exemplo anterior:// Desenhar os vertices da superficiegl. glColor3f (0f, 0f, 0f);// Mapear uma grelha <strong>de</strong> 10 × 10 pontos com u entre u min e u max e v entre v min e v maxgl. glMapGrid2d (10 , umin , umax , 10 , vmin , vmax );// Fazer o evaluate da grelha anterior utilizando linhasgl. glEvalMesh2 ( GL2 . GL_LINE , ( int )umin , ( int )umax , ( int )vmin , ( int ) vmax );O resultado final do programa Bezier3D po<strong>de</strong> ser analisado na Figura 10.13.Figura 10.13: Superfície 3D <strong>de</strong> Bézier10.2.6 Luzes e Vectores NormaisA fim <strong>de</strong> utilizar luzes com as nossas superfícies basta-nos substituir o código:gl. glEvalMesh2 ( GL2 . GL_LINE , ( int )umin , ( int )umax , ( int )vmin , ( int ) vmax );Pelo código:gl. glEvalMesh2 ( GL2 . GL_FILL , ( int )umin , ( int )umax , ( int )vmin , ( int ) vmax );gl. glEnable ( GL2 . GL_AUTO_NORMAL );O que produz o resultado da Figura 10.14, que po<strong>de</strong> ser conseguido correndo o programa BezierLighting.java(ver Apêndice C.9).109


Figura 10.14: Superfície <strong>de</strong> Bézier com Lighting110


Capítulo 11Conceitos Avançados11.1 Display ListsOs comandos (funções) especificados no código do nosso programa em OpenGL, não são inconsequentes, sãonormalmente compilados para um conjunto <strong>de</strong> instruções que são colocadas num buffer. Quando apropriado, obuffer <strong>de</strong> comandos é enviado para o hardware.Para cenários geometricamente complexos, este processo po<strong>de</strong> ser reptido milhares <strong>de</strong> vezes, antes que umasimples imagem seja <strong>de</strong>senhada no ecrã. Em várias situações, a geometria repete-se <strong>de</strong> frame para frame, o querepresenta um overhead <strong>de</strong>snecessário <strong>de</strong> processamento.O OpenGL oferece a possibilida<strong>de</strong> <strong>de</strong> agregar um conjunto <strong>de</strong> comandos numa lista pré-processada. Esta lista<strong>de</strong> comandos po<strong>de</strong>, mais tar<strong>de</strong> ser copiada para o command buffer, poupando o tempo da chamada e compilaçãodos comandos.Esta lista <strong>de</strong> comandos pré-processados é chamada <strong>de</strong> display list. O código necessário à criação <strong>de</strong> displaylists po<strong>de</strong>rá ser:int name = ...; // I<strong>de</strong>ntificador da listagl. glNewList (name , GL2 . GL_COMPILE );// Comandos OpenGLgl. glEndList ();A função glNewList cria a display list i<strong>de</strong>ntificada pelo valor <strong>de</strong> name, armazenando os comandos até que éencontrada a função glEndList. O parâmetro GL2.GL COMPILE, diz ao OpenGL para compilar esta lista. Esteprocesso é normalmente levado a cabo na inicialização do nosso programa (função init()) e mais tar<strong>de</strong> a lista éinvocada durante o processo <strong>de</strong> ren<strong>de</strong>rização.O parâmetro name po<strong>de</strong>rá tomar qualquer valor inteiro (unsigned int). No caso <strong>de</strong> ser especificado duasvezes o mesmo valor ao invocar glNewList, a lista actualmente associada ao i<strong>de</strong>ntificador, é substituída pelanova especificação.A fim <strong>de</strong> evitar a substituição aci<strong>de</strong>ntal <strong>de</strong> uma lista previamente criada, po<strong>de</strong>mos pedir ao OpenGL que gereum conjunto <strong>de</strong> i<strong>de</strong>ntificadores (um para cada lista).int NumLists = 3;// ...int first = gl. glGenLists ( NumLists );O código em cima gera os i<strong>de</strong>ntificadors para 3 display lists consecutivas, on<strong>de</strong> first será o valor do primeiroi<strong>de</strong>ntificador. Para especificarmos a lista <strong>de</strong> comandos para as listas recém-criadas, po<strong>de</strong>mos escrever:int first = gl. glGenLists ( NumLists );int second = first + 1;int third = first + 2;gl. glNewList ( first , GL2 . GL_COMPILE );// Desenhar Geometria (1)gl. glEndList ();gl. glNewList ( second , GL2 . GL_COMPILE );// Desenhar Geometria (2)gl. glEndList ();gl. glNewList ( third , GL2 . GL_COMPILE );111


Desenhar Geometria (3)gl. glEndList ();Os comandos da lista, ou listas criadas durante o processo <strong>de</strong> inicialização po<strong>de</strong>m ser executados durante oprocesso <strong>de</strong> ren<strong>de</strong>ring (função display()), utilizando a função:glCallLists ( int listId );Sendo o argumento listId o valor do i<strong>de</strong>ntificador da lista.Quando as display lists não estiverem a ser usadas, po<strong>de</strong>mos libertar os recursos reservados para elas utilizandoa função glDeleteLists:gl. glDeleteLists ( first , NumLists );On<strong>de</strong> o primeiro argumento é o primeiro i<strong>de</strong>ntificador da lista e o segundo argumento é o número <strong>de</strong> listas aapagar. Este procedimento é normalmente levado a cabo no encerramento do programa (ex.: método dispose()).11.1.1 Prós e ContrasA utilização <strong>de</strong> display lists representam, em hardwre da maior parte dos fabricantes, um aumento significativono <strong>de</strong>sempenho. O seu uso é i<strong>de</strong>al para acelerar mudanças <strong>de</strong> estado (p.ex.: activar/<strong>de</strong>sactivar lighting, etc. . . ).O carregamento <strong>de</strong> texturas e a leitura do color buffer (ex.: glReadPixel e glTexImage2D) não fazem muitosentido <strong>de</strong>ntro <strong>de</strong> uma lista. Faz antes sentido a criação <strong>de</strong> texture objects e a sua invocação (com glBindTexture)<strong>de</strong>ntro da lista.Dentro <strong>de</strong> uma display list po<strong>de</strong>mos ainda invocar a execução <strong>de</strong> uma outra, não sendo possível, no entanto,chamadas a (glNewList/glEndList) <strong>de</strong>ntro <strong>de</strong> uma display list.112


Apêndice AExemplosA.1 Exemplo Simplespublic class OpenGL0 extends GLFrame {public OpenGL0 () {setSize (300 , 300) ;}@Overri<strong>de</strong>public void display ( GLAutoDrawable drawable ) {}@Overri<strong>de</strong>public void init ( GLAutoDrawable drawable ) {GLCanvas canvas = getCanvas ();GLCapabilities cap = canvas . getChosenGLCapabilities ();cap . setDoubleBuffered ( false );}GL2 gl = drawable . getGL (). getGL2 ();gl. glClearColor (0.0f, 0.0f, 1.0f, 1.0 f);gl. glClear (GL. GL_COLOR_BUFFER_BIT );gl. glFlush ();public static void main ( String [] args ) {new OpenGL0 (). setVisible ( true );}}113


Apêndice BTabelasB.1 Formato <strong>de</strong> Imagem (pixmap)TipoGL RGBGL RGBAGL BGR / GL BGR EXTGL BGRA / GL BGRA EXTGL REDGL GREENGL BLUEGL ALPHAGL LUMINANCEGL LUMINANCE ALPHAGL STENCIL INDEXGL DEPTH COMPONENTDescriçãoCores representadas por red, green e blueCores representadas por red, green, blue e alphaCores representadas por blue, green e redCores representadas por blue, green, red e alphaCada pixel contém um único componente <strong>de</strong> redCada pixel contém um único componente <strong>de</strong> greenCada pixel contém um único componente <strong>de</strong> blueCada pixel contém um único componente <strong>de</strong> alphaCada pixel contém um único componente <strong>de</strong> intensida<strong>de</strong>(luminance)Cada pixel contém um componente <strong>de</strong> intensida<strong>de</strong> seguidopor um componente <strong>de</strong> alphaCada pixel contém um único valor <strong>de</strong> stencilCada pixel contém um único valor <strong>de</strong> profundida<strong>de</strong>(<strong>de</strong>pth)Tabela B.1: Formato <strong>de</strong> Imagem (pixmap)114


B.2 Tipo <strong>de</strong> Dados (pixmap)TipoGL UNSIGNED BYTEGL BYTEGL BITMAPGL UNSIGNED SHORTGL SHORTGL UNSIGNED INTGL INTGL FLOATGL UNSIGNED BYTE 3 2 2GL UNSIGNED BYTE 2 3 3 REVGL UNSIGNED SHORT 5 6 5GL UNSIGNED SHORT 5 6 5 REVGL UNSIGNED SHORT 4 4 4 4GL UNSIGNED SHORT 4 4 4 4 REVGL UNSIGNED SHORT 5 5 5 1GL UNSIGNED SHORT 1 5 5 5 REVGL UNSIGNED INT 8 8 8 8GL UNSIGNED INT 8 8 8 8 REVGL UNSIGNED INT 10 10 10 2GL UNSIGNED INT 2 10 10 10 REVDescriçãoCada componente é um unsigned integer <strong>de</strong> 8-bitinteger <strong>de</strong> 8-bitApenas bits, sem informação <strong>de</strong> cor (equivaliente aoglBitmap)unsigned integer <strong>de</strong> 16-bitsigned integer <strong>de</strong> 16-bitunsigned integer <strong>de</strong> 32-bitsigned integer <strong>de</strong> 32-bitsingle precision floatPacked RGB valuesPacked RGB valuesPacked RGB valuesPacked RGB valuesPacked RGBA valuesPacked RGBA valuesPacked RGBA valuesPacked RGBA valuesPacked RGBA valuesPacked RGBA valuesPacked RGBA valuesPacked RGBA valuesTabela B.2: Tipo <strong>de</strong> Dados (pixmap)115


Apêndice CCódigo FonteC.1 Example0.javapackage pt.ipb . esact .cg. examples ;import java . awt . Bor<strong>de</strong>rLayout ;import java . util . ArrayList ;import java . util . List ;import javax . media . opengl . GL2 ;import javax . media . opengl . GLAutoDrawable ;import javax . media . opengl . GLEventListener ;import javax . media . opengl . awt . GLCanvas ;import javax . media . opengl . glu . GLU ;import javax . swing . JFrame ;import pt.ipb . esact .cg.gl. CGEngine ;import pt.ipb . esact .cg.gl. GLVector ;import pt.ipb . esact .cg.gl. tools . GlUtil ;import com . jogamp . opengl . util . FPSAnimator ;import com . jogamp . opengl . util . gl2 . GLUT ;@SuppressWarnings (" serial ")public class Example0 extends JFrame implements GLEventListener {private GLU glu = new GLU ();private GLUT glut = new GLUT ();private GLCanvas canvas ;private GLVector fLightPos = new GLVector ( -100f, 100f, 50f, 1f);private float fNoLight [] = { 0.0f, 0.0f, 0.0f, 0.0 f };private float fLowLight [] = { 0.25f, 0.25f, 0.25f, 1.0 f };private float fBrightLight [] = { 1.0f, 1.0f, 1.0f, 1.0 f };private static final float FloorSize = 20f;private static final int NumSpheres = 30;private List < GLVector > spherePositions = new ArrayList < GLVector >();public static void main ( String [] args ) {new Example0 ();}116


public Example0 () {super (" Mo<strong>de</strong>lo OpenGL ");setUn<strong>de</strong>corated ( true );setDefaultCloseOperation ( JFrame . EXIT_ON_CLOSE );setSize (600 , 600);canvas = new GLCanvas ();add ( canvas , Bor<strong>de</strong>rLayout . CENTER );canvas . addGLEventListener ( this );GLVector location = new GLVector (0f, .5f, .5f);new CGEngine ( canvas , location );setVisible ( true );canvas . requestFocus ();}FPSAnimator animator = new FPSAnimator ( canvas , 25);animator . start ();public void dispose ( GLAutoDrawable drawable ) {}@Overri<strong>de</strong>public void init ( GLAutoDrawable drawable ) {GL2 gl = drawable . getGL (). getGL2 ();}// Grayish backgroundgl. glClearColor ( fLowLight [0] , fLowLight [1] , fLowLight [2] , fLowLight [3]);// Cull backs of polygonsgl. glCullFace ( GL2 . GL_BACK );gl. glFrontFace ( GL2 . GL_CCW );gl. glEnable ( GL2 . GL_CULL_FACE );gl. glEnable ( GL2 . GL_DEPTH_TEST );spherePositions = GlUtil . generateRandomPositions ( FloorSize , NumSpheres );setupLight (gl );private void setupLight ( GL2 gl) {// Setup light parametersgl. glLightMo<strong>de</strong>lfv ( GL2 . GL_LIGHT_MODEL_AMBIENT , fNoLight , 0);gl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_AMBIENT , fLowLight , 0);gl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_DIFFUSE , fBrightLight , 0);gl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_SPECULAR , fBrightLight , 0);gl. glEnable ( GL2 . GL_LIGHTING );gl. glEnable ( GL2 . GL_LIGHT0 );}// Mostly use material trackinggl. glEnable ( GL2 . GL_COLOR_MATERIAL );gl. glColorMaterial ( GL2 . GL_FRONT , GL2 . GL_AMBIENT_AND_DIFFUSE );gl. glMateriali ( GL2 . GL_FRONT , GL2 . GL_SHININESS , 128);@Overri<strong>de</strong>public void display ( GLAutoDrawable drawable ) {GL2 gl = drawable . getGL (). getGL2 ();gl. glClear ( GL2 . GL_COLOR_BUFFER_BIT | GL2 . GL_DEPTH_BUFFER_BIT );gl. glMaterialfv ( GL2 . GL_FRONT , GL2 . GL_SPECULAR , fBrightLight , 0);117


gl. glPushMatrix ();gl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_POSITION , fLightPos . toArray () , 0);// Desenhar um Torus Ver<strong>de</strong>gl. glColor3f (0 , 1f, 0);drawWorld (gl );}gl. glColor4f (0.60f, .40f, .10f, .5f);GlUtil . drawGround ( FloorSize , 1f);gl. glPopMatrix ();private void drawWorld ( GL2 gl) {// Desenhar o Torusgl. glPushMatrix ();gl. glTranslatef (0 , .5f, 0);glut . glutSolidTorus (.1f, .2f, 61 , 37);gl. glPopMatrix ();}// Desenhar esferas aleatoriamentefor ( GLVector s : spherePositions ) {gl. glPushMatrix ();gl. glTranslatef (s.x() , s.y() , s.z ());glut . glutSolidSphere (s.w() , 20 , 10);gl. glPopMatrix ();}@Overri<strong>de</strong>public void reshape ( GLAutoDrawable drawable , int x, int y, int width , int height ) {GL2 gl = drawable . getGL (). getGL2 ();if ( height == 0) height = 1;gl. glViewport (0 , 0, canvas . getSize (). width , canvas . getSize (). height );gl. glMatrixMo<strong>de</strong> ( GL2 . GL_PROJECTION );gl. glLoadI<strong>de</strong>ntity ();float volume = 100 f;float fAspect = ( float ) width / ( float ) height ;glu . gluPerspective (100f, fAspect , .001f, volume * 5f);}}C.2 Imaging.javapackage pt.ipb . esact .cg. examples ;import java . awt . Bor<strong>de</strong>rLayout ;import java . awt . event . KeyAdapter ;import java . awt . event . KeyEvent ;import java .io. IOException ;import java .io. InputStream ;import java . nio . ByteBuffer ;import java . nio . FloatBuffer ;import java . nio . IntBuffer ;import javax . media . opengl . GL2 ;import javax . media . opengl . GLAutoDrawable ;import javax . media . opengl . GLEventListener ;import javax . media . opengl . awt . GLCanvas ;import javax . media . opengl . glu . GLU ;import javax . swing . JFrame ;118


import com . jogamp . opengl . util . FPSAnimator ;import com . jogamp . opengl . util . texture . spi . TGAImage ;@SuppressWarnings (" serial ")public class Imaging extends JFrame implements GLEventListener {private GLU glu = new GLU ();private GLCanvas canvas ;private ByteBuffer bb;private TGAImage img ;private Ren<strong>de</strong>rMo<strong>de</strong> ren<strong>de</strong>rMo<strong>de</strong> = Ren<strong>de</strong>rMo<strong>de</strong> . Normal ;static enum Ren<strong>de</strong>rMo<strong>de</strong> {GaussianBlur , Sharpen , Emboss , Invert , Brighten , Normal}public static void main ( String [] args ) {new Imaging ();}public Imaging () {super (" Mo<strong>de</strong>lo OpenGL ");setUn<strong>de</strong>corated ( true );setDefaultCloseOperation ( JFrame . EXIT_ON_CLOSE );setSize (512 , 512);canvas = new GLCanvas ();add ( canvas , Bor<strong>de</strong>rLayout . CENTER );canvas . addGLEventListener ( this );canvas . addKeyListener ( new KeyAdapter () {@Overri<strong>de</strong>public void keyReleased ( KeyEvent e) {switch (e. getKeyCo<strong>de</strong> ()) {case KeyEvent . VK_1 : ren<strong>de</strong>rMo<strong>de</strong> = Ren<strong>de</strong>rMo<strong>de</strong> . Normal ; break ;case KeyEvent . VK_2 : ren<strong>de</strong>rMo<strong>de</strong> = Ren<strong>de</strong>rMo<strong>de</strong> . Brighten ; break ;case KeyEvent . VK_3 : ren<strong>de</strong>rMo<strong>de</strong> = Ren<strong>de</strong>rMo<strong>de</strong> . Invert ; break ;case KeyEvent . VK_4 : ren<strong>de</strong>rMo<strong>de</strong> = Ren<strong>de</strong>rMo<strong>de</strong> . Emboss ; break ;case KeyEvent . VK_5 : ren<strong>de</strong>rMo<strong>de</strong> = Ren<strong>de</strong>rMo<strong>de</strong> . Sharpen ; break ;case KeyEvent . VK_6 : ren<strong>de</strong>rMo<strong>de</strong> = Ren<strong>de</strong>rMo<strong>de</strong> . GaussianBlur ; break ;case KeyEvent . VK_Q : // SairsetVisible ( false );dispose ();System . exit (0);break ;<strong>de</strong>fault :break ;}}});setVisible ( true );canvas . requestFocus ();}FPSAnimator animator = new FPSAnimator ( canvas , 25);animator . start ();public void dispose ( GLAutoDrawable drawable ) {}@Overri<strong>de</strong>119


public void init ( GLAutoDrawable drawable ) {GL2 gl = drawable . getGL (). getGL2 ();// Grayish backgroundgl. glClearColor (0f, 0f, 0f, 0f);}try {InputStream stream = Imaging . class . getResourceAsStream (" horse . tga ");img = TGAImage . read ( stream );bb = img . getData ();stream . close ();} catch ( IOException e) {e. printStackTrace ();}@Overri<strong>de</strong>public void display ( GLAutoDrawable drawable ) {GL2 gl = drawable . getGL (). getGL2 ();// Clear the window with current clearing colorgl. glClear ( GL2 . GL_COLOR_BUFFER_BIT | GL2 . GL_DEPTH_BUFFER_BIT );// Armazena a informacao do viewPort lida com glGet// Contem : (x, y, width , height )IntBuffer iViewport = IntBuffer . allocate (4);// Tabela <strong>de</strong> cores invertida (3 cores * 256 niveis )ByteBuffer invertTable = ByteBuffer . allocate (256 * 3);// Matriz ( convolution kernel ) para efectuar o sharpenFloatBuffer mSharpen = FloatBuffer . wrap ( new float [] {0.0f, -1.0f, 0.0f,-1.0f, 5.0f, -1.0f,0.0f, -1.0f, 0.0 f});// Matriz ( convolution kernel ) para efectuar o embossFloatBuffer mEmboss = FloatBuffer . wrap ( new float [] {2.0f, 0.0f, 0.0f,0.0f, -1.0f, 0.0f,0.0f, 0.0f, -1.0f});// Matriz ( convolution kernel ) para efectuar gaussian blur/*| 1 2 1 || 2 4 2 || 1 2 1 |*/float norm = 16f; // somatorio dos valores da matrizFloatBuffer mGaussian = FloatBuffer . wrap ( new float [] {1f/norm , 2f/norm , 1f/norm ,2f/norm , 4f/norm , 2f/norm ,1f/norm , 2f/norm , 1f/ norm});// Posicao do raster comeca no canto inferior esquerdo por omissaogl. glRasterPos2i (0 , 0);// Ler o estado actual do viewportgl. glGetIntegerv ( GL2 . GL_VIEWPORT , iViewport );// Esticar a imagem para ficar do tamanho do viewport120


gl. glPixelZoom (( float ) iViewport . get (2) / ( float ) img . getWidth () ,( float ) iViewport . get (3) / ( float ) img . getHeight ());//switch ( ren<strong>de</strong>rMo<strong>de</strong> ) {case Sharpen : // Sharpengl. glConvolutionFilter2D (GL2 . GL_CONVOLUTION_2D , GL2 . GL_RGB , 3, 3,GL2 . GL_LUMINANCE , GL2 . GL_FLOAT , mSharpen);gl. glEnable ( GL2 . GL_CONVOLUTION_2D );break ;case Emboss : // Embossgl. glConvolutionFilter2D (GL2 . GL_CONVOLUTION_2D , GL2 . GL_RGB , 3, 3,GL2 . GL_LUMINANCE , GL2 . GL_FLOAT , mEmboss );gl. glEnable ( GL2 . GL_CONVOLUTION_2D );break ;case Invert : // Invertfor ( int i = 0; i < 255; i ++) {invertTable . put (i * 3 + 0, ( byte ) (255 - i ));invertTable . put (i * 3 + 1, ( byte ) (255 - i ));invertTable . put (i * 3 + 2, ( byte ) (255 - i ));}gl. glColorTable (GL2 . GL_COLOR_TABLE , GL2 . GL_RGB , 256 , GL2 . GL_RGB ,GL2 . GL_UNSIGNED_BYTE , invertTable);gl. glEnable ( GL2 . GL_COLOR_TABLE );break ;case Brighten : // Brighten// Mudar para o matrix mo<strong>de</strong> Colorgl. glMatrixMo<strong>de</strong> ( GL2 . GL_COLOR );// Aumentar todas as cores em 50%gl. glScalef (2f, 2f, 2f);// Voltar ao Mo<strong>de</strong>lViewgl. glMatrixMo<strong>de</strong> ( GL2 . GL_MODELVIEW );break ;case GaussianBlur : // Gaussian Blurgl. glConvolutionFilter2D (GL2 . GL_CONVOLUTION_2D , GL2 . GL_RGB , 3, 3,GL2 . GL_LUMINANCE , GL2 . GL_FLOAT , mGaussian );gl. glEnable ( GL2 . GL_CONVOLUTION_2D );break ;}case Normal : // Normal<strong>de</strong>fault :break ;// Desenhar a Imagem com as alteracoes efectuadas (ou nao )gl. glDrawPixels (img . getWidth () , img . getHeight () ,img . getGLFormat () ,GL2 . GL_UNSIGNED_BYTE , bb );121


Reverter as ( possiveis ) transformacoes efectuadas// na matriz Colorgl. glMatrixMo<strong>de</strong> ( GL2 . GL_COLOR );gl. glLoadI<strong>de</strong>ntity ();// Voltar ao Mo<strong>de</strong>lViewgl. glMatrixMo<strong>de</strong> ( GL2 . GL_MODELVIEW );// Desactivar as alteracoesgl. glDisable ( GL2 . GL_CONVOLUTION_2D );gl. glDisable ( GL2 . GL_COLOR_TABLE );}@Overri<strong>de</strong>public void reshape ( GLAutoDrawable drawable , int x, int y, int width , int height ) {GL2 gl = drawable . getGL (). getGL2 ();gl. glViewport (0 , 0, width , height );gl. glMatrixMo<strong>de</strong> ( GL2 . GL_PROJECTION );gl. glLoadI<strong>de</strong>ntity ();glu . gluOrtho2D (0 , width , 0, height );}gl. glMatrixMo<strong>de</strong> ( GL2 . GL_MODELVIEW );gl. glLoadI<strong>de</strong>ntity ();}C.3 Pyramid.javapackage pt.ipb . esact .cg. examples ;import java . awt . Bor<strong>de</strong>rLayout ;import java . awt . Container ;import java .io. IOException ;import java .io. InputStream ;import java . nio . ByteBuffer ;import javax . media . opengl . GL2 ;import javax . media . opengl . GLAutoDrawable ;import javax . media . opengl . GLEventListener ;import javax . media . opengl . awt . GLCanvas ;import javax . media . opengl . glu . GLU ;import javax . swing . JFrame ;import pt.ipb . esact .cg.gl. CGEngine ;import pt.ipb . esact .cg.gl. GLVector ;import pt.ipb . esact .cg.gl. tools . GlTools ;import pt.ipb . esact .cg.gl. tools . GlUtil ;import com . jogamp . opengl . util . FPSAnimator ;import com . jogamp . opengl . util . texture . spi . TGAImage ;@SuppressWarnings (" serial ")public class Pyramid extends JFrame implements GLEventListener {private GLU glu = new GLU ();private GLCanvas canvas ;FPSAnimator animator ;122


private GLVector lightPos = new GLVector ( -10f, 5f, 5f, 1f);private float whiteLight [] = { 0.05f, 0.05f, 0.05f, 1.0 f };private float sourceLight [] = { 0.25f, 0.25f, 0.25f, 1.0 f };private CGEngine e;private TGAImage img ;private ByteBuffer bb;private GLVector cTop ;private GLVector cBackLeft ;private GLVector cBackRight ;private GLVector cFrontRight ;private GLVector cFrontLeft ;public static void main ( String [] args ) {new Pyramid ();}public Pyramid () {super (" Mo<strong>de</strong>lo OpenGL ");setUn<strong>de</strong>corated ( true );setDefaultCloseOperation ( JFrame . EXIT_ON_CLOSE );setSize (600 , 600);Container p = getContentPane ();Bor<strong>de</strong>rLayout l = new Bor<strong>de</strong>rLayout (5 , 5);p. setLayout (l);canvas = new GLCanvas ();p. add ( canvas , Bor<strong>de</strong>rLayout . CENTER );canvas . addGLEventListener ( this );GLVector location = new GLVector (1f, 1f, 1f);e = new CGEngine ( canvas , location );e. camera (). lookAt (0f, 1f, 0f);setVisible ( true );canvas . requestFocus ();}animator = new FPSAnimator ( canvas , 25);animator . start ();@Overri<strong>de</strong>public void dispose ( GLAutoDrawable drawable ) {}@Overri<strong>de</strong>public void init ( GLAutoDrawable drawable ) {GL2 gl = drawable . getGL (). getGL2 ();// Fundo Pretogl. glClearColor (0f, 0f, 0f, 1f);gl. glEnable ( GL2 . GL_CULL_FACE ); // Nao calcular <strong>de</strong>ntro dos objectosgl. glFrontFace ( GL2 . GL_CCW ); // Poligonos CCW tem a face para foragl. glEnable ( GL2 . GL_DEPTH_TEST ); // Remocao <strong>de</strong> Superficies Escondidas123


Activar as Luzesgl. glEnable ( GL2 . GL_LIGHTING );// Configurar e Activar a luzgl. glLightMo<strong>de</strong>lfv ( GL2 . GL_LIGHT_MODEL_AMBIENT , whiteLight , 0);gl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_AMBIENT , sourceLight , 0);gl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_DIFFUSE , sourceLight , 0);gl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_POSITION , lightPos . toArray () , 0);gl. glEnable ( GL2 . GL_LIGHT0 );// Activar o Color Trackinggl. glEnable ( GL2 . GL_COLOR_MATERIAL );// Proprieda<strong>de</strong>s dos materiais obe<strong>de</strong>cem ’a luzgl. glColorMaterial ( GL2 . GL_FRONT , GL2 . GL_AMBIENT_AND_DIFFUSE );// Carregar e Ler a imagem para a nossa texturatry {InputStream stream = Pyramid . class . getResourceAsStream (" stone . tga ");img = TGAImage . read ( stream );bb = img . getData ();stream . close ();} catch ( IOException e) {e. printStackTrace ();}gl. glTexImage2D ( GL2 . GL_TEXTURE_2D , 0, GL2 . GL_RGBA ,img . getWidth () , img . getHeight () ,0, img . getGLFormat () , GL2 . GL_UNSIGNED_BYTE , bb );// Parametros da textura ( ignorar para ja)gl. glTexParameteri ( GL2 . GL_TEXTURE_2D ,GL2 . GL_TEXTURE_MIN_FILTER , GL2 . GL_LINEAR );gl. glTexParameteri ( GL2 . GL_TEXTURE_2D ,GL2 . GL_TEXTURE_MAG_FILTER , GL2 . GL_LINEAR );gl. glTexParameteri ( GL2 . GL_TEXTURE_2D ,GL2 . GL_TEXTURE_WRAP_S , GL2 . GL_CLAMP_TO_EDGE );gl. glTexParameteri ( GL2 . GL_TEXTURE_2D ,GL2 . GL_TEXTURE_WRAP_T , GL2 . GL_CLAMP_TO_EDGE );// Configuracao do Texture Environmentgl. glTexEnvi ( GL2 . GL_TEXTURE_ENV ,GL2 . GL_TEXTURE_ENV_MODE , GL2 . GL_MODULATE );// Activar as texturasgl. glEnable ( GL2 . GL_TEXTURE_2D );// Cantos da Pirami<strong>de</strong>cTop = new GLVector (0.0f, .80f, 0.0 f);cBackLeft = new GLVector ( -0.5f, 0.0f, -.50f);cBackRight = new GLVector (0.5f, 0.0f, -0.50 f);cFrontRight = new GLVector (0.5f, 0.0f, 0.5 f);cFrontLeft = new GLVector ( -0.5f, 0.0f, 0.5 f);}private float yRot = 0f;@Overri<strong>de</strong>public void display ( GLAutoDrawable drawable ) {GL2 gl = drawable . getGL (). getGL2 ();yRot += .1f;if( yRot >= GlTools . GL_PI * 2f)yRot = 0;124


GLVector vNormal ;// Limpar a cor <strong>de</strong> Fundogl. glClear ( GL2 . GL_COLOR_BUFFER_BIT | GL2 . GL_DEPTH_BUFFER_BIT );gl. glColor3f (.2f, .2f, .2f);gl. glDisable ( GL2 . GL_TEXTURE_2D );GlUtil . drawGround (20f, 1f);gl. glEnable ( GL2 . GL_TEXTURE_2D );gl. glPushMatrix ();// Levantar um pouco do chaogl. glTranslatef (0f, .5f, 0f);gl. glRotatef (( float ) Math . toDegrees ( yRot ), 0, 1f, 0);// Desenhar a Pirami<strong>de</strong>gl. glColor3f (1f, 1f, 1f);gl. glBegin ( GL2 . GL_TRIANGLES );// Seccao do Fundo (2 triangulos )gl. glNormal3f (0f, -1f, 0f); // Virada para baixo// Bottom 0gl. glTexCoord2f (1f, 1f);cBackRight . draw ();gl. glTexCoord2f (0f, 0f);cFrontLeft . draw ();gl. glTexCoord2f (0f, 1f);cBackLeft . draw ();// Bottom 1gl. glTexCoord2f (1f, 1f);cBackRight . draw ();gl. glTexCoord2f (1f, 0f);cFrontRight . draw ();gl. glTexCoord2f (0f, 0f);cFrontLeft . draw ();// Face da FrentevNormal = GlTools . getNormalVector (cTop , cFrontLeft , cFrontRight );gl. glNormal3fv ( vNormal . toArray () , 0);gl. glTexCoord2f (.5f, 1f);cTop . draw ();gl. glTexCoord2f (0f, 0f);cFrontLeft . draw ();gl. glTexCoord2f (1f, 0f);cFrontRight . draw ();// Face da EsquerdavNormal = GlTools . getNormalVector (cTop , cBackLeft , cFrontLeft );gl. glNormal3fv ( vNormal . toArray () , 0);gl. glTexCoord2f (.5f, 1f);cTop . draw ();gl. glTexCoord2f (0f, 0f);cBackLeft . draw ();gl. glTexCoord2f (1f, 0f);cFrontLeft . draw ();// Face <strong>de</strong> TrasvNormal = GlTools . getNormalVector (cTop , cBackRight , cBackLeft );gl. glNormal3fv ( vNormal . toArray () , 0);gl. glTexCoord2f (.5f, 1f);cTop . draw ();125


gl. glTexCoord2f (0f, 0f);cBackRight . draw ();gl. glTexCoord2f (1f, 0f);cBackLeft . draw ();// Face da DireitavNormal = GlTools . getNormalVector (cTop , cFrontRight , cBackRight );gl. glNormal3fv ( vNormal . toArray () , 0);gl. glTexCoord2f (.5f, 1f);cTop . draw ();gl. glTexCoord2f (0f, 0f);cFrontRight . draw ();gl. glTexCoord2f (1f, 0f);cBackRight . draw ();gl. glEnd ();gl. glPopMatrix ();}@Overri<strong>de</strong>public void reshape ( GLAutoDrawable drawable , int x, int y, int width , int height ) {GL2 gl = drawable . getGL (). getGL2 ();}if ( height == 0)height = 1; // prevnir divisao por 0// O meu viewport tera o tamanho da janela ( width x height )gl. glViewport (0 , 0, canvas . getSize (). width , canvas . getSize (). height );// Mudar para a matiz <strong>de</strong> PROJECTIONgl. glMatrixMo<strong>de</strong> ( GL2 . GL_PROJECTION );gl. glLoadI<strong>de</strong>ntity ();float volume = 100 f;float fAspect = ( float ) width / ( float ) height ;// Projeccao em Perspectivaglu . gluPerspective (100f, fAspect , .001f, volume * 5f);}C.4 TexGen.javapackage pt.ipb . esact .cg. examples ;import java . awt . Bor<strong>de</strong>rLayout ;import java . awt . Container ;import java . awt . event . KeyEvent ;import java .io. IOException ;import java .io. InputStream ;import java . nio . FloatBuffer ;import java . nio . IntBuffer ;import javax . media . opengl . GL2 ;import javax . media . opengl . GLAutoDrawable ;import javax . media . opengl . GLEventListener ;import javax . media . opengl . awt . GLCanvas ;import javax . media . opengl . glu . GLU ;import javax . swing . JFrame ;126


import pt.ipb . esact .cg.gl. CGEngine ;import pt.ipb . esact .cg.gl. GLKeyboardAdapter ;import pt.ipb . esact .cg.gl. GLVector ;import pt.ipb . esact .cg.gl. tools . GlTools ;import com . jogamp . opengl . util . FPSAnimator ;import com . jogamp . opengl . util . gl2 . GLUT ;import com . jogamp . opengl . util . texture . spi . TGAImage ;@SuppressWarnings (" serial ")public class TexGen extends JFrame implements GLEventListener {private GLU glu = new GLU ();private GLUT glut = new GLUT ();private GLCanvas canvas ;FPSAnimator animator ;private float bgColor [] = { 0f, 0f, 0f, 0f };private GLVector location = new GLVector (0f, 0f, 5f);private GLVector lookAt = new GLVector (0f, 0f, 0f);private IntBuffer textures = IntBuffer . allocate (2);private CGEngine e;// I<strong>de</strong>ntificadores das Texturaspublic static final int Stripes = 0, Environment = 1;public static final int ObjectLinear = 0, EyeLinear = 1, SphereMap = 2;private int ren<strong>de</strong>rMo<strong>de</strong> = SphereMap ;private TGAImage environment ;public static void main ( String [] args ) {new TexGen ();}public TexGen () {super (" TexGen ");setDefaultCloseOperation ( JFrame . EXIT_ON_CLOSE );setSize (600 , 600);Container p = getContentPane ();Bor<strong>de</strong>rLayout l = new Bor<strong>de</strong>rLayout (5 , 5);p. setLayout (l);canvas = new GLCanvas ();canvas . addGLEventListener ( this );p. add ( canvas , Bor<strong>de</strong>rLayout . CENTER );e = new CGEngine ( canvas , location );e. camera (). lookAt ( lookAt );setVisible ( true );canvas . requestFocus ();}animator = new FPSAnimator ( canvas , 25);animator . start ();127


@Overri<strong>de</strong>public void dispose ( GLAutoDrawable drawable ) {}@Overri<strong>de</strong>public void init ( GLAutoDrawable drawable ) {GL2 gl = drawable . getGL (). getGL2 ();gl. glClearColor ( bgColor [0] , bgColor [1] , bgColor [2] , bgColor [3]);// Cull backs of polygonsgl. glFrontFace ( GL2 . GL_CCW );gl. glEnable ( GL2 . GL_CULL_FACE );gl. glEnable ( GL2 . GL_DEPTH_TEST );gl. glEnable ( GL2 . GL_BACK );// As texturas substitem o material da geometriagl. glTexEnvi ( GL2 . GL_TEXTURE_ENV , GL2 . GL_TEXTURE_ENV_MODE , GL2 . GL_DECAL );// 2 Texturasgl. glGenTextures (2 , textures );/*** Carregamento da textura stripes*/environment = loadTexture (" environment . tga ");gl. glBindTexture ( GL2 . GL_TEXTURE_2D , textures . get ( Environment ));gl. glTexImage2D ( GL2 . GL_TEXTURE_2D , 0, GL2 . GL_RGBA ,environment . getWidth () , environment . getHeight () ,0, environment . getGLFormat () , GL2 . GL_UNSIGNED_BYTE ,environment . getData ());gl. glTexParameterf ( GL2 . GL_TEXTURE_2D ,GL2 . GL_TEXTURE_MIN_FILTER , GL2 . GL_LINEAR );gl. glTexParameterf ( GL2 . GL_TEXTURE_2D ,GL2 . GL_TEXTURE_MAG_FILTER , GL2 . GL_LINEAR );gl. glTexParameterf ( GL2 . GL_TEXTURE_2D ,GL2 . GL_TEXTURE_WRAP_S , GL2 . GL_REPEAT );gl. glTexParameterf ( GL2 . GL_TEXTURE_2D ,GL2 . GL_TEXTURE_WRAP_T , GL2 . GL_REPEAT );/*** Carregamento da textura <strong>de</strong> ambiente*/TGAImage sriptes = loadTexture (" stripes . tga ");gl. glBindTexture ( GL2 . GL_TEXTURE_2D , textures . get ( Stripes ));gl. glTexImage2D ( GL2 . GL_TEXTURE_2D , 0, GL2 . GL_RGBA ,sriptes . getWidth () , sriptes . getHeight () ,0, sriptes . getGLFormat () , GL2 . GL_UNSIGNED_BYTE ,sriptes . getData ());gl. glTexParameterf ( GL2 . GL_TEXTURE_2D ,GL2 . GL_TEXTURE_MIN_FILTER , GL2 . GL_LINEAR );gl. glTexParameterf ( GL2 . GL_TEXTURE_2D ,GL2 . GL_TEXTURE_MAG_FILTER , GL2 . GL_LINEAR );gl. glTexParameterf ( GL2 . GL_TEXTURE_2D ,GL2 . GL_TEXTURE_WRAP_S , GL2 . GL_REPEAT );gl. glTexParameterf ( GL2 . GL_TEXTURE_2D ,GL2 . GL_TEXTURE_WRAP_T , GL2 . GL_REPEAT );// Activar o Texture Mapping128


gl. glEnable ( GL2 . GL_TEXTURE_2D );// Activar a Geracao <strong>de</strong> Coor<strong>de</strong>nadasgl. glEnable ( GL2 . GL_TEXTURE_GEN_S );gl. glEnable ( GL2 . GL_TEXTURE_GEN_T );// Por omissao utilizamos SphereMapgl. glTexGeni ( GL2 .GL_S , GL2 . GL_TEXTURE_GEN_MODE , GL2 . GL_SPHERE_MAP );gl. glTexGeni ( GL2 .GL_T , GL2 . GL_TEXTURE_GEN_MODE , GL2 . GL_SPHERE_MAP );canvas . addKeyListener ( new GLKeyboardAdapter ( canvas ) {@Overri<strong>de</strong>public void handleKey ( GLAutoDrawable drawable , KeyEvent e) {GL2 gl = drawable . getGL (). getGL2 ();if(e. getKeyCo<strong>de</strong> () == KeyEvent . VK_1 )setObjectLinear (gl );if(e. getKeyCo<strong>de</strong> () == KeyEvent . VK_2 )setEyeLinear (gl );if(e. getKeyCo<strong>de</strong> () == KeyEvent . VK_3 )setSphereMap (gl );}});}// Mudar para Object Linear Functionprotected void setObjectLinear ( GL2 gl) {FloatBuffer zPlane = FloatBuffer . wrap ( new float [] { 1f, 0f, 0f, 0f });gl. glTexGeni ( GL2 .GL_S , GL2 . GL_TEXTURE_GEN_MODE ,GL2 . GL_OBJECT_LINEAR );gl. glTexGeni ( GL2 .GL_T , GL2 . GL_TEXTURE_GEN_MODE ,GL2 . GL_OBJECT_LINEAR );gl. glTexGenfv ( GL2 .GL_S , GL2 . GL_OBJECT_PLANE , zPlane );gl. glTexGenfv ( GL2 .GL_T , GL2 . GL_OBJECT_PLANE , zPlane );ren<strong>de</strong>rMo<strong>de</strong> = ObjectLinear ;}// Mudar para Eye Linear Functionprotected void setEyeLinear ( GL2 gl) {FloatBuffer zPlane = FloatBuffer . wrap ( new float [] {0f, 0f, 1f, 0f });gl. glTexGeni ( GL2 .GL_S , GL2 . GL_TEXTURE_GEN_MODE ,GL2 . GL_EYE_LINEAR );gl. glTexGeni ( GL2 .GL_T , GL2 . GL_TEXTURE_GEN_MODE ,GL2 . GL_EYE_LINEAR );gl. glTexGenfv ( GL2 .GL_S , GL2 . GL_EYE_PLANE , zPlane );gl. glTexGenfv ( GL2 .GL_T , GL2 . GL_EYE_PLANE , zPlane );ren<strong>de</strong>rMo<strong>de</strong> = EyeLinear ;}protected void setSphereMap ( GL2 gl) {gl. glTexGeni ( GL2 .GL_S , GL2 . GL_TEXTURE_GEN_MODE , GL2 . GL_SPHERE_MAP );gl. glTexGeni ( GL2 .GL_T , GL2 . GL_TEXTURE_GEN_MODE , GL2 . GL_SPHERE_MAP );ren<strong>de</strong>rMo<strong>de</strong> = SphereMap ;}private TGAImage loadTexture ( String name ) {TGAImage img = null ;try {InputStream stream = Tunnel . class . getResourceAsStream ( name );img = TGAImage . read ( stream );stream . close ();} catch ( IOException e) {e. printStackTrace ();System . exit (0);129


}}return img ;@Overri<strong>de</strong>public void display ( GLAutoDrawable drawable ) {GL2 gl = drawable . getGL (). getGL2 ();// Clear the window with current clearing colorgl. glClear ( GL2 . GL_COLOR_BUFFER_BIT );// Desnhar a imagem <strong>de</strong> fundo ( <strong>de</strong>sactivando as texturas )gl. glDisable ( GL2 . GL_TEXTURE_2D );gl. glPixelZoom (( float ) drawable . getWidth () / ( float ) environment . getWidth () ,( float ) drawable . getHeight () / ( float ) environment . getHeight ());gl. glDrawPixels ( environment . getWidth () , environment . getHeight () ,environment . getGLFormat () , GL2 . GL_UNSIGNED_BYTE ,environment . getData ());gl. glEnable ( GL2 . GL_TEXTURE_2D );gl. glClear ( GL2 . GL_DEPTH_BUFFER_BIT );// May need to switch to stripe textureif( ren<strong>de</strong>rMo<strong>de</strong> == SphereMap )gl. glBindTexture ( GL2 . GL_TEXTURE_2D , textures . get ( Environment ));elsegl. glBindTexture ( GL2 . GL_TEXTURE_2D , textures . get ( Stripes ));// Turn texgen and <strong>de</strong>pth writing back ongl. glEnable ( GL2 . GL_TEXTURE_GEN_S );gl. glEnable ( GL2 . GL_TEXTURE_GEN_T );// Save the matrix state and do the rotationsgl. glPushMatrix ();gl. glRotatef (45f, 0f, 1f, 0f);gl. glTranslatef ( -1.5f, 0f, 0f);// Draw the toursGlTools . gltDrawTorus (1f, 0.4f, 61 , 37);// Restore the matrix stategl. glPopMatrix ();// Desenhar uma amostra da textura actualgl. glPushMatrix ();gl. glDisable ( GL2 . GL_TEXTURE_GEN_S );gl. glDisable ( GL2 . GL_TEXTURE_GEN_T );gl. glRotatef ( -45f, 0f, 1f, 0f);gl. glTranslatef (1.5f, 0f, 0f);float quadSize = 1.2 f;gl. glBegin ( GL2 . GL_QUADS );gl. glTexCoord2f (0f, 0f);gl. glVertex2f (- quadSize , - quadSize );gl. glTexCoord2f (1f, 0f);gl. glVertex2f ( quadSize , - quadSize );gl. glTexCoord2f (1f, 1f);gl. glVertex2f ( quadSize , quadSize );130


}gl. glTexCoord2f (0f, 1f);gl. glVertex2f (- quadSize , quadSize );gl. glEnd ();gl. glPopMatrix ();@Overri<strong>de</strong>public void reshape ( GLAutoDrawable drawable , int x, int y, int width , int height ) {GL2 gl = drawable . getGL (). getGL2 ();if ( height == 0)height = 1; // prevnir divisao por 0gl. glViewport (0 , 0, canvas . getSize (). width , canvas . getSize (). height );gl. glMatrixMo<strong>de</strong> ( GL2 . GL_PROJECTION );gl. glLoadI<strong>de</strong>ntity ();float volume = 100 f;float fAspect = ( float ) width / ( float ) height ;glu . gluPerspective (100f, fAspect , .001f, volume * 5f);}}C.5 CubeMap.javapackage pt.ipb . esact .cg. examples ;import java . awt . Bor<strong>de</strong>rLayout ;import java . awt . Container ;import java .io. IOException ;import java .io. InputStream ;import java . nio . ByteBuffer ;import java . nio . IntBuffer ;import java . util . ArrayList ;import java . util . List ;import javax . media . opengl . GL2 ;import javax . media . opengl . GLAutoDrawable ;import javax . media . opengl . GLEventListener ;import javax . media . opengl . awt . GLCanvas ;import javax . media . opengl . glu . GLU ;import javax . swing . JFrame ;import pt.ipb . esact .cg.gl. CGEngine ;import pt.ipb . esact .cg.gl. GLVector ;import pt.ipb . esact .cg.gl. tools . GlTools ;import pt.ipb . esact .cg.gl. tools . GlUtil ;import com . jogamp . opengl . util . FPSAnimator ;import com . jogamp . opengl . util . gl2 . GLUT ;import com . jogamp . opengl . util . texture . spi . TGAImage ;@SuppressWarnings (" serial ")public class CubeMap extends JFrame implements GLEventListener {private GLU glu = new GLU ();private GLUT glut = new GLUT ();private GLCanvas canvas ;FPSAnimator animator ;private GLVector fLightPos = new GLVector ( -100f, 100f, 50f, 1f);private float fNoLight [] = { 0.0f, 0.0f, 0.0f, 0.0 f };131


private float fLowLight [] = { 0.25f, 0.25f, 0.25f, 1.0 f };private float fBrightLight [] = { 1.0f, 1.0f, 1.0f, 1.0 f };private static final float FloorSize = 20f;private static final int NumSpheres = 30;private List < GLVector > spherePositions = new ArrayList < GLVector >();private List < String > textureNames = new ArrayList < String >();private IntBuffer textures ;// I<strong>de</strong>ntificadores das texturas da Geometriaprivate static final int Earth = 0, Grass = 1;// Numero total <strong>de</strong> texturasprivate static final int NumTextures = 3;// I<strong>de</strong>ntificadores das texturas do Cuboprivate static final int CubeStartIn<strong>de</strong>x = 2;// Direccoes para as texuras do cuboprivate static final int [] CubeDirection = {GL2 . GL_TEXTURE_CUBE_MAP_POSITIVE_X ,GL2 . GL_TEXTURE_CUBE_MAP_NEGATIVE_X ,GL2 . GL_TEXTURE_CUBE_MAP_POSITIVE_Y ,GL2 . GL_TEXTURE_CUBE_MAP_NEGATIVE_Y ,GL2 . GL_TEXTURE_CUBE_MAP_POSITIVE_Z ,GL2 . GL_TEXTURE_CUBE_MAP_NEGATIVE_Z};public static void main ( String [] args ) {new CubeMap ();}public CubeMap () {super (" Mo<strong>de</strong>lo OpenGL ");setDefaultCloseOperation ( JFrame . EXIT_ON_CLOSE );setSize (600 , 600);Container p = getContentPane ();Bor<strong>de</strong>rLayout l = new Bor<strong>de</strong>rLayout (5 , 5);p. setLayout (l);canvas = new GLCanvas ();p. add ( canvas , Bor<strong>de</strong>rLayout . CENTER );canvas . addGLEventListener ( this );GLVector location = new GLVector (0f, .5f, .5f);new CGEngine ( canvas , location );setVisible ( true );canvas . requestFocus ();}animator = new FPSAnimator ( canvas , 25);animator . start ();@Overri<strong>de</strong>public void dispose ( GLAutoDrawable drawable ) {GL2 gl = drawable . getGL (). getGL2 ();gl. glDeleteTextures ( NumTextures , textures );}132


@Overri<strong>de</strong>public void init ( GLAutoDrawable drawable ) {GL2 gl = drawable . getGL (). getGL2 ();}// Grayish backgroundgl. glClearColor ( fLowLight [0] , fLowLight [1] , fLowLight [2] , fLowLight [3]);// Cull backs of polygonsgl. glCullFace ( GL2 . GL_BACK );gl. glFrontFace ( GL2 . GL_CCW );gl. glEnable ( GL2 . GL_CULL_FACE );gl. glEnable ( GL2 . GL_DEPTH_TEST );setupLight (gl );spherePositions = GlUtil . generateRandomPositions ( FloorSize , NumSpheres );setupTextures (gl );setupCubeMap (gl );private TGAImage loadTexture ( String name ) {TGAImage img = null ;try {InputStream stream = Anisotropic . class . getResourceAsStream ( name );img = TGAImage . read ( stream );stream . close ();} catch ( IOException e) {e. printStackTrace ();System . exit (0);}return img ;}private void setupTextures ( GL2 gl) {// Activar as texturasgl. glEnable ( GL2 . GL_TEXTURE_2D );// Obter os i<strong>de</strong>ntificadorestextures = IntBuffer . allocate ( NumTextures ); // 2 texturas + 1 cubogl. glGenTextures ( NumTextures , textures );// Configuracao do Texture Environmentgl. glTexEnvi ( GL2 . GL_TEXTURE_ENV , GL2 . GL_TEXTURE_ENV_MODE , GL2 . GL_MODULATE );// Texturas da GeometriatextureNames . add (" earth . tga "); // Para os circulostextureNames . add (" grass . tga ");// Texturas do Cube MaptextureNames . add (" cubemap / right . tga ");textureNames . add (" cubemap / left . tga ");textureNames . add (" cubemap /up.tga ");textureNames . add (" cubemap / down . tga ");textureNames . add (" cubemap / backward . tga ");textureNames . add (" cubemap / forward . tga ");// Carregar as texturas da Geometriafor ( int i = 0; i < CubeStartIn<strong>de</strong>x ; i ++) {// Fazer o bind do estado da textura ao i<strong>de</strong>ntificadorgl. glBindTexture ( GL2 . GL_TEXTURE_2D , textures . get (i ));133


Carregar e Ler a imagem para a nossa texturaTGAImage img = loadTexture ( textureNames . get (i ));ByteBuffer bb = img . getData ();// Carregar a textura ( comprimida RGB )gl. glTexParameteri ( GL2 . GL_TEXTURE_2D , GL2 . GL_GENERATE_MIPMAP , GL2 . GL_TRUE );gl. glTexImage2D ( GL2 . GL_TEXTURE_2D , 0, GL2 . GL_COMPRESSED_RGB ,img . getWidth () , img . getHeight () , 0, img . getGLFormat () ,GL2 . GL_UNSIGNED_BYTE , bb );// Parametros da texturagl. glTexParameteri ( GL2 . GL_TEXTURE_2D , GL2 . GL_TEXTURE_MAG_FILTER ,GL2 . GL_LINEAR );gl. glTexParameteri ( GL2 . GL_TEXTURE_2D , GL2 . GL_TEXTURE_MIN_FILTER ,GL2 . GL_LINEAR_MIPMAP_LINEAR );gl. glTexParameteri ( GL2 . GL_TEXTURE_2D , GL2 . GL_TEXTURE_WRAP_S ,GL2 . GL_CLAMP_TO_EDGE );gl. glTexParameteri ( GL2 . GL_TEXTURE_2D , GL2 . GL_TEXTURE_WRAP_T ,GL2 . GL_CLAMP_TO_EDGE );}}private void setupCubeMap ( GL2 gl) {// Carregar as texturas do Cubogl. glBindTexture ( GL2 . GL_TEXTURE_CUBE_MAP , textures . get ( CubeStartIn<strong>de</strong>x ));gl. glTexParameteri ( GL2 . GL_TEXTURE_CUBE_MAP , GL2 . GL_TEXTURE_MAG_FILTER ,GL2 . GL_LINEAR );gl. glTexParameteri ( GL2 . GL_TEXTURE_CUBE_MAP , GL2 . GL_TEXTURE_MIN_FILTER ,GL2 . GL_LINEAR );gl. glTexParameteri ( GL2 . GL_TEXTURE_CUBE_MAP , GL2 . GL_TEXTURE_WRAP_S ,GL2 . GL_REPEAT );gl. glTexParameteri ( GL2 . GL_TEXTURE_CUBE_MAP , GL2 . GL_TEXTURE_WRAP_T ,GL2 . GL_REPEAT );gl. glTexParameteri ( GL2 . GL_TEXTURE_CUBE_MAP , GL2 . GL_TEXTURE_WRAP_R ,GL2 . GL_REPEAT );// Restantes texturas preenchem o cubofor ( int i = CubeStartIn<strong>de</strong>x ; i < textureNames . size (); i ++) {// Carregar e Ler a imagem para a nossa texturaTGAImage img = loadTexture ( textureNames . get (i ));ByteBuffer bb = img . getData ();}gl. glTexImage2D ( CubeDirection [i - CubeStartIn<strong>de</strong>x ], 0, GL2 . GL_RGBA ,img . getWidth () , img . getHeight () , 0, img . getGLFormat () ,GL2 . GL_UNSIGNED_BYTE , bb );}gl. glTexGeni ( GL2 .GL_S , GL2 . GL_TEXTURE_GEN_MODE , GL2 . GL_REFLECTION_MAP );gl. glTexGeni ( GL2 .GL_T , GL2 . GL_TEXTURE_GEN_MODE , GL2 . GL_REFLECTION_MAP );gl. glTexGeni ( GL2 .GL_R , GL2 . GL_TEXTURE_GEN_MODE , GL2 . GL_REFLECTION_MAP );private void setupLight ( GL2 gl) {// Setup light parametersgl. glLightMo<strong>de</strong>lfv ( GL2 . GL_LIGHT_MODEL_AMBIENT , fNoLight , 0);gl. glLightMo<strong>de</strong>li ( GL2 . GL_LIGHT_MODEL_COLOR_CONTROL ,GL2 . GL_SEPARATE_SPECULAR_COLOR );gl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_AMBIENT , fLowLight , 0);gl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_DIFFUSE , fBrightLight , 0);gl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_SPECULAR , fBrightLight , 0);gl. glEnable ( GL2 . GL_LIGHTING );134


gl. glEnable ( GL2 . GL_LIGHT0 );}// Utilizar color trackinggl. glEnable ( GL2 . GL_COLOR_MATERIAL );gl. glColorMaterial ( GL2 . GL_FRONT , GL2 . GL_AMBIENT_AND_DIFFUSE );gl. glMateriali ( GL2 . GL_FRONT , GL2 . GL_SHININESS , 128);private float torusRot = 0, earthRot = 0;@Overri<strong>de</strong>public void display ( GLAutoDrawable drawable ) {GL2 gl = drawable . getGL (). getGL2 ();torusRot += .1f;if( torusRot > 2f * GlTools . GL_PI )torusRot = 0f;earthRot += .05 f;if( earthRot > 2f * GlTools . GL_PI )earthRot = 0f;// Clear the window with current clearing colorgl. glClear ( GL2 . GL_COLOR_BUFFER_BIT | GL2 . GL_DEPTH_BUFFER_BIT );// Componente Especulargl. glMaterialfv ( GL2 . GL_FRONT , GL2 . GL_SPECULAR , fBrightLight , 0);// Cor do Material Brancagl. glColor3f (1f, 1f, 1f);gl. glPushMatrix ();// Colocar a Luzgl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_POSITION , fLightPos . toArray () , 0);// Desenhar o ChaoGlUtil . drawTexturedGround ( textures . get ( Grass ),FloorSize , 1f);// Desenhar o nosso mundo <strong>de</strong> novodrawWorld (gl );gl. glPopMatrix ();}private void drawWorld ( GL2 gl) {// Desenhar esferas aleatoriamentegl. glBindTexture ( GL2 . GL_TEXTURE_2D , textures . get ( Earth ));for ( GLVector s : spherePositions ) {gl. glPushMatrix ();gl. glTranslatef (s.x() , s.y() , s.z ());gl. glRotatef (( float ) Math . toDegrees ( earthRot ), 0f, 1f, 0f);GlTools . gltDrawSphere (.2f, 20 , 10);gl. glPopMatrix ();}// Desenhar o Torusgl. glPushMatrix ();// Desactivar temporariamente TEXTURE_2Dgl. glDisable ( GL2 . GL_TEXTURE_2D );// Activar o Cube Mapgl. glEnable ( GL2 . GL_TEXTURE_CUBE_MAP );135


gl. glBindTexture ( GL2 . GL_TEXTURE_CUBE_MAP , textures . get ( CubeStartIn<strong>de</strong>x ));gl. glEnable ( GL2 . GL_TEXTURE_GEN_S );gl. glEnable ( GL2 . GL_TEXTURE_GEN_T );gl. glEnable ( GL2 . GL_TEXTURE_GEN_R );// Desenhar a Geometria que reflecte o cube mapgl. glTranslatef (0 , .5f, -.3f);glut . glutSolidSphere (.15 , 61 , 37);gl. glRotatef (( float ) Math . toDegrees ( torusRot ), 0, 1f, 0);glut . glutSolidTorus (.1f, .3f, 61 , 37);// Colocar tudo como estavagl. glDisable ( GL2 . GL_TEXTURE_GEN_S );gl. glDisable ( GL2 . GL_TEXTURE_GEN_T );gl. glDisable ( GL2 . GL_TEXTURE_GEN_R );gl. glDisable ( GL2 . GL_TEXTURE_CUBE_MAP );gl. glEnable ( GL2 . GL_TEXTURE_2D );gl. glPopMatrix ();}@Overri<strong>de</strong>public void reshape ( GLAutoDrawable drawable , int x, int y, int width , int height ) {GL2 gl = drawable . getGL (). getGL2 ();if ( height == 0)height = 1; // prevenir divisao por 0gl. glViewport (0 , 0, canvas . getSize (). width , canvas . getSize (). height );gl. glMatrixMo<strong>de</strong> ( GL2 . GL_PROJECTION );gl. glLoadI<strong>de</strong>ntity ();float volume = 100 f;float fAspect = ( float ) width / ( float ) height ;glu . gluPerspective (100f, fAspect , .001f, volume * 5f);}}C.6 SnowMan.javapackagept.ipb . esact .cg. examples .c9;import java . awt . Bor<strong>de</strong>rLayout ;import java . awt . Container ;import javax . media . opengl . GL2 ;import javax . media . opengl . GLAutoDrawable ;import javax . media . opengl . GLEventListener ;import javax . media . opengl . awt . GLCanvas ;import javax . media . opengl . glu . GLU ;import javax . media . opengl . glu . GLUquadric ;import javax . swing . JFrame ;import pt.ipb . esact .cg.gl. CGEngine ;import pt.ipb . esact .cg.gl. GLVector ;import com . jogamp . opengl . util . FPSAnimator ;@SuppressWarnings (" serial ")public class SnowMan extends JFrame implements GLEventListener {136


private GLU glu = new GLU ();private GLCanvas canvas ;FPSAnimator animator ;private GLVector fLightPos = new GLVector ( -100f, 100f, 50f, 1f);private float fNoLight [] = { 0.5f, 0.5f, 0.5f, 1.0 f };private float fLowLight [] = { 0.25f, 0.25f, 0.25f, 1.0 f };private float fBrightLight [] = { 1.0f, 1.0f, 1.0f, 1.0 f };private GLVector lookAt = new GLVector (0f, 0f, 0f);private CGEngine e;public static void main ( String [] args ) {new SnowMan ();}public SnowMan () {super (" Mo<strong>de</strong>lo OpenGL ");setDefaultCloseOperation ( JFrame . EXIT_ON_CLOSE );setSize (600 , 600);Container p = getContentPane ();Bor<strong>de</strong>rLayout l = new Bor<strong>de</strong>rLayout (5 , 5);p. setLayout (l);canvas = new GLCanvas ();p. add ( canvas , Bor<strong>de</strong>rLayout . CENTER );GLVector location = new GLVector (1f, .5f, 1f);e = new CGEngine ( canvas , location );e. camera (). lookAt ( lookAt );canvas . addGLEventListener ( this );setVisible ( true );canvas . requestFocus ();}animator = new FPSAnimator ( canvas , 25);animator . start ();@Overri<strong>de</strong>public void dispose ( GLAutoDrawable drawable ) {}@Overri<strong>de</strong>public void init ( GLAutoDrawable drawable ) {GL2 gl = drawable . getGL (). getGL2 ();// Grayish backgroundgl. glClearColor ( fLowLight [0] , fLowLight [1] , fLowLight [2] , fLowLight [3]);// Cull backs of polygonsgl. glCullFace ( GL2 . GL_BACK );gl. glFrontFace ( GL2 . GL_CCW );gl. glEnable ( GL2 . GL_CULL_FACE );gl. glEnable ( GL2 . GL_DEPTH_TEST );setupLight (gl );137


}private void setupLight ( GL2 gl) {// Setup light parametersgl. glLightMo<strong>de</strong>lfv ( GL2 . GL_LIGHT_MODEL_AMBIENT , fNoLight , 0);gl. glLightMo<strong>de</strong>li ( GL2 . GL_LIGHT_MODEL_COLOR_CONTROL ,GL2 . GL_SEPARATE_SPECULAR_COLOR );gl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_AMBIENT , fLowLight , 0);gl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_DIFFUSE , fBrightLight , 0);gl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_SPECULAR , fBrightLight , 0);gl. glEnable ( GL2 . GL_LIGHTING );gl. glEnable ( GL2 . GL_LIGHT0 );}// Utilizar color trackinggl. glEnable ( GL2 . GL_COLOR_MATERIAL );gl. glColorMaterial ( GL2 . GL_FRONT , GL2 . GL_AMBIENT_AND_DIFFUSE );gl. glMateriali ( GL2 . GL_FRONT , GL2 . GL_SHININESS , 128);@Overri<strong>de</strong>public void display ( GLAutoDrawable drawable ) {GL2 gl = drawable . getGL (). getGL2 ();// Clear the window with current clearing colorgl. glClear ( GL2 . GL_COLOR_BUFFER_BIT | GL2 . GL_DEPTH_BUFFER_BIT );// Componente Especulargl. glMaterialfv ( GL2 . GL_FRONT , GL2 . GL_SPECULAR , fBrightLight , 0);// Posicao da Luzgl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_POSITION , fLightPos . toArray () , 0);// Cor do Material Brancagl. glColor3f (0f, 0f, 0f);GLUquadric quad = glu . gluNewQuadric (); // Criar o quadricgl. glPushMatrix ();glu . gluQuadricNormals (quad , GLU . GLU_SMOOTH );// Corpogl. glPushMatrix ();// Bola do Fundogl. glColor3f (1.0f, 1.0f, 1.0 f);glu . gluSphere (quad , .40f, 26 , 13);// Bola do Corpogl. glTranslatef (0.0f, .550f, 0.0 f);glu . gluSphere (quad , .3f, 26 , 13);// Bola da Cabecagl. glTranslatef (0.0f, 0.45f, 0.0 f);glu . gluSphere (quad , 0.24f, 26 , 13);// Olhosgl. glColor3f (0.0f, 0.0f, 0.0 f);gl. glTranslatef (0.1f, 0.1f, 0.21 f);glu . gluSphere (quad , 0.02f, 26 , 13);gl. glTranslatef ( -0.2f, 0.0f, 0.0 f);glu . gluSphere (quad , 0.02f, 26 , 13);138


Narizgl. glColor3f (1.0f, 0.3f, 0.3 f);gl. glTranslatef (0.1f, -0.12f, 0.0 f);glu . gluCylin<strong>de</strong>r (quad , 0.04f, 0.0f, 0.3f, 26 , 13);gl. glPopMatrix ();// Chapeugl. glPushMatrix ();gl. glColor3f (0.0f, 0.0f, 0.0 f);gl. glTranslatef (0.0f, 1.17f, 0.0 f);gl. glRotatef ( -90.0f, 1.0f, 0.0f, 0.0 f);glu . gluCylin<strong>de</strong>r (quad , 0.17f, 0.17f, 0.4f, 26 , 13);// Hat brimgl. glDisable ( GL2 . GL_CULL_FACE ); // Pintar ambos os ladosglu . gluDisk (quad , 0.17f, 0.28f, 26 , 13);gl. glEnable ( GL2 . GL_CULL_FACE );gl. glTranslatef (0.0f, 0.0f, 0.40 f);glu . gluDisk (quad , 0.0f, 0.17f, 26 , 13);gl. glPopMatrix ();}gl. glPopMatrix ();@Overri<strong>de</strong>public void reshape ( GLAutoDrawable drawable , int x, int y, int width , int height ) {GL2 gl = drawable . getGL (). getGL2 ();if ( height == 0)height = 1; // prevenir divisao por 0gl. glViewport (0 , 0, canvas . getSize (). width , canvas . getSize (). height );}gl. glMatrixMo<strong>de</strong> ( GL2 . GL_PROJECTION );gl. glLoadI<strong>de</strong>ntity ();float volume = 100 f;float fAspect = ( float ) width / ( float ) height ;glu . gluPerspective (100f, fAspect , .001f, volume * 5f);}C.7 Bezier2D.javapackagept.ipb . esact .cg. examples .c9;import java . awt . Bor<strong>de</strong>rLayout ;import java . awt . Container ;import javax . media . opengl . GL2 ;import javax . media . opengl . GLAutoDrawable ;import javax . media . opengl . GLEventListener ;import javax . media . opengl . awt . GLCanvas ;import javax . media . opengl . glu . GLU ;import javax . swing . JFrame ;import pt.ipb . esact .cg.gl. GLVector ;import pt.ipb . esact .cg.gl. GLVectorList ;import com . jogamp . opengl . util . FPSAnimator ;@SuppressWarnings (" serial ")139


public class Bezier2D extends JFrame implements GLEventListener {private GLU glu = new GLU ();private GLCanvas canvas ;FPSAnimator animator ;private GLVectorList ctrlPoints ;public static void main ( String [] args ) {new Bezier2D ();}public Bezier2D () {super (" Mo<strong>de</strong>lo OpenGL ");setDefaultCloseOperation ( JFrame . EXIT_ON_CLOSE );setSize (600 , 600);Container p = getContentPane ();Bor<strong>de</strong>rLayout l = new Bor<strong>de</strong>rLayout (5 , 5);p. setLayout (l);canvas = new GLCanvas ();p. add ( canvas , Bor<strong>de</strong>rLayout . CENTER );canvas . addGLEventListener ( this );setVisible ( true );canvas . requestFocus ();}animator = new FPSAnimator ( canvas , 25);animator . start ();@Overri<strong>de</strong>public void dispose ( GLAutoDrawable drawable ) {}@Overri<strong>de</strong>public void init ( GLAutoDrawable drawable ) {GL2 gl = drawable . getGL (). getGL2 ();// Grayish backgroundgl. glClearColor (.5f, .5f, .5f, .5f);}// Definir Pontos <strong>de</strong> ControloctrlPoints = new GLVectorList ();ctrlPoints . add ( -4f, 0f, 0f); // StartctrlPoints . add ( -6f, 4f, 0f); // ControlctrlPoints . add (6f, -4f, 0f); // ControlctrlPoints . add (4f, 0f, 0f); // Endpublic void display ( GLAutoDrawable drawable ) {GL2 gl = drawable . getGL (). getGL2 ();gl. glClear ( GL2 . GL_COLOR_BUFFER_BIT );float umin = 0f; // Valor minimo do ufloat umax = 100 f; // Valor maximo do ugl. glMap1f (GL2 . GL_MAP1_VERTEX_3 , // Tipo <strong>de</strong> dados geradosumin , // Valor minimo do uumax , // Valor maximo do u3, // Distancia entre pontos140


ctrlPoints . size () , // Numero <strong>de</strong> pontos <strong>de</strong> controloctrlPoints . toFloatBuffer () // FloatBuffer contendo pontos <strong>de</strong> controlo);// Activar o Evaluatorgl. glEnable ( GL2 . GL_MAP1_VERTEX_3 );// Desenhar os vertices da curva/*gl. glColor3f (0f, 0f, 0f);gl. glBegin ( GL2 . GL_LINE_STRIP );for ( float i= umin ; i< umax ; i ++)gl. glEvalCoord1f (i);gl. glEnd ();*/gl. glColor3f (0f, 0f, 0f);gl. glMapGrid1d (100 , umin , umax );gl. glEvalMesh1 ( GL2 . GL_LINE , ( int )umin , ( int ) umax );// Desenhar Pontos <strong>de</strong> Contrologl. glPointSize (5.0 f);gl. glColor3f (1f, 0f, 0f);gl. glBegin ( GL2 . GL_POINTS );for ( GLVector point : ctrlPoints )point . draw ();gl. glEnd ();}@Overri<strong>de</strong>public void reshape ( GLAutoDrawable drawable , int x, int y, int width , int height ) {GL2 gl = drawable . getGL (). getGL2 ();if ( height == 0)height = 1; // prevenir divisao por 0gl. glViewport (0 , 0, canvas . getSize (). width , canvas . getSize (). height );gl. glMatrixMo<strong>de</strong> ( GL2 . GL_PROJECTION );gl. glLoadI<strong>de</strong>ntity ();glu . gluOrtho2D ( -10f, 10f, -10f, 10f);}gl. glMatrixMo<strong>de</strong> ( GL2 . GL_MODELVIEW );gl. glLoadI<strong>de</strong>ntity ();}C.8 Bezier3D.javapackagept.ipb . esact .cg. examples .c9;import java . awt . Bor<strong>de</strong>rLayout ;import java . awt . Container ;import javax . media . opengl . GL2 ;import javax . media . opengl . GLAutoDrawable ;import javax . media . opengl . GLEventListener ;import javax . media . opengl . awt . GLCanvas ;import javax . media . opengl . glu . GLU ;import javax . swing . JFrame ;import pt.ipb . esact .cg.gl. CGEngine ;import pt.ipb . esact .cg.gl. GLVector ;141


import pt.ipb . esact .cg.gl. GLVectorList ;import com . jogamp . opengl . util . FPSAnimator ;@SuppressWarnings (" serial ")public class Bezier3D extends JFrame implements GLEventListener {private GLU glu = new GLU ();private GLCanvas canvas ;FPSAnimator animator ;private GLVectorList ctrlPoints ;private GLVector lookAt = new GLVector (0f, .5f, 0f);private CGEngine e;public static void main ( String [] args ) {new Bezier3D ();}public Bezier3D () {super (" Mo<strong>de</strong>lo OpenGL ");setDefaultCloseOperation ( JFrame . EXIT_ON_CLOSE );setSize (600 , 600);Container p = getContentPane ();Bor<strong>de</strong>rLayout l = new Bor<strong>de</strong>rLayout (5 , 5);p. setLayout (l);canvas = new GLCanvas ();p. add ( canvas , Bor<strong>de</strong>rLayout . CENTER );canvas . addGLEventListener ( this );GLVector location = new GLVector (5f, .5f, 5f);e = new CGEngine ( canvas , location );e. camera (). lookAt ( lookAt );setVisible ( true );canvas . requestFocus ();}animator = new FPSAnimator ( canvas , 25);animator . start ();@Overri<strong>de</strong>public void dispose ( GLAutoDrawable drawable ) {}@Overri<strong>de</strong>public void init ( GLAutoDrawable drawable ) {GL2 gl = drawable . getGL (). getGL2 ();// Grayish backgroundgl. glClearColor (.5f, .5f, .5f, .5f);// Definir Pontos <strong>de</strong> ControloctrlPoints = new GLVectorList ();// v0ctrlPoints . add ( -4f, 0f, 4f); // Start (u0)ctrlPoints . add ( -2f, 4f, 4f); // Control (u0)ctrlPoints . add (4f, 0f, 4f); // End (u0)142


v1ctrlPoints . add ( -4f, 0f, 0f); // Start (u1)ctrlPoints . add ( -2f, 4f, 0f); // Control (u1)ctrlPoints . add (4f, 0f, 0f); // End (u1)// v2ctrlPoints . add ( -4f, 0f, -4f); // Start (u2)ctrlPoints . add ( -2f, 4f, -4f); // Control (u2)ctrlPoints . add (4f, 0f, -4f); // End (u2)}public void display ( GLAutoDrawable drawable ) {GL2 gl = drawable . getGL (). getGL2 ();gl. glClear ( GL2 . GL_COLOR_BUFFER_BIT );}float umin = 0f; // Valor minimo do ufloat umax = 10f; // Valor maximo do ufloat vmin = 0f; // Valor minimo do vfloat vmax = 10f; // Valor maximo do vgl. glMap2f (GL2 . GL_MAP2_VERTEX_3 , // Tipo <strong>de</strong> dados geradosumin , // Valor minimo do uumax , // Valor maximo do u3, // Distancia entre pontos ( dominio u)3, // Numero <strong>de</strong> pontos <strong>de</strong> controlo ( dominio u)vmin , // Valor minimo do vvmax , // Valor maximo do v9, // Distancia entre pontos ( dominio v)3, // Numero <strong>de</strong> pontos <strong>de</strong> controlo ( dominio v)ctrlPoints . toFloatBuffer () // FloatBuffer contendo pontos <strong>de</strong> controlo);// Activar o Evaluatorgl. glEnable ( GL2 . GL_MAP2_VERTEX_3 );// Desenhar os vertices da superficiegl. glColor3f (0f, 0f, 0f);gl. glMapGrid2d (10 , umin , umax , 10 , vmin , vmax );gl. glEvalMesh2 ( GL2 . GL_LINE , ( int )umin , ( int )umax , ( int )vmin , ( int ) vmax );// Desenhar Pontos <strong>de</strong> Contrologl. glPointSize (5.0 f);gl. glColor3f (1f, 0f, 0f);gl. glBegin ( GL2 . GL_POINTS );for ( GLVector point : ctrlPoints )point . draw ();gl. glEnd ();gl. glPopMatrix ();@Overri<strong>de</strong>public void reshape ( GLAutoDrawable drawable , int x, int y, int width , int height ) {GL2 gl = drawable . getGL (). getGL2 ();if ( height == 0)height = 1; // prevenir divisao por 0gl. glViewport (0 , 0, canvas . getSize (). width , canvas . getSize (). height );gl. glMatrixMo<strong>de</strong> ( GL2 . GL_PROJECTION );gl. glLoadI<strong>de</strong>ntity ();float volume = 100 f;float fAspect = ( float ) width / ( float ) height ;143


}glu . gluPerspective (100f, fAspect , .001f, volume * 5f);}C.9 BezierLighting.javapackagept.ipb . esact .cg. examples .c9;import java . awt . Bor<strong>de</strong>rLayout ;import java . awt . Container ;import javax . media . opengl . GL2 ;import javax . media . opengl . GLAutoDrawable ;import javax . media . opengl . GLEventListener ;import javax . media . opengl . awt . GLCanvas ;import javax . media . opengl . glu . GLU ;import javax . swing . JFrame ;import pt.ipb . esact .cg.gl. CGEngine ;import pt.ipb . esact .cg.gl. GLVector ;import pt.ipb . esact .cg.gl. GLVectorList ;import pt.ipb . esact .cg.gl. tools . GlTools ;import com . jogamp . opengl . util . FPSAnimator ;@SuppressWarnings (" serial ")public class BezierLighting extends JFrame implements GLEventListener {private GLU glu = new GLU ();private GLCanvas canvas ;FPSAnimator animator ;private GLVector fLightPos = new GLVector (100f, 100f, 50f, 1f);private float fNoLight [] = { 0.0f, 0.0f, 0.0f, 0.0 f };private float fLowLight [] = { 0.25f, 0.25f, 0.25f, 1.0 f };private float fBrightLight [] = { .5f, .5f, .5f, 1.0 f };private GLVectorList ctrlPoints ;private GLVector lookAt = new GLVector (0f, .5f, 0f);private CGEngine e;public static void main ( String [] args ) {new BezierLighting ();}public BezierLighting () {super (" Mo<strong>de</strong>lo OpenGL ");setDefaultCloseOperation ( JFrame . EXIT_ON_CLOSE );setSize (600 , 600);Container p = getContentPane ();Bor<strong>de</strong>rLayout l = new Bor<strong>de</strong>rLayout (5 , 5);p. setLayout (l);canvas = new GLCanvas ();p. add ( canvas , Bor<strong>de</strong>rLayout . CENTER );canvas . addGLEventListener ( this );144


GLVector location = new GLVector (5f, .5f, 5f);e = new CGEngine ( canvas , location );e. camera (). lookAt ( lookAt );setVisible ( true );canvas . requestFocus ();}animator = new FPSAnimator ( canvas , 25);animator . start ();@Overri<strong>de</strong>public void dispose ( GLAutoDrawable drawable ) {}@Overri<strong>de</strong>public void init ( GLAutoDrawable drawable ) {GL2 gl = drawable . getGL (). getGL2 ();}// Grayish backgroundgl. glClearColor (0f, 0f, 0f, 0f);// Cull backs of polygonsgl. glCullFace ( GL2 . GL_BACK );gl. glFrontFace ( GL2 . GL_CCW );gl. glEnable ( GL2 . GL_DEPTH_TEST );setupLight (gl );// Definir Pontos <strong>de</strong> ControloctrlPoints = new GLVectorList ();updateControlPoints ();// Activar a geracao automatica <strong>de</strong> normaisgl. glEnable ( GL2 . GL_AUTO_NORMAL );private float a0 = 0, a1 = GlTools . GL_PI / 4, a2 = GlTools . GL_PI / 2;private void updateControlPoints () {ctrlPoints . clear ();a0 += .1f;if(a0 >= GlTools . GL_PI )a0 = 0;a1 += .1f;if(a1 >= GlTools . GL_PI )a1 = 0;a2 += .1f;if(a2 >= GlTools . GL_PI )a2 = 0;// v0ctrlPoints . add ( -4f, 0f, 4f); // Start (u0)ctrlPoints . add ( -2f, ( float ) ( Math . sin (a0) * 8f - 4f), 4f); // Control (u0)ctrlPoints . add (4f, 0f, 4f); // End (u0)// v1ctrlPoints . add ( -4f, 0f, 0f); // Start (u1)ctrlPoints . add ( -2f, ( float ) ( Math . sin (a1) * 8f - 4f), 0f); // Control (u1)145


ctrlPoints . add (4f, 0f, 0f); // End (u1)}// v2ctrlPoints . add ( -4f, 0f, -4f); // Start (u2)ctrlPoints . add ( -2f, ( float ) ( Math . sin (a2) * 8f - 4f), -4f); // Control (u2)ctrlPoints . add (4f, 0f, -4f); // End (u2)private void setupLight ( GL2 gl) {// Setup light parametersgl. glLightMo<strong>de</strong>lfv ( GL2 . GL_LIGHT_MODEL_AMBIENT , fNoLight , 0);gl. glLightMo<strong>de</strong>li ( GL2 . GL_LIGHT_MODEL_COLOR_CONTROL ,GL2 . GL_SEPARATE_SPECULAR_COLOR );gl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_AMBIENT , fLowLight , 0);gl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_DIFFUSE , fBrightLight , 0);gl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_SPECULAR , fBrightLight , 0);gl. glEnable ( GL2 . GL_LIGHTING );gl. glEnable ( GL2 . GL_LIGHT0 );}// Utilizar color trackinggl. glEnable ( GL2 . GL_COLOR_MATERIAL );gl. glColorMaterial ( GL2 . GL_FRONT , GL2 . GL_AMBIENT_AND_DIFFUSE );gl. glMateriali ( GL2 . GL_FRONT , GL2 . GL_SHININESS , 128);public void display ( GLAutoDrawable drawable ) {GL2 gl = drawable . getGL (). getGL2 ();gl. glClear ( GL2 . GL_COLOR_BUFFER_BIT | GL2 . GL_DEPTH_BUFFER_BIT );updateControlPoints ();// Componente Especulargl. glMaterialfv ( GL2 . GL_FRONT , GL2 . GL_SPECULAR , fBrightLight , 0);// Posicao da Luzgl. glLightfv ( GL2 . GL_LIGHT0 , GL2 . GL_POSITION , fLightPos . toArray () , 0);float umin = 0f; // Valor minimo do ufloat umax = 10f; // Valor maximo do ufloat vmin = 0f; // Valor minimo do vfloat vmax = 10f; // Valor maximo do vgl. glMap2f (GL2 . GL_MAP2_VERTEX_3 , // Tipo <strong>de</strong> dados geradosumin , // Valor minimo do uumax , // Valor maximo do u3, // Distancia entre pontos ( dominio u)3, // Numero <strong>de</strong> pontos <strong>de</strong> controlo ( dominio u)vmin , // Valor minimo do vvmax , // Valor maximo do v9, // Distancia entre pontos ( dominio v)3, // Numero <strong>de</strong> pontos <strong>de</strong> controlo ( dominio v)ctrlPoints . toFloatBuffer () // FloatBuffer contendo pontos <strong>de</strong> controlo);// Activar o Evaluatorgl. glEnable ( GL2 . GL_MAP2_VERTEX_3 );// Desenhar os vertices da superficiegl. glColor3f (0f, 0f, 1f);gl. glMapGrid2d (10 , umin , umax , 10 , vmin , vmax );gl. glEvalMesh2 ( GL2 . GL_FILL , ( int )umin , ( int )umax , ( int )vmin , ( int ) vmax );// Desenhar Pontos <strong>de</strong> Controlo146


gl. glPointSize (5.0 f);gl. glColor3f (1f, 0f, 0f);gl. glBegin ( GL2 . GL_POINTS );for ( GLVector point : ctrlPoints )point . draw ();gl. glEnd ();gl. glPopMatrix ();}@Overri<strong>de</strong>public void reshape ( GLAutoDrawable drawable , int x, int y, int width , int height ) {GL2 gl = drawable . getGL (). getGL2 ();if ( height == 0)height = 1; // prevenir divisao por 0gl. glViewport (0 , 0, canvas . getSize (). width , canvas . getSize (). height );gl. glMatrixMo<strong>de</strong> ( GL2 . GL_PROJECTION );gl. glLoadI<strong>de</strong>ntity ();float volume = 100 f;float fAspect = ( float ) width / ( float ) height ;glu . gluPerspective (100f, fAspect , .001f, volume * 5f);}}147

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!