Différences et relation entre glacitivetexture 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 enums jusqu'à 32 dans ma bibliothèque. Je suppose que cela signifie essentiellement que je peux avoir le moindre de la limite de mon GPU (ce que je pense est 16 8) et 32 textures dans la mémoire GPU à tout moment? 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 GB).
Est-ce que je comprends 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 ne fonctionne-t-il pas comme ça?
Ensuite, vous voulez généralement charger un texture. Vous pouvez le faire via glTexImage2D
. Le premier argument est une cible de texture. Si cela Fonctionne comme glBufferData
, ensuite, nous lions essentiellement le "handle" / "texture name" à la cible de texture, puis chargeons les données de texture dans cette cible, et l'associons donc indirectement à ce handle.
Et 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 le Unité de texture active correcte?
glGenerateMipmap
fonctionne sur une cible trop...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 dessus, 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 des données de l'une des 4 cibles associées à cette unité? C'est la partie qui me déroute vraiment.
4 réponses
Tout Sur Les Objets OpenGL
Le modèle standard pour les objets OpenGL est le suivant.
Les objets ont un État. Pensez-y comme un struct
. Alors vous pourriez avoir un objet défini comme ceci:
struct Object
{
int count;
float opacity;
char *name;
};
L'objet a certaines valeurs stockées dans celui-ci et il a état. Les objets OpenGL ont aussi un État.
Changement D'État
En C / C++, si vous avez une instance de type Object
, vous modifierez son état comme suit: obj.count = 5;
vous référencerez directement une instance de l'objet, obtenez le morceau d'état particulier que vous voulez changer, et y enfoncer une valeur.
Dans OpenGL, vous ne faites pas cela.
Pour des raisons héritées mieux laissées inexpliquées, pour changer l'état D'un objet OpenGL, vous devez d'abord lier au contexte. Ceci est fait avec certains de glBind*
appel.
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; elles représentent un cas particulier de reliure. De nombreux appels glBind*
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 par écrit (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 car lorsque vous les liez d'abord à une cible, elles obtiennent des informations spéciales. Lorsque vous liez d'abord une texture en tant que GL_TEXTURE_2D
, Vous sont en train de définir un état spécial dans la texture. Vous dites que cette texture est une texture 2D. Et il sera toujours une texture 2D; cet état ne peut jamais ê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 queGL_TEXTURE_2D
; tenter de la lier en tant que GL_TEXTURE_1D
donnera lieu à une erreur (lors de l'exécution).
Une fois l'objet lié, son état peut être modifié. Ceci est fait via des fonctions génériques spécifiques à cela objet. Ils 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 ce qui se trouve dans la valeur loc
actuellement liée.
Pour les objets de texture, les principales fonctions de modification de l'état de texture sont glTexParameter
. Les seules autres fonctions qui changent l'état de la texture sont glTexImage
fonctions et leurs variations (glCompressedTexImage
, glCopyTexImage
, le récent glTexStorage
). Les différentes versions 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. Cela 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 la texture objet.
Texture Active
La situation pour les textures est plus complexe, encore une fois pour des raisons héritées mieux laisser non divulguées. C'est là glActiveTexture
vient dans.
Pour les textures, il n'y a pas que des cibles (GL_TEXTURE_1D
, GL_TEXTURE_CUBE_MAP
, etc). Il y a aussi texture unités . En termes de notre exemple C / C++, ce que nous avons est ceci:
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;
}
Remarquez que maintenant, nous avons non seulement une 2D liste de Object
s, mais nous avons également le concept d'un objet courant. Nous avons un 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'objets sont ajustées pour sélectionner l'objet actuel.
Lorsque vous modifiez l'objet 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 va modifier un objet complètement différent.
Cette analogie avec les objets de texture est parfaite... Presque.
Voir, glActiveTexture
ne prenez pas un entier, il faut un énumérateur. Ce qui en théorie signifie 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. Pour exemple, sur GL 3.matériel de classe x, 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, {[35] } sera 48.
glActiveTexture
ne prend pas vraiment les recenseurs. La façon 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
.
Rendu
Alors, qu'est-ce que tout cela a à voir avec le rendu?
Quand à l'aide de 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 sélectionnera la cible en fonction du type d'échantillonneur. Donc, un {[44] } choisira parmi la cible GL_TEXTURE_2D
. C'est l'une des raisons pour lesquelles les échantillonneurs ont des types différents.
Maintenant, cela semble étrangement comme si vous pouviez avoir deux échantillonneurs GLSL, avec des types différents 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 tentez de vous rendre.
Je vais essayer ! Tout cela n'est pas si compliqué, juste une question de termes, j'espère que je vais me faire comprendre.
Vous pouvez créer à peu près autant d'objets 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).
Lors de la création, vous devez affecter une cible de Texture à 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, texture de l'objet et texture cible représentent les données de la texture. Nous y reviendrons plus tard.
Unités de Texture
Maintenant, OpenGL fournit un tableau de unités de texture , qui peuvent être utilisées simultanément pendant le dessin. La taille du tableau dépend du système OpenGL, le vôtre a 8.
Vous pouvez lier un objet de texture à une unité de texture pour utiliser la texture donnée pendant le 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 d'état, cela, hélas, ne fonctionne pas de cette façon. Supposons que notre textureObject
ait des données pour la cible de texture GL_TEXTURE_2D
, 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 voulez 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:
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)
Les Shaders
Les Shaders ont accès à toutes les unités de texture, ils ne se soucient pas du active la texture.
Sampler uniformes sont int
valeurs représentant l'index de l'unité de texture à utiliser pour l'échantillonneur (et pas la texture de l'objet à utiliser).
Vous devez donc lier vos objets de texture aux unités que vous souhaitez utiliser.
Le type de l'échantillonneur faire la correspondance avec la texture de la cible qui est utilisé dans l'unité de texture : Sampler2D
pour GL_TEXTURE_2D
, et ainsi de suite...
Imaginez le GPU comme une usine de traitement de peinture.
Il y a un certain nombre de réservoirs, qui fournit le colorant à une machine de peinture. Dans la machine à peindre, le colorant est ensuite appliqué sur l'objet. Ces réservoirs sont les unités de texture
Ces réservoirs peuvent être équipés de différents types de colorant. Chaque type de colorant nécessite un autre type de solvant. Le "solvant" est la cible de texture . Pour plus de commodité, chaque réservoir est connecté à une alimentation en solvant, et mais seulement un type de solvant peut être utilisé à la fois dans chaque réservoir. Donc, il y a 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 "dilue" que le type de colorant correspondant. Ainsi, vous pouvez avoir chaque type de texture liée, mais la liaison avec le solvant "le plus important" ira réellement 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". C'est la texture de vos cheveux.
Si dans votre shader vous avez besoin de recherche à partir de 2 textures:
uniform sampler2D tex1;
uniform sampler2D tex2;
, Il est nécessaire d'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 un gl_bindtexture, pas possible de faire une telle chose. D'autre part, une utilisation possible de lier dans la boucle de rendu, dans le cas où vous nourrissez une texture avec un contenu en flux (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