# Capitolo 2: Concetti Base di Grafica 3D ## Introduzione Prima di capire come funziona OpenGL e questo progetto, dobbiamo comprendere alcuni concetti fondamentali della grafica 3D. Non preoccuparti se non hai mai lavorato con la 3D: spiegheremo tutto partendo da zero! ## Coordinate 3D: Il Sistema XYZ ### Dal 2D al 3D Nel mondo 2D (come un foglio di carta), usiamo due coordinate: - **X**: orizzontale (sinistra-destra) - **Y**: verticale (su-giù) ``` Y ↑ | | |______→ X ``` Nel mondo 3D aggiungiamo una terza coordinata: - **Z**: profondità (avanti-dietro) ``` Y ↑ | | |______→ X ╱ ╱ Z ``` ### Esempio Pratico Immagina di posizionare un oggetto nella tua stanza: - **X = 3**: 3 metri verso destra dalla porta - **Y = 1.5**: 1.5 metri da terra (altezza) - **Z = 5**: 5 metri dentro la stanza Nel nostro progetto: - **X**: posizione orizzontale sulla griglia - **Y**: altezza del terreno (colline/valli) - **Z**: altra dimensione orizzontale sulla griglia ## Punti, Linee e Facce ### Vertici (Vertices) Un **vertice** è semplicemente un punto nello spazio 3D. ```python vertice_1 = (0, 0, 0) # Origine vertice_2 = (1, 0, 0) # Un metro a destra vertice_3 = (0, 2, 0) # Due metri in alto ``` ### Linee (Edges) Una **linea** connette due vertici. ``` v2 ●────────● v3 \ / \ / \ / ● v1 ``` ### Facce (Faces) Una **faccia** è una superficie formata da 3 o più vertici. **Triangolo** (3 vertici): ``` v3 ● /│\ / │ \ / │ \ v1●───┴───● v2 ``` **Quadrilatero** (4 vertici - quello che usiamo!): ``` v4 ●────● v3 │ │ │ │ v1 ●────● v2 ``` ## Mesh: Unire le Facce Una **mesh** è un insieme di vertici e facce che forma un oggetto 3D. Esempio: cubo composto da 8 vertici e 6 facce: ``` v8────v7 /│ /│ / │ / │ v5────v6 │ │ v4─│─-v3 │ / │ / │/ │/ v1────v2 ``` Nel nostro progetto, il terreno è una grande mesh composta da tante piccole facce quadrate (tile). ## Vista Isometrica vs Prospettiva ### Prospettiva Reale Nella vita reale, gli oggetti lontani appaiono più piccoli: ``` Prospettiva (come l'occhio umano): /│\ Vicino = grande / │ \ / │ \ Lontano = piccolo / │ \ /____|____\ ``` ### Vista Isometrica Nella vista isometrica, gli oggetti mantengono la stessa dimensione indipendentemente dalla distanza: ``` Isometrica (dimensioni costanti): ╱╲ ╱ ╲ ╱ ╲ ╱ ╲ ╱ ╲ ``` **Vantaggi dell'isometrica**: - ✅ Ottima per giochi strategici - ✅ Tutto è sempre leggibile - ✅ Facilita la progettazione - ✅ Stile classico e nostalgico **Esempi di giochi isometrici**: - RollerCoaster Tycoon - Age of Empires II - SimCity 2000 - Diablo II - Civilization II ### Angolo Isometrico Nel nostro progetto usiamo un angolo di **45 gradi** sia in orizzontale che in verticale, creando la classica vista "diamante": ``` Vista dall'alto (2D): Vista isometrica (3D): ┌─┬─┬─┐ ◇─◇─◇ ├─┼─┼─┤ ╱│╱│╱│ ├─┼─┼─┤ → ◇─◇─◇ │ ├─┼─┼─┤ │╱│╱│╱ └─┴─┴─┘ ◇─◇─◇ ``` ## Colori in 3D ### RGB: Red, Green, Blue I colori sono rappresentati come combinazioni di rosso, verde e blu: ``` RGB(255, 0, 0) = Rosso puro 🔴 RGB(0, 255, 0) = Verde puro 🟢 RGB(0, 0, 255) = Blu puro 🔵 RGB(255, 255, 0) = Giallo 🟡 RGB(0, 0, 0) = Nero ⚫ RGB(255, 255, 255)= Bianco ⚪ ``` ### Normalizzazione (0-1) In OpenGL, i colori vanno da 0.0 a 1.0 invece che da 0 a 255: ```python # Invece di RGB(128, 200, 64) # Dividi per 255: colore = (0.5, 0.78, 0.25) ``` ### Colori nel Nostro Progetto Ogni bioma ha il suo colore: ```python 'water': (0.2, 0.4, 0.8) # Blu 🌊 'sand': (0.76, 0.7, 0.5) # Beige 🏖️ 'grass': (0.25, 0.6, 0.25) # Verde 🌿 'rock': (0.5, 0.5, 0.5) # Grigio ⛰️ 'snow': (0.9, 0.9, 0.95) # Bianco 🏔️ ``` ## Illuminazione e Shading ### Perché l'Illuminazione? Senza luce, tutto apparirebbe piatto: ``` Senza luce: Con luce: ┌──┐ ┌──┐ │ │ │░░│ ← Lato in ombra └──┘ └▓▓┘ ← Lato scuro ``` ### Come Funziona La luce colpisce le superfici da diverse angolazioni: ``` Luce ☀ │ ↓ ┌────────┐ │ │ ← Faccia illuminata (chiara) └────────┘ │ └─→ Ombra ``` ### Componenti della Luce **1. Luce Ambientale** (Ambient) Luce che colpisce tutto uniformemente, simula la luce diffusa nell'ambiente. **2. Luce Diffusa** (Diffuse) Luce direzionale che illumina le superfici in base all'angolo. **3. Luce Speculare** (Specular) Riflessi luminosi (non usati in questo progetto). ### Shading nel Nostro Progetto Applichiamo ombreggiatura alle facce laterali delle tile: ```python # Faccia superiore = 100% luminosità base_color = (0.3, 0.6, 0.3) # Faccia laterale destra = 70% luminosità side_color = (0.21, 0.42, 0.21) # Moltiplicato per 0.7 # Faccia laterale retro = 80% luminosità back_color = (0.24, 0.48, 0.24) # Moltiplicato per 0.8 ``` Questo crea un effetto 3D più realistico! ## Rendering: Dal 3D al 2D ### Il Problema Il monitor è 2D, ma vogliamo mostrare un mondo 3D. Come facciamo? ### La Soluzione: Pipeline di Rendering ``` 1. MODELLO 3D 2. TRASFORMAZIONI (Vertici in spazio 3D) (Posizione, rotazione) │ │ ↓ ↓ ┌─────────┐ ┌────────────┐ │ Cubo 3D │ │ Ruota, │ │ x,y,z │ → │ sposta, │ └─────────┘ │ scala │ └────────────┘ │ ↓ ↓ 3. CAMERA 4. PROIEZIONE (Punto di vista) (3D → 2D) │ │ ↓ ↓ ┌──────────┐ ┌───────────┐ │ Guarda da│ │ Appiattisci│ │ qui │ → │ su schermo │ └──────────┘ └───────────┘ │ ↓ 5. PIXEL SULLO SCHERMO │ ↓ ┌──────────┐ │ Immagine │ │ finale │ └──────────┘ ``` ### Matrici di Trasformazione Le trasformazioni 3D usano la matematica matriciale (non preoccuparti, OpenGL lo fa per te!): - **Model Matrix**: Posiziona l'oggetto nel mondo - **View Matrix**: Posiziona la camera - **Projection Matrix**: Crea la proiezione 2D ``` Vertice 3D × Model × View × Projection = Pixel 2D ``` ## Coordinate della Griglia nel Nostro Progetto ### Sistema Locale Ogni tile è posizionata in una griglia: ``` Griglia 5×5 (esempio semplificato): j→ 0 1 2 3 4 i↓ ┌───┬───┬───┬───┬───┐ 0 │0,0│0,1│0,2│0,3│0,4│ ├───┼───┼───┼───┼───┤ 1 │1,0│1,1│1,2│1,3│1,4│ ├───┼───┼───┼───┼───┤ 2 │2,0│2,1│2,2│2,3│2,4│ ├───┼───┼───┼───┼───┤ 3 │3,0│3,1│3,2│3,3│3,4│ ├───┼───┼───┼───┼───┤ 4 │4,0│4,1│4,2│4,3│4,4│ └───┴───┴───┴───┴───┘ ``` ### Conversione a Coordinate Mondo Per centrare la griglia sull'origine: ```python # Posizione in griglia i = 5 j = 10 # Converti a coordinate mondo (centrato) x = (i - grid_size/2) * tile_width z = (j - grid_size/2) * tile_depth y = heightmap[i][j] ``` Esempio con griglia 20×20: ``` Tile (0,0) → Mondo (-300, h, -300) Tile (10,10)→ Mondo (0, h, 0) # Centro Tile (19,19)→ Mondo (270, h, 270) ``` ## Depth Buffer (Z-Buffer) ### Il Problema dell'Ordine Quando disegni in 3D, devi sapere cosa sta davanti e cosa sta dietro: ``` ┌─────┐ │ A │ └─────┘ ┌─────┐ │ B │ ← B dovrebbe coprire A └─────┘ ``` ### La Soluzione: Depth Buffer OpenGL usa un **depth buffer** per tenere traccia della profondità di ogni pixel: ``` Per ogni pixel: 1. Calcola la distanza dalla camera (profondità Z) 2. Se Z è minore del valore nel buffer → disegna e aggiorna buffer 3. Se Z è maggiore → non disegnare (c'è qualcosa davanti) ``` Nel codice: ```python glEnable(GL_DEPTH_TEST) # Abilita il depth buffer glClear(GL_DEPTH_BUFFER_BIT) # Pulisci a ogni frame ``` ## Wireframe: Visualizzare la Struttura Un **wireframe** mostra solo i bordi della geometria: ``` Solido: Wireframe: ████████ ┌──┬──┐ ████████ ├──┼──┤ ████████ └──┴──┘ ``` Nel nostro progetto, disegniamo sia le facce solide che i bordi wireframe: ```python # 1. Disegna facce solide for tile in terrain: draw_solid_quad(tile) # 2. Disegna linee wireframe glLineWidth(5.0) for edge in grid: draw_line(edge) ``` Questo crea l'effetto griglia caratteristico! ## Riepilogo Concetti ### Sistema di Coordinate - **X, Y, Z**: tre dimensioni dello spazio - **Origine**: punto (0, 0, 0) - **Vertici**: punti nello spazio ### Geometria - **Vertici**: punti - **Linee**: connessioni tra vertici - **Facce**: superfici (triangoli o quad) - **Mesh**: insieme di facce ### Visualizzazione - **Isometrica**: vista 45° senza prospettiva - **Prospettiva**: come l'occhio umano vede - **Camera**: punto di vista virtuale ### Colori e Luce - **RGB**: combinazione di rosso, verde, blu - **Normalizzazione**: valori 0.0-1.0 - **Shading**: ombreggiatura per profondità - **Ambien/Diffuse**: tipi di illuminazione ### Rendering - **Pipeline**: processo dal 3D al 2D - **Matrici**: trasformazioni matematiche - **Depth Buffer**: gestione profondità - **Wireframe**: visualizzazione bordi ## Applicazione al Nostro Progetto Ora che conosci i concetti base, ecco come si applicano: 1. **Griglia 20×20** = 400 vertici con altezze diverse 2. **Ogni tile** = un quad (4 vertici) in 3D 3. **Vista isometrica** = camera a 45° dall'alto 4. **Colori** = mappati alle altezze (biomi) 5. **Shading** = facce laterali più scure 6. **Wireframe** = linee nere tra le tile 7. **Depth buffer** = gestisce sovrapposizioni --- **Prossimo Capitolo**: [OpenGL: La Tecnologia di Rendering →](03-opengl.md) [← Capitolo Precedente](01-introduzione.md) | [Torna all'indice](README.md)