# 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: ```python camera_position = (distance, height, distance) # Esempio: (800, 450, 800) ``` ### Target (Center) Dove guarda la camera: ```python look_at_point = (0, 0, 0) # Centro della scena ``` ### Up Vector Quale direzione è "su" per la camera: ```python 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 ```python # 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: ```python 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 ```python 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 ```python 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 ```python 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 ```python 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 ```python # 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: ```python 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: ```python 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 ```python 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: ```python 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: 1. Trasla il mondo in modo che la camera sia all'origine 2. 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: 1. Applica prospettiva (cose lontane = piccole) 2. 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 ```python # 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 ```python 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) ```python # 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) ```python 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: ```python camera = (distance, height, distance) target = (0, 0, 0) ``` --- **Prossimo Capitolo**: [Personalizzazione e Configurazione →](08-personalizzazione.md) [← Capitolo Precedente](06-rendering.md) | [Torna all'indice](README.md)