10 KiB
Capitolo 7: Il Sistema Camera
Cos'è una Camera Virtuale?
Una camera virtuale è il "punto di vista" da cui osserviamo la scena 3D. È come una videocamera nel mondo virtuale che determina cosa vediamo e da che angolazione.
Componenti della Camera
Posizione (Eye)
Dove si trova la camera nello spazio 3D:
camera_position = (distance, height, distance)
# Esempio: (800, 450, 800)
Target (Center)
Dove guarda la camera:
look_at_point = (0, 0, 0) # Centro della scena
Up Vector
Quale direzione è "su" per la camera:
up_vector = (0, 1, 0) # Y+ è "su"
Visualizzazione
Up (0,1,0)
↑
│
│
Eye ●─────→ Center
(800,450,800) (0,0,0)
Vista Isometrica
Cosa la Rende Isometrica?
La camera è posizionata a 45 gradi sia orizzontalmente che verticalmente:
Vista dall'alto:
N
↑
Camera
W ← ● → E Camera a 45° tra N e E
↓
S
Vista laterale:
↑
45°
Camera ●
╲
╲ 45°
↘
Terreno
Posizione Isometrica
# Distanza dalla scena
distance = 800.0
# Camera posizionata a (distance, height, distance)
# Questo crea l'angolo di 45° perfetto
camera_pos = (distance, height, distance)
look_at = (0, 0, 0)
Perché funziona?
Z
↑
│
│ distance
├──────────● Camera
│ ╱
│ 45° ╱
│ ╱ distance
O──────┴──────→ X
L'angolo tra X e Z è 45° quando le coordinate X e Z sono uguali!
Proiezione Prospettica
Field of View (FOV)
L'angolo di visione della camera:
fov = 45 # gradi
FOV piccolo (30°): FOV grande (90°):
│ \___
│ │ ___
│ │ ___
│ │ ___
Camera Camera
Vista stretta Vista ampia
(teleobiettivo) (grandangolo)
Clipping Planes
Near Plane: Distanza minima visibile Far Plane: Distanza massima visibile
near = 0.1 # Oggetti < 0.1 → invisibili
far = 5000.0 # Oggetti > 5000 → invisibili
Far Plane (5000)
│
╱╲ │
╱ ╲ │
╱ ╲ │
╱Camera╲ │
╱ ● ╲│
Near Plane (0.1)
Oggetti fuori da questo volume non vengono disegnati!
Codice Proiezione
def setup_projection(self, aspect_ratio):
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(
self.fov, # 45 gradi
aspect_ratio, # width/height (1.5 per 1200×800)
self.near_clip, # 0.1
self.far_clip # 5000.0
)
Trasformazione ModelView
Cosa Fa
Imposta la posizione e orientamento della camera.
Codice
def setup_modelview(self):
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
gluLookAt(
# Posizione camera (eye)
self.distance, self.height, self.distance,
# Punto guardato (center)
0, 0, 0,
# Direzione "su" (up)
0, 1, 0
)
Significato Parametri
gluLookAt(
eye_x, eye_y, eye_z, # Dove sta la camera
center_x, center_y, center_z, # Dove guarda
up_x, up_y, up_z # Quale direzione è "su"
)
Controlli Utente
Input Tastiera
def handle_input(self, keys):
# Zoom in/out
if keys[pygame.K_UP]:
self.distance -= self.zoom_speed
self.distance = max(self.min_distance, self.distance)
if keys[pygame.K_DOWN]:
self.distance += self.zoom_speed
self.distance = min(self.max_distance, self.distance)
# Altezza camera
if keys[pygame.K_LEFT]:
self.height += self.height_speed
if keys[pygame.K_RIGHT]:
self.height -= self.height_speed
Effetto Zoom
Zoom In (UP): Zoom Out (DOWN):
● ●
│\ │ \
│ \ │ \
│ \ │ \
Distance Distance
(più piccolo) (più grande)
│ │
Terreno Terreno
(più vicino, (più lontano,
più grande) più piccolo)
Effetto Altezza
Height Up (LEFT): Height Down (RIGHT):
● ●
╱│ ╱│\
╱ │ ╱ │ \
╱ │ ╱ │ \
Height Height
(più alto) (più basso)
│ │
Terreno Terreno
(vista dall'alto) (vista laterale)
Limiti della Camera
Perché i Limiti?
Evitare:
- Zoom eccessivo (camera dentro il terreno)
- Zoom troppo distante (terreno invisibile)
- Angolazioni strane
Implementazione
# Limiti zoom
MIN_DISTANCE = 200.0
MAX_DISTANCE = 2000.0
if keys[pygame.K_UP]:
self.distance -= self.zoom_speed
# Blocca al minimo
self.distance = max(MIN_DISTANCE, self.distance)
if keys[pygame.K_DOWN]:
self.distance += self.zoom_speed
# Blocca al massimo
self.distance = min(MAX_DISTANCE, self.distance)
Limiti Altezza
Nessun limite esplicito sull'altezza, ma potresti aggiungere:
MIN_HEIGHT = 100.0
MAX_HEIGHT = 1000.0
self.height = max(MIN_HEIGHT, min(MAX_HEIGHT, self.height))
Aspect Ratio
Cos'è
Il rapporto larghezza/altezza della finestra:
aspect_ratio = width / height
# 1200 / 800 = 1.5
Perché Importante?
Senza aspect ratio corretto, l'immagine si deforma:
Aspect corretto (1.5): Aspect sbagliato (1.0):
┌─────────────┐ ┌────────┐
│ │ │ │
│ ●○● │ │ ●○● │
│ │ │ │
└─────────────┘ └────────┘
Proporzioni OK Schiacciato!
Utilizzo
aspect_ratio = self.display[0] / self.display[1]
gluPerspective(fov, aspect_ratio, near, far)
Matrici di Trasformazione
Matrice Proiezione
Trasforma coordinate 3D in coordinate schermo 2D:
Mondo 3D → Matrice Proiezione → Spazio clip → Schermo 2D
Matrice ModelView
Combina trasformazione del modello E della vista:
ModelView = View × Model
View: Posizione camera
Model: Trasformazioni oggetto (nel nostro caso, nessuna)
Stack Matrici
OpenGL mantiene uno stack di matrici:
glMatrixMode(GL_PROJECTION) # Seleziona stack proiezione
glLoadIdentity() # Resetta a identità
gluPerspective(...) # Applica trasformazione
glMatrixMode(GL_MODELVIEW) # Seleziona stack modelview
glLoadIdentity() # Resetta a identità
gluLookAt(...) # Applica trasformazione
Calcoli Interni (Approfondimento)
gluLookAt - Cosa Fa
Crea una matrice che:
- Trasla il mondo in modo che la camera sia all'origine
- Ruota il mondo in modo che la camera guardi lungo -Z
Prima: Dopo gluLookAt:
Mondo Camera all'origine
│ │
● Camera O────→ Guarda -Z
╱ ╱
╱ ╱
Terreno Terreno (traslato/ruotato)
gluPerspective - Cosa Fa
Crea una matrice che:
- Applica prospettiva (cose lontane = piccole)
- Mappa il volume visibile in un cubo [-1,+1]³
Cubo normalizzato
Frustum visibile [-1,+1] in tutte
(pyramide tronca) le dimensioni
╱╲ ┌───┐
╱ ╲ │ │
╱ ╲ → │ │
╱ ╲ └───┘
╱ Camera ╲
Esempio Completo di Frame
# 1. Setup proiezione
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(45, 1.5, 0.1, 5000.0)
# 2. Setup camera
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
gluLookAt(
800, 450, 800, # Camera a (800,450,800)
0, 0, 0, # Guarda il centro
0, 1, 0 # Y è "su"
)
# 3. Disegna geometria
# OpenGL applica automaticamente entrambe le matrici
# a ogni vertice disegnato
glBegin(GL_QUADS)
glVertex3f(x, y, z) # Trasformato da matrici
...
glEnd()
Miglioramenti Possibili
Rotazione Camera
def handle_input(self, keys):
if keys[pygame.K_q]:
self.rotation_angle += self.rotation_speed
if keys[pygame.K_e]:
self.rotation_angle -= self.rotation_speed
def setup_modelview(self):
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
# Ruota attorno all'asse Y
glRotatef(self.rotation_angle, 0, 1, 0)
gluLookAt(...)
Camera Libera (First Person)
# Camera FPS-style
position = [x, y, z]
rotation = [pitch, yaw]
def move_forward():
position[0] += cos(yaw) * speed
position[2] += sin(yaw) * speed
def look_around(mouse_dx, mouse_dy):
yaw += mouse_dx * sensitivity
pitch += mouse_dy * sensitivity
Camera Smooth (Interpolazione)
target_distance = 500
current_distance = 800
# Invece di salto immediato
current_distance = target_distance
# Usa interpolazione smooth
lerp_speed = 0.1
current_distance += (target_distance - current_distance) * lerp_speed
Riepilogo
Componenti Camera
- Position: Dove sta la camera
- Target: Dove guarda
- Up Vector: Orientamento "su"
- FOV: Angolo di visione
- Clipping: Near e far plane
Controlli
- UP/DOWN: Zoom in/out (distance)
- LEFT/RIGHT: Altezza camera (height)
- Limiti: Min/max per evitare angoli strani
Matrici
- Projection: FOV, aspect, clipping
- ModelView: Posizione e orientamento camera
Vista Isometrica
Posizione a 45° sia orizzontalmente che verticalmente:
camera = (distance, height, distance)
target = (0, 0, 0)
Prossimo Capitolo: Personalizzazione e Configurazione →