Solution
Transcription
Solution
INF5071 — Infographie Automne 2016 Solution du devoir 1 Auteur : Alexandre Blondin Massé 1. Supposons qu’une application graphique utilise des images de dimensions 64 × 32 pixels2 pour représenter des tuiles en projection isométrique (dans le dessin ci-bas, un exemple d’image contenant une tuile est identifié par un rectangle bleu). (a) (15 points) Expliquez comment calculer la position du coin supérieur gauche d’une tuile 10 × 10 qu’on place dans une grille indexée comme l’illustre la grille ci-bas : (0, 0) 9 9 (352, 192) 8 8 7 7 6 6 5 5 4 4 3 3 2 2 1 1 0 0 (640, 320) Autrement dit, donnez le pseudocode d’une fonction fonction CoinSupGauche(i, j : indices) : point qui retourne la position que devrait occuper le coin supérieur gauche du rectangle contenant la tuile aux indices i et j. Par exemple, on s’attend à ce que l’appel CoinSupGauche(4, 2) retourne la valeur (352, 192) (voir le rectangle bleu dans l’image ci-haut). (b) (10 points) Dans la question ci-bas, nous avons supposé que toutes les tuiles se trouvaient à la même hauteur. Supposons que nous souhaitions également supporter la possibilité d’afficher des tuiles plus hautes que d’autres. En utilisant des notions d’algèbres linéaire et vectorielle, expliquez de quelle façon vous traiteriez cette situation et montrez en particulier que certaines tuiles à des hauteurs différentes pourraient apparaı̂tre exactement au même endroit. (c) (5 points boni ) Le concepteur principal d’un jeu appelé Super Hexagon est auteur d’un autre jeu qui exploite le fait que différentes tuiles à des hauteurs différentes apparaissent au même endroit. Quel est le nom de ce jeu et qui en est l’auteur ? 1/10 INF5071 — Infographie Automne 2016 Solution: (a) Reprenons l’image de l’énoncé et indiquons à l’aide de points bleus tous les coins que nous souhaitons atteindre (le rectangle bleu original a été laissé pour faciliter la visualisation) : (0, 0) 9 9 8 8 7 7 6 6 5 5 4 4 3 3 2 ~v1 0 ~u 2 1 0 (640, 320) Nous constatons donc que l’ensemble des points bleus peut être engendré à l’aide de deux vecteurs ~u et ~v (identifiés en rouge dans l’image). Plus précisément, les coordonnées des points bleus sont donnés par O + i~u + j~v , où 0 ≤ i, j ≤ 9 et où O est le point d’où originent les vecteurs ~u et ~v . Il ne reste qu’à calculer les composantes de ces deux vecteurs et les coordonnées du point O. Tout d’abord, on constate que O = (320 − 32, 320 − 32) = (288, 288). De plus, ~u = (32, −16) et ~v = (−32, −16) et donc O + i~u + j~v = (288, 288) + i(32, −16) + j(−32, −16) = (288 + 32i − 32j, 288 − 16i − 16j) En pseudocode, cela donne 1: fonction CoinSupGauche(i, j : indices) : point 2: retourner (288 + 32i − 32j, 288 − 16i − 16j) 3: fin fonction (b) Comme il a été mentionné dans la partie (a), les tuiles peuvent être engendrés par la base vectorielle formée des vecteurs ~u = (32, −16) et ~v = (−32, −16). Si on souhaitait introduire une notion de hauteurs, nous pourrions la représenter par un troisième vecteur w ~ qui devrait être orienté verticalement vers le haut, par exemple 2/10 INF5071 — Infographie Automne 2016 w ~ = (0, −32) ou w ~ = (0, −16) (il faut choisir ce qui correspond à un déplacement d’une unité vers le haut). En suivant la même logique qu’en (a), nous pourrions donc générer n’importe quelle tuile, peu importe la hauteur. Par contre, une limite de cette approche est que certaines tuiles distinctes apparaı̂traient exactement au même endroit. Par exemple, si on prend w ~ = (0, −32), alors les tuiles d’indices (i, j, k) = (0, 0, 1) et (i, j, k) = (1, 1, 0) seraient confondues. L’explication de ce phénomène est simple : il n’est pas possible de former un ensemble de trois vecteurs linéairement indépendants (ici, ~u, ~v et w) ~ dans un espace de dimension 2. C’est donc une limitation de la projection isométrique, qui est partiellement réglée lorsqu’on utilise une projection perspective ! (c) L’auteur du jeu Super Hexagon s’appelle Terry Cavanagh. Il a conçu un jeu sorti en 2013, appelé Naya’s Quest, dont le “game play” exploite justement cette observation. 2. (20 points) Supposez que les services suivants sont disponibles pour manipuler des maillages : • M ← MaillageVide() construit un maillage vide et le stocke dans la variable M . • M.AjouterSommet(p) ajoute un sommet dans le maillage M et dont les coordonnées sont données par le point p. • M.AjouterArete(p1 , p2 ) ajoute une arête entre les sommets p1 et p2 dans le maillage M . Si un ou les deux sommets n’existent pas dans la structure, une erreur est signalée. • M.AjouterFace(P ) ajoute une face dont les sommets sont donnés par la liste P . Ces sommets doivent être énumérés dans l’ordre antihoraire lorsqu’on regarde vers l’extérieur de la face. De plus, s’il n’y a pas d’arêtes entre deux sommets consécutifs, une erreur est signalée. Donnez le pseudocode d’une fonction dont l’en-tête est fonction Grille(m, n : entiers positifs, d, e : réels) : maillage et qui retourne un maillage modélisant une grille de m carrés par n carrés, dont l’épaisseur est donnée par e et la distance entre les carrés de la grille est donnée par d. Autrement dit, si vous implémentiez votre fonction dans un script Blender et que vous utilisiez les paramètres m = 3, n = 4, e = 0.1 et d = 1.0, alors vous vous attendriez à obtenir un maillage similaire à celui illustré dans l’image ci-bas. Attention ! La topologie de votre maillage doit être bien définie. Autrement dit, aucune erreur ne doit être signalée (vous devez donc d’abord ajouter les sommets, ensuite les arêtes, puis finalement les faces) et vous devez vous assurer que c’est le côté visible des faces (c’est-à-dire vers l’extérieur) qui est décrit en sens antihoraire. 3/10 INF5071 — Infographie Automne 2016 Note : Vous pouvez utiliser la notation [p1 , p2 , p3 , p4 ] pour dénoter une liste qui contient les quatre points p1 , p2 , p3 et p4 (autrement dit, vous pouvez utiliser des crochets pour dénoter des listes). Aussi, bien que ce ne soit pas demandé dans la question, n’hésitez pas à vérifier votre stratégie en l’implémentant comme un script Blender ! Voici une vue de face d’une portion de la grille avec les dimensions correspondantes : e d 4/10 INF5071 — Infographie Automne 2016 Solution: Cette question n’est pas facile si on souhaite écrire un code compact. Ici, je présente une solution la plus compacte possible. Pour cela, j’utilise une fonction auxiliaire qui permet de construire un prisme rectangulaire selon les paramètres suivants : • (h, k, `) est le centre du prisme. • (a, b, c) est un triplet représentant les “rayons” du prisme par rapport aux axes x, y et z respectivement. Autrement dit, les 8 points du prisme sont données par (h ± a/2, k ± b/2, ` ± c/2). • sommets est un booléen qui indique si on doit ajouter ou non les sommets (pour éviter de les ajouter s’ils y sont déjà). • arêtes est un triplet de booléens qui indique les directions des arêtes à ajouter. L’indice 0 correspond aux arêtes parallèles à l’axe x, l’indice 1 à celles parallèles à l’axe y et l’indice 2 à celles parallèles à l’axe z. • faces est un 6-tuplet de booléens indiquant quelles faces doivent être ajoutées (pour éviter d’ajouter les faces intérieures). Les indices 0 et 1 correspondent aux directions + et − de l’axe x, les indices 2 et 3 correspondent aux directions + et − de l’axe y et les indices 4 et 5 correspondent aux directions + et − de l’axe z. Voici donc la fonction en question : fonction AjouterPrisme(M, h, k, `, a, b, c, sommets, arêtes, faces) // On ajoute les sommets si sommets alors M.AjouterSommet((h − a, k − b, ` − c)) M.AjouterSommet((h − a, k − b, ` + c)) M.AjouterSommet((h − a, k + b, ` − c)) M.AjouterSommet((h − a, k + b, ` + c)) M.AjouterSommet((h − a, k − b, ` − c)) M.AjouterSommet((h − a, k − b, ` + c)) M.AjouterSommet((h − a, k + b, ` − c)) M.AjouterSommet((h − a, k + b, ` + c)) fin si // Ensuite les arêtes si arêtes[0] alors M.AjouterAretes((h − a, k − b, ` − c), (h + a, k − b, ` − c)) M.AjouterAretes((h − a, k − b, ` + c), (h + a, k − b, ` + c)) M.AjouterAretes((h − a, k + b, ` − c), (h + a, k + b, ` − c)) M.AjouterAretes((h − a, k + b, ` + c), (h + a, k + b, ` + c)) fin si si arêtes[1] alors M.AjouterAretes((h − a, k − b, ` − c), (h − a, k + b, ` − c)) M.AjouterAretes((h − a, k − b, ` + c), (h − a, k + b, ` + c)) M.AjouterAretes((h + a, k − b, ` − c), (h + a, k + b, ` − c)) M.AjouterAretes((h + a, k − b, ` + c), (h + a, k + b, ` + c)) fin si si arêtes[2] alors M.AjouterAretes((h − a, k − b, ` − c), (h − a, k − b, ` + c)) 5/10 INF5071 — Infographie Automne 2016 M.AjouterAretes((h − a, k + b, ` − c), (h − a, k + b, ` + c)) M.AjouterAretes((h + a, k − b, ` − c), (h + a, k − b, ` + c)) M.AjouterAretes((h + a, k + b, ` − c), (h + a, k + b, ` + c)) fin si // Finalement, les faces, décrites en sens antihoraire si faces[0] alors M.AjouterFace([(h − a, k − b, ` − c), (h − a, k + b, ` − c), (h − a, k + b, ` + c), (h − a, k + b, ` + c)]) fin si si faces[1] alors M.AjouterFace([(h + a, k − b, ` − c), (h + a, k − b, ` + c), (h + a, k + b, ` + c), (h + a, k + b, ` − c)]) fin si si faces[2] alors M.AjouterFace([(h − a, k − b, ` − c), (h + a, k − b, ` − c), (h + a, k − b, ` + c), (h − a, k − b, ` + c)]) fin si si faces[3] alors M.AjouterFace([(h − a, k + b, ` − c), (h − a, k + b, ` + c), (h + a, k + b, ` + c), (h + a, k + b, ` − c)]) fin si si faces[4] alors M.AjouterFace([(h − a, k − b, ` − c), (h − a, k + b, ` − c), (h + a, k + b, ` − c), (h + a, k − b, ` − c)]) fin si si faces[5] alors M.AjouterFace([(h − a, k − b, ` + c), (h + a, k − b, ` + c), (h + a, k + b, ` + c), (h − a, k + b, ` + c)]) fin si fin fonction Nous pouvons maintenant construire le maillage complet. Notons que si nous insérons d’abord les petits cubes (aux intersections), alors il n’y a plus besoin d’ajouter de sommets, et il ne manque que les arêtes entre les cubes. On obtient donc : fonction Grille(m, n : entiers positifs, d, e : réels) M ← MaillageVide() // On ajoute d’abord les petits cubes pour i ← 0, 1, . . . , m faire pour j ← 0, 1, . . . , n faire arêtes ← (vrai, vrai, vrai) faces ← (faux, faux, faux, faux, faux, faux) faces[0] ← j = 0 faces[1] ← j = m faces[4] ← i = n faces[5] ← i = 0 AjouterPrisme(M, j(d + e), 0, i(d + e), e/2, e/2, e/2, vrai, arêtes, faces) fin pour fin pour // Puis les prismes entre les petits cubes pour i ← 0, 1, . . . , m faire pour j ← 0, 1, . . . , n faire si i 6= m alors arêtes ← (vrai, faux, faux) faces ← (faux, faux, vrai, vrai, vrai, vrai) AjouterPrisme(M, j(d + e), 0, (i + 1/2)(d + e), e/2, e/2, (d + e)/2, faux, arêtes, faces) fin si si j 6= n alors arêtes ← (faux, faux, vrai) faces ← (vrai, vrai, vrai, vrai, faux, faux) AjouterPrisme(M, (j + 1/2)(d + e), 0, i(d + e), (d + e)/2, e/2, e/2, faux, arêtes, faces) fin si fin pour fin pour retourner M fin fonction 3. (20 points) Considérez la texture suivante T décrite par une image de dimensions 512 × 6/10 INF5071 — Infographie Automne 2016 768 pixels2 (0, 0) 1 2 3 4 5 6 (768, 512) Supposez que la couleur du pixel de la i-ième ligne et j-ième colonne est donné par T [i][j] et supposez que vous avez un cube C dont le centre est donné par C.centre et la demi-longueur d’un côté est C.rayon. Donnez le pseudocode d’une fonction dont l’en-tête est fonction CouleurPixel(p : point, C : cube, T : texture) : couleur qui indique pour chaque point p qui se trouve sur le cube C, la couleur du pixel qu’il devrait avoir. Il y a plusieurs solutions possibles à ce problème, mais vous devez respecter les contraintes suivantes : • Chacun des 6 chiffres doit apparaı̂tre sur une face; • Deux faces opposées doivent avoir une somme de 7. Remarque : vous n’avez pas à valider si p est bien un point qui se trouve sur le cube, vous pouvez supposer que c’est toujours le cas. Solution: Soit (h, k, `) le centre du cube C et r son rayon. Notons que les huit sommets du cube sont donnés par les points (h, k, `) + r(±1, ±1, ±1). Une autre façon de voir le cube C est de remarquer qu’il s’agit de la région comprise à l’intérieur des six plans d’équations cartésiennes x = h − r, x = h + r, y = k − r, y = k + r, z =`−r et z = ` + r. Pour savoir si un point se trouve sur une face donnée, il suffit donc de vérifier s’il satisfait une de ces équations. Évidemment, certains points (les huit sommets et les 7/10 INF5071 — Infographie Automne 2016 points le long des arêtes du cube) se trouvent sur plusieurs faces. Dans ce cas, on le traite comme s’il se trouvait sur n’importe laquelle des faces (ça n’a pas vraiment d’importance). Bref, nous pouvons maintenant écrire le pseudocode d’une fonction qui indique le numéro de la face sur laquelle se trouve n’importe quel point donné (pour simplifier le pseudocode plus tard, nous numérotons les faces de 0 à 5 plutôt que de 1 à 6) : 1: fonction NuméroFace(C : cube, p : point) : entier 2: (h, k, `) ← C.centre 3: r ← C.rayon 4: si p.x = h − r alors retourner 0 5: sinon si p.x = h + r alors retourner 5 6: sinon si p.y = k − r alors retourner 1 7: sinon si p.y = k + r alors retourner 4 8: sinon si p.z = ` − r alors retourner 2 9: sinon si p.z = ` + r alors retourner 3 10: sinon Erreur : Le point p n’est pas sur C. 11: fin si 12: fin fonction Maintenant, remarquons que la texture T est divisée en exactement 6 carrés de dimensions 256 × 256. Lorsque nous prenons un point quelconque p sur une face de C, nous devons lui faire correspondre un point p0 dans T qui se trouve entre 0 et 255 (puis ensuite, on décale le résultat pour atteindre le bon carré parmi les 6 disponibles). Nous devons donc, pour cela, calculer la distance relative du point p par rapport à un coin de la face sur laquelle il se trouve. La fonction suivante permet d’établir cette correspondance : 1: fonction PositionRelative(C : cube, p : point) : couple de réels 2: (h, k, `) ← C.centre 3: r ← C.rayon 4: si p.x = h − r ou p.x = h + r alors 5: retourner ((p.y − k)/2r, (p.z − `)/2r) 6: sinon si p.y = k − r ou p.y = k + r alors 7: retourner ((p.x − h)/2r, (p.z − `)/2r) 8: sinon si p.z = ` − r ou p.z = ` + r alors 9: retourner ((p.x − h)/2r, (p.y − k)/2r) 10: sinon 11: Erreur : Le point p n’est pas sur C. 12: fin si 13: fin fonction Il ne reste qu’à combiner les deux fonctions en une seule : 1: fonction CouleurPixel(p : point, C : cube, T : texture) : couleur 2: f ← NuméroFace(C, p) 8/10 INF5071 — Infographie 3: 4: 5: 6: 7: Automne 2016 t ← PositionRelative(C, p) i ← t.y + 256 · bf /3c j ← t.x + 256 · (f mod 3) retourner T [i][j] fin fonction 4. (15 points) Deux objets décrivent une trajectoire rectiligne dans l’espace 3D selon les fonctions vectorielles suivantes : r~1 (t) = (t + 5, 2t − 2, 3t − 1) r~2 (t) = (3t − 3, 2t − 2, 15 − t), pour t ≥ 0, où t représente le temps en heures. Est-ce que les trajectoires des objets s’intersectent ? Est-ce que les objets entreront en collision ? Si oui, à quel moment ? Justifiez. Solution: Vérifions d’abord si les objets entrent en collision. Si c’est le cas, alors on aurait une valeur de t telle que r~1 (t) = r~2 (t), c’est-à-dire (t + 5, 2t − 2, 3t − 1) = (3t − 3, 2t − 2, 15 − t). Comme les premières composantes sont égales, on aurait donc t + 5 = 3t − 3, c’està-dire 8 = 2t, ce qui entraı̂ne t = 4. Ainsi, s’il y a une collision, c’est nécessairement quand t = 4. Voyons quels sont les positions des deux objets en t = 4. On obtient r~1 (4) = (9, 6, 11) r~2 (4) = (9, 6, 11), ce qui confirme qu’il y a bien collision après 4 secondes et que les trajectoires se croisent. 5. (20 points) Considérez un cube dont les 8 sommets sont (±1, ±1, 1 ± 1) et qui repose sur le plan z = 0. Une source de lumière se trouve en position (4, 4, 2) et éclaire tout point de l’espace avec la même intensité, peu importe la distance (s’il existe un d’obstacle entre le point et la source de lumière, alors la lumière ne parvient pas au point). Décrivez la forme de l’ombre induite par le cube sur le plan et calculez son aire. Remarque : N’hésitez pas à joindre un schéma de la situation pour mieux expliquer votre réponse. Solution: Voici une illustration de la scène rendue par Blender : 9/10 INF5071 — Infographie Automne 2016 Même en éloignant la caméra, on s’aperçoit que l’ombre est infinie et donc que son aire est infinie. Décrivons maintenant sa forme. Le plus facile est de prendre une vue de haut de la même scène. Voici ce qu’on obtient : Noter que la face supérieure du cube apparaı̂t en noir puisqu’elle n’est pas éclairée par la lumière. La lumière est donc projetée sur le point (4, 4, 0). Il ne reste qu’à décrire le contour de la région ombrée. Tout d’abord, il y a le segment qui relie les points (−1, 1, 0) et (−1, −1, 0) ainsi que le segment qui relie (−1, −1, 0) et (1, −1, 0). Finalement, il y a les droites de vecteurs directeurs v~1 = (−1, 1, 0) − (4, 4, 0) = (−5, −3, 0) et v~2 = (1, −1, 0) − (4, 4, 0) = (−3, −5, 0) passant par le point (4, 4, 0). Plus précisément, ce sont les droites d’équations (x, y, z) = (4, 4, 0) + t(−5, −3, 0) et (x, y, z) = (4, 4, 0) + t(−3, −5, 0). 10/10