M2 - Images
TP1 - transformations et pipeline
Partie 1 : affichage d'une primitive.
L'opération fondamentale réalisée par un pipeline de rendu est le
dessin d'une primitive : déterminer quels pixels permettent de
remplir une forme "simple" dans l'image résultat.
Cette opération est découpée en plusieurs étapes :
- transformation des sommets de la primitive dans un des repères
liés à la camera,
- identifier les pixels qui font partie de la forme,
- colorier les pixels.
exercice 1 : transformations
Les objets et leurs sommets sont décrits dans un
repère local, puis ces objets sont placés et orientés dans le repère
de la scène. Un observateur / camera est également placé et orienté
dans le repère de la scène. Une transformation de projection est
aussi associée à l'observateur. Ces 3 transformations sont
classiquement représentées par des matrices homogenes 4x4 :
Model (transformation du repère local au repère
de la scène),
View (transformation du repère de la scène au
repère camera)
Projection (transformation du repère camera au
repère projectif homogène de la camera).
Plus une autre qui représente les dimensions de l'image résultat :
Viewport (transformation du repère projectif
homogène vers le repère de l'image).
Il est possible de composer ces matrices afin de construire une
seule matrice de transformation permettant de passer directement du
repère local de l'objet au repère de l'image. Ecrivez cette
relation.
Par construction de la transformation de projection, les points
visibles par l'observateur se retrouvent dans le repere projectif
(après transformation) à l'interieur du cube unitaire [-1 1] sur les
3 axes.
Si l'on choisit une matrice identité comme projection, ou peut on
placer des points qui seront visibles / associés à un pixel de
l'image ?
prise en main de gKit :
installez gKit, les informations sont sur la page précédente.
en cas de problèmes, voici une archive : export_gkit2light.zip premake4
est disponible dans le répertoire premake...
Vous pouvez générer la documentation avec doxygen. Elle sera
consultable dans html/index.html (ou en
ligne). Les classes de bases sont documentées dans la partie
module de la documentation générée.
gKit utilise la classe Transform pour représenter et manipuler les
transformations, (cf mat.h).
Les classes Point et Vector (cf vec.h)
permettent de représenter un point et un vecteur. Les fonctions de
construction des transformations standards sont aussi disponibles :
Translate(), Rotate(), Perspective().
La transformation d'un point s'écrit directement :
#include "mat.h"
#include "vec.h"
Transform T;
// identité
Transform T= Identity();
// identité
aussi
Transform T= RotateX(30);
// rotation de 30° autour de l'axe X
Transform
T= Translate( 0, 0, 50 );
//
translation sur l'axe Z
Point p;
Point q= T(p);
// renvoie le point reel
transforme
La composition de transformations est aussi disponible :
Transform A, B;
Transform C= A * B;
La transformation inverse est également calculée :
Transform M= C.inverse();
Transform M= Inverse(C);
Pour obtenir le point homogène après la transformation d'un point p
:
Point p= Point(1, 0, 1);
vec4 h= p;
vec4 ph= M(h);
// renvoie le point homogene
apres la transformation
gKit utilise la classe Image pour représenter un ensemble de pixels
et fournit également des fonctions permettant d'enregistrer l'image
dans un fichier.
#include "image.h"
#include "image_io.h"
Image image(largeur, hauteur);
write_image(image, "resultat.bmp");
Les operateurs () permettent de lire et de modifier la couleur du
pixel de coordonnées x, y.
Image image(1024, 512);
image(x, y)= Color(1, 0, 0);
Color pixel= image(x, y);
La classe de base Color représente une couleur par comme un vecteur
à 4 composantes : rouge, vert, bleu, transparence (alpha).
exemple:
#include
"vec.h" // type
vecteur, point, couleur, etc. de "base"
#include "image.h"
// classe image
#include
"image_io.h" // entrees / sorties sur des images
int main( )
{
Image image(512, 512)
;
// cree une image de 512x512 pixels
// parcourir tous
les pixels de l'image
for(int y= 0; y <
image.height(); y++)
// chaque ligne
for(int x= 0; x < image.width(); x++)
// chaque colonne
image(
x, y)= Color(1, 0, 0,
1);
// colorie chaque
pixel en rouge
opaque
// enregistre le
resultat
write_image(image,
"out.bmp");
return 0;
}
exercice 1 : version Reyes, subdivision.
Une solution relativement souple applique le principe algorithmique
"diviser pour règner" au problème. Il est immédiat de dessiner un
objet plus petit qu'un pixel, dans les autres cas, il faut le
découper.
Proposez une solution utilisant cette idée.
indication : pour subdiviser un triangle en 4,
une solution consiste à calculer le point milieu de chaque arête
et à construire les 4 sous triangles.
Cette solution peut-elle fonctionner lorsque certains sommets sont
en dehors de la zone visible ? Modifiez votre programme pour inclure
cette fonctionnalité.
indication : il serait judicieux d'arreter la
subdivision lorsque un sous triangle est entierement non visible /
à l'exterieur de la zone visible.
Ecrivez également une fonction permettant de savoir si un triangle
est visible pour la camera. C'est le même test, un triangle ne peut
pas etre visible par la camera si on peut trouver un plan séparant
le triangle et une face du volume visible par la camera.
indication : ou se trouvent les 8 sommets qui
définissent le volume visible par la camera ? dans quel repère
ont-ils des coordonnées "simples" ? comment connaitre les
coordonnées des sommets dans le repère du monde ? de la camera ?
etc.
indication : si les 3 sommets du triangle se trouvent du
mauvais cote d'une seule face du volume visible, le triangle ne
peut pas etre visible.
Comment arreter la subdivision ? comment déterminer qu'un triangle
est trop petit pour etre dessiné (il "passe" entre les pixels) ?
question bonus : et avec une sphère ? on fait comment ?
exercice 2 : version fragmentation / rasterization 2D.
Reprennez les indications du cours.
Soit 3 sommets p0, p1, et p2 dans le plan image.
Comment déterminer que le pixel (x, y) est "à gauche" ou "à droite"
d'une arete du triangle ?
indication : l'aire signée d'un
triangle orienté dans le plan se calcule directement : cf Modern
triangles / section Modern triangles
première solution :
testez tous les pixels du plan image.
solution efficace ?
Proposez une solution qui teste des blocs de pixels
dans un premier temps, puis qui teste tous les pixels des blocs
couvrant (partiellement ou entièrement) le triangle.
Comment déterminer efficacement qu'un bloc se trouve entièrement à
l'extérieur du triangle (sans tester tous les pixels) ?
indication : ses 4 sommets sont à l'exterieur
(du mauvais coté) de la même arête du triangle
Lorsqu'un bloc contient, au moins partiellement, le triangle, testez
tous les pixels du bloc (les blocs 1 à 6 dans l'exemple).
Cette "optimisation" est-elle interressante dans tous les cas ?
Comment choisir la taille des blocs ?
question bonus : et en 3D ?
on peut écrire exactement le même algorithme en 3D, en
utilisant des tetrahedres et leur volume signé, au lieu d'utiliser
l'aire signée de triangles : cf 3D
triple product / wikipedia
une solution complète est présentée dans cet article :
"3D
Rasterization: A Bridge between Rasterization and Ray Casting"
T. Davidovic, T. Engelhardt, I. Georgiev
2012
pour les curieux : et en 4D / 3D homogène ?
une solution élégante est présentée section 2
dans :
"Incremental
and
Hierarchical
Hilbert
Order
Edge Equation Polygon Rasterization"
M.D. McCool, C. Wales, K. Moule,
2001
Partie 2 : plusieurs primitives
Lorsque la scène est composée de plusieurs objets, il est (très)
fréquent que plusieurs triangles recouvrent le même pixel. En
général, on souhaite donner au pixel la couleur de l'objet (opaque)
le plus proche de la camera.
Comment déterminer la distance associée à un fragment issu d'un
triangle ? Comment conserver le plus proche de la camera ?
rappel : la somme des aires des sous-triangles pp0p1, pp1p2, pp2p0
correspond à l'aire du triangle p0p1p2.
Comment réaliser ce test dans la version fragmentation et dans la
version Reyes ?
question bonus : en plus de reconstruire la distance à la
camera, on peut appliquer la même démarche à d'autres valeurs
associées aux sommets des triangles.
Partie bonus :
complétez le tuto
et répondez aux questions posées
dans la solution directe.