Differenze e relazione tra glActiveTexture e glBindTexture


137

Da quello che raccolgo, glActiveTextureimposta l '"unità texture" attiva. Ogni unità texture può avere più target texture (di solito GL_TEXTURE_1D, 2D, 3D o CUBE_MAP).

Se ho capito bene, devi chiamare glActiveTextureper impostare prima l'unità texture (inizializzata su GL_TEXTURE0), e poi leghi (uno o più) "target texture" a quell'unità texture?

Il numero di unità di trama disponibili dipende dal sistema. Vedo enumerazioni per un massimo di 32 nella mia libreria. Immagino che questo essenzialmente significhi che posso avere il limite inferiore della mia GPU (cosa che penso sia168) e 32 trame nella memoria GPU contemporaneamente? Suppongo che ci sia un ulteriore limite per il fatto che non superi la memoria massima della mia GPU (presumibilmente 1 GB).

Comprendo correttamente la relazione tra target e unità texture? Diciamo che mi sono permesse 16 unità e 4 bersagli ciascuna, significa che c'è spazio per 16 * 4 = 64 bersagli, o non funziona così?

Successivamente in genere si desidera caricare una trama. Puoi farlo tramite glTexImage2D. Il primo argomento di cui è un obiettivo trama. Se funziona cosìglBufferData , essenzialmente leghiamo il "handle" / "nome della trama" alla destinazione della trama, quindi cariciamo i dati della trama in quella destinazione, e quindi associamo indirettamente a quella maniglia.

Che dire glTexParameter? Dobbiamo associare un target texture e quindi scegliere di nuovo lo stesso target come primo argomento? Oppure non è necessario che il target texture sia vincolato finché abbiamo l'unità texture attiva corretta?

glGenerateMipmap opera anche su un bersaglio ... quel bersaglio deve ancora essere legato al nome della trama per avere successo?

Quindi, quando vogliamo disegnare il nostro oggetto con una trama su di esso, dobbiamo scegliere sia un'unità di trama attiva, sia una destinazione di trama? Oppure scegliamo un'unità texture e quindi possiamo prendere i dati da uno dei 4 target associati a quell'unità? Questa è la parte che mi sta davvero confondendo.

Risposte:


259

Tutto sugli oggetti OpenGL

Il modello standard per gli oggetti OpenGL è il seguente.

Gli oggetti hanno stato. Pensa a loro come a struct. Quindi potresti avere un oggetto definito in questo modo:

struct Object
{
    int count;
    float opacity;
    char *name;
};

L'oggetto contiene alcuni valori memorizzati e ha stato . Anche gli oggetti OpenGL hanno stato.

Stato cambiante

In C / C ++, se si dispone di un'istanza di tipo Object, è necessario modificarne lo stato nel modo seguente: obj.count = 5;Fare riferimento direttamente a un'istanza dell'oggetto, ottenere il particolare pezzo di stato che si desidera modificare e inserire un valore in esso.

In OpenGL, non lo fai.

Per motivi legacy meglio lasciare inspiegabili, per cambiare lo stato di un oggetto OpenGL, è necessario prima vincolarlo al contesto. Questo viene fatto con alcuni di glBind*chiamata.

Il C / C ++ equivalente a questo è il seguente:

Object *g_objs[MAX_LOCATIONS] = {NULL};    
void BindObject(int loc, Object *obj)
{
  g_objs[loc] = obj;
}

Le trame sono interessanti; rappresentano un caso speciale di rilegatura. Molte glBind*chiamate hanno un parametro "target". Ciò rappresenta posizioni diverse nel contesto OpenGL in cui è possibile associare oggetti di quel tipo. Ad esempio, è possibile associare un oggetto framebuffer per reading ( GL_READ_FRAMEBUFFER) o per writing ( GL_DRAW_FRAMEBUFFER). Questo influenza il modo in cui OpenGL utilizza il buffer. Questo è ciò che rappresenta il locparametro sopra.

