web-dev-qa-db-fra.com

Différences et relation entre glActiveTexture et glBindTexture

D'après ce que je comprends, glActiveTexture définit "l'unité de texture" active. Chaque unité de texture peut avoir plusieurs cibles de texture (généralement GL_TEXTURE_1D, 2D, 3D ou CUBE_MAP).

Si je comprends bien, vous devez appeler glActiveTexture pour définir d'abord l'unité de texture (initialisée à GL_TEXTURE0), puis vous liez (une ou plusieurs) "cibles de texture" à cette unité de texture?

Le nombre d'unités de texture disponibles dépend du système. Je vois des énumérations jusqu'à 32 dans ma bibliothèque. Je suppose que cela signifie essentiellement que je peux avoir la moindre de la limite de mon GPU (ce qui, je pense, est 16 8) et 32 ​​textures en mémoire GPU à la fois? Je suppose qu'il y a une limite supplémentaire que je ne dépasse pas la mémoire maximale de mon GPU (soi-disant 1 Go).

Suis-je en train de comprendre correctement la relation entre les cibles de texture et les unités de texture? Disons que j'ai droit à 16 unités et 4 cibles chacune, cela signifie-t-il qu'il y a de la place pour 16 * 4 = 64 cibles, ou cela ne fonctionne-t-il pas comme ça?

Ensuite, vous voulez généralement charger une texture. Vous pouvez le faire via glTexImage2D. Le premier argument est une cible de texture. Si ceci fonctionne comme glBufferData , alors nous lions essentiellement le "handle"/"nom de texture" à la cible de texture, puis chargons les données de texture dans cette cible, et donc associons indirectement avec cette poignée.

Qu'en est-il de glTexParameter? Nous devons lier une cible de texture, puis choisir à nouveau cette même cible comme premier argument? Ou la cible de texture n'a-t-elle pas besoin d'être liée tant que nous avons la bonne unité de texture active?

glGenerateMipmap fonctionne aussi sur une cible ... cette cible doit toujours être liée au nom de la texture pour qu'elle réussisse?

Ensuite, lorsque nous voulons dessiner notre objet avec une texture, devons-nous les deux choisir une unité de texture active, puis une cible de texture? Ou choisissons-nous une unité de texture, puis nous pouvons récupérer les données de l'une des 4 cibles associées à cette unité? C'est la partie qui me dérange vraiment.

125
mpen

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. Vous pourriez donc 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 é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érenceriez directement une instance de l'objet, obtiendrez l'état particulier que vous veulent changer et y insérer une valeur.

Dans OpenGL, vous ne faites pas faites cela.

Pour des raisons héritées qu'il vaut mieux laisser inexpliquées, pour changer l'état d'un objet OpenGL, vous devez d'abord lier le mettre dans le contexte. Cela se fait avec certains appels de glBind*.

L'équivalent C/C++ 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 appels glBind* Ont un paramètre "cible". Cela représente différents emplacements dans le contexte OpenGL où des 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 paramètre loc ci-dessus.

Les textures sont spéciales parce que lorsque vous - d'abord les liez à 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 GL_TEXTURE_2D, Vous devez toujours la lier en tant que GL_TEXTURE_2D; une tentative de liaison avec GL_TEXTURE_1D provoquera 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. Ils prennent également un emplacement qui représente l'objet à modifier.

En C/C++, cela ressemble à ceci:

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;
  }
}

Remarquez comment cette fonction définit tout ce qui se trouve dans la valeur loc actuellement liée.

Pour les objets de texture, les principales fonctions de changement d'état de texture sont glTexParameter . Les seules autres fonctions qui changent l'état de texture sont les fonctions glTexImage et leurs variations ( glCompressedTexImage , glCopyTexImage , le récent glTexStorage ). Les différentes versions de SubImage modifient le contenu de la texture, mais elles ne changent pas techniquement son état. Les fonctions Image allouent le stockage de texture et définissent le format de la texture; les fonctions SubImage copient simplement les pixels. Ce n'est pas considéré comme l'état de la texture.

Permettez-moi de répéter: ce sont les fonctions uniquement qui modifient l'état de la texture. glTexEnv modifie l'état de l'environnement; il n'affecte rien stocké dans les objets de texture.

Texture active

La situation des textures est plus complexe, encore une fois pour des raisons héritées, il vaut mieux ne pas la divulguer. C'est là qu'intervient glActiveTexture .

Pour les textures, il n'y a pas que des cibles (GL_TEXTURE_1D, GL_TEXTURE_CUBE_MAP, Etc.). Il y a aussi la texture unités. En termes de notre exemple C/C++, ce que nous avons est le suivant:

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 Objects, mais nous avons également le concept d'un objet courant. Nous avons une fonction pour définir l'objet actuel, nous avons le concept d'un nombre maximum d'objets actuels, et toutes nos fonctions de manipulation d'objet sont ajustées pour sélectionner à partir de l'objet actuel.

