Laboratorio 2: Viewports e
Interacción con el Mouse
(en 2D)
Seleccionar un área de la figura para
hacer zoom.
Introducción
Los vértices sufren transformaciones para pasar del mundo a la pantalla.
Estas transformaciones, las especificamos mediante matrices.
Ventana del mundo 2D
● Luego de modelar los objetos y la escena 2D (mediante transformaciones, con más matrices!!)
● Debemos seleccionar qué área de este
mundo 2D queremos mostrar.
● Esto lo hacemos especificando una matriz de proyección.
En OpenGL
● Para especificar esa matriz de proyección, se utiliza(ba):
gluOrtho2D(left, right, bottom, top);
● Ahora, nosotros debemos ocuparnos de crear la matriz. :(
● Pero OpenTK provee clases y estructuras para trabajar con matrices y vectores.
○ Estas clases poseen operadores sobrecargados
para que podamos utilizarlos más fácilmente. (+, -, *, /)
En OpenTK
Matrix4 projMat;
projMat = Matrix4.CreateOrthographicOffCenter(
left, right, bottom, top, zNear, zFar);
Con esto creamos una matriz. Resta hacer que nuestro shader (de vértices) las utilice. (Lo
vemos luego). ● Usar:
○ zNear = -1.0f; ○ zFar = 1.0f;
Viewport
● La última de las transformaciones es la de
Viewport.
● La transformación de viewport especifica en qué región de la pantalla será dibujada la escena. (en nuestro caso, en qué región del GLControl)
● Al trabajar sobre la pantalla, las
coordenadas son números enteros (pixels). (A diferencia de las coordenadas de la
En OpenGL
En OpenGL especificamos la información del viewport mediante:
glViewport(x, y, width, height);
OpenTK sobrecarga este método:
GL.Viewport(x, y, width, height); GL.Viewport(rectangle);
GL.Viewport(point, size); GL.Viewport(size);
Viewport
Coordenadas de la ventana (mi glControl), según OpenGL x y (x,y) h w
Relación de aspecto
● Relación de aspecto = ancho / alto
○ Si la región del mundo que queremos dibujar tiene una relación de aspecto distinta a la del viewport, veremos nuestra escena deformada.
○ Debemos preocuparnos por definir el viewport con la misma relación de aspecto que la ventana 2D.
¿Cómo lo logramos?
Tenemos que dibujar la misma figura 2 veces: Primero:
● Una ventana 2D que abarque a toda la figura.
● Un viewport a la izquierda. Segundo:
● Una ventana que abarque sólo una parte de la figura.
Pseudocódigo
void On_Paint(){ ... SetearViewport(viewport1); SetearVentana(projMatrix1); figura.Dibujar(); SetearViewport(viewport2); SetearVentana(projMatrix2); figura.Dibujar(); ... }Pseudocódigo
● Podemos utilizar dos variables de tipo
Rectangle (System.Drawing) para guardar información de los viewports.
● Y dos variables de tipo OpenTK.Matrix4 para las matrices de proyección.
Rectangle viewport1 = new ...; Rectangle viewport2 = new ...;
Matrix4 projMat1 = Matrix4.CreateOrthog..; Matrix4 projMat2 = Matrix4.CreateOrthog...;
Pseudocódigo
¿Dónde actualizamos los valores de los viewports?
void glControl1_Resize(..){
//Sacar las cuentas en función de glControl1.Width //glControl1.Height y el aspectRatio de la ventana viewport1.X = ...; // 2D viewport1.Y = ...; viewport1.Width = ...; viewport1.Height = ...; viewport2.X = ...; ... glControl1.Invalidate(); };
Shaders
Las matrices que utilicemos para aplicarle transformaciones a nuestros datos, se las tenemos que enviar a nuestro shader (de vértices).
En este caso, le vamos a asignar dos matrices:
○ La matriz de ModelView; ○ La matriz de Proyeccion;
La transformación del Viewport la realiza OpenGL (esa es fácil).
Shaders (vértices)
in vec2 vPosition; //Una posicion en 2D
uniform mat4 mViewMatrix;
uniform mat4 projMatrix; void main(){
vec4 vPos = vec4(vPosition, 0.0, 1.0);
gl_Position = projMatrix * mViewMatrix * vPos; }
La posición ahora se ve afectada por las transformaciones de ModelView y de Proyección.
● Aparecen variables uniformes.
Shaders
Cuando se linkea el programa de shader, se crean (entre otras cosas) dos tablas con
índices (location) para las variables de entrada.
● Indices para las variables in (llamadas atributos de vértice)
● Indices para las variables uniform.
(vertex Attributes) (Uniform variables) Location Name Location Name
0 vPosition 0 mViewMatrix 1 projMatrix
Shaders
Sólo podemos asignar valor a las variables de un shader a través de su location.
Podemos obtener estos índices con:
int GL.GetAttribLocation(program, attrName);
int GL.GetUniformLocation(program, uniformName);
En nuestro caso:
int mvmLoc; int pLoc;
mvmLoc = GL.GetUniformLocation(prog, "vViewMatrix"); pLoc = GL.GetUniformLocation(prog, "projMatrix");
Shaders
Para asignarle valores a las variables uniform del shader existen varias funciones de la
forma:
GL.Uniform*(location, value);
En nuestro caso usamos:
GL.UniformMatrix4(mvmLoc, Matrix4.Identity); GL.UniformMatrix4(pLoc, projMatrix1);
● Para la matriz ModelView usamos la identidad. (Todavía no utilizamos
Pseudocódigo
void On_Paint(){ ... GL.Viewport(viewport1); GL.UniformMatrix4(mvmLoc, Matrix4.Identity); GL.UniformMatrix4(pLoc, projMatrix1); figura.Dibujar(); GL.Viewport(viewport2); GL.UniformMatrix4(pLoc, projMatrix2); figura.Dibujar(); ... }Interacción con el Mouse
● Ya tenemos medio laboratorio.. ;)
● Falta hacer que el área de zoom se pueda elegir interactivamente con el mouse.
● El componente GLControl (al igual que
todos los componentes de Windows) tiene el evento Click(...) que se dispara cuando se
realiza un click de mouse sobre el componente.
● Peeero, las coordenadas que reporta, ¿en que espacio están?
OpenGL vs Windows
x
y x
y
Unproject
● Los vértices se fueron transformando, hasta llegar a coordenadas de pantalla.
● Ahora quiero el camino inverso, es decir, a partir de una posición en pantalla,
determinar a qué posición en el mundo corresponde.
Unproject
● De eso se encarga(ba) la función
gluUnproject(...);
● Esta función no está disponible en OpenTK. La tenemos que implementar!
● Consultar la documentación de esta
función, para comprender bien el significado de los parámetros.
Unproject
Vector3 UnProject(Vector3 win,
Matrix4 mViewMat, Matrix4 projMat,
Rectangle viewport) {
Vector3 resul = Vector3.Zero; Vector4 _in = Vector4.Zero; Vector4 _out = Vector4.Zero; Matrix4 oneMatrix;
//Combinamos las dos matrices y las invertimos. oneMatrix = Matrix4.Multiply(mViewMat, projMat); oneMatrix.Invert();
Unproject (cont.)
_in.X = win.X; _in.Y = win.Y; _in.Z = win.Z; _in.W = 1.0f;
//Map x and y from window coordinates.
_in.X = (_in.X - viewport.X) / viewport.Width;
_in.Y = (_in.Y - viewport.Y) / viewport.Height; //Map to range -1 to 1.
_in.X = _in.X * 2 - 1; _in.Y = _in.Y * 2 - 1; _in.Z = _in.Z * 2 - 1;
Unproject (cont.)
//Antitransformamos.
_out = Vector4.Transform(_in, finalMatrix); if ((_out.W > float.Epsilon) ||
(_out.W < -float.Epsilon)) {
_out.X = _out.X / _out.W; _out.Y = _out.Y / _out.W; _out.Z = _out.Z / _out.W; }else{
throw new Exception("UnProject: No pudo antitransformar.");
Unproject (cont.)
resul.X = _out.X; resul.Y = _out.Y; resul.Z = _out.Z; return resul; };Este método lo vamos a utilizar en el manejador del evento Click();
Metodo On_Click
void glControl1_Click(EventArgs e){
MouseEventArgs mea = (MouseEventArgs)e; int x = mea.X;
int y = glControl1.Height - mea.Y;
Vector3 winPos = new Vector3(x, y, 0.0f); Vector3 worldPos;
worldPos = UnProject(winPos, mViewMat, projMat1, viewport1);
//Modificar projMat2 usando worldPos (...) glControl1.Invalidate();
Pseudocódigo - Método Paint()
void glControl1_Paint(){ GL.Clear(...);
Activar el programa de shader;
Asignarle valor a la matriz ModelView del shader Asignarle valor a la matriz Projection del shader SetearViewport(viewport1);
figura.Dibujar();
Asignarle valor a la matriz Projection del shader SetearViewport(viewport2);
figura.Dibujar();