Les Vertex Buffer Objects
Les Vertex Buffer Objects, abrégé VBOs, ou VBO au singulier, sont des zones mémoire où l'on peut stocker des données de
sommet (d'où le mot Vertex). Cette mémoire a la particularité d'être rapidement accessible par votre carte graphique, puisqu'il s'agit en fait de sa propre mémoire.
En effet, comme je l'ai dis lors du précédent chapitre, avec les vertex arrays les données étaient situées du côté du client, alors qu'avec les VBOs on héberge les données du côté du serveur. Nous allons donc commencer par étudier les différentes façon de procéder à cet hébergement (innombrables

), puis nous verrons comment exploiter efficacement nos données hébergées
La lecture préalable du chapitre précédent traitant des vertex arrays est primordiale.
C'est parti

Dans le dernier chapitre, nous avons vu que nos données étaient stockées côté client. Et bien ici ce sera l'inverse, c'est ce qui fait la puissance des VBOs
Résumons le principe des vertex arrays :
- activation;
- spécification des données;
- rendu;
- désactivation.
Lors de la seconde étape, OpenGL ne fait que retenir un pointeur (une adresse mémoire) et des informations sur la structuration des données (
stride,
type et
size). À la 3eme étape, OpenGL envoie les informations au serveur pour le traitement (upload).
Un peu vaseux comme explication ? Ok

:
Comme vous pouvez le voir, les données sont dupliquées en mémoire vidéo, dans le cas des vertex arrays, puis sont traitées pour enfin être affichées à l'écran. Ce processus a lieu à chaque rendu (
glDraw*() ), si vous avez beaucoup de géométrie à envoyer à votre carte graphique, je vous laisse imaginer le topo... Le transfert d'un million de vertices (par exemple) à chaque rendu mènerait à une saturation de la bande passante, et donc à une limitation de frame rate.
L'idée des VBOs est simple : on met les données directement du côté du serveur, et elles y restent

Cela nous économisera le transfert des données à chaque frame, ce qui est un énorme gain de performance
Voici ce qui se passe dans le cas des VBOs :
Les données ne sont ni dupliquées, ni transférées à chaque rendu

Du coup, lors d'un appel à
glDraw*(), OpenGL ira directement chercher nos données en mémoire vidéo. Que demande le peuple ?
Un VBO est un objet, un objet OpenGL, donc un
GLuint.
Celui-ci se manipule comme tous les objets OpenGL, sans exceptions apparente, avec des fonction préfixées Gen, Is, Delete et Bind.
Voici comment créer un identifiant pour un objet tampon :
Code : C1
2
3 | GLuint buf;
glGenBuffers(1, &buf);
|
Rien de bien sorcier
Lorsque nous voudrons appliquer des modifications à notre tampon (envoie de données, modifications diverses...) nous devrons, comme pour tout objet OpenGL, le binder. Voici le prototype de la fonction permettant le binding d'un objet tampon :
Code : C1 | void glBindBuffer(GLenum target, GLuint id);
|
C'est quoi ce paramètre
target ? Il me fait peur
Il s'agit de placer ici une constante symbolique, nous nous en tiendrons à GL_ARRAY_BUFFER pour le moment.
Bien, commençons la création d'un code source complet, que nous compléterons au fur et à mesure

