Deus-Ex:HR - Les passes de rendus analysées


14 mars 2015

Un post très intéressant décrivant dans le détail les différentes passes de rendu utilisées par le dernier Deux-Ex. Il n'y a rien de compliqué mais l'implémentation, le détail et l'optimisation sont remarquables.

WebGL 2


1 mai 2011

Premier programme en OpenGL sous X


22 octobre 2007

Premier code en OpenGL sous X

Il faut bien démarrer quelque part et c'est ici ! C'est sûr que c'est pas terrible, beaucoup de code pour un résultat bien consternant, mais je jette mes bases. Je dois reprendre ce qui était naturel il y a quelques années...

#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <GL/glx.h>
#include <GL/gl.h>

static  int snglBuf[] = {GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, GLX_DEPTH_SIZE, 12, None};
static  int dblBuf[] =  {GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, GLX_DEPTH_SIZE, 12, GLX_DOUBLEBUFFER, None};

Display*    Dpy;
Window  Win;
Bool    DoubleBuffer = True;

GLfloat XAngle = 42.0, YAngle = 82.0, ZAngle = 112.0;

int FatalError(int errorCode, char* str)
{
    printf("%s\n", str);
    return errorCode;
}

void    redraw()
{
static  Bool    DisplayListInited = False;
    if (DisplayListInited)      {
        glCallList(1);
    }   else    {
        glNewList(1, GL_COMPILE_AND_EXECUTE);
        //
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glBegin(GL_QUADS);
        glColor3f(0.0, 0.7, 0.1);       //Green
        glVertex3f(-1.0, 1.0, 1.0);
        glVertex3f(1.0, 1.0, 1.0);
        glVertex3f(1.0, -1.0, 1.0);
        glVertex3f(-1.0, -1.0, 1.0);
        glColor3f(0.9, 1.0, 0.0);       //Yellow
        glVertex3f(-1.0, 1.0, -1.0);
        glVertex3f(1.0, 1.0, -1.0);
        glVertex3f(1.0, -1.0, -1.0);
        glVertex3f(-1.0, -1.0, -1.0);
        glColor3f(0.2, 0.2, 1.0);       //Blue
        glVertex3f(-1.0, 1.0, 1.0);
        glVertex3f(1.0, 1.0, 1.0);
        glVertex3f(1.0, 1.0, -1.0);
        glVertex3f(-1.0, 1.0, -1.0);
        glColor3f(0.7, 0.0, 0.1);       //Red
        glVertex3f(-1.0, -1.0, 1.0);
        glVertex3f(1.0, -1.0, 1.0);
        glVertex3f(1.0, -1.0, -1.0);
        glVertex3f(-1.0, -1.0, -1.0);
        glEnd();
        //
        glEndList();
        DisplayListInited = True;
    }
    //On affiche réellement quelque chose...
    if (DoubleBuffer)       glXSwapBuffers(Dpy, Win);
    else                    glFlush();
}

