You are on page 1of 18

Manual de OpenGL

Contenido
1. Qu es OpenGL?................................................................................................................. 3
2. Cdigo basado en OpenGL .................................................................................................. 3
2.1

Sintaxis ......................................................................................................................... 3

3. El espacio 3D.......................................................................................................................... 4
4. Primitivas geomtricas .......................................................................................................... 5
5. Transformacin de las primitivas ......................................................................................... 7
6. El Pipeline Grfico ................................................................................................................. 8
6.1 Etapa de Aplicacin ......................................................................................................... 9
6.2 Etapa de Geometra ........................................................................................................ 9
6.3 Etapa Rasterizacin ...................................................................................................... 10
7. MATERIALES E ILUMINACION ........................................................................................ 11
7.1 Especificacin de normales.......................................................................................... 11
8. Color de fondo ...................................................................................................................... 12
9. Cdigo bsico ....................................................................................................................... 14
9.1 Anlisis del cdigo ......................................................................................................... 15
Analizando la funcin main(): ......................................................................................... 15
Analizando init(): ................................................................................................................... 16
Funcin display() .................................................................................................................. 17
10. Sistema de Deteccin de Colisiones .............................................................................. 18
10.1 Formas de Colisin ........................................................ Error! Marcador no definido.

1. Qu es OpenGL?
OpenGL (Open Graphics Library) es un API (Interfaz de Programacin de
Aplicaciones) libre, multiplataforma, orientado a la creacin de contenido 3D. A
un nivel ms simple se puede decir que es una librera que permite hacer
software con grficos 3D sin preocuparse por el hardware en que se va a ejecutar
y que tiene un gran soporte en diferentes sistemas operativos como Windows,
OS/2, UNIX, Linux, etc.
La compaa que desarrolla esta librera es Sillicon Graphics Inc (SGI), en pro
de hacer un estndar en la representacin 3D gratuito y con cdigo abierto (open
source). Est basado en sus propios OS y lenguajes IRIS, de forma que es
perfectamente portable a otros lenguajes. Entre ellos C, C++, etc y las libreras
dinmicas permiten usarlo sin problema en Visual Basic, Visual Fortran, Java,
etc.

2. Cdigo basado en OpenGL


2.1

Sintaxis

Todas las funciones de OpenGL comienzan con el prefijo gl y las constantes


con GL_. Como ejemplos, la funcin glClearColor() y la constante
GL_COLOR_BUFFER_BIT.
En muchas de las funciones, aparece un sufijo compuesto por dos caracteres,
una cifra y una letra, como por ejemplo glColor3f() o glVertex3i(). La cifra
simboliza el nmero de parmetros que se le deben pasar a la funcin, y la letra
el tipo de estos parmetros.
En OpenGL existen 8 tipos distintos de datos, de una forma muy parecida a los
tipos de datos de C o C++. Adems, OpenGL viene con sus propias definiciones
de estos datos (typedef en C). Los tipos de datos (vase la tabla 2.1):

Tabla 2.1 Tipos de datos en OpenGL

3. El espacio 3D
OpenGL trabaja, a grandes rasgos, en un espacio de tres dimensiones, aunque
veremos que realmente, trabaja con coordenadas homogneas (de cuatro
dimensiones). Las tres dimensiones que nos interesan ahora son las
especificadas por un sistema 3D ortonormal. Es decir, sus ejes son
perpendiculares, y cada unidad en uno de ellos est representada por un vector
de mdulo 1 (si nos alejamos una unidad, nos alejamos la misma distancia del
eje de coordenadas) vase figura 3.1.

La cuarta coordenada se utiliza entre otras razones, para representar la


perspectiva, de este modo, el sistema de coordenadas inicial de un sistema
OpenGL puede representarse con esta matriz:
4

4. Primitivas geomtricas
En OpenGL solo se pueden dibujar primitivas muy simples, tales como puntos
lneas, cuadrados, tringulos y polgonos, a partir de estas primitivas es posible
construir primitivas ms complejas como arcos y crculos aproximndolos por
polgonos.
Toda primitiva de dibujo se construye con un par: glBegin(tipo_de_primitiva);
glVertex2f(); ... glEnd(); donde tipo_de_primitiva puede ser cualquiera de las
siguientes (vase tabla 4.1 y figura 4.2):

