Skip to content

Clase 6 — Sistema de coordenadas de la vista: cámara y volumen de recorte

Resumen Ejecutivo

Última clase "dura" de OpenGL: cómo posicionar la cámara virtual en una escena 3D y cómo definir el volumen de recorte. Se introduce gluLookAt (posición, punto de referencia, APP) y se refuerza glOrtho para 3D. El concepto crítico que hay que interiorizar: el volumen de recorte se define en el sistema de coordenadas de la vista, no del mundo; y zNear/zFar son distancias sobre el eje Z negativo, no coordenadas. Superada esta clase, OpenGL queda prácticamente cerrado; solo queda el tema 5 (proyecciones) y un tema 6 muy breve (superficies ocultas). Corrección en clase de la actividad individual de surtidores. ⚠️ EXAMEN: será 3D; se permite llevar actividades propias al examen; el módulo IGV_Utils estará disponible.

Conceptos Clave

  • Cámara virtual: entidad sin físico real. No "ve" como una cámara de verdad; puede capturar objetos que estén "detrás" de ella siempre que caigan dentro del volumen de recorte.
  • Sistema de coordenadas de la vista: sistema local anclado a la cámara. Origen = posición de la cámara P0. Eje Z definido por la recta P0 → Pref (mirando hacia -Z). Eje Y definido por el APP (vector "up"). Eje X se autodefine por regla de la mano derecha. ⚠️ EXAMEN
  • gluLookAt(x0, y0, z0, xref, yref, zref, vx, vy, vz): define el sistema de coordenadas de la vista. 3 parámetros = posición cámara, 3 parámetros = punto de referencia, 3 parámetros = APP.
  • Punto de referencia Pref: dirección de visión, no posición exacta. Lo relevante es la dirección P0 → Pref; da igual si Pref = (0,0,-1) o (0,0,-100) mientras siga siendo en -Z.
  • Volumen de recorte: región del espacio que se proyecta y se ve en el viewport. Lo que cae fuera no se muestra. Se define en el sistema de coordenadas de la VISTA. ⚠️ EXAMEN — punto más confundido de toda la asignatura.
  • Plano de proyección: en OpenGL está vinculado al volumen de recorte: es el plano más cercano a la cámara del volumen (el plano Z = -zNear).
  • glOrtho(left, right, bottom, top, zNear, zFar): define el volumen de recorte como ortoedro. ⚠️ EXAMEN
  • left, right, bottom, top son coordenadas en los ejes X e Y de la vista.
  • zNear, zFar son distancias medidas sobre el eje Z negativo de la vista. El prefijo d de los parámetros originales (dNear, dFar) es intencional.

Desarrollo del Temario

1. Recordatorio: la Model View como pila (clase 5)

Todo objeto se dibuja entre glPushMatrix() y glPopMatrix() para que sus transformaciones no contaminen al siguiente. Objetos complejos pueden anidar varios push/pop internamente. Regla invariante: cada push debe emparejarse con un pop.

2. Del 2D al 3D: ¿por qué hace falta una cámara?

En 2D el universo se ve de frente, por lo que glOrtho2D basta. En 3D una misma escena admite infinitos puntos de vista: una cámara lateral ve un coche de perfil, una cámara trasera lo ve de atrás. Hace falta:

  1. Elegir dónde ponemos la cámara (P0).
  2. Elegir hacia dónde mira (Pref).
  3. Elegir su orientación vertical (APP).
  4. Elegir qué región 3D se proyecta (volumen de recorte).

3. Pipeline de transformaciones (repaso)

[Coord. modelo] → Transformación del modelo → [Coord. universo] → Transformación de visualización → [Coord. vista] → Transformación de proyección → [Coord. proyección] → Normalización → Transformación de visor → [Coord. dispositivo]
  • La transformación del modelo ubica los objetos en el mundo (traslación, escalado, rotación).
  • La transformación de visualización es la que esta clase explica: cómo la cámara reubica esos objetos en un sistema de coordenadas centrado en ella.
  • La transformación de proyección la vemos en el tema 5.
  • La normalización la hace OpenGL y no se toca.
  • La transformación de visor se controla con glViewport.

4. Construcción formal del sistema de coordenadas de la vista

Dado:

  • P0 = posición de la cámara.
  • Pref = punto hacia el que mira.
  • APP (vx, vy, vz) = vector up, orientación superior de la cámara.

Los ejes se construyen en este orden: ⚠️ EXAMEN

  1. Origen: P0.
  2. Eje Z: recta que une P0 y Pref. El tramo P0 → Pref es -Z (hacia donde mira la cámara). El otro lado es +Z.
  3. Eje Y+: vector APP.
  4. Eje X: se autodefine (perpendicular a Z e Y por mano derecha).
flowchart LR
    P0((P0 = origen vista)) -- Pref --> Pref((Pref))
    P0 -- Z- --> Pref
    P0 -- Y+ --> UP((APP))
    P0 -- X+ --> X((X por mano derecha))

