Tutoriais‎ > ‎Computação Gráfica‎ > ‎

Transformações no OpenGL

postado em 25 de mar de 2016 17:02 por Prof. Rodrigo Costa

O propósito desta lição é compreender como as transformações geométricas são realizadas sobre os objetos no OpenGL através de funções para realizar translação, rotação e escalamento.

A aparência final da cena ou do objeto depende muito da ordem na qual estas transformações são aplicadas. No pipeline gráfico do opengl, antes dos pontos definidos através do comando glVertex serem exibidos na tela, eles passam por três estágios mostrado na figura a seguir


O primeiro passo desse processo é feito através de uma simples multiplicação de matriz. Assim, todos os pontos a serem desenhados passam são multiplicados por uma matriz especial, chamada MODELVIEW, conforme mostrado em:



Inicialmente ela consiste em uma matriz identidade, mas a cada tranformação esta matriz é alterada através da seguinte equação de atualização:


Desta forma, a cada aplicação de uma transformação, a matriz vais sendo alterada e consequentemente o resultado na tela vai também sendo alterado.

 

TransformaçãoComandoDescriçãoMatriz de Tranformação
TranslaçãoglTranslatef(Dx, Dy, Dz) move todas as coordenadas dos objetos ao longo dos eixos coordenados
Rotação glRotatef(Angulo, x, y, z) 

gira o objeto ao redor do vetor (x,y,z). 
O giro é de Angulo graus, no sentido anti-horário.

Como a rotação depende do vetor x,y,z, a seguir é descrito exemplos de matrizes para realizar rotações em torno dos eixos X, Y e Z.

EscalaglScalef(Sx, Sy, Sz) altera a escala do objeto ao logo dos eixos (X,Y,Z).

Na figura a seguir é mostrado o comando de rotação dos eixos coordenados:


Ordem da aplicação das transformações

Lembrando que a ordem das matrizes durante a formação da matriz composta implica em uma mudança na sequencia dos passos. Essa seção tem como o objetivo de demonstrar a influência da sequencia de aplicações de comandos na matriz de transformação.

Lembrando que a matriz padrão é uma identidade, ou seja, não altera os pontos, conforme mostrada a seguir:

Se aplicarmos uma transformação de translação com parâmetros Dx (Deslocamento em X), Dy (Deslocamento em Y) e Dz ( Deslocamento em Z), a matriz de modelview se altera conforme mostrado a seguir:


Se aplicarmos em seguida o comando de escalamento com fatores Sx, Sy e Sz, a matriz resultante é convertida em:

Lembrando que a sequencia de matrizes em uma transformação composta é o inverso da sequencia de aplicação. Assim, a matriz resultante representa a seguinte sequencia de transformação

  • Escalamento com fatores Sx,Sy e Sz
  • Translação com deslocamentos Dx, Dy e Dz.

Perceba, então, que a sequencia de comandos é o inverso da sequencia de transformações realizadas.

No código fonte a seguir percebe-se esse fenômeno:

 1 #include <windows.h>
 2  #ifdef __APPLE__
 3  #include <GLUT/glut.h>
 4  #else
 5  #include <GL/glut.h>
 6  #endif
 7  #include <stdlib.h>
 8  static void resize(int width, int height)
 9  {
10      glViewport(0, 0, width, height);
11  }
12  
13  void origem() /// desenha os eixos x e y
14  {
15      glColor3f(0,0,0);
16      glBegin(GL_LINES);
17      glVertex2f(-1,0);
18      glVertex2f(1,0);
19      glVertex2f(0,-1);
20      glVertex2f(0,1);
21      glEnd();
22  }
23  void forma() /// desenha um triangulo
24  {
25      glBegin(GL_POLYGON);
26          glVertex2f(0.2,0.2);
27          glVertex2f(0.2,0.4);
28          glVertex2f(0.4,0.4);
29      glEnd();
30  }
31  static void display(void) /// função chamada ao ocorrer um evento de atualização de tela
32 { 33 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 34 glLoadIdentity(); 35 origem(); 36 glColor3d(1,0,0); 37 forma(); /// desenho da forma sem transformação
38 glColor3d(0,1,0); 39 glTranslatef(-0.2, -0.4,0); 40 forma(); /// desenho da forma após a primeira modificação da matriz MODELVIEW 41 glColor3d(0,0,1); 42 glScalef(2,0.5,1); 43 forma(); /// desenho da forma após a segunda modificação da matriz MODELVIEW
44 glutSwapBuffers(); 45 } 46 static void key(unsigned char key, int x, int y) 47 { 48 49 glutPostRedisplay(); 50 } 51 int main(int argc, char *argv[]) 52 { 53 glutInit(&argc, argv); 54 glutInitWindowSize(640,480); 55 glutInitWindowPosition(10,10); 56 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); 57 glutCreateWindow("Transformações"); 58 glutReshapeFunc(resize); 59 glutDisplayFunc(display); 60 glutKeyboardFunc(key); 61 glClearColor(1,1,1,1); 62 glutMainLoop(); 63 return EXIT_SUCCESS; 64 }