Tabla 4.1 Tipos de primitivas en OpenGL.

Figura 4.2 Tipos de primitivas

La librera GLUT amplia este conjunto de primitivas geomtricas con 9 nuevas


primitivas ms complejas y muy habituales en la representacin de escenas
tridimensionales como, por ejemplo, la esfera, el cubo, el toro o el cono.
Se han definido nueve primitivas graficas en dos modos:

Modo almbrico. Cada primitiva se visualiza utilizando nicamente lneas.


Todas las rdenes de dibujo comienzan por glutWire.

Modo slido. La superficie de la primitiva se visualiza mediante un


conjunto de polgonos. Todas las rdenes de dibujo comienzan por
glutSolid.

Los comandos se muestran a continuacin:


Esfera, void glutWireSphere(GLdouble radio, GLint slices, GLint
stacks);
Cubo, void glutWireTorus (GLdouble radio_interior, GLdouble
radio_exterior, GLint nsides, GLint rings);
Icosaedro, void glutWirecosahedron (void);
Octaedro, void glutWireOctahedron (void);
Tetraedro, void glutWireTetrahedrom (void);
Dodecaedro, void glutWireDodecahedron (void);
Cono, void glutWireCone (GLdouble radio, GLdouble altura, GLint
slices, GLint stacks);
Tetera, void glutWireTeapot (GLdouble tamao);
Los parmetros que poseen algunas de las primitivas siempre especifican
valores sobre su dimensin, nunca de su posicin u orientacin. Las
primitivas se dibujaran centradas en el origen de coordenadas a no ser que
previamente se haya definido alguna operacin de transformacin.
Algunos parmetros cuyo significado puede no resultar obvio son:

Slices, nsides: representan las secciones longitudinales de la primitiva.


Stacks, rings: las secciones transversales de la primitiva.

Los comandos anteriores visualizan las primitivas en modo almbrico. Para


visualizarlas en modo slido hay que cambiar Wire por Solid.
A continuacin se muestra un ejemplo de uso de las primitivas de la librera
GLUT:
glutSolidTorus(1.0,5.0,10.0,30.0);
6

glutSolidSphere(5.0,30.0,30.0);
glutSolidTorus(5.0,8.0,10.0,10.0);

5. Transformacin de las primitivas


En OpenGL existen funciones disponibles para especificar transformaciones
en 3D: traslacin, giro y escalado. Estas funciones sern de utilidad tanto
para aplicar transformaciones a los distintos objetos de la escena, como para
definir las transformaciones asociadas al posicionamiento de dicha escena
en el volumen de la vista.
OpenGL utiliza distintas matrices durante el proceso de visualizacin. Una de
ellas es la matriz de transformacin del modelo. As, en primer lugar, se debe
especificar que se desea trabajar con dicha matriz:

void glMatrixMode (GL_MODELVIEW);

Para inicializarla a la matriz de identidad se usa la orden:

void glLoadIdentify (void);

Las transformaciones realizadas, irn multiplicando esta matriz de forma


acumulativa. Existen tres rutinas en OpenGL para realizar transformaciones:

Rutina de traslacin, void glTranslate{f d} (TIPO x, TIPO y, TIPO z);


multiplica la matriz actual por una matriz con desplazamientos en los ejes
indicados en los parmetros x,y,z.

Rutina de rotacin, void glRotate{f d} (TIPO angulo, TIPO x, TIPO y,


TIPO z); la matriz actual es multiplicada por otra que realiza un giro en el
sentido contrario a las agujas del reloj, del valor indicado por el parmetro
angulo. El ngulo se define en grados. El giro se realiza alrededor del eje
que pasa por los puntos (0,0,0) y (x,y,z).

Rutina de escalado, void glScale{f d} (TIPO x, TIPO y, TIPO z); multiplica


la matriz por otra que ampla o reduce el objeto con respecto al objeto
indicado. Valores mayores de 2.0 lo amplan, y entre 0 y 1 lo reducen.

Las transformaciones siempre se aplican en orden inverso al que han sido


especificadas en el programa. Por ejemplo, si se desea trasladar y rotar un
objeto, la codificacin es la siguiente:
glRotatef ();
glTranslatef ();
dibuja_objeto();
7

Como las transformaciones se almacenan como matrices, OpenGL utiliza una