Le trame sono speciali perché quando le leghi per la prima volta a un bersaglio, ottengono informazioni speciali. Quando si associa per la prima volta una trama come a GL_TEXTURE_2D, in realtà si imposta uno stato speciale nella trama. Stai dicendo che questa trama è una trama 2D. E sarà sempre una trama 2D; questo stato non può essere cambiato mai . Se hai una trama che è stata prima associata come a GL_TEXTURE_2D, devi sempre legarla come a GL_TEXTURE_2D; il tentativo di vincolarlo in quanto GL_TEXTURE_1Dprovoca un errore (durante il runtime).

Una volta associato l'oggetto, è possibile modificarne lo stato. Questo viene fatto tramite funzioni generiche specifiche per quell'oggetto. Anche loro prendono una posizione che rappresenta quale oggetto modificare.

In C / C ++, questo assomiglia a:

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

Notare come questa funzione imposta qualunque cosa si trovi nel locvalore attualmente associato .

Per gli oggetti trama, sono le principali funzioni che cambiano lo stato della trama glTexParameter. Le uniche altre funzioni che cambiano stato trama sono le glTexImagefunzioni e le loro variazioni ( glCompressedTexImage, glCopyTexImage, il recente glTexStorage). Le varie SubImageversioni cambiano il contenuto della trama, ma tecnicamente non cambiano il suo stato . Le Imagefunzioni assegnano l'archiviazione delle trame e impostano il formato della trama; le SubImagefunzioni copiano solo i pixel. Questo non è considerato lo stato della trama.

Consentitemi di ripetere: queste sono le uniche funzioni che modificano lo stato della trama. glTexEnvmodifica lo stato dell'ambiente; non influisce su nulla memorizzato negli oggetti texture.

Trama attiva

La situazione delle trame è più complessa, sempre per motivi legati al passato che è meglio non rivelare. È qui che glActiveTextureentra in gioco.

Per le texture, non ci sono solo bersagli ( GL_TEXTURE_1D, GL_TEXTURE_CUBE_MAP, ecc). Ci sono anche unità di trama . In termini del nostro esempio C / C ++, ciò che abbiamo è questo:

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

Si noti che ora non abbiamo solo un elenco 2D di Objects, ma abbiamo anche il concetto di un oggetto corrente. Abbiamo una funzione per impostare l'oggetto corrente, abbiamo il concetto di un numero massimo di oggetti correnti e tutte le nostre funzioni di manipolazione degli oggetti sono regolate per selezionare l'oggetto corrente.

Quando si modifica l'oggetto attualmente attivo, si modifica l'intero set di posizioni di destinazione. Quindi puoi associare qualcosa che va nell'oggetto corrente 0, passare all'oggetto corrente 4 e modificherà un oggetto completamente diverso.

Questa analogia con gli oggetti texture è perfetta ... quasi.

Vedi, glActiveTexturenon accetta un numero intero; ci vuole un enumeratore . Il che in teoria significa che può richiedere qualsiasi cosa da GL_TEXTURE0a GL_TEXTURE31. Ma c'è una cosa che devi capire:

QUESTO È FALSO!

L'intervallo effettivo che glActiveTexturepuò assumere è regolato da GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS. Questo è il numero massimo di multitasche simultanee consentite da un'implementazione. Questi sono ciascuno suddiviso in diversi raggruppamenti per diversi stadi shader. Ad esempio, sull'hardware di classe GL 3.x, si ottengono 16 trame shader di vertici, 16 trame shader di frammenti e 16 trame shader di geometria. Pertanto, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITSsaranno 48.

Ma non ci sono 48 enumeratori. Ecco perché glActiveTexturenon accetta davvero gli enumeratori. Il modo corretto di chiamare glActiveTextureè il seguente:

glActiveTexture(GL_TEXTURE0 + i);

dove iè un numero compreso tra 0 e GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS.

interpretazione

Quindi cosa c'entra tutto questo con il rendering?

Quando si usano gli shader, si impostano le uniformi del campionatore su un'unità immagine trama ( glUniform1i(samplerLoc, i), dove si itrova l'unità immagine). Questo rappresenta il numero con cui hai usato glActiveTexture. Il campionatore sceglierà il target in base al tipo di campionatore. Quindi un sampler2Dsceglierà dal GL_TEXTURE_2Dbersaglio. Questo è uno dei motivi per cui i campionatori hanno tipi diversi.

