Skip to content

Clase 2 — Pipeline 2D, Sistemas de Coordenadas y Primeros Pasos en OpenGL

Resumen Ejecutivo

Continuación del Tema 1 con las matrices de transformación inversa y la composición de transformaciones (orden inverso de multiplicación). Se introdujo el Tema 2: sistemas de coordenadas y recorte 2D, explicando el pipeline 2D completo (modelo → mundo → vista → dispositivo). Se presentaron las funciones OpenGL para definir la ventana de recorte (gluOrtho2D), crear ventanas (glutInitWindowSize) y el mecanismo de la matriz ModelView que afecta a todos los vértices dibujados.

Conceptos Clave

  • Matrices de transformación inversa: Se obtienen invirtiendo los parámetros (negar traslación, negar ángulo, invertir factor de escala), no calculando la inversa matricial ⚠️ EXAMEN
  • Composición de transformaciones: El orden de multiplicación de matrices es inverso al orden deseado de transformación ⚠️ EXAMEN
  • Pipeline 2D: Sucesión de cambios de sistema de coordenadas: modelo → mundo → vista → normalizado → dispositivo ⚠️ EXAMEN
  • Ventana de recorte: Rectángulo que delimita qué zona de la escena se visualiza en el viewport ⚠️ EXAMEN
  • Viewport: Área del dispositivo de salida donde se muestra la escena recortada; por defecto ocupa toda la ventana
  • Matriz ModelView: Matriz 4×4 de OpenGL que se multiplica por cada glVertex, aplicando transformaciones a todos los vértices ⚠️ EXAMEN
  • gluOrtho2D: Función que define la ventana de recorte con los límites en X e Y ⚠️ EXAMEN
  • Single buffer vs. Double buffer: Single buffer vuelca directamente al viewport; double buffer permite preparar la imagen en un buffer oculto y hacer swap ⚠️ EXAMEN
  • glFlush: Fuerza la ejecución de los comandos de dibujo encolados; NO vuelca el buffer al viewport ⚠️ EXAMEN

Desarrollo del Temario

1. Matrices de transformación inversa

En lugar de calcular la inversa matricial (operación costosa), se construye la transformación inversa razonando sobre los parámetros:

  • Traslación inversa: Negar los factores de traslación. Si \(T(t_x, t_y)\) es la traslación directa, entonces \(T^{-1} = T(-t_x, -t_y)\).
  • Rotación inversa: Negar el ángulo. Si \(R(\theta)\) es la rotación directa, entonces \(R^{-1} = R(-\theta)\). Al sustituir \(\theta\) por \(-\theta\) en la matriz, los senos cambian de signo por trigonometría básica.
  • Escalado inverso: Invertir los factores de escala. Si \(S(s_x, s_y)\) es el escalado directo, entonces \(S^{-1} = S(1/s_x, 1/s_y)\).

Ejemplo: No necesitamos memorizar estas matrices. Lo importante es saber que construir la transformación inversa "pensando" es mucho más eficiente que invertir la matriz computacionalmente.

2. Composición de transformaciones — Orden inverso

Cuando se encadenan varias transformaciones, el orden de multiplicación de matrices es inverso al orden en que queremos que se apliquen.

Si queremos primero trasladar y luego rotar:

\[P' = T \cdot P \quad \text{(traslación)}$$ $$P'' = R \cdot P' = R \cdot T \cdot P\]

En la expresión final \(P'' = R \cdot T \cdot P\), la matriz de rotación \(R\) aparece primero (a la izquierda), aunque la traslación se aplica primero al punto.

Ejemplo: Si en OpenGL queremos primero trasladar y luego rotar un objeto, en el código debemos escribir primero la rotación y después la traslación. El código se escribe en orden inverso al deseado. Esto causa confusión al principio pero es fundamental dominarlo.

En OpenGL esto se traduce directamente: las instrucciones de transformación se escriben en orden inverso al que queremos que se apliquen sobre el objeto. ⚠️ EXAMEN

