Computação Gráfica I

Trabalho Prático

Exercícios



5. Hierarquia

Aviso: Lembro que o código fonte dado não é compilável por si só, e que um ambiente OpenGL completo - aquele que fomos construindo ao longo das aulas deve funcionar. Para compilar e executar os exemplos dados aqui, é necessário apenas adicionar partes de código nos locais corretos das suas implementações.

Objetivo:
Construção e animação de objetos hierárquicos.

A animação hierárquica é uma das técnicas existentes para animação de objetos cujos movimentos dependem dos movimentos de outros objetos. É usada também para animar objetos complexos (compostos por diferentes elementos que se movem uns em relação aos outros).

Dois exemplos:

1. Um planetário: neste caso os objetos dependem uns dos outros (Planetas girando em torno do Sol e satélites em torno dos planetas).
2. Braço de robô articulado: neste caso temos um objeto complexo (partes conectadas).

O movimento dos elementos será definido por transformações (translações, rotações e possivelmente escala).

Note que animação hierárquica diz respeito a segmentos rígidos; neste curso não veremos objetos deformáveis.

Exercício 1: hierarquia de um planetário (nosso Sistema Solar).

Iniciemos por um modelo simplificado do Sistema Solar. Este modelo é composto pelo Sol, em torno do qual giram a Terra e Marte. Ele tem também a Lua, que gira em torno da Terra.
A hierarquia é representada da seguinte maneira:

Sol
  ¦
  +- Terra
  ¦    ¦
  ¦    +- Lua
  ¦
  +- Marte

Então diz-se que:

Cada filho depende do movimento do seu pai/mãe. Uma imagem renderizada desse modelo é mostrada na figura 1.


Figura 1. Vista do planetário simplificado.

Começamos inicializando os ângulos de rotação para os corpos celestes.

/* angulos de rotacao para os corpos celestes */
float angleEarth = 0;
float angleMoon = 0;
float angleMars = 0;

/* para a camera, lembrem-se dos exercicios anteriores */
#define y_min 60
#define ro_min 120
float eyex = 0;
float eyey = y_min;
float eyez = ro_min;

Depois, escrevemos as funções que desenham os diferentes corpos (vocês podem substitui-la por uma das suas funções drawMesh para ver o que acontece).

void drawSun(){

glColor3f( 1, 1, 0 );
glutSolidSphere( 12, 16, 16 );

}

void drawEarth(){

glColor3f( 0, 0, 1 );
glutSolidSphere( 5, 16, 16 );

}

void drawMoon(){

glColor3f( 0.5, 0.5, 0.5 );
glutSolidSphere( 1, 16, 16 );

}

void drawMars(){

glColor3f( 1, 0.2, 0 );
glutSolidSphere( 4, 16, 16 );

}

Então, na função display, criamos a hierarquia. Observem com atenção o uso dos blocos glPushMatrix/glPopMatrix que permitem salvar e restaurar o contexto de transformações.

O uso desses blocos é necessário quando há ramificações na hierarquia, i. e., quando um pai tem mais de um filho. Nesse caso, é necessário usar PushMatrix para salvar o contexto e PopMatrix para recuperá-lo.

É o que acontece com as ligações (Sol >> Terra) e (Sol >> Marte) e que caracteriza uma bifurcação (revise o quadro da hierarquia). Assim, depois de fazer as transformações da Terra, que depende do Sol, será necessário voltar ao contexto do Sol para aplicar as transformações referentes a Marte, porque ele também depende do Sol.
Se o contexto não for recarregado, a hierarquia vai se perder completamente.

void display(void)
{
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

glLoadIdentity();

gluLookAt(eyex,eyey,eyez,0,0,0,0,1,0);

// Draw planetary system
glPushMatrix();
  drawSun();
  glPushMatrix();
    glRotatef( angleEarth, 0, 1, 0 );
    glTranslatef( 30, 0, 0 );
    drawEarth();
    glPushMatrix();
      glRotatef( angleMoon, 0, 1, 0 );
      glTranslatef( 7, 0, 0 );
      drawMoon();
    glPopMatrix();
  glPopMatrix();
  glPushMatrix();
    glRotatef( angleMars, 0, 1, 0 );
    glTranslatef( 50, 0, 0 );
    drawMars();
  glPopMatrix();
glPopMatrix();

glutSwapBuffers();
}

E aqui, finalmente, o timer para animação do sistema. Pode ser usada a função idle também, embora a TimerFunc da GLUT já traga um mecanismo de controle de temporização.

void
TimerFunction( int value ){

angleEarth += 3;
if( angleEarth >= 360 ) angleEarth = 0;

angleMoon += 6;
if( angleMoon >= 360 ) angleMoon = 0;

angleMars += 2;
if( angleMars >= 360 ) angleMars = 0;

glutPostRedisplay();
glutTimerFunc( 33, TimerFunction, 1);
}

Para navegar em torno do sistema e observá-lo de diferentes pontos de vista, se pode reutilizar o código para animação de câmera visto anteriormente.