Ora sembra sospetto che tu possa avere due campionatori GLSL, con tipi diversi che usano la stessa unità di immagine di trama. Ma non puoi; OpenGL lo proibisce e ti darà un errore quando tenti di renderizzare.


12
Wow! Ancora un'altra risposta meravigliosa - grazie Nicol! Mi piace soprattutto quel paragrafo su una trama 2D che è sempre una trama 2D. Sto costruendo un involucro attorno ad alcune di queste cose ora, e non ero sicuro se avrei dovuto lasciarlo aperto al cambiamento. E la parte su GL_TEXTURE0 + i- intendevo ispezionare i valori di enum per vedere se era valido o no. E l'ultimo paragrafo - non sapevo se fosse legale o no. Eccellente! Aggiungo un segnalibro a tutte le tue risposte in modo da poterle fare nuovamente riferimento.
apre il

6
@Nicol Bolas: questo è davvero ben spiegato. Dovresti copiarne una parte nel capitolo delle trame del tuo libro online. Penso che sia molto più chiaro e complimenti per il capitolo.
Wes Dec

3
@Nicol Bolas Sto iniziando a imparare OpenGL e questa risposta mi ha aiutato moltissimo. Grazie!
linea

2
Ehi nico, voglio solo sottolineare il tuo piccolo errore di battitura: è GL_DRAW_FRAMEBUFFER non GL_WRITE_FRAMEBUFFER
Defd

3
@Nicol: Wow, la migliore definizione che ho avuto di questo prima ora era dai tuoi tutorial sull'arcosintesi, ora hai superato anche quella fonte brillante. Grazie
Baggers,

20

Lo proverò ! Tutto ciò non è così complicato, solo una questione di termini, spero che mi chiarisca.


Puoi creare all'incirca tanti oggetti trama quanti sono i dati disponibili nel tuo sistema. Questi oggetti contengono i dati effettivi (texel) delle tue trame, insieme ai parametri, forniti da glTexParameter (vedi FAQ ).

Quando si sta creando, è necessario assegnare una destinazione texture ad un oggetto tessitura, che rappresenta il tipo di trama ( GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE, ...).

Questi due elementi, oggetto trama e destinazione trama rappresentano i dati della trama. Torneremo da loro più tardi.

Unità di trama

Ora OpenGL fornisce una serie di unità di trama , che possono essere utilizzate contemporaneamente durante il disegno. La dimensione dell'array dipende dal sistema OpenGL, il tuo ne ha 8.

È possibile associare un oggetto trama a un'unità trama per utilizzare la trama data durante il disegno.

In un mondo semplice e facile, per disegnare con una determinata trama, assoceresti un oggetto trama all'unità trama e lo faresti (pseudocodice):

glTextureUnit[0] = textureObject

Poiché GL è una macchina a stati, purtroppo non funziona in questo modo. Supponendo che abbiamo textureObjectdati per il GL_TEXTURE_2Dtarget texture, esprimeremo l'assegnazione precedente come:

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

Nota che GL_TEXTURE_2Ddipende davvero dal tipo di trama che vuoi legare.

Oggetti texture

Nello pseudo codice, per impostare i dati di trama o i parametri di trama, dovresti fare ad esempio:

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

OpenGL non può manipolare direttamente gli oggetti trama, per aggiornare / impostare il loro contenuto o modificare i loro parametri, devi prima vincolarli all'unità trama attiva (qualunque essa sia). Il codice equivalente diventa:

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

Gli shader hanno accesso a tutte le unità di trama, a loro non importa della trama attiva.