3. Efecto colateral del escalado fuera del origen

Si un objeto no pasa por el origen de coordenadas, al aplicar un escalado se produce una traslación colateral no deseada.

Ejemplo: Un cuadrado con vértices como \((1,1), (2,1), (2,2), (1,2)\) al escalarlo por 2 produce \((2,2), (4,2), (4,4), (2,4)\): no solo se duplica su tamaño, sino que se desplaza. Esto ocurre porque \(x' = 2x\) e \(y' = 2y\), y ningún punto del objeto está en el origen \((0,0)\).

Solución en 3 pasos (composición de transformaciones): ⚠️ EXAMEN 1. Trasladar el objeto al origen de coordenadas 2. Aplicar el escalado 3. Deshacer la traslación inicial (traslación inversa)

Con esta técnica el objeto queda escalado en su posición original, sin desplazamiento colateral. Cualquier punto del objeto puede usarse como referencia para llevarlo al origen.

4. Pipeline 2D — Sistemas de coordenadas

El pipeline 2D describe la transformación que sufren los vértices desde su definición hasta su visualización en pantalla:

Etapa Sistema de coordenadas Descripción
1 Modelo Coordenadas locales del objeto (vértices definidos por el diseñador)
2 Mundo Coordenadas de la escena donde se posicionan todos los objetos
3 Vista En 2D coincide con el del mundo (no hay puntos de vista alternativos). En 3D será diferente
4 Normalizadas Sistema intermedio normalizado (rectángulo de lado 1) donde se aplican algoritmos de recorte
5 Dispositivo Coordenadas en píxeles del viewport, con origen en la esquina inferior izquierda

Conclusión fundamental: Desde el punto de vista geométrico, el pipeline es una sucesión de cambios de sistema de coordenadas. Desde el punto de vista computacional, todo se reduce a productos de matrices sobre los vértices. Así trabaja la GPU. ⚠️ EXAMEN

Ejemplo: Un vértice del tejado de la casa con coordenadas \((3.5, 5)\) en el sistema del modelo se transforma a lo largo del pipeline hasta convertirse en un píxel concreto (p.ej. \((1200, 700)\)) en la pantalla.

5. Sistema de coordenadas de la vista (anticipación a 3D)

En 3D, una escena puede observarse desde infinitos puntos y con infinitas inclinaciones (como posicionar una cámara). Eso define un nuevo sistema de coordenadas de la vista con valores diferentes para cada vértice.

En 2D solo podemos ver la imagen "de frente", por lo que el sistema de la vista coincide con el del mundo. ⚠️ EXAMEN

6. Ventana de recorte y viewport en OpenGL

Ventana de recorte — Se define con gluOrtho2D(left, right, bottom, top): - left, right: límites en el eje X - bottom, top: límites en el eje Y - Delimita qué porción de la escena se verá en el viewport

Viewport — Por defecto ocupa toda la ventana. Se pueden definir múltiples viewports (se verá en clases posteriores).

Ventana de pantalla — Se configura con funciones de GLUT: - glutInitWindowSize(ancho, alto): tamaño en píxeles - glutInitWindowPosition(x, y): posición de la esquina superior izquierda

Ejemplo: Si la ventana de recorte es gluOrtho2D(0, 7, 0, 7), solo se verán los objetos dentro del rectángulo \([0,7] \times [0,7]\) del mundo. Si cambiamos a gluOrtho2D(0, 20, 0, 20) veremos una zona más amplia.

Proporcionalidad ventana de recorte / viewport: Para que los objetos no se deformen, la relación alto/ancho de la ventana de recorte debe coincidir con la del viewport. Si no se respeta, los objetos aparecen estirados o comprimidos. ⚠️ EXAMEN

7. Funcionamiento de la matriz ModelView en OpenGL

La matriz ModelView es una matriz 4×4 (preparada para 3D en coordenadas homogéneas) que OpenGL multiplica por cada instrucción glVertex.

