Proyección en opengl (ortogonal y perspectiva)
Una proyección ortogonal define un volumen de la vista de tipo
paralelepipédico tal y como se muestra en la siguiente figura. La principal
característica de esta proyección es que el tamaño de los objetos es
independiente de la distancia a la que estén del observador, por ejemplo, dos
cilindros del mismo tamaño, uno a cinco unidades y el otro a diez unidades de
distancia del observador se proyectarán con el mismo tamaño.
Aunque
OpenGL dispone de pilas para
Las
matrices GL_MODELVIEW y GL_PROJECTION, sólo se suele utilizar la pila de
GL_MODELVIEW.
Para
definir una proyección ortogonal en OpenGL hay que dar los siguiente pasos:
glMatrix(GL_PROJECTION); /* Voy
a manejar la matriz de proyección */
glLoadIdentity(); /* Cargo inicialmente la identidad */
/* Y ahora defino la proyección ortogonal */
void glOrtho(izquierda, derecha, abajo, arriba, cerca, lejos);
Si lo que deseamos es
trabajar con una proyección ortogonal 2D:
void gluOrtho2D(izquierda,
derecha, abajo, arriba);
que no es más que una
proyección ortogonal donde el plan delantero está en -1 y el trasero en 1.
Por ejemplo, la ilustración 1.2 es un render de un coche con
proyección ortográfica, visto desde delante.
Imagen 1.2
El código utilizado para esta proyección ha sido
glOrtho(-0.5f, 0.5f, -0.5f, 0.5f, 0.01f, 20.0f
Proyección perspectiva
La proyección ortogonal no da sensación de
profundidad porque el tamaño de los objetos no depende de su distancia al
observador. Para conseguir este efecto necesitamos definir una proyección
perspectiva. Esta proyección define un volumen de la vista que es una prisma
truncado de base rectangular, como el de la siguiente figura:
|
la
función OpenGL que establece este tipo de perspectiva es:
void glFrustum(izquierda,
derecha, abajo, arriba, cerca, lejos);
este modo de definir la
proyección perspectiva no es demasiado intuitivo, es más sencillo establecerla
con un esquema como el que se muestra en la siguiente figura:
y la función OpenGL que la
establece es:
La
función OpenGL que establece el tamaño del vierport es:
void glViewport(GLint x, GLint
y, GLsizei ancho, GLsizei alto);
La ilustración 1.3 muestra la escena del coche de la sección
anterior, esta vez con una proyección en perspectiva:
Ilustracion 1.3
El código utilizado para definir la proyección ha sido:
gluPerspective(45.0f,(GLfloat)(width/height),0.01f,100.0f);
Se usan 45º de ángulo, la relación entre el ancho y alto de
la pantalla (width y height son el ancho y alto actual de la ventana) y las
distancias a los planos de corte znear y zfar son 0.01 y 100 respectivamente.
Un poco mas complejo es este
// Le digo a OpenGL que voy a cambiar la matriz de
proyeccion
glMatrixMode(GL_PROJECTION);
// Le digo a OpenGL que use proyeccion perspectiva. Uso el
ancho
// y alto de mi viewport para calcular el segundo parametro
gluPerspective(60.0f,
(float)rect.right/(float)rect.bottom, 0.5f, 50.0f);
// Muevo para atras el objeto. El punto de vista esta
// en la posicion 0,0,0 porque no lo he cambiado, asi que
// alejo el objeto para poder verlo.
glTranslatef(0,0,-4.0f);
// Giro el objeto 30 grados en el eje x, luego otros
// 30 en el eje y. Es para que quede bonito.
glRotatef(30,1,0,0);
glRotatef(30,0,1,0);
// y pinto el objeto con coordenadas genericas alrededor
// del eje de coordenadas. Estas coordenadas que pongo
// ahora son modificadas por las modificaciones que
// hemos hecho en la matriz modelview (glTranslate,
glRotate).
// Le digo a OpenGL que voy a pintar y con cuadrados:
glBegin(GL_QUADS);
// Cara de arriba
glColor3f(1,0,0); // rojo
glVertex3f( 1.0f, 1.0f,-1.0f);
glVertex3f(-1.0f, 1.0f,-1.0f);
glVertex3f(-1.0f, 1.0f, 1.0f);
glVertex3f( 1.0f, 1.0f, 1.0f);
// Cara de abajo
glColor3f(1,0,0); // rojo
glVertex3f( 1.0f,-1.0f, 1.0f);
glVertex3f(-1.0f,-1.0f, 1.0f);
glVertex3f(-1.0f,-1.0f,-1.0f);
glVertex3f( 1.0f,-1.0f,-1.0f);
// Cara frontal
glColor3f(0,0,1); // azul
glVertex3f( 1.0f, 1.0f, 1.0f);
glVertex3f(-1.0f,
1.0f, 1.0f);
glVertex3f(-1.0f,-1.0f,
1.0f);
glVertex3f( 1.0f,-1.0f, 1.0f);
// Cara trasera
glColor3f(0,0,1); // azul
glVertex3f( 1.0f,-1.0f,-1.0f);
glVertex3f(-1.0f,-1.0f,-1.0f);
glVertex3f(-1.0f,
1.0f,-1.0f);
glVertex3f( 1.0f, 1.0f,-1.0f);
// Cara izquierda
glColor3f(0,1,0); // verde
glVertex3f(-1.0f, 1.0f, 1.0f);
glVertex3f(-1.0f,
1.0f,-1.0f);
glVertex3f(-1.0f,-1.0f,-1.0f);
glVertex3f(-1.0f,-1.0f, 1.0f);
// Cara derecha
glColor3f(0,1,0); // verde
glVertex3f( 1.0f, 1.0f,-1.0f);
glVertex3f(
1.0f, 1.0f, 1.0f);
glVertex3f(
1.0f,-1.0f, 1.0f);
glVertex3f( 1.0f,-1.0f,-1.0f);
glEnd();
Transformaciones
Geométricas (EJEMPLOS DE TRASLACION, ROTACION Y ESCALADO)
Traslacion
void dibujar_triangulo(){
glBegin(GL_TRIANGLES)
glVertex3f(0,0,0);
glVertex3f(1,0,0);
glVertex3f(0,1,0);
glEnd();
}
// dibuja el triangulo en la posición original
Dibujar_triángulo();
// dibuja el triangulo desplazado
glTranslate(10,0,0);
Dibujar_triángulo();
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
Rotacion y escalacionglMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotate(45,0,0,1);
glTranslate(10,0,0)
glScale(2,2,2)
Dibujar_triangulo();
glLoadIdentity();
glRotate(45,0,0,1);
glTranslate(0,10,0)
glScale(2,2,2)
Dibujar_cuadrado();
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
Manejo de matrices en opengl para Para proyeccion y para
modelado
Proyeccion
La matriz de proyección especifica el tamaño y la forma del
volumen de visualización. El volumen de visualización es aquel cuyo contenido
es el que se representa en pantalla. Está delimitado por una serie de planos de
trabajo. De estos planos, los más importantes son los planos de corte, que son
los que nos acotan el volumen de visualización por delante y por detrás. En el
plano más cercano a la cámara (znear) es donde se proyecta la escena para luego
pasarla a la pantalla.
Modelado
La matriz del modelador es una matriz 4x4 que representa el
sistema de coordenadas transformado que se está usando para colocar y orientar
los objetos. Si se multiplica la matriz del vértice (de tamaño 1x4) por ésta se
obtiene otra matriz 1x4 con los vértices transformados sobre ese sistema de
coordenadas.
RESUMEN
PIla de Matrices
En la función display() se encuentran las llamadas a dos
funciones de matrices que todavía no han sido comentadas. Se trata de
glPushMatrix() y glPopMatrix(). Para comprender su funcionamiento, primero se
va a experimentar que es lo que ocurre cuando no están dichas llamadas. Para
ello se comentan en la función display() ambas llamadas:
void display(void) {
// glPushMatrix();
glTranslatef(0.0, 0.0, .5);
...
// glPopMatrix();
glutSwapBuffers();
}
La
razón de este movimiento es que en la función display está incluida una llamada
a glTranslatef() que se utiliza para posicionar uno de los objetos. Como se ha
explicado anteriormente, las funciones de traslación multiplican la matriz
actual por una matriz de traslación creada con los argumentos que se le pasan,
por tanto, sucesivas llamadas a la función display() provocan sucesivas
multiplicaciones de la matriz actual con el efecto que se observa de incrementar
la traslación. Para solucionar este problema OpenGL dispone de unos stacks o
pilas de matrices, que permiten almacenar y recuperar una matriz anterior.
aunque opengl dispone de pilas para las matrices gl_modelview y gl_projection,
sólo se suele utilizar la pila de gl_modelview. Una pila es un almacén con
funcionamiento LIFO, el último en entrar es el primero en salir, por lo que
suele comparar a una pila de platos en la que sólo se puede dejar uno encima de
la pila o coger el superior que es el último depositado. La pila de matrices
tiene el mismo funcionamiento sustituyendo los platos por matrices. La matriz
superior de la pila es sobre la que se aplican las distintas transformaciones,
multiplicándola por la matriz que generan las disntintas
funciones.
La
función glpushmatrix() realiza una copia de la matriz superior y la pone encima
de la pila, de tal forma que las dos matrices superiores son iguales.. Las
siguientes transformaciones que se realizan se aplican sólo a la matriz superior
de la pila, quedando la anterior con los valores que tenía en el momento de
llamar a la función glpushmatrix(). La función glpopmatrix() elimina la matriz
superior, quedando en la parte superior de la pila la matriz que estaba en el
momento de llamar a la función glpushmatrix().
Necesitaremos
realizar tres operaciones: calcular la coordenadas donde se encuentra la
tortuga, almacenar dicha coordenada y dibujar el rastro. Para almacenar los
puntos se utiliza una variable para indicar el número de puntos y tres vectores
para las coordenadas x, y, z.
int np = 0;
float px [10000];
float py [10000];
float
pz [10000];
Para
calcular las coordenadas de la tortuga es necesario conocer la matriz de
transformación de modelado. Debido a que en OpenGL, la matriz de modelado se
almacena junto con la de visualización en la matriz GL_MODELVIEW, es necesario
guardar de modo independiente esta matriz de modelado. Para ello definimos la
variable mModel, como una variable global, ya que va a ser accedida en distinos
puntos de la aplicación.
glmatrixmode(gl_modelview);
glpushmatrix();
glLoadIdentity();
glgetdoublev (gl_modelview_matrix, mmodel);
glPopMatrix();
En
este código se realizan las siguientes operaciones:
<!--[if !supportLists]-->·
<!--[endif]-->œ
se indica primeramente sobre que matriz se van a realizar las opereraciones con
glMatrixMode();
<!--[if !supportLists]-->·
<!--[endif]-->œ
se crea una nueva matriz con glPushMatrix();
<!--[if !supportLists]-->·
<!--[endif]-->œ
se carga la matriz identidad con glLoadIdentity();
<!--[if !supportLists]-->·
<!--[endif]-->œ
se almacena la matriz superior de la pila en el vector mModel con la función
glGetDoublev();
<!--[if !supportLists]-->·
<!--[endif]-->œ
y finalmente se elimina la matriz superior de la pila con glPopMatrix() para
dejar la
que
estaba antes de este proceso.
En
realidad todo este proceso lo que ha hecho ha sido inicializar la matriz que
represena
mModel
con la matriz identidad. La función glMultMatrixd() multiplica la matriz
superior de la pila por la matriz que tiene como argumento. Al multiplicar en
este caso por la matriz identidad, la matriz que queda en la posición superior
de la pila es mModel. La llamada a addPointToTrace() se introduce despues de las
llamadas a gltranslatef() en las instrucciones correspondientes al forward y
back. addPointToTrace();
La
llamada a displayTrace() se realiza en la función display(). El dibujo del
objeto que representa la tortuga se debe realizar despues de multiplicar la
matriz actual (la matriz de visualización) por la matriz de modelado que se
almacena en mModel. Dibujar un rastro que consista en unas superficie en lugar
de una línea. Para ello se puede utilizar glBegin(GL_QUAD_STRIP) que dibuja una
sucesión de rectángulos para cada pareja de puntos que
recibe.
Mostrar el texto
Las
instrucciones introducidas se muestran en la ventana MSDOS. Se pueden mostrar en
la ventana gráfica. Para ello es necesario cambiar las matrices de
transformación. La siguiente función realiza la representación del
texto:
void text(GLuint x, GLuint y, GLfloat scale, char* format, ...)
{
va_list args;
char buffer[255], *p;
GLfloat font_scale = 119.05f + 33.33f;
va_start(args, format);
vsprintf(buffer, format, args);
va_end(args);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluOrtho2D(0, glutGet(GLUT_WINDOW_WIDTH), 0,
glutGet(GLUT_WINDOW_HEIGHT));
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glPushAttrib(GL_ENABLE_BIT);
glDisable(GL_LIGHTING);
glDisable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
glTranslatef(x, y, 0.0);
glScalef(scale/font_scale, scale/font_scale,
scale/font_scale);
for(p = buffer; *p; p++)
glutStrokeCharacter(GLUT_STROKE_ROMAN, *p);
glPopAttrib();
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
}
Para
mostrar el texto se llama desde la función display() con la
sentencia:
if (command) {
glColor3f(1.0,1.,0.0) ;
text(5, 5, 20, "->%s", strCommand);
PUNTOS
A REALIZAR
Utilizando
los comandos de logo representar una esfera compuesta por
un
conjunto
de circunferencias en el espacio.
Utilizando
los comandos de logo
realizar
la representación de una
helicoidal.
La transformación de puerto de vista:
Esta transformación define el tamaño y la posición de la imagen final (como
la impresión de una fotografía), se usa el comando
glViewPort() y es necesario
modificar el puerto de vista cada que la ventana se modifica.
Una vez que todas estas transformaciones son aplicadas a cada uno de los
vértices, la imagen puede ser desplegada dentro de la ventana.
La sintaxis de los comandos de transformación es la siguiente:
glTranslate {fd} (TYPE
x, TYPE y TYPE z);
glRotate {fd} (TYPE
ángulo,TYPE x, TYPE y, TYPE z);
glScale {fd} (TYPE x,
TYPE y, TYPE z);
En donde
TYPE es
float o
int, dependiendo del comando
que se escogió.
10.- Normales, Color e Iluminación:
glColor3 {b,s,i,f,d} (TYPE rojo,
TYPE verde, TYPE azul)
Un ejemplo de esto es el siguiente:
glBegin(GL_POLYGON);
glNormal3fv(n0); /* n0 es
un arreglo que contiene los datos */
glVertex3fv(v0);
glNormal3fv(n1);
glVertex3fv(v1);
glNormal3fv(n2);
glVertex3fv(v2);
glEnd();
__________________________________________________________________________________
Cubo y Piramide Modificada
#include <GL/glut.h>
GLfloat anguloCuboX = 0.0f;
GLfloat
anguloCuboY = 0.0f;
GLfloat anguloEsfera = 0.0f;
GLint
ancho=600;
GLint alto=600;
int hazPerspectiva = 0;
void
reshape(int width, int height)
{
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(hazPerspectiva)
gluPerspective(60.0f,
(GLfloat)width/(GLfloat)height, 1.0f, 20.0f);
else
glOrtho(-4,4, -4, 4, 1, 10);
glMatrixMode(GL_MODELVIEW);
ancho = width;
alto = height;
}
void drawCube(void)
{
glColor3f(1.0f, 0.0f, 0.0f);
glBegin(GL_QUADS); //cara frontal
glVertex3f(-1.0f, -1.0f, 1.0f);
glVertex3f( 1.0f, -1.0f, 1.0f);
glVertex3f( 1.0f, 1.0f, 1.0f);
glVertex3f(-1.0f, 1.0f,
1.0f);
glEnd();
glColor3f(0.0f, 1.0f, 0.0f);
glBegin(GL_QUADS); //cara trasera
glVertex3f( 1.0f, -1.0f,
-1.0f);
glVertex3f(-1.0f, -1.0f, -1.0f);
glVertex3f(-1.0f, 1.0f,
-1.0f);
glVertex3f( 1.0f, 1.0f, -1.0f);
glEnd();
glColor3f(0.0f, 0.0f, 1.0f);
glBegin(GL_QUADS); //cara lateral
izq
glVertex3f(-1.0f,-1.0f, -1.0f);
glVertex3f(-1.0f,-1.0f,
1.0f);
glVertex3f(-1.0f, 1.0f, 1.0f);
glVertex3f(-1.0f, 1.0f,
-1.0f);
glEnd();
glColor3f(1.0f, 1.0f, 0.0f);
glBegin(GL_QUADS); //cara lateral dcha
glVertex3f(1.0f, -1.0f,
1.0f);
glVertex3f(1.0f, -1.0f, -1.0f);
glVertex3f(1.0f, 1.0f,
-1.0f);
glVertex3f(1.0f, 1.0f, 1.0f);
glEnd();
glColor3f(0.0f, 1.0f, 1.0f);
glBegin(GL_QUADS); //cara
arriba
glVertex3f(-1.0f, 1.0f, 1.0f);
glVertex3f( 1.0f, 1.0f,
1.0f);
glVertex3f( 1.0f, 1.0f, -1.0f);
glVertex3f(-1.0f, 1.0f,
-1.0f);
glEnd();
glColor3f(1.0f, 0.0f, 1.0f);
glBegin(GL_QUADS); //cara abajo
glVertex3f( 1.0f,-1.0f,
-1.0f);
glVertex3f( 1.0f,-1.0f, 1.0f);
glVertex3f(-1.0f,-1.0f,
1.0f);
glVertex3f(-1.0f,-1.0f, -1.0f);
glEnd();
}
void
display()
{
glClear(GL_COLOR_BUFFER_BIT |
GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(0.0f,
0.0f, -5.0f);
glTranslatef(-3,0,0);
glRotatef(anguloCuboX, 1.0f,
0.0f, 0.0f);
glRotatef(anguloCuboY, 0.0f, 1.0f, 0.0f);
glScalef(0.5f,
0.5f, 0.5f);
drawCube();
glLoadIdentity();
glTranslatef(0.0f, 0.0f, -5.0f);
glRotatef(anguloEsfera, 0.0f, 1.0f,
0.0f);
glTranslatef(3.0f, 0.0f, 0.0f);
glColor3f(1.0f, 1.0f,
1.0f);
glutWireSphere(0.5f, 8, 8);
glFlush();
glutSwapBuffers();
anguloCuboX+=0.1f;
anguloCuboY+=0.1f;
anguloEsfera+=0.2f;
}
void init()
{
glClearColor(0,0,0,0);
glEnable(GL_DEPTH_TEST);
ancho = 600;
alto = 600;
}
void idle()
{
display();
}
void keyboard(unsigned char key, int x, int y)
{
switch(key)
{
case 'p':
case 'P':
hazPerspectiva=1;
reshape(ancho,alto);
break;
case
'o':
case 'O':
hazPerspectiva=0;
reshape(ancho,alto);
break;
case 27: // escape
//
exit(0);
break;
}
}
int main(int argc, char
**argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowPosition(100,
100);
glutInitWindowSize(ancho, alto);
glutCreateWindow("Cubo
1");
init();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutIdleFunc(display);
glutKeyboardFunc(keyboard);
glutMainLoop();
return 0;
}
Corrida