M2 - Images

TP2 - openGL et buffers


Ecrire une application openGL - presentation de gk::App

Il suffit de deriver de la classe gk::App pour profiter de la creation du contexte openGL, de la creation de la fenetre et de la gestion d'evenements :

class TP : public gk::App
{
public:
    TP( ) : gk::App()
    {
        // specifie le type de contexte openGL a creer :
        gk::AppSettings settings;
        settings.setGLVersion(3,3);      // version 3.3
        settings.setGLCoreProfile();     // core profile
        settings.setGLDebugContext();    // version debug pour obtenir les messages d'erreur en cas de probleme
       
        // cree le contexte et une fenetre
        if(createWindow(512, 512, settings) < 0)
            closeWindow();       
    }
}

et de séparer la creation des objets opengl (cf gk::App::init( )) de leur utilisation (cf. gk::App::draw( )).

La methode draw( ) est appelee regulierement pour dessiner une nouvelle image, 60 fois par seconde, par defaut.

La methode update( ) est appellee juste avant draw( ) et permet d'animer quelquechose avant de le dessiner.

tutorial3.cpp est un code de depart illustrant une application tres simple.


Partie 1 - buffers + vertex array object

exercice 1 : le debut

Chargez un objet, bigguy.obj, par exemple, et affichez-le.


Utilisez le shader par defaut : dFnormal.glsl

        // compilation simplifiee d'un shader program
        gk::programPath("shaders");
        m_program= gk::createProgram("dFnormal.glsl");
        if(m_program == gk::GLProgram::null())
            return -1;

Le shader est pre-configure pour lire la position des sommets sur l'attribut numero 0...

remarques :
    la camera par defaut est placee en (0, 0, 0) et regarde -z (0, 0, -1).
    l'objet est centre en (0, 0, 0), conclusion ? pour le voir, il faut soit le reculer, soit bouger la camera...


exercice 2 : transformations et evenements

Déplacez la camera ou l'objet avec les fleches du clavier, par exemple.
Modifiez la transformation Model ou View avec gk::Translate( ) ou gk::RotateY( ).

remarque :
    utilisez gk::App::key( const SDL_Keycode ) pour connaitre l'etat d'une touche du clavier.
    le keycode de chaque touche se trouve dans la doc de sdl2 : SDLK_LEFT, SDLK_RIGHT, SDLK_UP, SDLK_DOWN...


Partie 2 - gestion de scene

exercice 1 : plein de bigguys...

Chargez plusieurs objets, placez-les dans la scene et affichez-les.

La solution la plus directe consiste a creer un vertex buffer, un index buffer et un vertex array object par objet.
Conservez egalement une transformation Model pour placer et orienter l'objet dans la scene.


vous pouvez utiliser les modeles : bigguy.tar.gz

exercice 2 : n'afficher que ce qui est visible

Verifiez que la boite englobante d'un objet est, au moins, partiellement visible, avant d'afficher l'objet.


indications :
    la boite englobante d'un objet est disponible dans gk::Mesh::bbox

    dans quel espace est-il le plus simple de faire le test de visibilite ?
        dans le repere du monde ?
        ou dans le repere projectif de la camera ?


    le principe du test de visibilite est toujours le meme :
    si les 8 sommets de la boite englobante de l'objet sont tous à l'exterieur d'un plan du volume visible, l'objet ne peut pas etre visible.


construire les 8 sommets d'une boite englobante :

gk::Point vertex[8];

for(int i= 0; i < 8; i++)
{
    vertex[i]= bbox.pMin;
    if(i & 1) vertex[i].x= bbox.pMax.x;
    if(i & 2) vertex[i].y= bbox.pMax.y;
    if(i & 4) vertex[i].z= bbox.pMax.z;
}

rappel : gk::Transform, gk::Point et gk::HPoint
    gk::Transform T;
    gk::Point q= T(p);        // transforme le point p et renvoie le point 3d q

    gk::HPoint h; T(p, h);    // transforme le point p et renvoie le point homogene h.



Partie 3 : affichage efficace et gestion de donnees

La premiere solution utilisee consiste en general a creer un vertex buffer et un index buffer par chaque objet. Cette organisation des donnees a une consequence assez directe sur l'affichage d'un ensemble d'objets :

pour chaque objet
    activer le vertex array de l'objet
    {  // ce qui provoque :
        activer le vertex buffer de l'objet
        activer l'index buffer de l'objet
    }

    activer le shader program
    parametrer le shader program
    draw( )

Cettte solution est tres souple mais egalement la plus lente : pour chaque draw, l'ensemble des buffers est change ainsi que le shader program.

Comment organiser les buffers pour eviter de changer de buffers/vao a chaque fois ? et obtenir une solution de ce type :

pour chaque objet
    parametrer le shader program
    draw( )

Quel gain pouvez-vous esperer ? un affichage plus rapide pour le gpu, ou une utilisation cpu moins importante ?


Partie 4 : shaders et matieres

Placez une source lumiere dans la scene. Ecrivez un shader program permettant de calculer la lumiere reflechie par une matiere diffuse.
Quelles sont les informations necessaires pour realiser le calcul dans le fragment shader ? Comment les transmettre aux shaders ?

Memes questions pour une matiere plus reflechissante.


Partie 5 : ombres

Le principe des shadow maps est tres simple :
    1. dessiner les objets de la scene du point de vue de la source de lumiere, et conserver le zbuffer (la distance entre la source et le point visible et eclaire),
    2. dessiner les objets de la scene du point de vue de la camera et recalculer leur position par rapport a la source de lumiere pour verifier s'ils sont eclaires ou pas.

Memes questions que dans la partie precedente, quelles informations sont necessaires pour realiser chaque etape, comment les transmettre aux shaders ?
Combien de shader programs sont necessaires ?

Comment "conserver" le zbuffer de l'etape 1 ?
Comment verifier qu'un point de la scene est visible par la source de lumiere en utilisant le zbuffer de l'etape 1 ? Quelles transformations doivent etre connues ?


D'ou viennent les differents defauts de cette methode ?