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 loc
parametro 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_1D
provoca 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 loc
valore 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 glTexImage
funzioni e le loro variazioni ( glCompressedTexImage
, glCopyTexImage
, il recente glTexStorage
). Le varie SubImage
versioni cambiano il contenuto della trama, ma tecnicamente non cambiano il suo stato . Le Image
funzioni assegnano l'archiviazione delle trame e impostano il formato della trama; le SubImage
funzioni 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. glTexEnv
modifica 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 glActiveTexture
entra 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 Object
s, 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, glActiveTexture
non accetta un numero intero; ci vuole un enumeratore . Il che in teoria significa che può richiedere qualsiasi cosa da GL_TEXTURE0
a GL_TEXTURE31
. Ma c'è una cosa che devi capire:
QUESTO È FALSO!
L'intervallo effettivo che glActiveTexture
può 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_UNITS
saranno 48.
Ma non ci sono 48 enumeratori. Ecco perché glActiveTexture
non 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 i
trova 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 sampler2D
sceglierà dal GL_TEXTURE_2D
bersaglio. 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.
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.