Tout sur les objets OpenGL
Le modèle standard pour les objets OpenGL est le suivant.
Les objets ont un état. Considérez-les comme un struct
. Ainsi, vous pourriez avoir un objet défini comme ceci:
struct Object
{
int count;
float opacity;
char *name;
};
L'objet a certaines valeurs stockées et il a un état . Les objets OpenGL ont également un état.
Changement d'état
En C / C ++, si vous avez une instance de type Object
, vous changeriez son état comme suit: obj.count = 5;
vous référeneriez directement une instance de l'objet, obtiendrez l'élément d'état particulier que vous souhaitez modifier et y inséreriez une valeur.
Dans OpenGL, vous ne faites pas cela.
Pour des raisons d'héritage qu'il vaut mieux laisser inexpliquées, pour changer l'état d'un objet OpenGL, vous devez d'abord le lier au contexte. Ceci est fait avec certains de l' glBind*
appel.
L'équivalent C / C ++ de ceci est le suivant:
Object *g_objs[MAX_LOCATIONS] = {NULL};
void BindObject(int loc, Object *obj)
{
g_objs[loc] = obj;
}
Les textures sont intéressantes; ils représentent un cas particulier de liaison. De nombreux glBind*
appels ont un paramètre "cible". Cela représente différents emplacements dans le contexte OpenGL où les objets de ce type peuvent être liés. Par exemple, vous pouvez lier un objet framebuffer pour la lecture ( GL_READ_FRAMEBUFFER
) ou pour l'écriture ( GL_DRAW_FRAMEBUFFER
). Cela affecte la façon dont OpenGL utilise le tampon. C'est ce que représente le loc
paramètre ci-dessus.
Les textures sont spéciales car lorsque vous les liez pour la première fois à une cible, elles obtiennent des informations spéciales. Lorsque vous liez une texture pour la première fois en tant que GL_TEXTURE_2D
, vous définissez en fait un état spécial dans la texture. Vous dites que cette texture est une texture 2D. Et ce sera toujours une texture 2D; cet état ne peut pas être changé jamais . Si vous avez une texture qui a d'abord été liée en tant que a GL_TEXTURE_2D
, vous devez toujours la lier en tant que GL_TEXTURE_2D
; tenter de le lier comme GL_TEXTURE_1D
cela entraînera une erreur (lors de l'exécution).
Une fois l'objet lié, son état peut être modifié. Cela se fait via des fonctions génériques spécifiques à cet objet. Eux aussi prennent un emplacement qui représente l'objet à modifier.
En C / C ++, cela ressemble à:
void ObjectParameteri(int loc, ObjectParameters eParam, int value)
{
if(g_objs[loc] == NULL)
return;
switch(eParam)
{
case OBJECT_COUNT:
g_objs[loc]->count = value;
break;
case OBJECT_OPACITY:
g_objs[loc]->opacity = (float)value;
break;
default:
//INVALID_ENUM error
break;
}
}
Notez comment cette fonction définit tout ce qui se trouve être dans la loc
valeur actuellement liée .
Pour les objets de texture, les principales fonctions de changement d'état de texture sont glTexParameter
. Les seules autres fonctions que l' état de texture de changement sont les glTexImage
fonctions et leurs variations ( glCompressedTexImage
, glCopyTexImage
, la récente glTexStorage
). Les différentes SubImage
versions changent le contenu de la texture, mais elles ne changent pas techniquement son état . Les Image
fonctions allouent le stockage de texture et définissent le format de la texture; les SubImage
fonctions copient simplement les pixels autour. Cela n'est pas considéré comme l'état de la texture.
Permettez-moi de répéter: ce sont les seules fonctions qui modifient l'état de la texture. glTexEnv
modifie l'état de l'environnement; cela n'affecte rien de stocké dans les objets de texture.
Texture active
La situation des textures est plus complexe, encore une fois pour des raisons d'héritage qu'il vaut mieux ne pas divulguer. C'est là glActiveTexture
qu'intervient.
Pour les textures, il n'y a pas des cibles (seulement GL_TEXTURE_1D
, GL_TEXTURE_CUBE_MAP
, etc.). Il existe également des unités de texture . En termes de notre exemple C / C ++, voici ce que nous avons:
Object *g_objs[MAX_OBJECTS][MAX_LOCATIONS] = {NULL};
int g_currObject = 0;
void BindObject(int loc, Object *obj)
{
g_objs[g_currObject][loc] = obj;
}
void ActiveObject(int currObject)
{
g_currObject = currObject;
}
Notez que maintenant, nous avons non seulement une liste 2D de Object
s, mais nous avons également le concept d'un objet courant. Nous avons une fonction pour définir l'objet courant, nous avons le concept d'un nombre maximum d'objets courants, et toutes nos fonctions de manipulation d'objets sont ajustées pour sélectionner à partir de l'objet courant.
Lorsque vous modifiez l'objet actuellement actif, vous modifiez l'ensemble des emplacements cibles. Ainsi, vous pouvez lier quelque chose qui va dans l'objet actuel 0, passer à l'objet actuel 4 et modifier un objet complètement différent.
Cette analogie avec les objets de texture est parfaite ... presque.
Voir, glActiveTexture
ne prend pas un entier; il faut un enquêteur . Ce qui signifie en théorie qu'il peut prendre n'importe quoi de GL_TEXTURE0
à GL_TEXTURE31
. Mais il y a une chose que vous devez comprendre:
C'EST FAUX!
La plage réelle que glActiveTexture
peut prendre est régie par GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS
. C'est le nombre maximum de multitextures simultanées qu'une implémentation permet. Ceux-ci sont chacun divisés en différents groupes pour différentes étapes de shader. Par exemple, sur le matériel de classe GL 3.x, vous obtenez 16 textures de shader de sommet, 16 textures de shader de fragment et 16 textures de shader de géométrie. Par conséquent, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS
sera 48.
Mais il n'y a pas 48 recenseurs. C'est pourquoi glActiveTexture
ne prend pas vraiment les recenseurs. La manière correcte d'appeler glActiveTexture
est la suivante:
glActiveTexture(GL_TEXTURE0 + i);
où i
est un nombre entre 0 et GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS
.
Le rendu
Alors qu'est-ce que tout cela a à voir avec le rendu?
Lorsque vous utilisez des shaders, vous définissez les uniformes de votre échantillonneur sur une unité d'image de texture ( glUniform1i(samplerLoc, i)
, où i
est l'unité d'image). Cela représente le nombre que vous avez utilisé glActiveTexture
. L'échantillonneur choisira la cible en fonction du type d'échantillonneur. Donc, un sampler2D
choisira de la GL_TEXTURE_2D
cible. C'est l'une des raisons pour lesquelles les échantillonneurs ont différents types.
Maintenant, cela semble suspect que vous pouvez avoir deux échantillonneurs GLSL, avec différents types qui utilisent la même unité d'image de texture. Mais vous ne pouvez pas; OpenGL interdit cela et vous donnera une erreur lorsque vous tenterez de rendre.
GL_TEXTURE0 + i
- je voulais inspecter les valeurs d'énumération pour voir si cela était valide ou non. Et le dernier paragraphe - je ne savais pas si c'était légal ou non. Excellent! Je mets en favori toutes vos réponses afin que je puisse les consulter à nouveau.