pila de matrices para recordar situaciones anteriores a las transformaciones
aplicadas. La matriz actual es siempre la que est en la posicin ms elevada de
la pila. Existen dos rutinas para gestionar esta pila:

Void glPushMatrix (void); esta rutina almacena en la pila una copia de


la matriz actual.
void glPopMatrix (void); elimina de la pila la matriz que est en la
posicin ms elevada. La matriz actual ser la que se encontraba en
segundo lugar desde arriba antes de la operacin. En el caso de que antes
de ejecutar esta rutina solo existiera una matriz en la pila, se producira
un error.

A continuacin se muestra un pequeo ejemplo, donde se dibujar un cono y un


toro separado en 22.0 unidades a lo largo del eje x.
glMatrixMode (GL_MODEVIEW) ;
glLoadIdentify ();
glutSolidCone (9.0,20.0,16,16);
glTranslatef(22.0,0.0,0.0);
glutSolidTorus(3.0,7.0,16,16);
Se aade una rotacin, -90 grados alrededor del eje X, que produce el giro de
ambas primitivas.

6. El Pipeline Grfico
Para obtener una imagen de una escena 3D definida en el Sistema de Referencia
Universal, necesitamos definir un sistema de referencia de coordenadas para los
parmetros de visualizacin (tambin denominados parmetros de cmara).
Este sistema de referencia nos definir el plano de proyeccin, que sera el
equivalente de la zona de la cmara sobre la que se registrar la imagen. De
este modo se transfieren los objetos al sistema de coordenadas de visualizacin
y finalmente se proyectan sobre el plano de visualizacin Para obtener una
imagen de una escena 3D definida en el Sistema de Referencia Universal,
necesitamos definir un sistema de referencia de coordenadas para los
parmetros de visualizacin (tambin denominados parmetros de cmara).

Este sistema de referencia nos definir el plano de


proyeccin, que sera el equivalente de la zona de la
cmara sobre la que se registrar la imagen. De este
modo se transfieren los objetos al sistema de
coordenadas de visualizacin y finalmente se proyectan
sobre el plano de visualizacin.
El Pipeline est dividido en etapas funcionales. Algunas
de estas etapas se realizan en paralelo y otras
secuencialmente.
Como seala Akenine-Mler , el pipeline interactivo se
divide en tres etapas conceptuales de Aplicacin,
Geometra y Rasterizacin (ver Figura 6.1). A
continuacin estudiaremos estas etapas.

6.1 Etapa de Aplicacin


La etapa de aplicacin se ejecuta en la CPU.
Actualmente la mayora de las CPUs son multincleo,
por lo que el diseo de esta aplicacin se realiza
mediante diferentes hilos de ejecucin en paralelo.
Habitualmente en esta etapa se ejecutan tareas
asociadas al clculo de la posicin de los modelos 3D
mediante simulaciones fsicas, deteccin de colisiones,
gestin de la entrada del usuario (teclado, ratn,
joystick...).
De igual modo, el uso de estructuras de datos de alto
nivel para la aceleracin del despliegue (reduciendo el
nmero de polgonos que se envan a la GPU) se
implementa en la etapa de aplicacin.

Figura 6.1 Etapas del pipeline

6.2 Etapa de Geometra


A los vrtices de cada modelo se le aplican la denominada Transformacin de
Modelado para posicionarlo y orientarlo respecto del Sistema de Coordenadas
Universal, obteniendo as las denominadas Coordenadas Universales o
Coordenadas del Mundo.
La posicin y orientacin de la cmara nos determinar qu objetos aparecern
en la imagen final. Esta cmara tendr igualmente unas coordenadas
universales. El propsito de la Transformacin de Visualizacin es posicionar la
cmara en el origen del SRU, apuntando en la direccin negativa del eje Z y el
eje Y hacia arriba. Obtenemos de este modo las Coordenadas de Visualizacin
o Coordenadas en Espacio Cmara (ver Figura 6.2).

Figura 6.2 Espacio de trabajo

Habitualmente el pipeline contiene una etapa adicional intermedia que se