Questões:

1. Testem o exemplo anterior e ENTENDAM a hierarquia.

2. Completem a cena:

Exercice 2 : Braço de robô articulado

Agora veremos como animar outro tipo de hierarquia. Um braço robótico, que é um conjunto de partes articuladas que poderá dar algumas idéias para o projeto.

O braço é composto de 6 segmentos:

  • Braço
  • Antebraço
  • Mão
    • Dedo 1
    • Dedo 2
    • Dedo 3

Uma imagem renderizada é mostrada na figura 2:


Figure 2. Vista do braço articulado

De forma análoga ao planetário, implementaremos os vários elementos.

As variáveis globais:

/* angulos para as articulacoes */
float shoulderAngle = 0;
float elbowAngle = 0;
float handAngle = 0;
float finger1Angle = 0;
float finger2Angle = 0;
float finger3Angle = 0;

int flag = 0; // flag para selecionar o elemento a ser animado
int stop = 1; // flag para parar a animacao

/* para a camera, vale o mesmo do exercício anterior */
#define y_min 60
#define ro_min 120
float eyex = 0;
float eyey = y_min;
float eyez = ro_min;

Depois, a função display com a hierarquia do robô.

void display(void)
{
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

glLoadIdentity();

gluLookAt(eyex,eyey,eyez,0,0,0,0,1,0);

glPushMatrix();
glScalef( 100, 100, 100 );
glTranslatef( -0.5, 0, -0.5 );
drawPlane();
glPopMatrix();

// Draw Robot Arm
glPushMatrix();
glColor3f( 0.1, 1, 0.1 );
glRotatef( shoulderAngle, 0, 0, 1 );
glTranslatef( 0, 4, 0 );
glPushMatrix();
glScalef( 1, 8, 1 );
glutSolidCube(1);
glPopMatrix();

glTranslatef( 0, 4, 0 );

glColor3f( 0.1, 0.1, 1 );
glRotatef( elbowAngle, 0, 0, 1 );
glTranslatef( 0, 3.5, 0 );
glPushMatrix();
glScalef( 0.8, 7, 0.8 );
glutSolidCube(1);
glPopMatrix();

glTranslatef( 0, 3.5, 0 );

glColor3f( 1, 0.1, 0.1 );
glRotatef( handAngle, 1, 0, 0 );
glTranslatef( 0, 0.75, 0 );
glPushMatrix();
glScalef( 1.0, 1.5, 0.6 );
glutSolidCube(1);
glPopMatrix();

glTranslatef( 0, 0.75, 0 );

glPushMatrix();
glRotatef( finger1Angle, 1, 0, 0 );
glTranslatef( -0.5, 0.75, 0 );
glScalef( 0.2, 1.5, 0.2 );
glutSolidCube(1);
glPopMatrix();

glPushMatrix();
glRotatef( finger2Angle, 1, 0, 0 );
glTranslatef( 0, 0.75, 0 );
glScalef( 0.2, 1.5, 0.2 );
glutSolidCube(1);
glPopMatrix();

glPushMatrix();
glRotatef( finger3Angle, 1, 0, 0 );
glTranslatef( 0.5, 0.75, 0 );
glScalef( 0.2, 1.5, 0.2 );
glutSolidCube(1);
glPopMatrix();

glPopMatrix();

glutSwapBuffers();
}

Então, as funções para mover as juntas.
(lembrem-se da interação pelo teclado)

void increment( int delta ){

switch( flag ){
case 1: shoulderAngle+=delta; break;
case 2: elbowAngle+=delta; break;
case 3: handAngle+=delta; break;
case 4: finger1Angle+=delta; break;
case 5: finger2Angle+=delta; break;
case 6: finger3Angle+=delta; break;

}
glutPostRedisplay();
}

void key( unsigned char k, int x, int y ){

switch( k ){
case '1': flag = 1; break;
case '2': flag = 2; break;
case '3': flag = 3; break;
case '4': flag = 4; break;
case '5': flag = 5; break;
case '6': flag = 6; break;
case ' ': stop = 1-stop; break;
}

}

void specialkey( int k, int x, int y ){

switch( k ){

case GLUT_KEY_RIGHT: increment(5); break;
case GLUT_KEY_LEFT: increment(-5); break;
}

}

Mais estas duas para lembrarem da interação pelo teclado:

glutKeyboardFunc(key);
glutSpecialFunc(specialkey);

E o timer para controle da câmera.

void
TimerCamera( int value ){

float ro;

if( stop ){

value++;

if( value == 720 ) value = 0;

ro = ro_min - sin(value/2.0*PI/360)*ro_min*0.8;
eyey = y_min - sin(value/2.0*PI/360)*y_min*0.8;
eyex = ro * sin(value/2.0*PI/180);
eyez = ro * cos(value/2.0*PI/180);

glutPostRedisplay();
}
glutTimerFunc( 33, TimerCamera, value);
}

Questão:

1. Melhorar o realismo da câmera em dois quesitos: