-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdocumentazione.html
269 lines (239 loc) · 19.4 KB
/
documentazione.html
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Progetto Motorbike</title>
<style>
h1 {text-align: center}
</style>
</head>
<body>
<h1>PROGETTO MOTORBIKE</h1>
<h1>di Vito Vincenzo Covella</h1>
<h2>Scopo del gioco e comandi</h3>
<p>
Il gioco consiste nel guidare una moto facendola passare sotto il maggior numero possibile di archi (detti waypoints) entro lo scadere del tempo. Per guidare la moto è possibile usare i classici tasti <span style="font-family: monospace;">WASD</span>. Sono inoltre disponibili i seguenti tasti funzione:
<ul>
<li><span style="font-family: monospace;">F1</span>: cicla attraverso i vari tipi di camera</li>
<li><span style="font-family: monospace;">F2</span>: abilita/disabilita la visualizzazione wireframe</li>
<li><span style="font-family: monospace;">F3</span>: abilita/disabilita l'environment mapping della statua</li>
<li><span style="font-family: monospace;">F4</span>: abilita/disabilita la luce del faro anteriore della moto</li>
<li><span style="font-family: monospace;">F5</span>: abilita/disabilita la visualizzazione delle ombre</li>
</ul>
Il gioco dovrebbe supportare anche l'uso di joystick, poiché vengono catturati anche gli eventi <code>SDL_JOYAXISMOTION</code> e <code>SDL_JOYBUTTONDOWN</code>, ma a causa dell'assenza di supporto nativo su Linux per controller XBox 360, tali funzionalità non sono state testate.
</p>
<h2>Meshes</h2>
<p>
La maggior parte delle meshes usate è stata scaricata dal sito <a href="https://www.turbosquid.com/">Turbosquid</a>. La mesh del pilota, inizialmente in T-pose, è stata modificata in Blender per apparire seduta sulla moto. La mesh della moto è stata suddivisa in differenti mesh separate (carlinga, ruota anteriore, cerchione anteriore, ruota posteriore, cerchione posteriore), in modo da poter gestire la rotazione e i movimenti delle ruote (è possibile visualizzare i movimenti della ruota anteriore scegliendo un'apposita camera tramite il pulsante <code>F1</code> ).
<br>
La mesh della pozzanghera è stata creata in Blender usando delle curve di Bézier; tale curva è stata succesivamente trasformata in mesh e l'interno del contorno definito dalla curva è stato riempito, creando una superficie piana.
<div style="display: block;">
<figure style="text-align: center; display: inline-block;">
<img src="images/waterpuddle.png" width="400" height="300">
<figcaption>Mesh della pozzanghera</figcaption>
</figure>
<figure style="text-align: center; display: inline-block;">
<img src="images/moto_pilota.png" width="400" height="300">
<figcaption>Meshes del pilota e della moto</figcaption>
</figure>
</div>
Tutte le mesh vengono renderizzate usando la normale per vertice (Gouraud shading), tranne la mesh delle ruote, renderizzate usando la normale per faccia (flat shading).
</p>
<h2>Textures</h2>
<p>
Nel gioco sono state applicate diverse textures: sul pavimento, sul tabellone, sui waypoints, sulla panchina, sui barili e sulla statua.
<br>
Per quanto riguarda il pavimento, è stata applicata una texture specificando manualmente le coordinate texture per ogni vertice di ogni quad che costituisce il pavimento, tramite <code>glTexCoord2f</code>. Il colore della textures viene poi moltiplicato per il colore scelto per il pavimento, componente per componente.
<br>
Per quanto concerne il tabellone, è stata applicata una foto personale nella seguente maniera: viene creato un quad, tramite <code>glBegin(GL_QUADS)</code>, le cui coordinate corrispondono ai vertici del bounding box della parte superiore del tabellone (il pannello); si specificano poi manualmente le coordinate textures per ogni vertice del quad. È stata inoltre abilitata l'interpolazione con correzione prospettica mediante <code>glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)</code>.
<figure style="text-align: center; display: block;">
<img src="images/tabellone_foto.png" width="400" height="400">
<figcaption>Tabellone con la foto</figcaption>
</figure>
</p>
<p>
Per i barili è stata invece abilitata la generazione automatica delle coordinate textures, tramite <code>glEnable(GL_TEXTURE_GEN_S)</code> e <code>glEnable(GL_TEXTURE_GEN_T)</code>, specificando poi <code>s</code> e <code>t</code>, ossia i coefficienti da usare nella corrispondente funzione per la generazione delle coordinate texture, tramite <code>glTexGenfv(GL_S, GL_OBJECT_PLANE, s)</code> e <code>glTexGenfv(GL_S, GL_OBJECT_PLANE, t)</code>. La texture generation function è scelta usando il parametro <code>GL_OBJECT_LINEAR</code> in <code>glTexGeni(GL_S, GL_TEXTURE_GEN_MODE , GL_OBJECT_LINEAR)</code>
e <code>glTexGeni(GL_T, GL_TEXTURE_GEN_MODE , GL_OBJECT_LINEAR)</code>. Il risultato è mostrato nella foto sottostante.
<div style="display: block;">
<figure style="text-align: center; display: inline-block;">
<img src="images/barili_texture.png" width="400" height="400">
<figcaption>Barili con texture</figcaption>
</figure>
<figure style="text-align: center; display: inline-block;">
<img src="images/statua_envmap.png" width="400" height="400">
<figcaption>Statua con environment mapping</figcaption>
</figure>
</div>
</p>
<p>
La texture della statua viene applicata usando la tecnica dell'environment mapping, specificando il parametro <code>GL_SPHERE_MAP</code> in <code>glTexGeni</code>.
<br>
Per quanto riguarda la panchina, per avere risultati gradevoli alla vista, è stata applicata una texture ad ogni faccia/triangolo della mesh, aggiungendo al file <code>mesh.cpp</code> la funzione <code>void Mesh::RenderNxV_TexturedFaces</code>. È stata effettuata questa scelta perché la generazione automatica delle coordinate texture produceva risultati sgradevoli (la texture appariva "stretchata" e allungata eccessivamente).
<br>
La texture dei waypoints (gli archi), che simula l'effetto del marmo, è applicata usando <code>gluQuadricTexture</code> passando <code>true</code> come secondo parametro per abilitare la generazione di coordinate texture. I waypoints verranno discussi in maniera più approfondita nella sezione a loro dedicata.
<div style="display: block;">
<figure style="text-align: center; display: inline-block;">
<img src="images/panchina.png" width="400" height="400">
<figcaption>Texturing della panchina</figcaption>
</figure>
<figure style="text-align: center; display: inline-block;">
<img src="images/waypoint.png" width="400" height="400">
<figcaption>Texturing del waypoint</figcaption>
</figure>
</div>
</p>
<h2>Illuminazione e materiali</h2>
<p>
Il modello di illuminazione usato dal gioco è il modello di Phong, caratterizzato da 3 componenti: illuminazione ambiente, componente riflessa diffusa e speculare. La componente ambientale è settata tramite il seguente codice:
<pre>
<code>
float ambientLight[] = {0.2, 0.2, 0.2, 1};
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
</code>
</pre>
Il parametro <code>GL_LIGHT_MODEL_LOCAL_VIEWER</code> settato a true permette di calcolare la riflessione speculare dall'origine del sistema di coordinate dell'osservatore (<a href="https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glLightModel.xml">fonte</a>).
Sono inoltre disponibile altre luci: una luce puntiforme settata tramite <code>glLightfv</code> e due luci spotlight, quella del faro della moto (che può essere abilitata e disabilitata tramite <code>F4</code>) e quella di un lampione di luce.
<br>
Le direzioni della luce del faro della moto e del lampione sono state specificate settando <code>GL_SPOT_DIRECTION</code> nella funzione <code>glLightfv</code>; inoltre sono stati specificati <code>GL_SPOT_CUTOFF</code> e <code>GL_SPOT_EXPONENT</code>. La luce del faro della moto viene attenuata tramite l'utilizzo di <code>GL_COSTANT_ATTENUATION</code> e <code>GL_LINEAR_ATTENUATION</code>, mentre la luce del lampione, per renderla più evidente, non viene attenuata.
</p>
<br>
<p>
Per quanto riguarda la statua, la pompa della benzina, la pistola della pompa della benzina, la carlinga della moto e i suoi cerchioni, sono stati settati i materiali, in modo da fare in modo che la luce si comporti in maniera diversa su queste componenti. Ciò è stato fatto specificando i parametri delle componenti ambientale, riflessa diffusa e speculare precedentemente menzionate. Qui sotto viene riportato come esempio la funzione <code>setupStatuaMaterial</code> usata per configurare il materiale della statua (bronzo) qualora l'environment mapping ad essa applicato venga disattivato:
<pre>
<code>
float mat[4] = {0.2125, 0.1275, 0.054, 1.0};
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat);
mat[0] = 0.714;
mat[1] = 0.4284;
mat[2] = 0.18144;
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat);
mat[0] = 0.393548;
mat[1] = 0.271906;
mat[2] = 0.166721;
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat);
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 0.2*128);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
</code>
</pre>
I valori per i materiali sono stati ottenuti dal sito <a href="http://devernay.free.fr/cours/opengl/materials.html">devernay</a>.
<figure style="text-align: center; display: block;">
<img src="images/statua_material.png" width="400" height="400">
<figcaption>Statua in bronzo senza environment mapping</figcaption>
</figure>
</p>
<h2>Waypoint</h2>
<p>
Anzichè usare le mesh, i waypoint sono stati realizzati usando <code>GLUquadricObj</code>, in modo tale da fare esercizi con le altre funzioni GLU. Nello specifico vengono disegnati due cilindri concentrici (tramite <code>gluCylinder</code>) e due dischi (tramite <code>gluDisk</code>) agli estremi dei cilindri, in modo tale da creare un arco con spessore. All'arco viene poi applicata la texture nel modo specificato nel paragrafo Textures. La geometria così definita viene poi collocata nello spazio in maniera tale da essere visibile solo per metà (per dare appunto l'idea di un arco), l'altra metà della "ciambella" è nascosta sotto il pavimento.
</p>
<h2>Ombre</h2>
<p>
Tutti gli oggetti, tranne i waypoints, producono ombre. Tali ombre sono prodotte prendendo ispirazione dall'algoritmo di Blinn (1988), ossia nella seguente maniera:
<ul>
<li>si costruisce la matrice di proiezione al piano;</li>
<li>si applica tale matrice alla geometria 3D di cui si vuole ottenere l'ombra;</li>
<li>si disegna l'oggetto piatto con colore ombra.</li>
</ul>
Nello specifico il codice fa uso della funzione <code><b>glShadowProjection</b></code>, definita in <code>utils.h</code> e <code>utils.cpp</code>, trovata nel tutorial <code>glut_OpenGL_ffp/opengl_advanced/shadow/schatten.cpp</code>.
<br>
L'ombra della moto, grazie all'abilitazione di <code>GL_STENCIL_TEST</code>, non viene disegnata sulla pozzanghera, in modo da far risaltare il riflesso (sono disponibili maggiori informazioni sul riflesso nella sezione dedicata alla pozzanghera).
<figure style="text-align: center; display: block;">
<img src="images/ombre.png" width="400" height="400">
<figcaption>Ombre della moto, della statua e della panchina</figcaption>
</figure>
</p>
<h2>Pozzanghera: trasparenza e riflesso</h2>
<p>
Per gestire trasparenza e riflessione nella pozzanghera sono stati abilitati il blending dei colori e lo stencil test. Nello specifico, per quanto riguarda lo stencil test, viene inizializzato lo stencil a 0. Poi viene disegnata la mesh della pozzanghera nello stencil con valore 1. Infine viene disegnata la seconda moto (la riflessione) solo dove lo stencil è pari a 1, ossia nella pozzanghera. Inoltre lo stencil test viene usato per disegnare l'ombra della moto solo dove non è presente la pozzanghera (ossia dove lo stencil ha valore diverso da 1), in modo tale da far risaltare il riflesso.
<br>
Grazie a <code>glEnable(GL_BLEND)</code> e a <code>glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)</code> è possibile fare sia il blending della pozzanghera con il riflesso sia il blending della pozzanghera con la texture del pavimento sottostante.
<div style="display: block;">
<figure style="text-align: center; display: inline-block;">
<img src="images/riflesso.png" width="400" height="400">
<figcaption>Riflesso della moto visualizzato dalla camera che osserva la moto di lato e dall'alto;<br> è possibile notare come l'ombra venga proiettata solo al di fuori della pozzanghera.</figcaption>
</figure>
<figure style="text-align: center; display: inline-block;">
<img src="images/trasparenza.png" width="400" height="400">
<figcaption>Riflesso della moto e trasparenza della pozzanghera, con pavimento visibile</figcaption>
</figure>
</div>
</p>
<p>
Spostando nel codice la pozzanghera nel punto <code>(0,0,0)</code>, usando la camera che permette di muovere la visuale con il mouse e zoomare e abilitando e disabilitando da codice lo stencil test, è possibile confrontare il rendering prodotto con stencil test abilitato con quello prodotto con stencil test disabilitato, come mostrato dalle figure sottostanti.
<div style="display: block;">
<figure style="text-align: center; display: inline-block;">
<img src="images/stencil_on.png" width="400" height="400">
<figcaption>Stencil test abilitato</figcaption>
</figure>
<figure style="text-align: center; display: inline-block;">
<img src="images/stencil_off2.png" width="400" height="400">
<figcaption>Stencil test disabilitato</figcaption>
</figure>
</div>
</p>
<h2>HUD: Heads-up display</h2>
<p>
L'HUD è composto da due gruppi di elementi: il testo contenente le informazioni sul gioco (FPS, tempo rimanente, punteggio) e la minimappa/radar. <br>
La funzione <code>HUD_RenderText</code> fa uso a sua volta della funzione <code>SDL_GL_DrawText</code> (appresa da un esempio del Professor Casciola, nello specifico nel filex <code>sdl2_OpenGL_ffp/SDL2prg2_gl/persp_cube_sdl_gl.c</code>), la quale si occupa di usare una <b>SDL_Surface</b> su cui viene scritto il testo e che viene successivamente interpretato come texture. <code>SDL_GL_DrawText</code> fa uso della libreria <b>SDL_TTF</b>, che mette a disposizione varie primitive per scrivere del testo su schermo, e delle funzioni <code>SDL_BlitSurface</code> e <code>SDL_CreateRGBSurface</code>, per creare delle <code>SDL_Surface</code> e copiare una surface su un'altra. Il font usato è <code>FreeSans.ttf</code>.
</p>
<p>
Per quanto riguarda la minimappa presente in basso a destra, è stato disegnato un cerchio e la sua cornice usando rispettivamente <code>GL_TRIANGLE_FAN</code> e <code>GL_LINE_LOOP</code>. Su questo cerchio sono stati successivamente disegnati gli indicatori della moto (in verde) e del waypoint (in rosso), facendo uso di <code>GL_POINTS</code>, previo settaggio della dimensione dei punti a 5 (tramite <code>glPointSize(5)</code>) e abilitazione dello smoothing dei punti (tramite <code>glEnable(GL_POINT_SMOOTH)</code>).
</p>
<h2>Telecamere</h2>
<p>
Come già detto precedentemente, premendo <code>F1</code> è possibile ciclare attraverso i vari tipi di camera messe a disposizione. Le camere a disposizione sono quelle del progetto Car più un'altra camera, denominata CAMERA_FOLLOW, che permette di seguire la moto da una visuale dall'alto e laterale, in modo da apprezzare inclinazione e rotazione delle ruote.
Tuttavia la camera che è possibile ruotare con il mouse non segue la moto, quindi è utile soltanto finché la moto resta all'interno della piramide di vista. Di seguito vengono mostrate le visuali delle camere.
<div style="display: block;">
<figure style="text-align: center; display: inline-block;">
<img src="images/camera_back.png" width="400" height="400">
<figcaption>Camera dal retro della moto</figcaption>
</figure>
<figure style="text-align: center; display: inline-block;">
<img src="images/camera_laterale.png" width="400" height="400">
<figcaption>Camera laterale</figcaption>
</figure>
</div>
<div style="display: block;">
<figure style="text-align: center; display: inline-block;">
<img src="images/camera_top.png" width="400" height="400">
<figcaption>Camera dall'alto</figcaption>
</figure>
<figure style="text-align: center; display: inline-block;">
<img src="images/camera_pilot.png" width="400" height="400">
<figcaption>Camera che mostra la visuale del pilota</figcaption>
</figure>
</div>
<div style="display: block;">
<figure style="text-align: center; display: inline-block;">
<img src="images/camera_mouse.png" width="400" height="400">
<figcaption>Camera che è possibile muovere col mouse</figcaption>
</figure>
<figure style="text-align: center; display: inline-block;">
<img src="images/camera_top_right.png" width="400" height="400">
<figcaption>Camera che segue la moto da una posizione in alto a destra</figcaption>
</figure>
</div>
</p>
<h2>Schermata di game over</h2>
<p>
È stata implementata una schermata di game over visualizzata quando scade il tempo a disposizione. Anche in questo caso si fa uso di <code>SDL_GL_DrawText</code> per visualizzare il testo, mentre il background è impostato con colore nero.
<figure style="text-align: center; display: block;">
<img src="images/game_over.png" width="400" height="400">
<figcaption>Schermata di game over</figcaption>
</figure>
</p>
<h2>Skybox</h2>
<p>
Lo skybox è costruito usando una sfera a cui viene applicata la texture del cielo tramite environment mapping (ossia usando <code>GL_SPHERE_MAP</code>). In <code>motorbike.cpp</code> la funzione <code>handleInvisibleWall(float *posx, float *posz)</code> si occupa di fissare un muro invisibile nella mappa, in modo da evitare che la moto vada oltre lo skybox.
</p>
<h2>Performance</h2>
<p>
Sulla piattaforma di sviluppo (Fedora 30 su Intel Core i5-5300U, Intel HD Graphics 5500) il gioco viene eseguito con una media di 39 FPS, con rari picchi di minimo a 21 FPS. Disattivando le ombre, si riescono a raggiungere i 60 FPS. Ne consegue che gran parte del peso computazionale è dovuto all'algoritmo di generazione delle ombre.
</p>
<h2>Conclusioni e sviluppi futuri</h2>
<p>
Durante la realizzazione del progetto è stato decisivo capire l'importanza dell'ordine delle trasformazioni geometriche. Inoltre è stata constatata l'importanza di un corretto uso di <code>glPushMatrix()</code> e <code>glPopMatrix()</code>, in modo tale da gestire bene lo stack di matrici.<br>Per quanto riguarda la panchina, l'applicazione della texture si è rivelata particolarmente difficile, poiché la generazione automatica delle coordinate texture dava risultati insoddisfacenti anche qualora fossero specificate i parametri <code>s</code> e <code>t</code>, da usare nella funzione di generazione delle coordinate. Per questo motivo, come già menzionato nell'apposita sezione di questa documentazione, è stato effettuato il texturing faccia per faccia.<br>L'uso dello stencil è stato impegnativo, ma le difficoltà sono state superate tramite lo studio delle slide e la lettura del codice di esempio <code>glut_OpenGL_ffp/opengl_advanced/reflectdino/reflectdino.c</code><br>Come sviluppi futuri potrebbero essere implementati la collision detection con gli oggetti e un particle engine per arricchire la scena con effetti particellari come il fuoco e il fumo.
</p>
</body>
</html>