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 loc
reprezentuje 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_1D
spowoduje 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 loc
wartości.
W przypadku obiektów tekstur głównymi funkcjami zmiany stanu tekstury są glTexParameter
. Tylko inne funkcje, które zmieniają stan tekstury są glTexImage
funkcje i ich odmiany ( glCompressedTexImage
, glCopyTexImage
, ostatnie glTexStorage
). Różne SubImage
wersje zmieniają zawartość tekstury, ale technicznie nie zmieniają jej stanu . Te Image
funkcje przeznaczyć przechowywania tekstur i ustawić format fakturze za; te SubImage
funkcje 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. glTexEnv
modyfikuje 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 glActiveTexture
pojawia się.
Tekstury, nie są tylko cele ( GL_TEXTURE_1D
, GL_TEXTURE_CUBE_MAP
itp). 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 Object
s, 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, glActiveTexture
nie przyjmuje liczby całkowitej; wymaga modułu wyliczającego . Co w teorii oznacza, że może to potrwać od GL_TEXTURE0
do GL_TEXTURE31
. Ale jest jedna rzecz, którą musisz zrozumieć:
TO NIEPRAWDA!
Rzeczywisty zakres, jaki glActiveTexture
moż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_UNITS
będzie 48.
Ale nie ma 48 rachmistrzów. Dlatego glActiveTexture
tak naprawdę nie biorą rachunków. Prawidłowy sposób połączenia glActiveTexture
jest następujący:
glActiveTexture(GL_TEXTURE0 + i);
gdzie i
jest 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 i
jest jednostka obrazu). To oznacza liczbę, z którą korzystałeś glActiveTexture
. Próbnik wybierze cel na podstawie typu próbnika. Więc sampler2D
wybierze z GL_TEXTURE_2D
celu. 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ć.