Perceba através da figura que ao aplicar os comandos na sequencia: 

  • glTranslatef(-0.2, -0.4,0);
  • glScalef(2,0.5,1);

resultou no triângulo de cor azul. Com essa sequencia de parametros, primeiramente o objeto sofreu um escalamento, fazendo com que o ponto (0.2, 0.2) se transformasse no ponto (0.4, 0.2). Em seguida, o objeto sofreu uma translação, fazendo com que o ponto (0.4, 0.2) se transformasse no ponto (0.2, -0.2). 

Esse exemplo, reforça ainda mais a importância da sequencia de comandos. Para aplicar uma sequencia desejada, deve inserir os comandos na sequencia do último até o primeiro, similar a posição das matrizes de transformação.

Aplicando sua própria matriz de transformação

O OpenGL permite você configurar manualmente a matriz de transformação através do comando 

void glLoadMatrixf( const GLfloat * A )

O comando recebe um vetor contendo cada uma das posições da matriz de transformação seguindo a sequencia mostrada na figura a seguir:

, em  que a é A matriz declarada ciomo GLfloat  A = {a0, a1, a2, ..., a15};

por exemplo, 

GLfloat  A = { 1, 0, 0, 0,  0, 1, 0, 0,  0, 0, 1, 0,  0, 0, 0, 1 };
glLoadMatrixf( A); // carrega a matriz identidade e equivale ao comando glLoadIdentity();

GLfloat  B = { 
1, 0, 0, 0,
 0, 1, 0, 0,  
0, 0, 1, 0,  
Dx, Dy, Dz, 1 };

glLoadMatrixf( B); // carrega uma matriz de uma transformação de translação

Pilhas de Matrizes

O OpenGL permite com que as matrizes de trabalho sejam guardadas para posteriormente serem restauradas através de uma pilha.

O OpenGL permite a utilização de pilhas para 4 tipos de matrizes: cor, textura, transformações geométricas (MODELVIEW) e projeções. No momento, vamos nos ater a matriz padrão (MODELVIEW). Mas em aulas posteriores, trataremos das demais matrizes.

Esta funcionalidade é provida pelas funções glPushMatrix() glPopMatrix(). A primeira insere a matriz de transformação atual na pilha enquanto que a segunda retira a matriz do topo da pilha e torna esta última a matriz de transformação a atual.

Na figura a seguir é mostrado um trecho de código e a visualização das pilhas das matrizes


Existe uma forma avançada de se perceber o efeito do GLPUSHMATRIX e GLPOPMATRIX . Segundo especialistas, as transformações geométricas aplicads na matirz de modelview representam as açoes tomadas para reposicionamento da origem do sistema de coordenadas. 