:
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 | #include <SDL/SDL.h>
#include <GL/glew.h>
/* dimensions de la fenetre */
#define W 300
#define H 200
int main(int argc, char **argv)
{
int loop = 1; /* booleen du 'main loop' */
SDL_Event ev; /* structure d'evenement(s) SDL */
GLuint buf; /* identifiant de notre objet tampon */
/* initialisation de la SDL en mode OpenGL */
SDL_Init(SDL_INIT_VIDEO);
SDL_SetVideoMode(W, H, 32, SDL_OPENGL);
/* nom de la fenetre */
SDL_WM_SetCaption("Vertex Buffer Objects GL", NULL);
/* initialisation de glew */
glewInit();
/* creation d'un objet tampon et recuperation de son identifiant */
glGenBuffers(1, &buf);
/* boucle d'affichage principale */
while(loop)
{
/* recuperation d'un evenement */
SDL_WaitEvent(&ev);
/* analyse */
if(ev.type == SDL_QUIT)
loop = 0;
glClear(GL_COLOR_BUFFER_BIT);
/* rendu ... */
/* on flip les tampons */
glFlush();
SDL_GL_SwapBuffers();
}
return 0;
}
|
Notez la présence du fichier glew.h à la place de gl.h.
En effet, les Vertex Buffer Objects sont en réalité une
extension d'OpenGL.
C'est quoi une extension d'OpenGL ?
Je vous conseille d'aller lire
mon tutoriel sur la gestion des extensions d'OpenGL, il vous expliquera entre autres la présence de glew.h à la place de gl.h.
Sa lecture est indispensable si vous souhaitez pouvoir utiliser les VBOs.
L'extension des VBOs s'appelle
GL_ARB_vertex_buffer_object.
Allouer un espace de stockage
Comme nous le savons à présent, les VBOs sont des buffers (tampons) contenant des données qui se trouvent du côté de la mémoire du serveur. Comme pour toute gestion de mémoire, il faut commencer par l'allouer. Nous allons donc allouer de la mémoire côté serveur, pour y placer ensuite les données de nos sommets.
Voici la fonction permettant d'allouer de la mémoire pour un VBO :
Code : C1 | void glBufferData(GLenum target, GLsizei size, const GLvoid *data, GLenum mode);
|
- target : dans notre cas, il s'agit de GL_ARRAY_BUFFER. Attention, un buffer bindé avec cette constante doit toujours être utilisé avec la même, donc à partir de maintenant, partout où vous verrez target, il faudra mettre le target de votre buffer. Un buffer ne peut avoir qu'un seul target.
- size : c'est la taille de vos données, en bytes.
- data : ceci désigne l'emplacement de vos données, pour l'instant nous ne metterons rien, la fontion glBufferData() est surtout déstinée à réserver un espace mémoire.
- mode : paramètre un peu particulier. Il sert à définir le mode de traitement de vos données lors de leur mise à jour.
Comme je viens de le dire, le dernier paramètre est un peu bizarre... En fait il défini votre fréquence d'accès à vos données (voir dernière partie de ce chapitre sur la mise à jour d'un VBO). Voyons la liste des constantes acceptées pour
mode :
- GL_STREAM_DRAW : vous mettez vos données à jour à chaque affichage ( glDraw*() );
- GL_DYNAMIC_DRAW : vous mettez fréquemment à jour vos données. (plus d'une fois par affichage);
- GL_STATIC_DRAW : vous mettez peu souvent voir jamais vos données à jour.
Nous nous contenterons de GL_STREAM_DRAW pour l'instant, qui s'adapte très bien à toutes les situations
Euh, où est-ce qu'on dit à cette fonction qu'on veut appliquer nos modifications sur notre objet buf et pas ailleurs ?
Comme pout tout objet OpenGL, il est nécessaire de le binder (
glBindBuffer() dans notre cas) pour spécifier l'objet actif, et ainsi appliquer toutes les modifications futures sur cet objet et pas un autre. Afin de dire à OpenGL qu'aucun objet tampon n'est actif, appelez
glBindBuffer() avec un identifiant de 0 :
Code : C1 | glBindBuffer(GL_ARRAY_BUFFER, 0);
|
Cela fonctionne pareil pour tous les objets OpenGL, exemple pour les textures : glBindTexture(GL_TEXTURE_2D, 0)
Voyons à présent à quoi ressemble notre code :
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 | #include <SDL/SDL.h>
#include <GL/glew.h>
/* dimensions de la fenetre */
#define W 300
#define H 200
int main(int argc, char **argv)
{
int loop = 1; /* booleen du 'main loop' */
SDL_Event ev; /* structure d'evenement(s) SDL */
GLuint buf; /* identifiant de notre objet tampon */
#define N_VERTS 3
#define P_SIZE 2
#define C_SIZE 3
float pos[N_VERTS*P_SIZE] =
{
-0.8, -0.8,
0.8, -0.8,
0.0, 0.8
};
float colors[N_VERTS*C_SIZE] =
{
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0
};
/* initialisation de la SDL en mode OpenGL */
SDL_Init(SDL_INIT_VIDEO);
SDL_SetVideoMode(W, H, 32, SDL_OPENGL);
/* nom de la fenetre */
SDL_WM_SetCaption("Vertex Buffer Objects GL", NULL);
/* initialisation de glew */
glewInit();
/* creation d'un objet tampon et recuperation de son identifiant */
glGenBuffers(1, &buf);
/* on bind le buffer afin que glBufferData s'applique sur buf */
glBindBuffer(GL_ARRAY_BUFFER, buf);
/* on alloue de l'espace */
glBufferData(GL_ARRAY_BUFFER, /* target */
(N_VERTS*P_SIZE*sizeof *pos) + /* taille des positions */
(N_VERTS*C_SIZE*sizeof *colors), /* taille des couleurs */
NULL, /* ... */
GL_STREAM_DRAW); /* mode */
/* boucle d'affichage principale */
while(loop)
{
/* recuperation d'un evenement */
SDL_WaitEvent(&ev);
/* analyse */
if(ev.type == SDL_QUIT)
loop = 0;
glClear(GL_COLOR_BUFFER_BIT);
/* rendu ... */
/* on flip les tampons */
glFlush();
SDL_GL_SwapBuffers();
}
/* suppression de l'objet tampon */
glDeleteBuffers(1, &buf);
return 0;
}
|
La suppression d'un objet tampon se fait en toute simplicité :
Code : C1 | glDeleteBuffers(1, &buf);
|
Et on ne libère pas la mémoire ? (free())
OpenGL s'en charge lors de l'appel à
glDeleteBuffers()
Bon, c'est bien joli tout ça, je sais créer un VBO, lui allouer de la mémoire sur le serveur, le détruire, mais je ne sais toujours pas quoi faire de mes tableaux de sommets ?
Nous y voilà
Pour ceux qui maîtriseraient déjà la création et la manipulation des textures, vous devriez savoir que
glTexImage*() c'est pour
allouer un espace,
glTexSubImage*() pour
remplir/
mettre à jour cet espace.
Eh bien avec les VBOs c'est pareil
Code : C1 | glBufferSubData(GLenum target, GLint first, GLsizei size, const GLvoid *data);
|
target,
size et
data sont équivalents à ceux de la fonction
glBufferData(). Ici, nous avons droit à un nouveau paramètre :
first.
- first: indique l'emplacement mémoire (en bytes) dans le VBO où commencer le stockage des données.
Au début, cette valeur sera logiquement à zéro (début de la mémoire de notre VBO, OpenGL y additionnera automatiquement la véritable adresse de notre buffer). Mais supposons que nous voulions ajouter des données par la suite, pour éviter d'écraser les précédentes, il faudra spécifier explicitement via
first où commencer la copie des données dans la mémoire du VBO.
J'ai rien compris... ?
Je vous propose de reprendre mes super schémas

Voici la mémoire du VBO, sa taille est égale à celle spécifiée dans
glBufferData() :
Bien, nous allons commencer simplement.
Tout d'abord, nous allons y stocker nos positions de sommet :
Code : C1
2
3
4 | glBufferSubData(GL_ARRAY_BUFFER,
0, /* emplacement des donnees dans le VBO (first) */
(N_VERTS*P_SIZE*sizeof *pos), /* taille des donnees (size) */
pos); /* adresse des donnees */
|
|
 |
Voilà qui est fait
En fait,
c'est ici que le transfert des données (upload) de la mémoire centrale vers la mémoire vidéo a lieu.
Bien, puisque nous avons stocké avec succès nos positions de sommets, faisons de même avec les couleurs :
Code : C1
2
3
4 | glBufferSubData(GL_ARRAY_BUFFER,
(N_VERTS*P_SIZE*sizeof *pos), /* emplacement (first) */
(N_VERTS*C_SIZE*sizeof *colors),/* taille (size) */
colors); /* donnees */
|
|
 |
Comme vous le voyez (j'espère

) nous avons dû spécifier explicitement à partir d'où OpenGL avait le droit de stocker nos données dans la mémoire du VBO. Puisque nous avions déjà stocké nos positions de sommet, si on avait laissé
first à zéro, il y aurait eu un écrasement de données (écrasement de
pos par
colors).
Pourquoi OpenGL ne gère-t-il pas tout seul le stockage de nos données ?
Parce qu'OpenGL est une API bas niveau, il faut donc que vous puissiez faire un maximum de choses, imaginez que vous vouliez mettre à jour vos données, comment feriez vous ? OpenGL vous laisse l'opportunité de vous gourer, mais aussi de faire ce que vous voulez
Faisons le point
- Nous savons créer un VBO.
- Nous savons lui allouer de l'espace mémoire sur le serveur.
- Nous savons remplir cet espace convenablement.
- Nous savons détruire un VBO (cela implique la destruction des données du côté serveur).
C'est bien me direz-vous, mais comment on va afficher ça ?
Spécifier une adresse mémoire située du côté serveur
En fait c'est rien de bien compliqué, vous allez voir. Pour tout vous dire, l'explication tient en deux phrases
- bindez votre buffer ( glBindBuffer() );
- appelez gl*Pointer() avec pour adresse... 0.
Ça mérite bien quelques explications tout de même. En effet, lors de l'appel à
glBindBuffer() avec un identifiant valide, OpenGL passe en mode VBO et tous les appels à
gl*Pointer() pointent alors sur les données de notre VBO gentiment bindé.
Vous allez voir, avec un exemple ça ira tout de suite mieux :
Code : C1
2
3
4
5
6
7 | #define BUFFER_OFFSET(a) ((char*)NULL + (a))
/* on passe en mode VBO */
glBindBuffer(GL_ARRAY_BUFFER, buf);
glVertexPointer(P_SIZE, GL_FLOAT, 0, BUFFER_OFFSET(0));
glColorPointer(C_SIZE, GL_FLOAT, 0, BUFFER_OFFSET(N_VERTS*P_SIZE*sizeof *pos));
|
BUFFER_OFFSET ? Avons-nous été présentés ?
Non effectivement. Il s'agit d'une macro fournie par les spécifications qui vous permet de donner un
offset et non une adresse.
En fait, à la place de donner notre tableau à
gl*Pointer(), nous donnons
sa position de stockage dans le VBO...
Son first !
Tout à fait
Voici notre code à présent complété :
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112 | #include <SDL/SDL.h>
#include <GL/glew.h>
/* dimensions de la fenetre */
#define W 300
#define H 200
#define BUFFER_OFFSET(a) ((char*)NULL + (a))
int main(int argc, char **argv)
{
int loop = 1; /* booleen du 'main loop' */
SDL_Event ev; /* structure d'evenement(s) SDL */
GLuint buf; /* identifiant de notre objet tampon */
#define N_VERTS 3
#define P_SIZE 2
#define C_SIZE 3
float pos[N_VERTS*P_SIZE] =
{
-0.8, -0.8,
0.8, -0.8,
0.0, 0.8
};
float colors[N_VERTS*C_SIZE] =
{
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0
};
/* initialisation de la SDL en mode OpenGL */
SDL_Init(SDL_INIT_VIDEO);
SDL_SetVideoMode(W, H, 32, SDL_OPENGL);
/* nom de la fenetre */
SDL_WM_SetCaption("Vertex Buffer Objects GL", NULL);
/* initialisation de glew */
glewInit();
/* creation d'un objet tampon et recuperation de son identifiant */
glGenBuffers(1, &buf);
/** creation de notre VBO **/
/* on bind le buffer */
glBindBuffer(GL_ARRAY_BUFFER, buf);
/* on alloue un espace */
glBufferData(GL_ARRAY_BUFFER, /* target */
(N_VERTS*P_SIZE*sizeof *pos) + /* taille des positions */
(N_VERTS*C_SIZE*sizeof *colors),/* taille des couleurs */
NULL, /* ... */
GL_STREAM_DRAW); /* mode */
/* on specifie les donnees */
glBufferSubData(GL_ARRAY_BUFFER,
0, /* emplacement des donnees dans le VBO */
(N_VERTS*P_SIZE*sizeof *pos), /* taille des donnees */
pos); /* adresse des donnees */
glBufferSubData(GL_ARRAY_BUFFER,
(N_VERTS*P_SIZE*sizeof *pos), /* emplacement */
(N_VERTS*C_SIZE*sizeof *colors),/* taille */
colors); /* donnees */
glBindBuffer(GL_ARRAY_BUFFER, 0);
/** fin **/
/* boucle d'affichage principale */
while(loop)
{
/* recuperation d'un evenement */
SDL_WaitEvent(&ev);
/* analyse */
if(ev.type == SDL_QUIT)
loop = 0;
glClear(GL_COLOR_BUFFER_BIT);
/* on passe en mode VBO */
glBindBuffer(GL_ARRAY_BUFFER, buf);
glVertexPointer(P_SIZE, GL_FLOAT, 0, BUFFER_OFFSET(0));
glColorPointer(C_SIZE, GL_FLOAT, 0, BUFFER_OFFSET(N_VERTS*P_SIZE*sizeof *pos));
/* activation des tableaux de sommets */
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glDrawArrays(GL_TRIANGLES, 0, 3);
/* desactivation des tableaux de sommet */
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
/* on flip les tampons */
glFlush();
SDL_GL_SwapBuffers();
}
glDeleteBuffers(1, &buf);
return 0;
}
|
Hé oui, j'en ai profité pour rajouter un petit
glDrawArrays(). En effet, le rendu est à présent possible puisque nous avons spécifié nos données à
gl*Pointer()
Utiliser plusieurs VBOs en même temps
Sachez que c'est tout à fait possible, et même conseillé dans certains cas.
Imaginez que vous avez fait un programme qui permet à l'utilisateur d'activer et de désactiver la couleur à volonté. Lorsqu'elle est désactivée, l'utilisation des informations de coloriage est inutile et s'avèrerait être une perte de performance plus qu'autre chose.
Nous allons donc séparer nos couleurs de nos positions de sommet. Je pense que pour l'occasion, un bout de code vaut mieux qu'un long discours, si vous avez bien compris l'utilisation d'un VBO, ce code ne devrait pas vous poser de problèmes :
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 | GLuint buf_pos, buf_col;
...
/* creation de nos VBO */
glGenBuffers(1, &buf_pos);
glGenBuffers(1, &buf_col);
/* on bind le buffer des positions de sommet */
glBindBuffer(GL_ARRAY_BUFFER, buf_pos);
/* on alloue un espace */
glBufferData(GL_ARRAY_BUFFER, (N_VERTS*P_SIZE*sizeof *pos), NULL, GL_STREAM_DRAW);
/* on specifie les donnees */
glBufferSubData(GL_ARRAY_BUFFER, 0, (N_VERTS*P_SIZE*sizeof *pos), pos);
/* on bind le buffer des positions de sommet */
glBindBuffer(GL_ARRAY_BUFFER, buf_col);
/* on alloue un espace */
glBufferData(GL_ARRAY_BUFFER, (N_VERTS*C_SIZE*sizeof *colors), NULL, GL_STREAM_DRAW);
/* on specifie les donnees */
glBufferSubData(GL_ARRAY_BUFFER, 0, (N_VERTS*C_SIZE*sizeof *colors), colors);
/* plus aucun buffer n'est a utiliser */
glBindBuffer(GL_ARRAY_BUFFER, 0);
...
/* specification du buffer des positions de sommet */
glBindBuffer(GL_ARRAY_BUFFER, buf_pos);
glVertexPointer(P_SIZE, GL_FLOAT, 0, BUFFER_OFFSET(0));
/* specification du buffer des couleurs de sommet */
glBindBuffer(GL_ARRAY_BUFFER, buf_col);
glColorPointer(C_SIZE, GL_FLOAT, 0, BUFFER_OFFSET(0));
...
/* suppression de nos VBOs */
glDeleteBuffers(1, &buf_col);
glDeleteBuffers(1, &buf_pos);
|
Et oui, à nouveau ces fameux indices, sachez qu'ils sont tout à fait adaptés avec l'utilisation des VBOs

Tout comme nos sommets, nos indices seront également hébergés sur notre serveur.
Héberger un tableau d'indices
Cela se passe exactement comme pour héberger un tableau de données quelconques

À quelques exceptions près.
Vous vous souvenez de
target, ce paramètre à priori inutile ? Eh bien dans le cas des index buffers (ou tableau d'indices, ou encore tampon d'indices) il va falloir placer ce paramètre à GL_ELEMENT_ARRAY_BUFFER.
Nous appellerons donc ces buffers des IBOs (Index Buffer Objects), car ils hébergent des indices (index), et non des sommets (vertex).
Exemple d'utilisation
Nous allons sauter les étapes de stockage des tableaux de sommets, vous les connaissez suffisamment bien :
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | GLuint buf_index;
...
glGenBuffers(1, &buf_index);
/* construction du IBO */
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf_index);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, N_VERTS * sizeof *index, NULL, GL_STREAM_DRAW);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, N_VERTS * sizeof *index, index);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
...
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf_index);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, BUFFER_OFFSET(0));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
À l'instar d'un appel à
glBindBuffer() avec GL_ARRAY_BUFFER, lorsqu'on met GL_ELEMENT_ARRAY_BUFFER c'est la fonction
glDrawElements() qui est affectée et qui pointe alors vers le tampon (IBO) actif.
Je pense que le reste se passe aisément de commentaires, surtout si vous avez bien compris le concept des VBOs
Le stockage des indices dans un tampon côté serveur n'est pas obligatoire, mais cela a de fortes chances d'augmenter les performances de votre application
Il nous reste un dernier point à éclairer, et nous aurons fait le tour des VBOs et de leur utilisation en général
Supposons que vous vouliez faire une animation (un personnage, ou n'importe quel maillage animé), il va bien falloir déplacer vos sommets. Mais ce n'est pas tant le déplacement en lui-même qui nous intéresse, mais plutôt la façon de mettre à jour nos vertex buffers, qui sont perdus au fin-fond de la mémoire de notre carte graphique.
Lire et écrire dans un VBO
Bien, raisonnons logiquement :
Si nous voulons accéder à nos données, il va falloir trouver leur adresse. Simple, il suffit de la demander à OpenGL

La fonction
glMapBuffer() est là pour ça :
Code : C1 | void* glMapBuffer(GLenum target, GLenum mode);
|
- target: bon, maintenant celui-là vous le connaissez
si c'est pour mettre à jour un vertex buffer, vous mettrez GL_ARRAY_BUFFER, si c'est pour mettre à jour un index buffer, vous mettrez GL_ELEMENT_ARRAY_BUFFER.
- mode: ça, c'est déjà un paramètre un peu plus subtile, il vous permet de définir le mode d'accès à vos données. On peut comparer ce mode aux modes d'ouvertures d'un fichier en C.
Constantes symboliques acceptables pour
mode :
- GL_READ_ONLY : les données seront accessibles en lecture seule.
- GL_WRITE_ONLY : les données seront accessibles en écriture seule.
- GL_READ_WRITE : les données seront accessibles en lecture et en écriture.
glMapBuffer() renvoie un pointeur sur votre VBO.
En supposant que vous ayez gardé une copie de vos données dans votre mémoire centrale (ce qui est le cas dans nos exemples ci-dessus, en effet, les tableaux
pos et
colors existent toujours), vous pouvez procéder à une mise à jour de vos données côté serveur de cette façon :
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | #include <string.h>
...
float *pos_vbo = NULL;
glBindBuffer(GL_ARRAY_BUFFER, buf_pos);
pos_vbo = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
if(pos_vbo == NULL)
{
fprintf(stderr, "impossible d'acceder aux donnees du vbo!\n");
exit(EXIT_FAILURE);
}
/* transfert */
memcpy(pos_vbo, pos, N_VERTS * P_SIZE * sizeof *pos);
glUnmapBuffer(GL_ARRAY_BUFFER);
pos_vbo = NULL;
|
Nouveauté du jour, la fonction
glUnmapBuffer(). Elle a pour effet de rendre invalide le pointeur renvoyé par
glMapBuffer(), je place donc logiquement mon pointeur à NULL après l'appel à
glUnmapBuffer().
Autre détail important, il se peut (mais c'est rare, ou alors l'erreur provient de vous) que la récupération de l'adresse de votre VBO via
glMapBuffer() échoue, dans ce cas
glMapBuffer() renvoie NULL. C'est important de le vérifier, car écrire dans NULL provoque une erreur de segmentation (segmentation fault) assurée
Les VBOs représentent à l'heure actuelle, et pour encore longtemps, la meilleure méthode de rendu et la plus utilisée. Les VBOs combinent performances et flexibilité à merveille. Le fait de stocker nos données dans la mémoire de la carte graphique nous permet de libérer la nôtre (client) et d'optimiser le transfert des données pour procéder au rendu, en fait, plus aucun transfert n'a lieu, les données restent chez le serveur.
Les VBOs sont les plus utilisés dans les rendus 3D récents, et possèdent leur équivalent chez DirectX.
Je vous conseille par dessus tout les VBOs (et IBOs) pour effectuer vos rendus, vous verrez que lorsqu'on les maîtrises il nous devient impossible de s'en passer
Et voilà, c'est la fin de ce tutoriel.
J'espère que vous aurez su en tirer le meilleur profit
Informations sur le tutoriel