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.Je vais essayer ! Tout cela n'est pas si compliqué, juste une question de termes, j'espère que je vais être clair.
Vous pouvez créer à peu près autant d' objets de texture que de mémoire disponible dans votre système. Ces objets contiennent les données réelles (texels) de vos textures, ainsi que les paramètres, fournis par glTexParameter (voir FAQ ).
Au moment de la création, vous devez attribuer une texture cible à un objet de texture, ce qui représente le type de la texture (
GL_TEXTURE_2D
,GL_TEXTURE_3D
,GL_TEXTURE_CUBE
, ...).Ces deux éléments, objet de texture et cible de texture, représentent les données de texture. Nous y reviendrons plus tard.
Unités de texture
Maintenant, OpenGL fournit un tableau d' unités de texture , qui peuvent être utilisées simultanément pendant le dessin. La taille du tableau dépend du système OpenGL, la vôtre en a 8.
Vous pouvez lier un objet de texture à une unité de texture pour utiliser la texture donnée lors du dessin.
Dans un monde simple et facile, pour dessiner avec une texture donnée, vous lieriez un objet de texture à l'unité de texture, et vous feriez (pseudocode):
Comme GL est une machine à états, hélas, cela ne fonctionne pas de cette façon. En supposant que notre
textureObject
possède des données pour laGL_TEXTURE_2D
cible de texture, nous exprimerons l'affectation précédente comme suit:Notez que
GL_TEXTURE_2D
cela dépend vraiment du type de texture que vous souhaitez lier.Objets de texture
Dans le pseudo code, pour définir des données de texture ou des paramètres de texture, vous feriez par exemple:
OpenGL ne peut pas manipuler directement les objets de texture, mettre à jour / définir leur contenu, ou modifier leurs paramètres, vous devez d'abord les lier à l'unité de texture active (quelle qu'elle soit). Le code équivalent devient:
Shaders
Les shaders ont accès à toutes les unités de texture, ils ne se soucient pas de la texture active.
Les uniformes de l'échantillonneur sont des
int
valeurs représentant l'indice de l'unité de texture à utiliser pour l'échantillonneur (et non l'objet de texture à utiliser).Vous devez donc lier vos objets de texture aux unités que vous souhaitez utiliser.
Le type de l'échantillonneur fera la correspondance avec la cible de texture qui est utilisée dans l'unité de texture:
Sampler2D
pourGL_TEXTURE_2D
, et ainsi de suite ...la source
Imaginez le GPU comme une usine de traitement de peinture.
Il existe un certain nombre de réservoirs qui fournissent du colorant à une machine à peindre. Dans la machine à peindre, le colorant est ensuite appliqué sur l'objet. Ces chars sont les unités de texture
Ces réservoirs peuvent être équipés de différents types de colorants. Chaque type de colorant nécessite un autre type de solvant. Le "solvant" est la cible de la texture . Pour plus de commodité, chaque réservoir est connecté à une alimentation en solvant, mais un seul type de solvant peut être utilisé à la fois dans chaque réservoir. Donc , il y a une vanne / commutateur
TEXTURE_CUBE_MAP
,TEXTURE_3D
,TEXTURE_2D
,TEXTURE_1D
. Vous pouvez remplir tous les types de colorants dans le réservoir en même temps, mais comme un seul type de solvant entre, il "diluera" uniquement le type de colorant correspondant. Vous pouvez donc avoir chaque type de texture lié, mais la liaison avec le solvant "le plus important" ira en fait dans le réservoir et se mélangera avec le type de colorant auquel elle appartient.Et puis il y a le colorant lui-même, qui vient d'un entrepôt et est rempli dans le réservoir en le "liant". C'est ta texture.
la source
Si dans votre shader vous avez besoin d'une recherche à partir de 2 textures:
il faut indiquer pour tex1 et tex2 leurs sources comme suit:
dans la boucle de rendu:
Avec une gl_bindtexture, pas possible de faire une telle chose. Par contre une utilisation possible d'un bind dans la boucle de rendu, est le cas où vous alimentez une texture avec un contenu en flux (vidéo, webcam):
la source