Flujo de trabajo: 1. glMatrixMode(GL_MODELVIEW) — selecciona la matriz ModelView 2. glLoadIdentity() — carga la identidad (partir de cero) 3. glTranslatef(3, 3, 0) — carga una traslación en la matriz 4. house() — cada glVertex dentro de esta función se multiplica por la ModelView

Ejemplo del código de la clase:

glClear(GL_COLOR_BUFFER_BIT)       # Limpiar buffer
glMatrixMode(GL_MODELVIEW)          # Seleccionar matriz ModelView
glLoadIdentity()                    # Cargar identidad
drawAxes()                          # Dibujar ejes
glTranslatef(3.0, 3.0, 0.0)        # Cargar traslación en ModelView
house()                             # Cada vértice se traslada (3,3)
glFlush()                           # Forzar ejecución de comandos

8. Single buffer vs. Double buffer

  • Single buffer (GLUT_SINGLE): Un único buffer que se refleja en el viewport. Se usa glFlush() para forzar la ejecución de los comandos encolados.
  • Double buffer (GLUT_DOUBLE): Dos buffers; uno se muestra mientras el otro se prepara. Se usa glutSwapBuffers() para intercambiarlos. Útil para animaciones y escenas complejas.

En esta asignatura se usa single buffer porque se trabajan escenas estáticas y sencillas.

Importante: glFlush() NO vuelca el buffer al viewport. Lo que hace es forzar que los comandos glVertex encolados se procesen efectivamente en el buffer. La actualización buffer→ventana no la controla el programador. ⚠️ EXAMEN

9. Callback de display

OpenGL usa un modelo basado en callbacks. La función de display se registra con glutDisplayFunc(display) y el sistema la invoca automáticamente cuando: - Se abre la ventana por primera vez - Se minimiza y se vuelve a abrir - La ventana queda tapada por otra y vuelve a primer plano

glutDisplayFunc(display)   # Registrar callback
glutMainLoop()             # Bucle principal de eventos

10. Estructura general de una aplicación OpenGL

# 1. Imports (OpenGL, GLUT, GLU)
# 2. Función initGL(): inicialización de GLUT, modo display, ventana, color de fondo, ventana de recorte
# 3. Funciones de modelo: definen vértices, colores y polígonos de cada objeto
# 4. Callback display(): limpia buffer, configura ModelView, posiciona objetos, dibuja, glFlush
# 5. Main: llama a initGL(), registra callbacks, entra en glutMainLoop()

glClearColor(R, G, B, A) establece el color de fondo que se usa al limpiar el buffer con glClear().

Preguntas de Autoevaluación

  1. ¿Por qué no conviene calcular la inversa de una matriz de transformación y qué alternativa se usa para la traslación, rotación y escalado?
  2. Si quiero primero rotar un objeto 45° y luego trasladarlo (5, 0), ¿en qué orden debo escribir las instrucciones en OpenGL?
  3. ¿Qué efecto colateral se produce al escalar un objeto que no pasa por el origen? Describe la técnica de 3 pasos para evitarlo.
  4. Enumera los sistemas de coordenadas del pipeline 2D y explica por qué en 2D el sistema de la vista coincide con el del mundo.
  5. ¿Qué función de OpenGL define la ventana de recorte y qué parámetros recibe?
  6. ¿Qué ocurre si la proporción alto/ancho de la ventana de recorte no coincide con la del viewport?
  7. ¿Qué hace realmente glFlush()? ¿En qué se diferencia de glutSwapBuffers()?
  8. ¿Qué contiene la matriz ModelView y cómo afecta a las instrucciones glVertex?
  9. ¿En qué situaciones el sistema invoca automáticamente la callback de display?
  10. ¿Qué diferencia hay entre single buffer y double buffer? ¿Cuándo es necesario usar double buffer?

Guía generada automáticamente a partir de transcripción con faster-whisper + Claude Sonnet.