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ć.Dam temu szansę ! To wszystko nie jest takie skomplikowane, tylko kwestia terminów, mam nadzieję, że wyjaśnię.
Możesz utworzyć mniej więcej tyle obiektów tekstury, ile jest dostępnej pamięci w twoim systemie. Obiekty te przechowują rzeczywiste dane (tekstury) twoich tekstur, wraz z parametrami dostarczonymi przez glTexParameter (patrz FAQ ).
Kiedy powstaje, trzeba przypisać jedną Texture docelowy do jednego obiektu tekstury, która reprezentuje typ tekstury (
GL_TEXTURE_2D
,GL_TEXTURE_3D
,GL_TEXTURE_CUBE
, ...).Te dwa elementy, obiekt tekstury i obiekt docelowy tekstury, reprezentują dane tekstury. Wrócimy do nich później.
Jednostki tekstury
Teraz OpenGL zapewnia szereg jednostek tekstur , których można używać jednocześnie podczas rysowania. Rozmiar tablicy zależy od systemu OpenGL, twoja ma 8.
Możesz powiązać obiekt tekstury z jednostką tekstury, aby używać danej tekstury podczas rysowania.
W prostym i łatwym świecie, aby narysować daną teksturę, związałbyś obiekt tekstury z jednostką tekstury i zrobiłbyś (pseudokod):
Ponieważ GL jest maszyną stanową, niestety nie działa w ten sposób. Zakładając, że mamy
textureObject
dane dlaGL_TEXTURE_2D
celu tekstury, poprzednie przypisanie wyrażamy jako:Zauważ, że to
GL_TEXTURE_2D
naprawdę zależy od rodzaju tekstury, którą chcesz związać.Obiekty tekstury
W pseudokodzie, aby ustawić dane tekstury lub parametry tekstury, zrobiłbyś na przykład:
OpenGL nie może bezpośrednio manipulować obiektami tekstur, aby zaktualizować / ustawić ich zawartość lub zmienić ich parametry, musisz najpierw powiązać je z aktywną jednostką tekstury (cokolwiek to jest). Odpowiedni kod to:
Shadery
Shadery mają dostęp do wszystkich tekstur, nie dbają o aktywną teksturę.
Mundury próbnika to
int
wartości reprezentujące indeks jednostki tekstury, która ma być używana przez próbnik (a nie obiekt tekstury, który ma być użyty).Musisz więc powiązać obiekty tekstur z jednostkami, których chcesz użyć.
Typ próbnika dopasuje się do docelowej tekstury używanej w jednostce tekstury:
Sampler2D
forGL_TEXTURE_2D
itd.źródło
Wyobraź sobie GPU jak jakąś fabrykę farb.
Istnieje wiele zbiorników, które dostarczają barwnik do niektórych maszyn malarskich. W maszynie malarskiej na przedmiot nanoszony jest następnie barwnik. Te czołgi to jednostki tekstury
Zbiorniki te można wyposażyć w różnego rodzaju barwniki. Każdy rodzaj barwnika wymaga innego rodzaju rozpuszczalnika. „Rozpuszczalnik” to docelowa tekstura . Dla wygody każdy zbiornik jest podłączony do pewnego źródła rozpuszczalnika, ale w każdym zbiorniku można stosować tylko jeden rodzaj rozpuszczalnika. Więc nie jest to zawór / wyłącznik
TEXTURE_CUBE_MAP
,TEXTURE_3D
,TEXTURE_2D
,TEXTURE_1D
. Możesz napełniać zbiornik jednocześnie wszystkimi typami barwników, ale ponieważ do zbiornika trafia tylko jeden rodzaj rozpuszczalnika, „rozcieńczy” on tylko ten rodzaj barwnika, który pasuje. Można więc związać każdą teksturę, ale wiązanie „najważniejszym” rozpuszczalnikiem w rzeczywistości trafia do zbiornika i miesza się z rodzajem barwnika, do którego należy.A potem jest sam barwnik, który pochodzi z magazynu i jest napełniany do zbiornika poprzez „wiązanie”. To twoja tekstura.
źródło
Jeśli w swoim module cieniującym potrzebujesz wyszukiwania z 2 tekstur:
dla tex1 i tex2 należy wskazać ich źródła w następujący sposób:
w pętli renderowania:
Z gl_bindtexture nie można tego zrobić. Z drugiej strony możliwe użycie wiązania w pętli renderowania ma miejsce w przypadku, gdy podajesz teksturę z zawartością w strumieniu (wideo, kamera internetowa):
źródło