You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

11 KiB

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.

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:

# 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:

'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:

# 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:

# 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:

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:

# 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 →

← Capitolo Precedente | Torna all'indice