Questa volta utilizzeremo PyGame e OpenGL per costruire la simulazione, similmente a quanto fatto nel precedente articolo. Il programma è stato svolto in Python 3.9.
Anzitutto assicuriamoci di avere installate le librerie PyOpenGL e pygame, qualora non le avessimo sarà sufficiente installarle mediante pip con i seguenti due comandi:
1
2
pip install PyOpenGL
pip install pygame
Fatto questo possiamo cominciare a predisporre il nostro programma.
Anzitutto voglio fare un breve approfondimento su alcune funzioni che andremo ad utilizzare.
Cominciamo da gluPerspective. Questa funzione accetta 4 argomenti:
fovy: sostanzialmente l’angolo di visione in gradi, come illustrato nell’immagine di seguito
aspect ratio: il rapporto tra altezza e larghezza, sostanzialmente ci andiamo ad inserire il rapporto tra le dimensioni del nostro schermo (poi ne discutiamo meglio)
zNear: il piano più vicino dopo il quale comincia la visione
zFar: il piano più lontano al quale termina la visione
Ricordiamoci che andremo a disegnare un oggetto in 3D, che dovrà essere posizionato all’interno del tronco di piramide definito dai due piani e dall’angolo di apertura. Se l’oggetto dovesse uscire da questo spazio semplicemente non risulterebbe visibile sullo schermo (o risulterebbe parzialmente tagliato). Da questo si capisce come l’angolo di apertura determinerà la prospettiva e lo spazio di interazione dell’oggetto. La situazione sostanzialmente è la seguente:
Dal momento che andrò a posizionare i nostri oggetti a partire da (0,0,0) voglio anche spostare il punto di visione (viewpoint) indietro, in modo da farceli entrare, altrimenti non sarebbero visibili. Per farlo utilizzo la funzione glTranslatef che prendere come argomenti i valori x, y e z sui quali effettuare la traslazione.
Infine voglio disegnare delle linee usando i rispettivi vertici. Per farlo dovrò aggiungere coppie di vertici alla “matrice del mondo” con il metodo glVertex3fv. Nel caso specifico posso disegnare una linea orizzontale che vada da (0,0,0) a (1,0,0) scrivendo le seguenti quattro righe di codice:
Python
1
2
3
4
glBegin(GL_LINES)
glVertex3fv((0.0,0.0,0.0))
glVertex3fv((1.0,0.0,0.0))
glEnd()
Se volessi disegnare un quadrato dovrei disegnare tutte le linee a coppie di vertici, nel modo seguente:
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
glBegin(GL_LINES)
glVertex3fv((0.0,0.0,0.0))
glVertex3fv((1.0,0.0,0.0))
glVertex3fv((1.0,0.0,0.0))
glVertex3fv((1.0,1.0,0.0))
glVertex3fv((1.0,1.0,0.0))
glVertex3fv((0.0,1.0,0.0))
glVertex3fv((0.0,1.0,0.0))
glVertex3fv((0.0,0.0,0.0))
glEnd()
Sostanzialmente quello che facciamo in questo caso potrebbe essere riassunto col seguente schema:
Disegniamo le varie linee del quadrato seguendo le direzioni delle frecce rosse.
Adesso se volessimo limitarci a disegnare un quadrato e farlo ruotare davanti alla telecamera potremmo scrivere il seguente programma:
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
importpygame aspg
frompygame.localsimport*
fromOpenGL.GLimport*
fromOpenGL.GLU import*
importmath
classFioccoDiNeve():
def__init__(s):
pg.init()
schermo=(800,600)
pg.display.set_mode(schermo,DOUBLEBUF|OPENGL)
gluPerspective(30,(schermo[0]/schermo[1]),0.1,50)
glTranslatef(0.0,0.0,-5)
whileTrue:
forevent inpg.event.get():
ifevent.type==pg.QUIT:
pg.quit()
quit()
glRotatef(1,1,1,1)
glClearColor(0,0,0.1,1)
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
s.quadrato()
pg.display.flip()
pg.time.wait(10)
defquadrato(s):
glBegin(GL_LINES)
glVertex3fv((0.0,0.0,0.0))
glVertex3fv((1.0,0.0,0.0))
glVertex3fv((1.0,0.0,0.0))
glVertex3fv((1.0,1.0,0.0))
glVertex3fv((1.0,1.0,0.0))
glVertex3fv((0.0,1.0,0.0))
glVertex3fv((0.0,1.0,0.0))
glVertex3fv((0.0,0.0,0.0))
glEnd()
if__name__=="__main__":
FioccoDiNeve()
Grazie a glClearColor(0, 0, 0.1, 1) coloriamo lo sfondo di un blu scuro. Eseguendo il programma vedremo qualcosa di simile:
Il quadrato sarà in movimento ovviamente, grazie alla funzione glRotatef che ruota la matrice del mondo.
Arrivati a questo punto ci manca solo di disegnare il fiocco di neve, ovvero preparare le coppie di vertici di tutte le linee del fiocco di neve stesso.
Per farlo voglio seguire il seguente schema:
Partiamo dal basso e disegniamo un ramo di lunghezza L. Prendiamo come riferimento un angolo α di 45° (ovvero π/4). Alla fine del ramo teniamo conto dell’angolo di arrivo e disegniamo altri due rami, ciascuno spostato di 45°. Procediamo in avanti ripetendo questo schema per niterazioni.
Creiamo quindi due classi, una per il Fiocco e una per ciascun Ramo, nel modo seguente (nei commenti in Python ulteriori dettagli):
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
classFiocco():
# elenco di tutti i vertici, ovvero delle coppie di vertici
Per fornire le migliori esperienze, utilizziamo tecnologie come i cookie per memorizzare e/o accedere alle informazioni del dispositivo. Il consenso a queste tecnologie ci permetterà di elaborare dati come il comportamento di navigazione o ID unici su questo sito. Non acconsentire o ritirare il consenso può influire negativamente su alcune caratteristiche e funzioni.
Funzionale
Sempre attivo
L'archiviazione tecnica o l'accesso sono strettamente necessari al fine legittimo di consentire l'uso di un servizio specifico esplicitamente richiesto dall'abbonato o dall'utente, o al solo scopo di effettuare la trasmissione di una comunicazione su una rete di comunicazione elettronica.
Preferenze
L'archiviazione tecnica o l'accesso sono necessari per lo scopo legittimo di memorizzare le preferenze che non sono richieste dall'abbonato o dall'utente.
Statistiche
L'archiviazione tecnica o l'accesso che viene utilizzato esclusivamente per scopi statistici.L'archiviazione tecnica o l'accesso che viene utilizzato esclusivamente per scopi statistici anonimi. Senza un mandato di comparizione, una conformità volontaria da parte del vostro Fornitore di Servizi Internet, o ulteriori registrazioni da parte di terzi, le informazioni memorizzate o recuperate per questo scopo da sole non possono di solito essere utilizzate per l'identificazione.
Marketing
L'archiviazione tecnica o l'accesso sono necessari per creare profili di utenti per inviare pubblicità, o per tracciare l'utente su un sito web o su diversi siti web per scopi di marketing simili.