Wszystko o obiektach OpenGL
Standardowy model obiektów OpenGL jest następujący.
Obiekty mają stan. Pomyśl o nich jak o struct. Więc możesz mieć obiekt zdefiniowany w ten sposób:
struct Object
{
int count;
float opacity;
char *name;
};
Obiekt ma zapisane w sobie określone wartości i ma stan . Obiekty OpenGL również mają stan.
Zmiana stanu
W C / C ++, jeśli masz wystąpienie typu Object, możesz zmienić jego stan w następujący sposób: obj.count = 5;Możesz bezpośrednio odwołać się do wystąpienia obiektu, pobrać konkretny stan, który chcesz zmienić, i wrzucić do niego wartość.
W OpenGL tego nie robisz.
Ze starszych powodów lepiej pozostawić je niewyjaśnione, aby zmienić stan obiektu OpenGL, należy najpierw powiązać go z kontekstem. Odbywa się to z niektórymi z glBind*połączeń.
Odpowiednik C / C ++ do tego jest następujący:
Object *g_objs[MAX_LOCATIONS] = {NULL};
void BindObject(int loc, Object *obj)
{
g_objs[loc] = obj;
}
Tekstury są interesujące; stanowią szczególny przypadek wiążący. Wiele glBind*wywołań ma parametr „target”. Reprezentuje różne lokalizacje w kontekście OpenGL, w których można wiązać obiekty tego typu. Na przykład, możesz powiązać obiekt bufora ramki do odczytu ( GL_READ_FRAMEBUFFER) lub do zapisu ( GL_DRAW_FRAMEBUFFER). Ma to wpływ na sposób, w jaki OpenGL używa bufora. To właśnie locreprezentuje powyższy parametr.
Tekstury są wyjątkowe, ponieważ kiedy po raz pierwszy związujesz je z celem, otrzymują specjalne informacje. Kiedy po raz pierwszy wiążesz teksturę jako teksturę GL_TEXTURE_2D, w rzeczywistości ustawiasz specjalny stan tekstury. Mówisz, że ta tekstura jest teksturą 2D. I zawsze będzie to tekstura 2D; tego stanu nigdy nie można zmienić . Jeśli masz teksturę, która została najpierw oprawiona jako a GL_TEXTURE_2D, musisz zawsze związać ją jako GL_TEXTURE_2D; próba powiązania go jako GL_TEXTURE_1Dspowoduje błąd (w czasie wykonywania).
Po związaniu obiektu można zmienić jego stan. Odbywa się to za pomocą ogólnych funkcji specyficznych dla tego obiektu. One również przyjmują lokalizację, która reprezentuje obiekt do zmodyfikowania.
W C / C ++ wygląda to tak:
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;
}
}
Zwróć uwagę, jak ta funkcja ustawia wszystko, co znajduje się w aktualnie powiązanej locwartości.
W przypadku obiektów tekstur głównymi funkcjami zmiany stanu tekstury są glTexParameter. Tylko inne funkcje, które zmieniają stan tekstury są glTexImagefunkcje i ich odmiany ( glCompressedTexImage, glCopyTexImage, ostatnie glTexStorage). Różne SubImagewersje zmieniają zawartość tekstury, ale technicznie nie zmieniają jej stanu . Te Imagefunkcje przeznaczyć przechowywania tekstur i ustawić format fakturze za; te SubImagefunkcje po prostu skopiować piksele wokół. Nie jest to uważane za stan tekstury.
Pozwólcie, że powtórzę: to jedyne funkcje, które modyfikują stan tekstury. glTexEnvmodyfikuje stan środowiska; nie wpływa na nic przechowywanego w obiektach tekstur.
Aktywna tekstura
Sytuacja dotycząca tekstur jest bardziej złożona, ponownie ze względu na dziedzictwo lepiej nie ujawniać. Tutaj glActiveTexturepojawia się.
Tekstury, nie są tylko cele ( GL_TEXTURE_1D, GL_TEXTURE_CUBE_MAPitp). Istnieją również jednostki tekstur . Jeśli chodzi o nasz przykład C / C ++, mamy to:
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;
}
Zauważ, że teraz mamy nie tylko listę dwuwymiarowych Objects, ale mamy również koncepcję bieżącego obiektu. Mamy funkcję ustawiania bieżącego obiektu, koncepcję maksymalnej liczby bieżących obiektów, a wszystkie nasze funkcje manipulacji obiektami są dostosowane do wybierania z bieżącego obiektu.
Zmiana aktualnie aktywnego obiektu powoduje zmianę całego zestawu docelowych lokalizacji. Możesz więc powiązać coś, co trafia do bieżącego obiektu 0, przełączyć się na bieżący obiekt 4 i będzie modyfikować zupełnie inny obiekt.
Ta analogia z obiektami tekstur jest idealna ... prawie.
Widzisz, glActiveTexturenie przyjmuje liczby całkowitej; wymaga modułu wyliczającego . Co w teorii oznacza, że może to potrwać od GL_TEXTURE0do GL_TEXTURE31. Ale jest jedna rzecz, którą musisz zrozumieć:
TO NIEPRAWDA!
Rzeczywisty zakres, jaki glActiveTexturemoże zająć, jest regulowany przez GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS. To jest maksymalna liczba jednoczesnych multitekstur, na jaką pozwala implementacja. Są one podzielone na różne grupy dla różnych etapów modułu cieniującego. Na przykład na sprzęcie klasy GL 3.x otrzymasz 16 tekstur Vertex Shader, 16 tekstur Fragment Shader i 16 Geometry Shader. Dlatego GL_MAX_COMBINED_TEXTURE_IMAGE_UNITSbędzie 48.
Ale nie ma 48 rachmistrzów. Dlatego glActiveTexturetak naprawdę nie biorą rachunków. Prawidłowy sposób połączenia glActiveTexturejest następujący:
glActiveTexture(GL_TEXTURE0 + i);
gdzie ijest liczbą od 0 do GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS.
Wykonanie
Więc co to wszystko ma wspólnego z renderowaniem?
Korzystając z shaderów, ustawiasz mundury próbnika na jednostkę obrazu tekstury ( glUniform1i(samplerLoc, i)gdzie ijest jednostka obrazu). To oznacza liczbę, z którą korzystałeś glActiveTexture. Próbnik wybierze cel na podstawie typu próbnika. Więc sampler2Dwybierze z GL_TEXTURE_2Dcelu. Jest to jeden z powodów, dla których próbniki mają różne typy.
Teraz brzmi to podejrzanie, jakbyś mógł mieć dwa samplery GLSL, z różnymi typami, które używają tej samej jednostki obrazu tekstury. Ale nie możesz; OpenGL zabrania tego i wyświetli błąd podczas próby renderowania.
GL_TEXTURE0 + i- miałem zamiar sprawdzić wartości wyliczenia, aby sprawdzić, czy są prawidłowe, czy nie. I ostatni akapit - nie wiedziałem, czy to legalne, czy nie. Doskonały! Zapisuję wszystkie Twoje odpowiedzi jako zakładki, więc mogę do nich ponownie zajrzeć.