L3IF Images
année 2005-2006
http://www710.univ-lyon1.fr/~jciehl/

TD3 - Création d'un monde


Partie 1 : le terrain

    Il est relativement naturel de représenter un terrain par une fonction 2D et de la stocker sous forme d'une image en niveau de gris. Le blanc représentera l'altitude maximale et le noir, l'altitude minimale.

height field

    Ce "champ d'altitude" doit ensuite être affiché, sous forme de triangles, ou de quadrangles, comme d'habitude. Voici un exemple :


visualisation


    exercice :
    Générez une grille régulière et affichez-la. Les sommets nécessaires à l'affichage seront stockés dans un tableau.
    Réutilisez le TD1 pour vous déplacer au dessus de la grille. Il serait également assez pratique de "plaquer" la caméra sur le sol.

    exercice :
    Générez une fonction d'altitude et stockez les sommets dans un tableau équivalent au premier exercice.

    exercice :
    Utilisez le programme de génération de terrain. Les explications correspondantes sont disponibles sur gamedev.
    Les valeurs stockées dans l'image sont comprises entre 0 et 255, il sera peut être nécessaire de modifer leur amplitude.

    Il existe de nombreuses méthodes de génération, plus ou moins réalistes, vous pouvez parcourir "Real Time Procedural Terrain Generation" pour vous faire une idée et l'archive bibliographique associée.


    exercice :
    Réalisez un affichage efficace du terrain (cf. TD 2) et testez-le avec des dimensions plus importantes (128x128, 256x256, 512x512, 1024x1024, ...).
    Une méthode simple consiste à superposer un quad-tree au dessus de la grille de sommets et à ne dessiner que les cellules potentiellement visibles.
    Il n'est pas nécessaire de parcourir la totalité de la hiérarchie : un bloc 16x16 représente peu de géométrie à tracer, pour openGL. Il n'est pas non plus nécessaire de construire la représentation mémoire du quad-tree, un simple algorithme récursif fournit des résultats très corrects.


    exercice :
    Observez l'exemple ci-dessous, il est possible de supprimer énormément de triangles sans dégrader la perception du relief.


visualisation

    Il existe de nombreuses méthodes. Le point de départ consiste à sous échantillonner régulièrement la grille de sommets en fonction de la distance de l'utilisateur. L'idée est de considérer que le terrain est composé de blocs 16x16 (par exemple) et de choisir une précision correcte pour les afficher : 16x16, 8x8, 4x4, 2x2. Proche de l'horizon un bloc 16x16 pourra être dessiné avec 2 triangles (les 4 extrémités du bloc 16x16) sans introduire d'erreurs importantes.

    Si vous observez plus attentivement l'exemple ci-dessus, vous verrez que les blocs de précisions différentes sont connectés par une fine bande de triangles, pourquoi ??



Partie 2 : l'animation des personnages

    L'idée est d'interpoler les données de l'animation en fonction de la fréquence d'affichage. Les données représentent fréquemment une animation mesurée à 24 images par secondes. Il ne reste plus qu'à générer les images intermédiaires.

Par exemple : le modèle bigguy (nvidia SDK)
image 0
image 1
t= 8, Image 8 de l'animation
t= 10, Image 10 de l'animation

    Une des nombreuses techniques d'animation consiste à déformer le maillage de l'objet dans le temps. Le sommet 0 se déplace donc en fonction du temps et représente toujours le même point de l'objet (ce qui n'est pas nécessairement le cas avec d'autres méthodes d'animations).

    Pour construire le maillage à l'instant t, il ne reste plus qu'à interpoler la position du sommet entre les instants t0 et t1.

    exercice :
    Charger les maillages à deux instants assez éloignés (bigguy_00.obj et bigguy_10.obj, par exemple) et écrivez une fonction d'affichage capable d'interpoler la position de chaque sommet du maillage en fonction du temps.

    Des remarques sur la qualité de l'interpolation ? Quels peuvent être les problèmes ? Comment les régler ?


    Vous aurez sans doute besoin de glutGet(GLUT_ELAPSED_TIME) pour vous repérer dans le temps (cette fonction renvoie le temps en millisecondes depuis le début de l'éxécution du programme, ou depuis sa première utilisation).


    exercice :
    Charger tous les maillages et reconstruisez l'animation complète.



Annexe : la structure MODEL

    Pour interpoler la position des sommets du maillage, il va falloir parcourir la structure qui les stocke : MODEL, définit dans model.h.

    Un MODEL est composé de plusieurs tableaux contenant chacun des informations différentes : sommets, faces, normales, couleurs, coordonnées de textures, et attributs de faces.
 
typedef struct
{
    VERTEX *v;            // sommets (coordonnees)
    int v_n;

    FACE *faces;          // faces
    int faces_n;
  
    ATTR *attr;           // liste d'attributs des faces
    int attr_n;
  
    VERTEXT *tex;         // textures (coordonnees) associees aux sommets
    int tex_n;

    VERTEXN *norm;        // normales aux sommets
    int norm_n;
} MODEL;

    Les types VERTEX, VERTEXT et VERTEXN représentent respectivement un sommet, des coordonnées de textures, et une normale. Ce sont des vecteurs de réels à 4, 2, et 3 coordonnées.

    Pour accéder à tous les sommets de MODEL, il suffit de parcourir le tableau v (de taille logique v_n) :

void f(MODEL *model)
{
    int i;

    for(i= 0; i < model->v_n; i++)
    {
       printf("sommet %d: %f %f %f\n", i,
            model->v[i][0],
            model->v[i][1],
            model->v[i][2]);
    }
}


    Chaque face est décrite par une structure FACE (cf. le tableau faces de MODEL, taille logique faces_n) :

typedef struct
{
    int attr;         // indice du premier attribut
    int n;            // nombre d'attributs
} FACE;

    Les n sommets d'une maille sont décrits par 3 attributs : leur position géométrique (gl_vertex), leur coordonnées de texture (gl_tex) et leur normale (gl_norm). Ces informations sont stockées dans le tableau ATTR de MODEL. Seuls des indices sont présents dans le tableau ATTR, les valeurs sont en fait stockées dans les tableaux principaux de MODEL (v, tex, et norm).

    Exemple, si a est un indice d'attribut (le premier de la face, par exemple), l'accès aux autres données est très simple :

        int v= model->attr[a][gl_vertex];        // indice des coordonnees du sommet
        int t= model->attr[a][gl_tex];           // indice des coordonnees de texture
        int n= model->attr[a][gl_norm];          // indice des coordonnees de la normale


    Exemple : accéder à tous les sommets d'une face :

void f(MODEL *model, int f)
{
    FACE *face;
    int a;
    int v, t, n;
    int i;

    face= &model->faces[f];
    printf("face %d :\n", f);
    for(i= 0; i < face->n; i++)
    {
        a= face->attr + i;

        v= model->attr[a][gl_vertex];
        printf("  sommet %d : %f %f %f, ", i,
            model->v[v][0],
model->v[v][1], model->v[v][2]);

    /* et pour les autres attributs :
        t=
model->attr[a][gl_tex];
        printf("texture %f %f, ",
            model->tex[t][0], model->tex[t][1]);

        n=
model->attr[a][gl_norm];
        printf("normale %f %f %f",
            model->norm[n][0], model->norm[n][1], model->norm[n][2]);
     */
    }
}