denomina Vertex Shader Sombreado de Vrtice que consiste en obtener la
representacin del material del objeto modelando las transformaciones en las
fuentes de luz, utilizando los vectores normales a los puntos de la superficie,
informacin de color, etc. Es conveniente en muchas ocasiones transformar las
posiciones de estos elementos (fuentes de luz, cmara) a otro espacio (como
Coordenadas de Modelo) para realizar los clculos.
La Transformacin de Proyeccin convierte el volumen de visualizacin en un
cubo unitario. Este volumen de visualizacin se define mediante planos de
recorte 3D y define todos los elementos que sern visualizados.

6.3 Etapa Rasterizacin


A partir de los vrtices proyectados (en Coordenadas de Pantalla) y la
informacin asociada a su sombreado obtenidas de la etapa anterior, la etapa de
rasterizacin se encarga de calcular los colores finales que se asignarn a los
pxeles de los objetos.
Esta etapa de rasterizacin se divide normalmente en las siguientes etapas
funciones para lograr mayor paralelismo.
En la primera etapa del pipeline llamada Configuracin de Tringulos (Triangle
Setup), se calculan las coordenadas 2D que definen el contorno de cada
tringulo (el primer y ltimo punto de cada vrtice). Esta informacin es utilizada
en la siguiente etapa (y en la interpolacin), y normalmente se implementa
directamente en hardware dedicado.
A continuacin, en la etapa del Recorrido de Tringulo (Triangle Traversal) se
generan fragmentos para la parte de cada pxel que pertenece al tringulo. El
recorrido del tringulo se basa por tanto en encontrar los pxeles que forman
parte del tringulo, y se denomina Triangle Traversal (o Scan Conversion). El
10

fragmento se calcula interpolando la informacin de los tres vrtices definidos en


la etapa de Configuracin de Tringulos y contiene informacin calculada sobre
la profundidad desde la cmara y el sombreado (obtenida en la etapa de
geometra a nivel de todo el tringulo).
Finalmente en la etapa de Fusin (Merging) se almacena la informacin del color
de cada pxel en un array de colores denominado Color Buffer. Para ello, se
combina el resultado de los fragmentos que son visibles de la etapa de
Sombreado de Pxel.
La visibilidad se suele resolver en la mayora de los casos mediante un buffer de
profundidad Z-Buffer, empleando la informacin que almacenan los fragmentos.

7. MATERIALES E ILUMINACION
7.1 Especificacin de normales
Las normales en OpenGL son utilizadas para la iluminacin, indicando la
orientacin relativa de un objeto respecto a las fuentes de iluminacin.
Las rutinas que se especifican la normal en OpenGL son:

void glNormal3 {bsidf} (TIPO nx, TIPO ny, TIPO nz);