Le uniformi del campionatore sono intvalori che rappresentano l'indice dell'unità di trama da utilizzare per il campionatore (e non l'oggetto di trama da utilizzare).

Quindi devi associare i tuoi oggetti texture alle unità che vuoi usare.

Il tipo di campionatore farà la corrispondenza con il target di trama che viene utilizzato nell'unità di trama: Sampler2Dper GL_TEXTURE_2De così via ...


Una cosa che non capisco. Supponiamo che io abbia un po 'di trama ed è usato in molti shader su diverse unità di trama. Supponiamo di voler cambiare il filtro delle trame in fase di esecuzione. Quale unità texture dovrei usare? Posso cambiare lo stato della trama sull'Unità 0 e quindi usare quella trama su un'altra unità?
majakthecoder

@majakthecoder Nella mia risposta, considero il filtro come una proprietà dell'oggetto trama , il che significa che non è possibile modificarlo in modo specifico in un'unità trama. A seconda del sapore di OpenGL che stai prendendo di mira , potresti essere in grado di campionare oggetti per risolvere questo problema ( opengl.org/wiki/Sampler_Object ), altrimenti potresti dover duplicare l'oggetto texture, per avere più filtri simultanei.
Rotoglup

12

Immagina la GPU come un impianto di lavorazione della vernice.

Ci sono un certo numero di carri armati, che consegna la tintura ad alcune macchine di verniciatura. Nella macchina di verniciatura la tinta viene quindi applicata all'oggetto. Quei carri armati sono le unità di trama

Quei serbatoi possono essere equipaggiati con diversi tipi di colorante. Ogni tipo di colorante richiede qualche altro tipo di solvente. Il "solvente" è l' obiettivo della trama . Per comodità, ogni serbatoio è collegato a una certa quantità di solvente, ma in ciascun serbatoio è possibile utilizzare un solo tipo di solvente alla volta. Quindi c'è una valvola / interruttore TEXTURE_CUBE_MAP, TEXTURE_3D, TEXTURE_2D, TEXTURE_1D. Puoi riempire tutti i tipi di colorante contemporaneamente nel serbatoio, ma poiché entra solo un tipo di solvente, "diluirà" solo il tipo di colorazione corrispondente. Quindi puoi avere ogni tipo di trama legata, ma il legame con il solvente "più importante" andrà effettivamente nel serbatoio e si mescolerà con il tipo di colorante a cui appartiene.

E poi c'è il colorante stesso, che proviene da un magazzino e viene riempito nel serbatoio "legandolo". Questa è la tua trama.


2
Una specie di strana analogia ... Non sono sicuro che chiarisca davvero qualcosa. Soprattutto la parte relativa alla "diluizione" e al "solvente più importante". Stai dicendo che se associo una trama 2d a una trama 3d, posso usarne solo una, o cosa? Quale sarebbe ritenuto più importante?
Aprire il

2
@Mark: Beh, stavo cercando di parlare in termini di un pittore che lavora con il colorante letterale (diciamo a base di olio e di acqua). Ad ogni modo, sì, se si associano e si abilitano più target texture, c'è la precedenza: CUBE_MAP> 3D> TEXTURE_ARRAY> 2D> 1D.
datenwolf

1
! Neat Non sapevo della precedenza. Ha più senso ora che so che è possibile utilizzare solo un target trama per unità trama.
apre il

1
@ legends2k: Bene, ora sta diventando interessante. Stiamo parlando del core o del profilo di compatibilità. Supponiamo guidatori ideali o buggy. In teoria il tipo di uniforme seleziona quale target dell'unità texture selezionare. In pratica ciò accade nel profilo principale. Nel profilo di compatibilità, si aspettano che alcuni driver difettosi presentino la trama predefinita completamente bianca se il target precedente dell'unità trama non corrisponde al tipo di campionatore.
datenwolf,

1
@ legends2k: Pensa anche a cosa succederebbe se una trama 2D e una 3D fossero legate alla stessa unità e avessi un'uniforme di campionamento 2D e 3D, che legheresti alla stessa unità? Con questo puoi innescare tutti i tipi di strani bug del driver. In pratica, pensare nel vecchio modello di precedenza a funzione fissa mantiene la mente sana e il programma funzionante, perché è così che la maggior parte dei conducenti si comporterà in modo prevedibile.
datenwolf,

2

Se nel tuo shader hai bisogno di cercare da 2 trame:

uniform sampler2D tex1;  
uniform sampler2D tex2;  

è necessario indicare per tex1 e tex2 le loro fonti come segue:

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");

nel ciclo di rendering:

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

Con una gl_bindtexture, non è possibile fare una cosa del genere. D'altra parte un possibile uso di un bind nel ciclo di rendering è il caso in cui si nutre una trama con un contenuto in streaming (video, 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.