Projection 3D

Transformation

Remplissage des faces

Eclairage

Textures

Z-Buffering



Programmation 3D



Chapitre V
Les textures




Il est bien beau de dessiner des objets avec une couleur pleine mais il serai bien de pouvoir y ajouter un dessin (une texture).

Nous avons vue dans les chapitres précédant comment modéliser des objets en 3 dimension, mais aussi comment alléger le remplissage graphique avec la méthode de la normale aux surfaces, ainsi que l'algorithme du peintre.

Nous pouvons donc considéré que nous avons délimité un triangle a dessiner.

Découpage 2D et projection linéaire en 3D :

Texture a appliquer

Triangle a projeter

Découpage de la texture

Projection Bi-linaire

Ci vous avez suivis le cour jusque là, le decoupage de la texture en 2D ne seras alors qu'une simple formalité.
Il sufit de rajouter pour chaque sommet les cordonnées en 2D de sa position sur la texture.

typedef struct str_triangle
{
        int a,b,c;            /* Numéro de point */
        _2D PointTexture[3];  /* Coordonnée des point sur la texture */
}TRIANGLE;

Et maintenant la projection linéaire. Lorsque nous avons déterminé la projection des sommets dans le plan 2D de l'écran, on utiliser la fonction pour dessiner les polygones en y ajoutant quelque modification.

#define MIN(a,b)        ( a < b ?  a :  b )
#define MAX(a,b)        ( a > b ?  a :  b )
#define ABS(a)          ( a < 0 ? -a :  a )
#define Swap(a,b)       {int temp; temp=a; a=b; b=temp; }

int MinY,MaxY;

static struct str_scan
{
/* Coordonnée de la projection vertical */
        int gauche,droite;
/* Coordonnée relative de la texture a gauche */
        float PGXtexture,PGYtexture;
/* Coordonnée relative de la texture a droite */
        float PDXtexture,PDYtexture;
}scan[800];

/*
Projection du triangle en ligne horizontal avec,
(x1,y1) et (x2,y2) les coordonnées du coté a traiter,
(rx1,ry1) et (rx2,ry2) leur coordonnées plan sur la texture.
*/
void ScanLine(int x1,int y1,int x2,int y2,int rx1,int ry1,int rx2,int ry2)
{
        double pente,PosX;
        int PosY;
        double prtx,PosRTX;
        double prty,PosRTY;

/* On force y1<= y2 */
        if(y1>y2)
        {
                Swap(x1,x2);
                Swap(y1,y2);
                Swap(rx1,rx2);
                Swap(ry1,ry2);
        }

/* On teste si le triangle apparaît à l'écran */
        if(y2<0 || y1>ResY)
                return;

/*
        Ceci est un cas à traiter a part car plus bas
        on divise par la différence de y1 et y2
*/
        if(y1==y2)
        {
                if(x1>x2)
                {
                        scan[y1].droite = x1;
                        scan[y1].PDXtexture=rx1;
                        scan[y1].PDYtexture=ry1;
                        scan[y1].gauche = x2;
                        scan[y1].PGXtexture=rx2;
                        scan[y1].PGYtexture=ry2;
                }
                else
                {
                        scan[y1].gauche = x1;
                        scan[y1].PGXtexture=rx1;
                        scan[y1].PGYtexture=ry1;
                        scan[y1].droite = x2;
                        scan[y1].PDXtexture=rx2;
                        scan[y1].PDYtexture=ry2;
                }
                return;
        }

/* On calcule la pente réel de notre droite */
        pente = (float)(x1-x2) / (float)(y1-y2);
/* Puis la pente relative a la texture de notre droite */
        prtx = (float)(rx1-rx2) / (float)(y1-y2);
        prty = (float)(ry1-ry2) / (float)(y1-y2);

        PosX=x1;
        PosY=y1;
        PosRTX=rx1;
        PosRTY=ry1;

/* On rabat y2 afin d'éviter un dépassement d'espace mémoire */
        y2=MIN(y2,ResY);

/* Puis on rabat PosY afin d'éviter le même problèmes */
        while(PosY<0)
        {
                PosX+=pente;
                PosY++;
                PosRTX+=prtx;
                PosRTY+=prty;
        }

/* On sauvegarde la position la plus haute et la position la plus bas */
        MinY=MIN(PosY,MinY);
        MaxY=MAX(y2,MaxY);


/* On projette linéairement la droite */
        while(PosY<=y2)
        {
                if(scan[PosY].gauche>PosX)
                {
                        scan[PosY].gauche=PosX;
                        scan[PosY].PGXtexture=PosRTX;
                        scan[PosY].PGYtexture=PosRTY;
                }
                if(scan[PosY].droite<PosX)
                {
                        scan[PosY].droite=PosX;
                        scan[PosY].PDXtexture=PosRTX;
                        scan[PosY].PDYtexture=PosRTY;
                }
                PosX+=pente;
                PosY++;
                PosRTX+=prtx;
                PosRTY+=prty;
        }
}