void glNormal3{bsidf}v (const (TIPO *v);

Una vez establecida la normal, todos los vrtices que se dibujen a continuacin
asumirn la misma, a no ser que se especifiqu de forma explcita una normal
para cada uno de los vrtices a dibujar.
Los vectores normales permanecen normalizados siempre que las
transformaciones realizadas sean rotaciones y traslaciones. Si bien se realizan
otras transformaciones o bien se especifican vectores normales no unitarios, se
deben normalizar de forma automtica las normales de los objetos con la rutina:

glEnable (GL_NORMALIZE);

Las primitivas bsicas creadas directamente con las rutinas de la GLUT


(esfera, cono, toro, etc) tienen definidas ya las normales.

11

Ejemplo
En la siguiente figura se muestra el cdigo que define la misma normal para
todos los vrtices de un polgono. En consecuencia, el polgono se pintar de
color uniforme tal y como se muestra en la imagen.
En la siguiente figura se muestra el cdigo que define una normal diferente para
cada vrtice. Esto permite que el color vare a lo largo de la superficie del
polgono pudiendo obtener un resultado como el que se muestra en la imagen.

8. Color de fondo
Antes de modificar el color de fondo de la ventana, debemos tener en mente
como almacena la informacin de pantalla los dispositivos grficos, normalmente
la intensidad de un pixel corresponde a la suma de diferentes buffers, cada uno
de ellos almacena informacin referente al pixel en cuestin, OpenGL utiliza
estos buffers para determinar el color y alguna informacin adicional de cada
pixel.

12

Los buffers que utiliza son:

Para modificar el color de fondo de la ventana podemos utilizar los siguientes


comandos OpenGL:
glClearColor (Rojo, Verde, Azul, Alpha);
glClearDepth( 1.0 );
Esta funcin define el color de fondo que se aplicar, utiliza los valores de RGBA,
por lo tanto cada argumento corresponde a la intensidad del color, Rojo, Verde
y Azul, el rango de estos valores debe ir comprendido entre el valor 0.0, menor
intensidad, 1.0 mxima intensidad.
El cuarto parmetro que utiliza la funcin glClearColor, corresponde al valor
alpha del color que se utiliza para determinar informacin referente a la
transparencia del color, de igual forma que los tres argumentos anteriores, est
tambin toma valores comprendidos entre 0.0 opaco, y 1.0 totalmente
transparente.
La funcin glClearDepth, se utiliza para especificar informacin sobre la
profundidad.
Una vez que s a definido el color de fondo debemos utilizar la funcin glClear
para borrar la pantalla con el color que habamos definido, el argumento de esta
funcin podr ser simple o compuesto, ya que podremos combinar la informacin
de varios buffers tal como:
glClearColor() //borra el buffer de color.
glClearDepth() //Buffer de profundidad
glClearAccum() //Buffer acumulativo
glClearStencil() //Buffer de plantilla

13

En este caso el color de fondo depende de la suma de los valores de los buffers
de color y de profundidad.
Generalmente, el glColor3f(rojo, verde, azul) especifica el primer plano del color
del dibujo, que es el color aplicado a los objetos que se est elaborando.
El valor de cada componente de color, que debe ser un nmero entre 0.0 y 1.0,
determina su intensidad. Por ejemplo, glColor3f (1.0, 1.0, 0.0) es el amarillo
brillante mientras el glColor3f(0.5,0.5,0.0) es un dbil amarillo.
Nota: Los valores de color se sujetan cada uno a la gama [0:1]. Esta significa
que, si un valor pasa a ser mayor que 1, entonces se toma a ser 1; si es inferior
a 0, se toma como 0.

9. Cdigo bsico
Lo que podra considerarse la aplicacin ms sencilla utilizando OpenGL se
presenta en esta seccin. El siguiente cdigo abre una ventana y dibuja un
tringulo de color blanco.

14

9.1 Anlisis del cdigo


La estructura de un clsico programa sobre GLUT es la que se aprecia en el
cdigo anterior.
Analizando la funcin main():
glutInit(&argc, argv);
Esta funcin es la que inicializa a OpenGL, y negocia con el sistema de ventanas
para abrir una. Los parmetros deben ser los mismos argc y argv sin modificar
el main().
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
Define el modo en el que debe dibujar en la ventana. GLUT_SINGLE indica que
se debe usar un solo buffer y GLUT_RGB el tipo de modelo de color con el que
se dibujar.
glutInitWindowPosition(50, 50);
Posicin x e y de la esquina superior izquierda de la nueva ventana, con respecto
al escritorio en el que se trabaje.
glutInitWindowSize(500, 500);
El ancho y alto de la nueva ventana.
glutCreateWindow("Hello OpenGL");
15

Esta funcin es la que propiamente crea la ventana, y el parmetro es el nombre


de la misma.
init();
Es una funcin que hemos definido nosotros, activamos y definimos una serie de
estados de OpenGL.
glutDisplayFunc(display);
Aqu se define la llamada a la funcin display. La funcin pasada como parmetro
ser llamada cada vez que GLUT determine oportuno que la ventana debe ser
redibujada, como al maximizarse, poner otras ventanas por encima o sacarlas,
etc.
glutReshapeFunc(reshape);
En este caso para saber que hace cuando la ventana es explcitamente
reescalada. Esta accin afecta en principio directamente al render, puesto que
se est cambiando el tamao del plano de proyeccin. Por eso en esta funcin
(en este caso reshape) se suele corregir esto de alguna forma.
glutMainLoop();
Esta funcin cede el control del flujo del programa a GLUT, que a partir de estos
"eventos", ira llamando a las funciones que han sido pasadas como parametros.

Analizando init():
glClearColor(0,0,0,0);
Con esto se define el color con el que se borrara el buffer al hacer un glClear().
Los 3 primeros parmetros son las componentes R, G y B, siguiendo un rango
de [0..1]. La ltima es el valor alpha.
reshape().
Esta funcin, al ser pasada a glutReshapeFunc, ser llamada cada vez que se
reescale a ventana. La funcin siempre debe ser definida con el siguiente
esqueleto:
void reshape(int, int) { ... }
El primer parmetro ser el ancho y el segundo el alto, despus del reescalado.
Con estos dos valores trabajara la funcin cuando, en tiempo de ejecucin, el
usuario reescale la ventana.
glViewport(0, 0, width, height);
Esta funcin define la porcin de ventana donde puede dibujar OpenGL. Los
parmetros son x e y, esquina superior izquierda del "cuadro" donde puede
dibujar (con referencia la ventana), y ancho y alto. En este caso toma el width y
16

height, que son los parmetros de reshape(), es decir, los datos que acaba de
recibir para el reescalado de la ventana.
glMatrixMode(GL_PROJECTION);
Especifica la matriz actual. En OpenGL las operaciones de rotacin, translacin,
escalado, etc. se realizan a travs de matrices de transformacin. Dependiendo
de lo que estemos tratando, hay tres tipos de matriz (que son los tres posibles
flags que puede llevar de parmetro la funcin): matriz de proyeccin
(GL_PROJECTION), matriz de modelo (GL_MODELVIEW) y matriz de textura
(GL_TEXTURE). Con esta funcin indicamos a cual de estas tres se deben
afectar las operaciones. Concretamente, GL_PROJECTION afecta a las vistas o
perspectivas o proyecciones.
glLoadIdentity();
Con esto cargamos en el "tipo" de matriz actual la matriz identidad (es como
resetear la matriz).

glOrtho(-1, 1, -1, 1, -1, 1);


glOrtho() define una perspectiva ortonormal. Esto quiere decir que lo que
veremos ser una proyeccin en uno de los planos definidos por los ejes. Es
como plasmar los objetos en un plano, y luego observar el plano. Los parmetros
son para delimitar la zona de trabajo, y son x_minima, x_maxima, y_minima,
y_maxima, z_minima, z_maxima. Con estos seis puntos, definimos una caja que
ser lo que se proyecte.
glMatrixMode(GL_MODELVIEW);
Se vuelve a este tipo de matrices, que afecta a las primitivas geomtricas.

Funcin display().
Como se ha dicho, al ser pasada a glutDisplayFunc(), ser llamada cada vez que
haya que redibujar la ventana. La funcin debe ser definida con el siguiente
esqueleto:
void display(void) { ... }
Analicemos el contenido de la funcin.
glClear(GL_COLOR_BUFFER_BIT);
Borra un buffer, en este caso, el buffer de los colores lo borra (en realidad, cada
componente R G y B tienen un buffer distinto, pero aqu los trata como el mismo).
Para borrarlos utiliza el color que ha sido previamente definido en init() mediante
glClearColor(), en este caso, el (0,0,0,0) es decir, negro.
17

glColor3f(1,1,1);
Selecciona el color actual con el que dibujar. Parmetros R G y B, rango [0..1],
as que estamos ante el color blanco.
glLoadIdentity();
Carga la matriz identidad.
glBegin(GL_TRIANGLES);
glVertex3f(-1,-1,0);
glVertex3f(1,-1,0);
glVertex3f(0,1,0);
glEnd();
Analizamos toda esta parte entera, por formar una estructura. glBegin()
comienza una secuencia de vrtices con los que se construirn primitivas. El tipo
de primitivas viene dado por el parmetro de glBegin(), en este caso
GL_TRIANGLES.
Al haber tres vrtices dentro de la estructura, est definiendo un tringulo.
glEnd() simplemente cierra la estructura. Los posibles parmetros de glBegin.
glFlush();
El comando glFlush causa la ejecucin de cualquier comando en espera.

10. Sistema de Deteccin de Colisiones


La responsabilidad principal del Sistema de Deteccin de Colisiones (SDC) es
calcular cundo colisionan los objetos de la escena. Para calcular esta colisin,
los objetos se representan internamente por una forma geomtrica sencilla
(como esferas, cajas, cilindros...).
Adems de comprobar si hubo colisin entre los objetos, el SDC se encarga de
proporcionar informacin relevante al resto de mdulos del simulador fsico sobre
las propiedades de la colisin. Esta informacin se utiliza para evitar efectos
indeseables, como la penetracin de un objeto en otro, y conseguir la estabilidad
en la simulacin cuando el objeto llega a la posicin de equilibrio.

18

You might also like