Code Vonc

Hologramme 3D

CommentairesComments

Réalisation d'un hologramme 3d basé sur le principe de réflexion d'image sur une pyramide transparente.

Projet réalisé avec Cédric Bitsch.

Rendu 3D en OpenGL.





Présentation
Modèle 3D

Guêpe modélisée dans Cinema 4D.

Export 3D en Python

Pour exporter notre guêpe nous avons utilisé un script Python dans Cinema 4D afin d’obtenir les coordonnées des points et des polygones sous forme de tableaux en langage C.

Fonctionnalités

Chaque partie du corps de la guêpe est indépendante des autres, ce qui permet d’animer l’insecte.

Nous avons utilisé une méthode hiérarchique pour lier les éléments entre eux (méthode push / pop).


    glPushMatrix();
    dessineObjet(&obj_Guepe);



    dessineObjet(&obj_Corps); // A un enfant

    glPushMatrix();

    dessineObjet(&obj_Tete); // A un enfant

    glPushMatrix();
    dessineObjet(&obj_AntenneG); // A un suivant
    glPopMatrix();

    glPushMatrix();
    dessineObjet(&obj_AntenneD); // Suivant
    glPopMatrix();

    glPopMatrix();

    glPushMatrix();
    dessineObjet(&obj_Cul); // Parent, Suivant
    glPopMatrix();

    glPushMatrix();
    dessineObjet(&obj_PatteD1); // Suivant
    glPopMatrix();

    glPushMatrix();
    dessineObjet(&obj_PatteD2); // Suivant
    glPopMatrix();

    glPushMatrix();
    dessineObjet(&obj_PatteD3); // Suivant
    glPopMatrix();

    glPushMatrix();
    dessineObjet(&obj_PatteG1); // Suivant
    glPopMatrix();

    glPushMatrix();
    dessineObjet(&obj_PatteG2); // Suivant
    glPopMatrix();

    glPushMatrix();
    dessineObjet(&obj_PatteG3); // Suivant
    glPopMatrix();

    glPushMatrix();
    dessineObjet(&obj_AileD1); // Suivant
    glPopMatrix();

    glPushMatrix();
    dessineObjet(&obj_AileD2); // Suivant
    glPopMatrix();

    glPushMatrix();
    dessineObjet(&obj_AileG1); // Suivant
    glPopMatrix();

    glPushMatrix();
    dessineObjet(&obj_AileG2); // Suivant
    glPopMatrix();


    glPopMatrix();
Rotation des membres

Nous avons créé une rotation automatique des antennes et des pattes afin de créer un effet de réalisme.

Nous avons utilisé 2 rotations dans 2 axes différents, chacune animée d’un mouvement sinusoïdal décalé afin d’obtenir un mouvement circulaire.

Mouvements au sol