Assim, ao realizar um GlPushMatrix, estamos definindo que todo desenho passa a ser representado relativamente em função de uma nova origem. O trecho de codigo a seguir mostra esse efeito.

  1 glLoadIdentity(); // faz com que O seja colocado em (0,0).
  2  origem(); // desenha a origem
  3      glPushMatrix(); /// faz com que o desenho seja feito em relação a (0,0)
  4          glRotatef(90,0,0,1);
  5          glTranslatef(0.2, 0.2, 0);
  6          // altera o (0,0) para o ponto (-0.2, 0.2). Pois ele sofreu uma translação seguida de rotação
  7          origem(); // desenha a nova origem O'
  8          .... // desenha os objetos em relação a essa nova origem
  9          glPushMatrix(); // guarda
 10             glTranslatef(0.4, 0.1, 0);
 11             glRotatef(angulo,0,0,1);
 12            // faz com que o eixo seja rotacionado por angulo graus e depois transladado em (0.4, 0.1).
 13            origem(); // desenha a nova origem
 14            ... // desenha em relação a 0''
 15          glPopMatrix(); // volta ao eixo de coordenada 0'
 16      glPopMatrix(); // volta ao eixo de coordenada 0


Exemplo 2 de código de tranformações 

#include<windows.h>
#include <GL/gl.h>
#include <GL/glut.h>
#include <math.h>
#include<stdio.h>
float lim = 10;

void eixos()
{
glLineWidth(1);
glBegin(GL_LINES);
glColor3f(0.5,0.5,0.5);
for (int x = - lim; x<lim;x++)
{
glVertex2f(x,lim);
glVertex2f(x,-lim);
glVertex2f(lim,x);
glVertex2f(-lim,x);
}
glEnd();
glLineWidth(4);
glBegin(GL_LINES);
glColor3f(0,0,0);

glVertex2f(0,lim);
glVertex2f(0,-lim);
glVertex2f(-lim,0);
glVertex2f(lim,0);
glEnd();
}
void triangulo_original()
{
glColor3f(1,0,0);
glBegin(GL_LINE_LOOP);
glVertex2f(3,3);
glVertex2f(6,7);
glVertex2f(3,10);
glEnd();
}

void triangulo()
{
glBegin(GL_LINE_LOOP);
glVertex2f(3,3);
glVertex2f(6,7);
glVertex2f(3,10);
glEnd();
}

void triangulo_final()
{
glColor3f(0,0,1);
glBegin(GL_LINE_LOOP);
glVertex2f(3,3);
glVertex2f(6,7);
glVertex2f(6,3);
glEnd();
}


void Desenha()
{
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(-lim,lim,-lim,lim);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
/*glViewport(400,300,400,300);
eixos();
triangulo_original();
triangulo_final();*/

glViewport(0,300,400,300);
eixos();
triangulo_original();
glColor3f(0,1,1);
glPushMatrix();
glTranslatef(-6,-7,0);
triangulo();
glPopMatrix();

glViewport(0,0,400,300);
eixos();
triangulo_original();
glColor3f(1,1,0);
glPushMatrix();
glRotatef(-atan(4.0/3.0)*180/M_PI,0,0,1);
glTranslatef(-6,-7,0);
triangulo();
glPopMatrix();

glViewport(400,0,400,300);
eixos();
triangulo_original();
glColor3f(0,1,0);
glPushMatrix();
glScalef(3.0/5.0,1,1);
glRotatef(-atan(4.0/3.0)*180/M_PI,0,0,1);
glTranslatef(-6,-7,0);
triangulo();
glPopMatrix();

glViewport(400,300,400,300);
eixos();
triangulo_original();
glColor3f(0,0,1);
glPushMatrix();
glTranslatef(6,3,1);
glScalef(3.0/5.0,1,1);
glRotatef(-atan(4.0/3.0)*180/M_PI,0,0,1);
glTranslatef(-6,-7,0);
triangulo();
glPopMatrix();
//triangulo_final();
glFlush();
}


int main (void)
{
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(800,600);
glutInitWindowPosition(283,84);
glutCreateWindow("exemplo");
glutDisplayFunc(Desenha);
glClearColor(1,1,1,1);
//printf("%f\n",atan(4.0/3.0)*180/M_PI);
glutMainLoop();
return 0;
}

Comments