int main(int argc, char** argv)
{
    XVisualInfo*    vi;
    Colormap    CMap;
    XSetWindowAttributes    swa;
    GLXContext  cx;
    XEvent  Event;
    Bool    NeedRedraw = False, RecalcModelView = True;
    int Dummy;
    //
    Dpy = XOpenDisplay(NULL);
    if (!Dpy)       return FatalError(1, "Couldn't open display...");
    if (!glXQueryExtension(Dpy, &Dummy, &Dummy))        return FatalError(2, "X n'a pas d'extension OpenGL.");
    //
    vi = glXChooseVisual(Dpy, DefaultScreen(Dpy), dblBuf);
    if (!vi)        {
        vi = glXChooseVisual(Dpy, DefaultScreen(Dpy), snglBuf);
        if (!vi)        return FatalError(3, "Pas de visuel RGB avec buffer de profondeur.");
        DoubleBuffer = False;
    }
    printf("DoubleBuffer : %d\n", DoubleBuffer);
    if (vi->class != TrueColor)     return FatalError(4, "J'ai besoin d'un visuel en true color !");
    //
    cx = glXCreateContext(Dpy, vi, None, True);
    if (!cx)        return FatalError(5, "Impossible de créer le contexte de rendu.");
    //
    CMap = XCreateColormap(Dpy, RootWindow(Dpy, vi->screen), vi->visual, AllocNone);
    swa.colormap = CMap;
    swa.border_pixel = 0;
    swa.event_mask = ExposureMask | ButtonPressMask | StructureNotifyMask;
    Win = XCreateWindow(Dpy, RootWindow(Dpy, vi->screen), 0, 0, 300, 300, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel|CWColormap|CWEventMask, &swa);
    XSetStandardProperties(Dpy, Win, "glxsimple", "glxsimple", None, argv, argc, NULL);
    //
    glXMakeCurrent(Dpy, Win, cx);
    XMapWindow(Dpy, Win);
    //
    glEnable(GL_DEPTH_TEST);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum(-1.0, 1.0, -1.0, 1.0, 1.0, 10.0);
    //
    while (True)        {
        do {
            XNextEvent(Dpy, &Event);
            switch (Event.type)     {
                case    ButtonPress:
                    RecalcModelView = True;
                    switch(Event.xbutton.button)        {
                        case    1:  XAngle += 10.0;     break;
                        case    2:  YAngle += 10.0;     break;
                        case    3:  ZAngle += 10.0;     break;
                    }
                    break;
                case    ConfigureNotify:
                    glViewport(0, 0, Event.xconfigure.width, Event.xconfigure.height);
                case    Expose:
                    NeedRedraw = True;
                    break;
            }
        } while(XPending(Dpy));
        //
        if (RecalcModelView)        {
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
            glTranslatef(0.0, 0.0, -3.0);
            glRotatef(XAngle, 0.1, 0.0, 0.0);
            glRotatef(YAngle, 0.0, 0.1, 0.0);
            glRotatef(ZAngle, 0.0, 0.0, 1.0);
            //
            RecalcModelView = False;
            NeedRedraw = True;
        }
        if (NeedRedraw)     {
            redraw();
            NeedRedraw = False;
        }
    }
    //
    return 0;
}

Pour compiler ça, si on suppose que le code est sauvé dans un fichier simplegl.c, on lance :

gcc -o simplegl -lGL -lX11 simplegl.c

Le résulat du gloubi-boulga ci-dessus ? Et bien voilà, bow you sucker !

Nouveau framework et moteur 3D


26 août 2007

Schéma d'un moteur 3D simple à appréhender, fonctionnel (objets procéduraux - metaball, terrains -, effets type post-prod, rendus hiérarchiques - personnages -, GUI 3D, films en tant que textures, particules. Il faut rassembler toutes les idées qui surgissent.

Plusieurs Scene3D gérées par un moteur de gestion d'objets (faut-il plusieurs moteur 3D ?). Chaque objets à une interface de rendu qui prépare ses données spécifiquement pour un moteur de rendu, géré par Renderer appelé par le moteur. Un objet 3D est rattaché à une scène et un viewport est rattaché à un moteur 3D. Une scène peut être rattachée à plusieurs viewport. Un objet 3D contient sa description 3D qu'il peut communiquer sous certaines formes à son renderer via son interface de rendu. S'il y a n objets il y a approximativement log(n) interfaces de rendus présentes en run-time. Une interface de rendu peut être : objet classique (géo+topo+texture), objet procédural, moteur de particule etc. Des sous renderer pourront gérer les films au sein de textures. La hiérarchie est gérée par le moteur haut-niveau, la position et l'orientation d'un objet sont contenues dans l'objet lui-même.

Pour organiser un peu tout ce bordel, un tagage automatique semble être la bonne solution. �a impose un contenu riche mais ça permet un classement automatique.
A faire de toute urgence donc.
Il me faut adapter de toute urgence mon parser XML sous Linux, j'ai besoin de faire des profiles au cas où des applications custom seraient nécessaires.

J'ai rassemblé ci-dessous des notes C++ représentant les axiomes de bases implémentées par le framework pour toutes les utilisations auxquelles je le destine.