Au sol notre guêpe adapte l’allure du mouvement de ses pattes suivant la vitesse de déplacement. Nous avons un système de rotation identique à celui des antennes.


        // Animation pattes
        {
            float vitesse = sqrt(_obj->vit.x * _obj->vit.x + _obj->vit.z * _obj->vit.z);
            vitesse *= 50;

            vitesse *= 1. - limite(_obj->posAbs.y, 0.0, 1.0);;

            if (_obj->posAbs.y < 0.1) {

                // Patte avant gauche

                obj_PatteD1.rotcos += 0.1 * vitesse;
                limCos(&obj_PatteD1.rotcos);
                obj_PatteD1.angle = cos(obj_PatteD1.rotcos) * 20;
                obj_PatteD1.rotation = {0., 1., 0.};

                obj_PatteD1.rotcos2 += 0.1 * vitesse;
                limCos(&obj_PatteD1.rotcos2);
                obj_PatteD1.angle2 = cos(obj_PatteD1.rotcos2 + PIs2) * 20;
                obj_PatteD1.rotation2 = {0., 0., 1.};

                // Patte avant droite

                obj_PatteG1.rotcos += -0.1 * vitesse;
                limCos(&obj_PatteG1.rotcos);
                obj_PatteG1.angle = cos(obj_PatteG1.rotcos) * 20;
                obj_PatteG1.rotation = {0., 1., 0.};

                obj_PatteG1.rotcos2 += -0.1 * vitesse;
                limCos(&obj_PatteG1.rotcos2);
                obj_PatteG1.angle2 = cos(obj_PatteG1.rotcos2 + PIs2) * 20;
                obj_PatteG1.rotation2 = {0., 0., 1.};

                // Patte milieu gauche

                obj_PatteD2.rotcos += 0.1 * vitesse;
                limCos(&obj_PatteD2.rotcos);
                obj_PatteD2.angle = cos(obj_PatteD2.rotcos + PIs2) * 20;
                obj_PatteD2.rotation = {0., 1., 0.};

                obj_PatteD2.rotcos2 += 0.1 * vitesse;
                limCos(&obj_PatteD2.rotcos2);
                obj_PatteD2.angle2 = cos(obj_PatteD2.rotcos2) * 20;
                obj_PatteD2.rotation2 = {0., 0.5, 1.};

                // Patte milieu droite

                obj_PatteG2.rotcos += -0.1 * vitesse;
                limCos(&obj_PatteG2.rotcos);
                obj_PatteG2.angle = cos(obj_PatteG2.rotcos + 1.7) * 20;
                obj_PatteG2.rotation = {0., 1., 0.};

                obj_PatteG2.rotcos2 += 0.1 * vitesse;
                limCos(&obj_PatteG2.rotcos2);
                obj_PatteG2.angle2 = cos(obj_PatteG2.rotcos2 + PIs2) * 20;
                obj_PatteG2.rotation2 = {0., 0., 1.};

                // Patte arrère gauche

                obj_PatteD3.rotcos += 0.1 * vitesse;
                limCos(&obj_PatteD3.rotcos);
                obj_PatteD3.angle = cos(obj_PatteD3.rotcos) * 20;
                obj_PatteD3.rotation = {0., 1., 0.};

                obj_PatteD3.rotcos2 += 0.1 * vitesse;
                limCos(&obj_PatteD3.rotcos2);
                obj_PatteD3.angle2 = cos(obj_PatteD3.rotcos2 + PIs2) * 20;
                obj_PatteD3.rotation2 = {1., 0., 0.};

                // Patte arrère droite

                obj_PatteG3.rotcos += -0.1 * vitesse;
                limCos(&obj_PatteG3.rotcos);
                obj_PatteG3.angle = cos(obj_PatteG3.rotcos) * 20;
                obj_PatteG3.rotation = {0., 1., 0.};

                obj_PatteG3.rotcos2 += 0.1 * vitesse;
                limCos(&obj_PatteG3.rotcos2);
                obj_PatteG3.angle2 = cos(obj_PatteG3.rotcos2 + PIs2) * 20;
                obj_PatteG3.rotation2 = {1., 0., 0.};
            }
        }
Animation en vol

Lors de son envol nous avons un mouvement de repli des pattes. Nous avons également un mouvement des ailes basé sur le même principe d’accélération que pour le mouvement au sol.

Lumières

Nous avons créé 2 lumières :
- La première est une lumière ambiante (couleur froide).
- La seconde est un projecteur (couleur chaude) qui met en relief la guêpe.

Caméra

Nous avons 4 caméras :
- La première pour la vue pleine écran qui tourne autour de la guêpe.
- Les 3 autres sont réservé pour afficher l’hologramme.


Vecteur calculeCam(int ax, int ay) {
    Vecteur _c = {1,1,1};
    _c.x = 2 * sin(ax * 3.14 / 180.) * cos(ay * 3.14 / 180.) + cameraPos.x;
    _c.y = 2 * sin(ay * 3.14 / 180.) + cameraPos.y;
    _c.z = 2 * cos(ax * 3.14 / 180.) * cos(ay * 3.14 / 180.) + cameraPos.z;
    return _c;
}