/*
Texture représente l'image a appliquer, mais si je n'ai pas
déclaré le type IMAGE, c'est par-ce qu'il dépend directement
du mode de stockage des textures que vous aurais choisi.
Vous pourrez facilement vous aider avec la librairie SDL.
*/
void DrawPoly(_2D Point[],_2D PointTexture,IMAGE Texture)
{
        int i;
        COULEUR dc={0,0,0};

/* On initialise les variables */
        MinY=ResY;
        MaxY=0;
        for(i=0;i<=ResY;i++)
        {
                scan[i].droite=0; scan[i].gauche=799;
        }


/* Premiere projection linéaire */
        ScanLine(Point[0].x,Point[0].y,
                 Point[1].x,Point[1].y,
                 PointTexture[0].x,PointTexture[0].y);
                 PointTexture[1].x,PointTexture[1].y);
        ScanLine(Point[1].x,Point[1].y,
                 Point[2].x,Point[2].y,
                 PointTexture[1].x,PointTexture[1].y);
                 PointTexture[2].x,PointTexture[2].y);
        ScanLine(Point[2].x,Point[2].y,
                 Point[0].x,Point[0].y,
                 PointTexture[2].x,PointTexture[2].y);
                 PointTexture[0].x,PointTexture[0].y);

        if(MinY!=ResY)
                for(i=MinY;i<=MaxY;i++)
                        DrawLinear2(i,Texture);
}

Il ne nous reste plus cas écrire la fonction DrawLinear2() et le tour est jouer.
Mais celle-ci dépend directement du mode graphique, j'ai donc décidé d'écrire un pseudo algorithme pour expliquer la démarche a suivre.

void DrawLinear2(int ligne,IMAGE Texture)
{
        int X;
        double PosTX,PenteX;
        double PosTY,PenteY;

        PenteX=(double)(scan[ligne].PDXtexture-scan[ligne].PGXtexture)/
               (double)(scan[ligne].droite-scan[ligne].gauche);
        PenteY=(double)(scan[ligne].PDYtexture-scan[ligne].PGYtexture)/
               (double)(scan[ligne].droite-scan[ligne].gauche);
        
        PosTX=scan[ligne].PGXtexture;
        PosTY=scan[ligne].PGXtexture;

        X=scan[ligne].gauche;

        if(scan[ligne].droite>ResX)
                scan[ligne].droite=ResX;

        while(X<0)
        {
                X++;
                PosTX+=PenteX;
                PosTY+=PenteY;
        }

        while(X<=scan[ligne].droite)
        {
On dessine le point de coordonnées (PosTX,PosTY) de la texture, au coordonnées (X,ligne) de l'écran.
Cette méthode n'est pas la meilleur mais elle gagne en rapidité.
Toute fois, avec des méthode plus complexe, on obtient pas toujours mieux.
                X++;
                PosTX+=PenteX;
                PosTY+=PenteY;
        }
}

Pour dessiner il existe plusieurs méthodes dont 3 grandes :
La méthode du pixel, qui consiste a prendre la partie entière de PosX et PosY puis a la copié tel quel à l'écran.
La méthode de la moyenne,qui consiste a prendre la moyenne des quatre points qui entour PosX et PosY (Somme des couleur/4)
La méthode dite Gaussienne, qui consiste a donner des coefficients de proximité des points qui entour PosX et PosY.

Comme vous devais vous en douter la méthode Gaussienne et la plus compliquer a mettre en œuvre, mais c'est aussi la plus belle.
Je ne traiterais pas cette méthode dans ce chapitre car elle est très complexe.

Astuce



Si l'on travail avec PosX modulo ResImageX et PosY modulo ResImageY dans la fonction DrawLinear2(), on peut ainsi donner des coordonnées supérieur à la taille de l'image et le motif sera donc reproduit plusieurs fois.