Duda frecuente en clase (Juan Antonio): "¿No sobra una dimensión en APP?" En el plano del visor, sí — pero APP es un punto/vector en 3D absoluto, no una coordenada relativa al plano. Hacen falta 3 componentes.

5. La función gluLookAt

gluLookAt(x0, y0, z0,      # P0 — posición de la cámara
         xref, yref, zref,  # Pref — punto al que mira
         vx, vy, vz)        # APP — vector up

Todos los valores en coordenadas del mundo. Con esta única llamada queda definido el sistema de la vista.

Valores por defecto si NO llamas a gluLookAt: ⚠️ EXAMEN

P0   = (0, 0, 0)
Pref = (0, 0, -1)
APP  = (0, 1, 0)

Es decir: cámara en el origen, mirando hacia -Z, con Y hacia arriba. En este caso el sistema de la vista coincide con el del mundo.

6. La cámara es virtual — no tiene "trasera ciega"

Ejercicio mental clave: una cámara real solo captura lo que está delante de su lente. La cámara virtual de OpenGL puede capturar cualquier objeto que caiga dentro del volumen de recorte, incluso si está "detrás" del eje de visión. Esto se demostró en clase poniendo el cubo en +Z con la cámara mirando a -Z pero con un volumen de recorte que incluía +Z: el cubo se vio.

No intentes proyectar tu intuición de cámara física sobre la cámara virtual. Lo que determina si un objeto se ve es puramente si cae en el volumen de recorte, no si está "delante" o "detrás".

7. Volumen de recorte 3D con glOrtho

glOrtho(left, right, bottom, top, zNear, zFar)

Define un ortoedro en el sistema de coordenadas de la vista.

  • left, right = coordenadas X de las caras izquierda y derecha.
  • bottom, top = coordenadas Y de las caras inferior y superior.
  • zNear, zFar = distancias sobre -Z. Si pasas zNear=2, el plano cercano está en la coordenada Z = -2 (porque son distancias en el eje Z negativo). ⚠️ EXAMEN

Plano de proyección = plano más cercano a la cámara del volumen = plano Z = -zNear. En OpenGL NO se puede desvincular plano de proyección y volumen.

Consecuencia contraintuitiva: si quisieras el plano de proyección en +Z = 8, tendrías que escribir zNear = -8. Ejemplo de clase.

8. Ejercicios de cambio de volumen (callback displayChangeClippingVolume)

Cubo unitario centrado en el origen, variando zNear/zFar:

zNear zFar Resultado
-5 5 Cubo completo visible (volumen entre Z = +5 y Z = -5).
-3 5 Plano cercano pasa al Z = +3; cubo visible pero el eje Z+ solo llega hasta 3.
0 5 Plano cercano coincide con Z = 0 → secciona el cubo por la mitad mostrando las caras internas.
1 5 Plano cercano en Z = -1 → el cubo queda fuera del volumen, no se ve nada (blanco).

Efecto didáctico: al escalar un cubo grande (glScalef(3,3,3)) alrededor de uno pequeño, normalmente solo se ve el grande. Si se secciona con zNear=0, se ve el pequeño dentro del grande.

9. Ejercicios de cambio de cámara (callback displayChangeLookAt)

Volumen fijo (-5..5) en todos los ejes, varía la cámara:

P0 Pref APP Resultado
(0,0,0) (0,0,-1) (0,1,0) Defecto. Cara frontal visible.
(0,0,0) (0,0,+1) (0,1,0) Cámara mirando a +Z → se ve la cara posterior del cubo.
(0,0,0) (0,0,+1) (0,-1,0) Mismo punto, pero cámara cabeza abajo: Y+ hacia abajo en el visor.
(0,0,0) (-1,0,0) (0,1,0) Cámara mirando a -X → se ve la cara lateral izquierda.

Nota del profesor: no se piden "giros raros" de cámara en el examen. El objetivo es consolidar los parámetros de gluLookAt.

10. Ejercicios combinados (callback displayChangeAll) — ⚠️ EXAMEN

Cambiar cámara + volumen simultáneamente. Ejemplo que se ve en blanco:

glOrtho(-2, 2, -2, 2, -2, 2)
gluLookAt(5, 0, 5, 0, 0, 0, 0, 1, 0)

¿Por qué no se ve nada? Porque el volumen (-2..2 en todos los ejes) se define en el sistema de la vista, cuyo origen ha sido desplazado a (5, 0, 5) del mundo y cuyos ejes han rotado. Ese ortoedro queda a un lado del cubo del mundo, sin incluirlo.

Usando un ortoedro muy largo en Z pero muy estrecho en X/Y (-0.25..0.25, -0.8..0.8, -8..8), se logra que el volumen atraviese el cubo y se ven solo trozos de dos caras (frontal rosa + lateral gris), por el recorte estrecho en X/Y.

La lección: ⚠️ EXAMEN el volumen de recorte se define en el sistema de coordenadas de la vista. Si cambias la cámara, debes pensar el volumen en la nueva base local, no en la del mundo.