Vecteur calculeU(Vecteur camPos, Vecteur ciblePos, int idCam) {

    Vecteur _v1 = vecteurMoins(camPos, ciblePos);
    Vecteur _v2 = vecteurMoins(camPos, ciblePos); _v2.y = 0;
    Vecteur _u;

    if (idCam == 2) { // Milieu
        Vecteur _w = produitVectoriel(_v1, _v2);
        _u = produitVectoriel(_v1, _w);
        _u = vecteurProduit(_u, -1);

        if (camPos.y - ciblePos.y < 0) _u = vecteurProduit(_u, -1);
    }

    else if (idCam == 1) { // Gauche
        _u = produitVectoriel(_v1, _v2);
        _u = vecteurProduit(_u, -1);

        if (camPos.y - ciblePos.y < 0) _u = vecteurProduit(_u, -1);
    }

    else if (idCam == 3) { // Droite
        _u = produitVectoriel(_v1, _v2);

        if (camPos.y - ciblePos.y < 0) _u = vecteurProduit(_u, -1);
    }

    else if (idCam == 4) { // Gauche
        Vecteur _w = produitVectoriel(_v1, _v2);
        _u = produitVectoriel(_v1, _w);

        if (camPos.y - ciblePos.y < 0) _u = vecteurProduit(_u, -1);
    }

    return _u;
}


[...]


                gluPerspective(60., 1920. / 1080. , 0.01, 40.);
                Vecteur camPos = calculeCam(anglex, angley);
                Vecteur _u = calculeU(camPos, obj_Guepe.posAbs, 4);

                gluLookAt(camPos.x, camPos.y, camPos.z, // Position de la caméra
                        obj_Guepe.posAbs.x, obj_Guepe.posAbs.y, obj_Guepe.posAbs.z, // Position où la cam regarde
                        _u.x, _u.y, _u.z); // Vecteur haut
Textures

Nous avons trois textures :
- Une pour la guêpe.
- Une pour le sol.
- Une pour les arbres.

Nous avons utilisé une fonction qui lit les fichiers bmp pour importer les textures dans GLUT.


GLuint LoadTexture(const char * filename, int width, int height) {
    GLuint texture;
    unsigned char * data;
    FILE* file;

    file = fopen(filename, "rb");

    if (file == NULL) return 0;

    data = (unsigned char *)malloc(width * height * 3);
    //int size = fseek(file,);
    fread(data, width * height * 3, 1, file);
    fclose(file);

    for (int i = 0; i < width * height ; ++i) {
        int index = i*3;
        unsigned char B,R;
        B = data[index];
        R = data[index+2];

        data[index] = R;
        data[index+2] = B;
    }


    glGenTextures( 1, &texture );
    glBindTexture( GL_TEXTURE_2D, texture );
    glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_MODULATE );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST );


    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_REPEAT );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_REPEAT );
    gluBuild2DMipmaps( GL_TEXTURE_2D, 3, width, height,GL_RGB, GL_UNSIGNED_BYTE, data );

    free( data );

    return texture;
}
Normales

Nous avons calculé les normales des sommets manuellement.
- La normale d’un sommet = moyenne des normales des polygones autours du sommet.
- La normale d’un polygone = produit vectoriel de 2 côtés adjacents.

Les normales permettent d’avoir un rendu plus lisse de l’objet face à la lumière.