class   BaseObject
{
    private:
            BaseObject();       //On ne peut pas faire de new Engine() par exemple
            ~BaseObject();      //Seul une factory peut effacer réellement un objet, sinon il est collecté lors d'un remove
            friend  class   Factory();

    public:
            //Fonctions génériques de (dé)sérialisations et d'import/export XML
            HCODE   toXML(XMLElement* dest);
            HCODE   toXML(String& dest);
            HCODE   fromXML(const XMLElement* obj);         //Initialise l'objet courant depuis une description XML
            HCODE   fromXML(const String& obj);             //Initialise l'objet courant depuis une description XML
            HCODE   serialize(Flow& output);
            HCODE   unserialize(Flow& input);
            //Fonctions communiquant avec d'autres objets
            HCODE   send(ObjectId dest, Message& msg)
            HCODE   getNextMessage(Message& msg);
            HCODE   flushMessage();
            //Thread management de l'objet
            //Gestion des propriétés de l'objet : ses données / champs, ses méthodes / actions
            HCODE   Get(const String& fieldName, Field& field);         //Récupère la valeur d'un champ de l'objet courant
            HCODE   Set(const String& fieldName, const Field& field);   //Fixe la valeur d'un champ de l'objet courant
            String  compName = type de l'objet
            //Références maintenues par l'objet : des références
            delete();           //Efface l'objet et les éventuels liens qu'il avait avec d'autres
            //.............
            //Ses champs
            Array<Field>    Fields;
            ObjectId        ObjectSchema;       //Référence du schéma (la structure) de l'objet
            uint            Id;                 //L'Id unique de l'objet courant
};

Objets utilitaires : Field, Array, String, HashTable, Flow (pour les flux de lecture ou d'écriture, sur disque, HTTP ou autres.).

//Tous les objets sont représentés par leur Id, unique, et gérées par un singleton : Objects
class   ObjectId
{
    operator->(const String& funcName);
};

//Une classe qui crée des composants
class   Objects
{
    HCODE       RegisterType()      //Enregistre un type connu et ses champs
    HCODE       FromType            //IDs de tous les objets d'un certain type
    HCODE       Select              //Requête complète (plus complexe mais adapté à des utilisations plus pointues que FromType)
    ObjectId    FromId()            //Renvoie l'ObjectId d'un objet donné

    ObjectId    createComponent(const String& compName)
    {
        BaseObject*     NewObject = new BaseObject();
        NewObject->ObjectSchema = Type[compName][\"schema\"];
    }
};

//Une fonction globale pour créer des composants
ObjectId    CreateComponent(const String& compName)
{
    ObjectId    Fact = Objects->getFactory();
    if (!Fact)      return LastError();
    return Fact->createComponent(compName);
};

Classe de base dont devrait hériter tous les composants

Selection d'objets

Il faut une méthode pour, avec une zone rectangulaire de sélection, sélectionner le plus rapidement possible les objets, 2D pour commencer, dont la bounding-box est interceptée. Je pense à deux range trees (ou des priority tree) pour le stockage des bbox (AABB) - un arbre pour chacune des dimensions considérées, et à deux hash-tables pour le parcours des deux arbres et l'instersection des 4 interrogations suivantes, si S est l'ensemble des objets de la scène :

L1 = { o de S tel que (o.bbox.x1 || o.bbox.x2) >= selectArea.y1 && !(o.bbox.x1 > selectArea.y2) }
L2 = L1 &#8745; { o de S tel que (o.bbox.x1 || o.bbox.x2) <= selectArea.y2 && !(o.bbox.x2 < selectArea.y1) }
L3 = L2 &#8745; { o de S tel que (o.bbox.y1 || o.bbox.y2) >= selectArea.y1 && !(o.bbox.y1 > selectArea.y2) }
L4 = L3 &#8745; { o de S tel que (o.bbox.y1 || o.bbox.y2) >= selectArea.y2 && !(o.bbox.y2 > selectArea.y1) }
Accueil