Clase 3 — Transformaciones 2D en OpenGL y la Matriz ModelView
Resumen Ejecutivo
En esta sesión se profundizó en el funcionamiento de la Matriz ModelView de OpenGL, demostrando de forma práctica cómo las funciones glTranslateF, glRotateF y glScaleF la modifican mediante multiplicación acumulativa. Se abordaron también las transformaciones no primitivas (reflexión, cizalla) cargando matrices directamente con glLoadMatrixF o glMultMatrixF. Finalmente, se explicó cómo dibujar sectores circulares y circunferencias completas usando coordenadas polares.
Conceptos Clave
- Matriz ModelView: Matriz interna de OpenGL que transforma cada vértice dibujado, determinando su posición final en la escena. ⚠️ EXAMEN
- glLoadIdentity(): Resetea la Matriz ModelView a la matriz identidad (unos en la diagonal, ceros en el resto).
- Acumulación de transformaciones: Cada llamada a
glTranslateF,glRotateFoglScaleFmultiplica la ModelView actual, no la reemplaza. ⚠️ EXAMEN - Orden inverso de operaciones: En OpenGL las transformaciones se escriben en orden inverso al orden lógico de aplicación. ⚠️ EXAMEN
- Orden de vértices: Los vértices de un polígono deben definirse en sentido antihorario (counter-clockwise). ⚠️ EXAMEN
- Coordenadas polares: Método para generar vértices de sectores circulares: \(x = r \cos\theta\), \(y = r \sin\theta\).
Desarrollo del Temario
1. Repaso: Pipeline de coordenadas y la Matriz ModelView
Cada vértice definido en el sistema de coordenadas del modelo (la función que dibuja el objeto) es multiplicado por la Matriz ModelView antes de dibujarse. Si la ModelView contiene la identidad, los vértices permanecen en sus posiciones originales.
Ejemplo: Si un vértice está en \((0, 0, 0)\) y la ModelView tiene cargada una traslación \((3, 3, 0)\), el vértice se dibuja en \((3, 3, 0)\).
La estructura básica de la callback de display es:
1. glClear() — borrar el buffer
2. glMatrixMode(GL_MODELVIEW) — seleccionar la matriz
3. glLoadIdentity() — resetear a identidad
4. Aplicar transformaciones y dibujar objetos
2. Orden de los vértices en polígonos
El orden en que se escriben los vértices en el código determina cómo se unen. ⚠️ EXAMEN
Un orden incorrecto produce figuras deformadas (cruces de segmentos). Dos reglas fundamentales:
- Los vértices deben estar en una secuencia que, al recorrerse, no cruce los segmentos del perímetro.
- Siempre en sentido antihorario (independientemente del vértice de inicio).
Ejemplo: Para un rectángulo (tronco de un árbol), si los vértices se ponen en orden V1, V3, V4, V2 en lugar de V1, V2, V3, V4, los segmentos se cruzan y se obtiene una figura tipo "reloj de arena" en vez de un rectángulo.
3. Función glTranslateF(tx, ty, tz) — Traslación
Multiplica la ModelView por una matriz de traslación con los valores indicados.
Las traslaciones sucesivas se acumulan: ⚠️ EXAMEN
Ejemplo: Si se ejecuta
glTranslateF(3, 3, 0)dos veces seguidas (sin resetear), la ModelView acumula una traslación de \((6, 6, 0)\). Para evitar la acumulación, se debe llamar aglLoadIdentity()entre transformaciones.
4. Función glRotateF(ángulo, vx, vy, vz) — Rotación
Multiplica la ModelView por una matriz de rotación.
- Primer parámetro: ángulo en grados. Positivo = sentido antihorario.
- Parámetros 2-4: vector de rotación. En 2D siempre se usa \((0, 0, 1)\) (eje Z). ⚠️ EXAMEN
La matriz resultante contiene \(\cos\theta\) y \(\sin\theta\) del ángulo indicado.
Ejemplo:
glRotateF(30, 0, 0, 1)rota 30° antihorario. En la ModelView aparecen \(\cos(30°) = 0.87\) y \(\sin(30°) = 0.5\). Si se aplica dos veces, se acumula una rotación de 60°.
5. Función glScaleF(sx, sy, sz) — Escalado
Multiplica la ModelView por una matriz de escalado.
Problema del desplazamiento por escalado: Si el objeto no pasa por el origen, el escalado produce un desplazamiento no deseado. ⚠️ EXAMEN
Solución (3 pasos, orden lógico): 1. Trasladar el objeto al origen 2. Escalar 3. Deshacer la traslación
En OpenGL se escribe en orden inverso: ⚠️ EXAMEN
glTranslateF(1, 0, 0) # Paso 3: deshacer traslación
glScaleF(2, 2, 1) # Paso 2: escalar
glTranslateF(-1, 0, 0) # Paso 1: llevar al origen
house()
Ejemplo: La casa tiene su esquina inferior izquierda en \((1, 0)\). Al escalar ×2 sin compensar, esa esquina se mueve a \((2, 0)\). Con la secuencia trasladar-escalar-destrasladar, la esquina se mantiene en \((1, 0)\) y la casa duplica su tamaño.
6. Transformaciones no primitivas: Reflexión y Cizalla
OpenGL no proporciona funciones primitivas para reflexión ni cizalla. Se deben cargar las matrices manualmente. ⚠️ EXAMEN
Dos funciones disponibles:
- glLoadMatrixF(m) — Carga directamente la matriz en la ModelView (reemplaza).
- glMultMatrixF(m) — Multiplica la ModelView por la matriz dada.
Importante: La matriz se pasa como array lineal, linearizada por columnas (column-major order). ⚠️ EXAMEN
Matrices de reflexión (en coordenadas homogéneas):
| Reflexión respecto a | Matriz | Equivalente en escalado |
|---|---|---|
| Eje X | \(s_x=1, s_y=-1\) | glScaleF(1, -1, 1) |
| Eje Y | \(s_x=-1, s_y=1\) | glScaleF(-1, 1, 1) |
| Origen | \(s_x=-1, s_y=-1\) | glScaleF(-1, -1, 1) |
Ejemplo (reflexión respecto al eje X): La matriz linearizada por columnas es:
[1,0,0,0, 0,-1,0,0, 0,0,1,0, 0,0,0,1]. Se carga conglLoadMatrixF(m)y luego se dibuja el objeto.
Cizalla: Se realiza de forma análoga, cargando la matriz correspondiente con factor \(c_x\) (horizontal) o \(c_y\) (vertical).
7. Dibujo de sectores circulares y circunferencias
Se utiliza la conversión de coordenadas polares a cartesianas: ⚠️ EXAMEN
- Sector circular: incluir el punto \((0,0)\) como primer vértice; \(\theta\) varía entre el ángulo inicial y final.
- Circunferencia completa: no incluir \((0,0)\); \(\theta\) varía de \(0°\) a \(360°\).
- Las funciones
sin()ycos()requieren el ángulo en radianes. - A mayor número de puntos (menor incremento de \(\theta\)), más suave la curva. Con incremento de 1° se obtiene buena apariencia.
Ejemplo (sol — circunferencia de radio 1):
Para una semicircunferencia, se usaglBegin(GL_POLYGON) for theta in range(0, 360): rad = theta * math.pi / 180 x = math.cos(rad) # Para radio r: r * math.cos(rad) y = math.sin(rad) # Para radio r: r * math.sin(rad) glVertex2f(x, y) glEnd()range(0, 180).
8. Buenas prácticas de organización del código
- Una función por objeto (modelo): contiene la definición de vértices, colores y la generación de polígonos.
- En la callback de display: se prepara la ModelView para cada objeto (posición, rotación, escala) y luego se llama a la función del modelo.
- Este esquema reproduce el pipeline de cambio de sistemas de coordenadas: modelo → mundo → pantalla.
Preguntas de Autoevaluación
-
¿Qué ocurre si se llama a
glTranslateF(5, 0, 0)dos veces seguidas sin resetear la ModelView? ¿Qué traslación total se aplica? -
¿Por qué las transformaciones compuestas (ej: trasladar → escalar → destrasladar) se escriben en orden inverso en el código OpenGL?
-
Si se quiere escalar un objeto al doble de su tamaño sin que se desplace, y el punto de anclaje está en \((3, 2)\), ¿qué secuencia de llamadas OpenGL se debe escribir?
-
¿Cómo se realiza una reflexión respecto al eje Y si OpenGL no tiene función primitiva para ello? Describe dos formas alternativas.
-
¿Por qué los vértices de un polígono deben definirse en sentido antihorario? ¿Qué ocurre si se ponen en un orden que cruza segmentos?
-
Escribe el pseudocódigo para dibujar un sector circular de radio 5, con ángulo inicial 45° y ángulo final 135°.
-
¿Cuál es la diferencia entre
glLoadMatrixF(m)yglMultMatrixF(m)? -
¿Qué función se utiliza para seleccionar la Matriz ModelView y por qué es necesario llamar a
glLoadIdentity()al inicio de la callback de display?
Guía generada automáticamente a partir de transcripción con faster-whisper + Claude Sonnet.