void calculeNormaleObjet(Objet* _obj) {
    Vecteur (*_normales)[4] = _obj->normales;
    Vecteur *_p = _obj->points;
    int (*_f)[4] = _obj->polys;
    int nbPolys = _obj->nbpolys;
    int nbPts = _obj->nbpts;
    int i;
    Vecteur tabNorPts[nbPts];

    for (i = 0 ; i < nbPts ; i++) {
        tabNorPts[i] = {0.0, 0.0, 0.0};
    }

    for (i = 0 ; i < nbPolys ; i++) {
        Vecteur normalePoly = calculeNormalePol(_p[_f[i][0]], _p[_f[i][1]], _p[_f[i][2]], _p[_f[i][3]]);
        tabNorPts[_f[i][0]] = vecteurPlus(tabNorPts[_f[i][0]], normalePoly);
        tabNorPts[_f[i][1]] = vecteurPlus(tabNorPts[_f[i][1]], normalePoly);
        tabNorPts[_f[i][2]] = vecteurPlus(tabNorPts[_f[i][2]], normalePoly);
        tabNorPts[_f[i][3]] = vecteurPlus(tabNorPts[_f[i][3]], normalePoly);
    }

    for (i = 0 ; i < nbPts ; i++) {
        tabNorPts[i] = vecteurNormaliser(tabNorPts[i]);
    }

    for (i = 0 ; i < nbPolys ; i++) {
        _normales[i][0] = tabNorPts[_f[i][0]];
        _normales[i][1] = tabNorPts[_f[i][1]];
        _normales[i][2] = tabNorPts[_f[i][2]];
        _normales[i][3] = tabNorPts[_f[i][3]];
    }

    free(tabNorPts);
}
Commandes

Flèches directionnelles : Avancer, reculer, pivoter à droite, pivoter à gauche.

Espace ou + : Grand saut

C : Petit saut

H ou * : Vue hologramme / vue 3d classique

/ : Attaque dard

Q : Quitter

Difficultés rencontrées
Push et pop

Nous avons rencontré une difficulté en voulant créer automatiquement la génération des push et des pop.

Nous voulions gérer la mise en place des différentes parties de notre guêpe dans une fonction.

Cette fonction aurait été très bénéfique car nous aurions pu intégrer dans notre projet n’importe quel « robot » sans avoir à gérer manuellement les push et les pop.

Nous avions commencé a créer un système de hiérarchie entre les différents éléments de la guêpe grâce à un système de liste chainé.


obj_Guepe.enfant = &obj_Corps;

obj_Corps.parent = &obj_Guepe;
obj_Corps.enfant = &obj_Tete;

obj_Tete.parent = &obj_Corps;
obj_Tete.enfant = &obj_AntenneG;
obj_Tete.suivant = &obj_PatteD1;

obj_AntenneG.parent = &obj_Tete;
obj_AntenneG.suivant = &obj_AntenneD;

obj_AntenneD.parent = &obj_Tete;
obj_AntenneD.precedent = &obj_AntenneG;

obj_PatteD1.parent = &obj_Corps;
obj_PatteD1.precedent = &obj_Tete;
obj_PatteD1.suivant = &obj_PatteD2;

obj_PatteD2.parent = &obj_Corps;
obj_PatteD2.precedent = &obj_PatteD1;
obj_PatteD2.suivant = &obj_PatteD3;

obj_PatteD3.parent = &obj_Corps;
obj_PatteD3.precedent = &obj_PatteD2;
obj_PatteD3.suivant = &obj_Cul;

obj_Cul.parent = &obj_Corps;
obj_Cul.precedent = &obj_PatteD3;
Disposition des écrans pour la création de l’hologramme

Une des parties les plus délicates du projet a été la mise en place des 3 vues pour générer l’hologramme.

Nous avons rencontré des difficultés sur l’aménagement des vues. Au final nous avons réussi à figer les vues et à obtenir un très bel hologramme.

Support en bois

Nous avons réalisé un socle en bois avec une ouverture à l’arrière afin d’insérer l’ordinateur portable.
Nous avons créé un logo représentant notre guêpe.
Le support est totalement rétractable pour le rangement et le déplacement.

Polyméthacrylate de méthyle

Nous avons opté pour un hologramme avec 3 bouts de plexiglas.
Nous avons 1 pièce centrale et 2 pièces pour les cotés.