Computação Gráfica
Prof.
Márcio Sarroglia Pinho
Transformações
Geométricas Hierárquicas em OpenGL
As transformações
geométricas aplicadas usando comandos OpenGL do tipo glTranslate, glRotate ou glScale, são sempre armazenadas em uma matriz chamada MODELVIEW.
A cada chamada de uma destas funções OpenGL cria uma matriz de transformação
específica para a função e a seguir multiplica esta matrix pela matriz
MODELVIEW atual.
Por exemplo, na chamada da função glTranslatef(-3, 2, -8)é criada a seguinte matriz:
1.00
0.00 0.00 0.00
0.00
1.00 0.00 0.00
0.00
0.00 1.00 0.00
-3.00 2.00
-8.00 1.00
Note que na última linha da matriz são colocados os valores passados como parâmetros para a função.
No momento de exibir um objeto o que OpenGL faz é a multiplicação desta matriz pelo vértices do objeto, obtendo assim a posição final do objeto. Por exemplo, no caso de exibir o ponto (10, 15, 20) seria obtido o ponto (7,17, 12) da seguinte forma:
Caso seja desejável ou necessário, é possivel setar "à mão" esta matriz de transformação. Esta operação manual pode ser feita de tres formas:
-
usando a função glLoadIdentity(): com esta função a matriz de
transformação converte-se na matriz identidade;
- usando a função glLoadMatrixf(matrix):
com esta
função é possível definir uma nova matriz de transformação destruindo-se a
matrix anterior.
- usando a função glMultMatrixf(matrix):
com esta
função é possível multiplicar a matriz de transformação atual por uma segunda
matriz.
Caso
se tenha uma nova transformação (também representada em forma de matriz) é
possivel aplicá-la a matriz atual, bastando multiplicar a matriz atual pela
nova matriz.
Por exemplo, caso tenhamos a matriz de transformação para uma translação (5, 7,
2) a equação a seguir calcula a nova matriz.
Para
obter a matriz de transformação geométrica atual em OpenGL usa-se a
função glGetFloatv da seguinte forma:
GLfloat Matriz[4][4];
glGetFloatv(GL_MODELVIEW_MATRIX,
&Matriz[0][0]);
Para
armazenar as transformações geométricas de um objeto em uma matriz deve-se
chamar a função glGetFloatv imediatamente após terem
sido setadas todas estas transformações do objeto.
Por exemplo:
//
**********************************************************************
// void RecalculaMatrizObjeto()
//
**********************************************************************
void RecalculaMatrizObjeto()
{
glPushMatrix();
glLoadIdentity();
glTranslatef
(PosObj.X, PosObj.Y, PosObj.Z);
// guarda a
matriz de transformação do objeto para um futuro uso
glGetFloatv(GL_MODELVIEW_MATRIX, &MatObj[0][0]);
glPopMatrix();
}
Uma
vez que se tem a matriz de transformações de um objeto é possível aplicar estas
transformações multiplicando-se esta matriz pela matriz atual. Isto é feito da
seguunte forma:
//
**********************************************************************
// void DesenhaObjeto()
//
**********************************************************************
void DesenhaObjeto()
{
glColor3f(0.0f,0.0f,0.6f);
glPushMatrix();
glMultMatrixf(&MatObj[0][0]);
DesenhaCubo();
glPopMatrix();
}
A
montagem de uma hierarquia de trnasfomações serve para que se possa definir um
objeto como filho de outro.
Neste caso, o objeto filho sofrerá todas as transformações geométrica aplicadas
a seu pai. Para tanto deve-se apenas desenhar o objeto logo após o desenho de
seu. O exemplo a seguir ilustra este procediemnto.
//
**********************************************************************
// void DesenhaPAI()
//
**********************************************************************
void DesenhaPAI()
{
glColor3f(0.7f,0.7f,0.0f);
glPushMatrix();
glMultMatrixf(&Mat_Pai[0][0]); // aplica as transformações do pai
DesenhaCubo();
if (TemFilho)
DesenhaObjeto(); // desenha Filho
glPopMatrix();
}
Neste caso, entretato, no instante em que se torna um objeto filho de outro, este filho irá sofrer as transformações geométricas de seu pai adicionadas às suas. Por exemplo, se o pai tem uma translação em X igual a 40 e o filho outra igual a 30, então o objeto filho irá ser transladado de 70 unidades. Isto irá ocorrer no momento em que o objeto filho for desenhado pela primeira vez após o pai, causando uma translação indesejada.
Para
contornar este efeito colateral é ncessário que no momento do estabelecimento
da relação pai-filho, a matriz do filho desfaça as transformações existentes no
pai naquele momento. Para tanto deve-se multiplicar a matriz do filho pela inversa
da matriz do pai, conforme o exemplo a seguir.
//
**********************************************************************
// void CriaVinculoPai_Filho()
//
**********************************************************************
void CriaVinculoPai_Filho()
{
GLfloat MatInv[4][4];
TemFilho = TRUE;
// calcula a inversa
da matriz do Pai
CopyMatrix(MatInv_Pai,
Mat_Pai);
M_invert(MatInv_Pai);
// Multiplica a Matriz do
filho pela inversa da matriz do pai
M_mult(MatObj,
MatInv_Pai); // MatObj = MatObj * MatInv_Pai
}
Note que isto deve ser feito somente no instante em que se estabelece o
vínculo entre pai e filho. Isto porque só deve-se desfazer as transformações do
pai que existem no momento do estabelecimento do vínculo. A partir deste
momento, se uma nova transformaçõa for aplicada ao pai, ela deverá ser também
aplicada ao filho.
Para
desfazer o vínculo de hierarquia e deixar o objeto no mesmo lugar
deve-se multiplicar sua matriz pela matriz atual de seu pai. Se isto não for
feito ocorrerá uma translação indesejada no objeto filho pois este deixará de
sofrer as translações do pai (após o término do vínculo).
//
**********************************************************************
// void DesfazVinculoPai_Filho()
//
**********************************************************************
void DesfazVinculoPai_Filho()
{
TemFilho
= FALSE;
M_mult(MatObj, MatPai);
// MatObj = MatObj * Mat_Pai
}
A partir deste momento o objeto filho não mais deverá ser desenhado após o
pai.