Cuando empieces a dibujar escenas y no veas nada o veas recortes raros, la causa casi siempre es esta.

11. Corrección de la actividad individual (surtidores de gasolina)

Objetivo: leer un CSV con líneas que contienen datos de repostajes, contar ocurrencias por marca, calcular porcentajes y dibujar cada marca como un surtidor de tamaño proporcional con su nombre y porcentaje.

Puntos clave del enfoque del profesor:

  • Proporcionalidad ventana de recorte ↔ viewport. Si la ventana es 200×100, el viewport debe mantener esa proporción (en clase, 1000×500) para no deformar los surtidores.
  • Diccionario de colores por marca + diccionario de porcentajes. Lectura con try + with open(...) para cierre automático.
  • Recorrido del diccionario para dibujar:
  • currentX inicializado a 10 (o similar).
  • gap fijo (ej. 20) entre surtidores.
  • Para cada entrada: glTranslatef(currentX, 0, 0), glScalef(porcentaje/100, porcentaje/100, 1), dibujar surtidor.
  • glLoadIdentity() antes de dibujar el texto para que las coordenadas de drawText(x, y) no se vean afectadas por la Model View actual.
  • Actualizar currentX += ancho_surtidor + gap.
  • drawText y Model View. El texto en sí no escala ni rota, pero sus coordenadas sí son afectadas por la Model View. Por eso se limpia antes de escribir el nombre de la marca y el porcentaje.
  • Composición del surtidor. Descomponer la figura en rectángulos, evitando concavidades/convexidades. Un rectángulo base + rectángulo superior (display) + esquina + mango.

12. Información examen ⚠️ EXAMEN

  • El examen será 3D. (En convocatorias pasadas hubo parte 2D + 3D; este curso ya es solo 3D.)
  • Se puede llevar la actividad propia al examen.
  • El módulo IGV_Utils estará disponible.
  • Leer ficheros NO se pedirá en el examen.
  • El profesor compartirá un módulo adicional con funciones para construir ortoedros huecos y piezas para la actividad grupal (mundo estilo Minecraft).

Preguntas de Autoevaluación

  1. ¿Qué define gluLookAt y con cuántos parámetros? Define el sistema de coordenadas de la vista con 9 parámetros: 3 para P0, 3 para Pref y 3 para APP.
  2. ¿Cuáles son los valores por defecto si no llamas a gluLookAt? P0 = (0,0,0), Pref = (0,0,-1), APP = (0,1,0).
  3. ¿Qué es zNear y por qué decimos que es una distancia y no una coordenada? Es la distancia desde la cámara al plano de proyección, medida sobre el eje Z negativo del sistema de la vista. Si zNear = 3, el plano está en la coordenada Z = -3.
  4. ¿Por qué el plano de proyección no se puede definir independientemente del volumen en OpenGL? Porque OpenGL los vincula por diseño: el plano de proyección es siempre el plano más cercano a la cámara del volumen de recorte.
  5. ¿En qué sistema de coordenadas se define el volumen de recorte? En el sistema de coordenadas de la vista, no del mundo.
  6. ¿Qué ves si haces glOrtho(-5,5,-5,5,-5,5) sin llamar a gluLookAt? Ves un cubo en el origen desde la cara +Z con Y hacia arriba (valores por defecto de la cámara coinciden con el sistema del mundo).
  7. Si quieres ver la cara trasera del cubo sin moverlo, ¿qué parámetro cambias de gluLookAt? Pref a un punto con Z > 0 (la cámara mira a +Z).
  8. ¿Por qué la cámara puede capturar objetos "detrás de ella"? Porque es virtual: lo que se ve está determinado exclusivamente por el volumen de recorte, no por la orientación física de una lente.
  9. ¿Qué pasa si defines un volumen que no contiene ningún objeto? El viewport se ve en blanco (color de fondo).
  10. ¿Qué define el APP de la cámara? La orientación vertical: el vector "up" que indica hacia dónde es el Y positivo del sistema de la vista.
  11. ¿Qué truco usas para que drawText(x,y) imprima en coordenadas absolutas aunque antes hayas escalado/trasladado un objeto? Cargar la matriz identidad con glLoadIdentity() antes de escribir el texto.
  12. Al dibujar muchos objetos que comparten la Model View, ¿qué patrón aplicas por cada objeto? glPushMatrix al entrar, transformaciones propias, dibujo, glPopMatrix al salir.

Referencias a cuadernos

  • tema4_coordenadas_vista_3D.ipynb — tres callbacks: displayChangeClippingVolume, displayChangeLookAt, displayChangeAll.
  • Cuaderno de visualización del volumen de recorte en el mundo (no se distribuye con soporte, pero el profesor comprometió compartirlo tras clase).
  • Próxima clase: tema 5 — proyecciones (dos semanas). Después, tema 6 (superficies ocultas) divulgativo.