Lorsque vous modifiez l'objet actuellement actif, vous modifiez l'ensemble complet des emplacements cibles. Vous pouvez donc lier quelque chose qui va dans l'objet actuel 0, basculer vers 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 énumérateur. Ce qui en théorie signifie que cela peut prendre n'importe quoi de GL_TEXTURE0 À GL_TEXTURE31. Mais il y a une chose que vous devez comprendre:

CECI IS 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 GL 3.x matériel de classe, vous obtenez 16 textures de shader de vertex, 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 énumérateurs. La manière correcte d'appeler glActiveTexture est la suivante:

glActiveTexture(GL_TEXTURE0 + i);

i est un nombre compris 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 vos uniformes d'é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é avec glActiveTexture. L'échantillonneur choisira la cible en fonction du type d'échantillonneur. Ainsi, un sampler2D Choisira parmi la cible GL_TEXTURE_2D. C'est une des raisons pour lesquelles les échantillonneurs ont différents types.

Maintenant, cela ressemble étrangement à la possibilité d'avoir deux échantillonneurs GLSL, avec différents types qui utilisent la même unité d'image de texture. Mais vous ne pouvez pas; OpenGL l'interdit et vous donnera une erreur lorsque vous tenterez d'effectuer le rendu.

242
Nicol Bolas

Je vais essayer ! Tout cela n'est pas si compliqué, juste une question de termes, j'espère que je serai clair.


Vous pouvez créer à peu près autant Objets de texture qu'il y a de mémoire disponible sur 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 [~ # ~] ) .

Lors de la création, vous devez affecter un Texture Target à un objet de texture, qui représente le type de la texture (GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE, ...).

Ces deux éléments, objet texture et cible texture représentent les données de texture. Nous y reviendrons plus tard.

Unités de texture

Maintenant, OpenGL fournit un tableau de unités de texture, qui peut être utilisé 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):

glTextureUnit[0] = textureObject

Comme GL est une machine à états, elle ne fonctionne hélas pas de cette façon. Supposons que notre textureObject ait des données pour le GL_TEXTURE_2D cible de texture, nous exprimerons l'affectation précédente comme suit:

glActiveTexture(GL_TEXTURE0);                   // select slot 0 of the texture units array
glBindTexture(GL_TEXTURE_2D, textureObject);    // do the binding

Notez que GL_TEXTURE_2D dépend vraiment du type de texture que vous souhaitez lier.

Objets de texture

En pseudo code, pour définir des données de texture ou des paramètres de texture, vous feriez par exemple:

setTexData(textureObject, ...)
setTexParameter(textureObject, TEXTURE_MIN_FILTER, LINEAR)

OpenGL ne peut pas manipuler directement les objets de texture, pour 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:

glBindTexture(GL_TEXTURE_2D, textureObject)       // this 'installs' textureObject in texture unit
glTexImage2D(GL_TEXTURE_2D, ...)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)

Shaders

Les shaders ont accès à toutes les unités de texture, ils ne se soucient pas de la texture active.

Les uniformes d'échantillonneur sont des valeurs de int représentant l'indice de l'unité de texture à utiliser pour l'échantillonneur (et pas 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 utilisée dans l'unité de texture: Sampler2D pour GL_TEXTURE_2D, etc...

17
rotoglup

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, la teinture est ensuite appliquée sur l'objet. Ces réservoirs sont les nité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 le cible de 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. Il y a donc une vanne/interrupteur 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 ne "diluera" que 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 il 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". Voilà votre texture.

12
datenwolf

Si dans votre shader vous avez besoin d'une recherche à partir de 2 textures:

uniform sampler2D tex1;  
uniform sampler2D tex2;  

il faut indiquer pour tex1 et tex2 leurs sources comme suit:

tex1 = gl.createTexture();  
gl.activeTexture(gl.TEXTURE3);  
gl.bindTexture(gl.TEXTURE_2D, tex1); 
gl.texParameteri(gl.TEXTURE_2D, ...);  
....


tex2 = gl.createTexture();  
gl.activeTexture(gl.TEXTURE7);  
gl.bindTexture(gl.TEXTURE_2D, tex2); 
gl.texParameteri(gl.TEXTURE_2D, ...);  
....  
var tex1Loc  = gl.getUniformLocation(your_shader,"tex1");  
var tex2Loc  = gl.getUniformLocation(your_shader,"tex2");

dans la boucle de rendu:

gl.uniform1i(tex1Loc, 3);  
gl.uniform1i(tex2Loc, 7);  
// but you can dynamically change these values

Avec une gl_bindtexture, impossible 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 stream (vidéo, webcam):

gl.bindTexture(gl.TEXTURE_2D, tex1);  
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, video);  
// in the render loop
1