Skip to content

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, glRotateF o glScaleF multiplica 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:

  1. Los vértices deben estar en una secuencia que, al recorrerse, no cruce los segmentos del perímetro.
  2. 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.

\[T = \begin{pmatrix} 1 & 0 & 0 & t_x \\ 0 & 1 & 0 & t_y \\ 0 & 0 & 1 & t_z \\ 0 & 0 & 0 & 1 \end{pmatrix}\]

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 a glLoadIdentity() 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 con glLoadMatrixF(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

\[x = r \cdot \cos\theta \qquad y = r \cdot \sin\theta\]
  • 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() y cos() 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):

glBegin(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()
Para una semicircunferencia, se usa 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

  1. ¿Qué ocurre si se llama a glTranslateF(5, 0, 0) dos veces seguidas sin resetear la ModelView? ¿Qué traslación total se aplica?

  2. ¿Por qué las transformaciones compuestas (ej: trasladar → escalar → destrasladar) se escriben en orden inverso en el código OpenGL?

  3. 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?

  4. ¿Cómo se realiza una reflexión respecto al eje Y si OpenGL no tiene función primitiva para ello? Describe dos formas alternativas.

  5. ¿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?

  6. Escribe el pseudocódigo para dibujar un sector circular de radio 5, con ángulo inicial 45° y ángulo final 135°.

  7. ¿Cuál es la diferencia entre glLoadMatrixF(m) y glMultMatrixF(m)?

  8. ¿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.