Все об объектах OpenGL
Стандартная модель для объектов OpenGL выглядит следующим образом.
Объекты имеют состояние.Думайте о них как о struct
.Таким образом, у вас может быть объект, определенный следующим образом:
struct Object
{
int count;
float opacity;
char *name;
};
В этом объекте хранятся определенные значения, и он имеет состояние .У объектов OpenGL тоже есть состояние.
Изменение состояния
В C / C ++, если у вас есть экземпляр типа Object
, вы бы изменили его состояние следующим образом: obj.count = 5;
Вы бы напрямуюссылаться на экземпляр объекта, получить конкретную часть состояния, которую вы хотите изменить, и вставить в нее значение.
В OpenGL вы не делаете это.
По старым причинам, которые лучше оставить необъясненными, чтобы изменить состояние объекта OpenGL, вы должны сначала связать его с контекстом.Это делается с помощью некоторых из вызовов glBind*
.
Эквивалент C / C ++ выглядит следующим образом:
Object *g_objs[MAX_LOCATIONS] = {NULL};
void BindObject(int loc, Object *obj)
{
g_objs[loc] = obj;
}
Текстуры интересны;они представляют собой особый случай связывания.Многие вызовы glBind*
имеют параметр target.Это представляет различные местоположения в контексте OpenGL, где объекты этого типа могут быть связаны.Например, вы можете связать объект framebuffer для чтения (GL_READ_FRAMEBUFFER
) или для записи (GL_DRAW_FRAMEBUFFER
).Это влияет на то, как OpenGL использует буфер.Это то, что представляет параметр loc
выше.
Текстуры особенные, потому что когда вы first привязываете их к цели, они получают специальную информацию.Когда вы впервые связываете текстуру как GL_TEXTURE_2D
, вы фактически устанавливаете специальное состояние в текстуре.Вы говорите, что эта текстура является 2D текстурой.И это будет всегда будет 2D текстура;это состояние нельзя изменить когда-либо .Если у вас есть текстура, которая была сначала связана как GL_TEXTURE_2D
, вы должны всегда связать ее как GL_TEXTURE_2D
;попытка связать его как GL_TEXTURE_1D
приведет к ошибке (во время выполнения).
Когда объект привязан, его состояние можно изменить.Это делается с помощью общих функций, специфичных для этого объекта.Они также занимают место, представляющее, какой объект нужно изменить.
В C / C ++ это выглядит следующим образом:
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;
}
}
Обратите внимание, как эта функция устанавливает то, что оказывается в текущем связанном loc
значение.
Для объектов текстуры основными функциями изменения состояния текстуры являются glTexParameter
.Единственными другими функциями, которые изменяют состояние текстуры, являются функции glTexImage
и их варианты (glCompressedTexImage
, glCopyTexImage
, последние glTexStorage
).Различные версии SubImage
изменяют содержимое текстуры, но технически не изменяют ее состояние .Функции Image
распределяют хранилище текстур и устанавливают формат текстуры;SubImage
функции просто копируют пиксели вокруг.Это не считается состоянием текстуры.
Позвольте мне повторить: это только функции * , которые изменяют состояние текстуры.glTexEnv
изменяет состояние среды;это не влияет ни на что, хранящееся в объектах текстуры.
Активная текстура
Ситуация с текстурами более сложная, опять же по старым причинам лучше оставить нераскрытой.Вот тут и приходит glActiveTexture
.
Для текстур существуют не только цели (GL_TEXTURE_1D
, GL_TEXTURE_CUBE_MAP
и т. Д.).Есть также текстура единиц .С точки зрения нашего примера C / C ++, у нас есть следующее:
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;
}
Обратите внимание, что теперь у нас есть не только 2D-список Object
с, но у нас также есть концепция текущегообъект.У нас есть функция для установки текущего объекта, у нас есть концепция максимального количества текущих объектов, и все наши функции манипулирования объектами настроены для выбора из текущего объекта.
Когда вы изменяете текущий активный объектобъект, вы меняете весь набор целевых локаций.Таким образом, вы можете привязать что-то, что входит в текущий объект 0, переключиться на текущий объект 4 и будет изменять совершенно другой объект.
Эта аналогия с объектами текстуры идеальна ... почти.
Видите, glActiveTexture
не принимает целое число;требуется перечислитель .Что в теории означает, что это может занять от GL_TEXTURE0
до GL_TEXTURE31
.Но есть одна вещь, которую вы должны понять:
ЭТО ЛОЖЬ!
Фактический диапазон, который может принять glActiveTexture
, определяется GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS
.Это максимальное количество одновременных мультитекстур, которое допускает реализация.Каждый из них разделен на разные группы для разных этапов шейдера.Например, на оборудовании класса GL 3.x вы получаете 16 текстур вершинных шейдеров, 16 текстур фрагментных шейдеров и 16 текстур геометрических шейдеров.Следовательно, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS
будет 48.
Но нет 48 счетчиков.Вот почему glActiveTexture
на самом деле не использует счетчики. правильный способ вызова glActiveTexture
выглядит следующим образом:
glActiveTexture(GL_TEXTURE0 + i);
, где i
- это число от 0 до GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS
.
Рендеринг
Так, как все это связано с рендерингом?
При использовании шейдеров вы устанавливаете униформу сэмплера на единицу текстуры изображения (glUniform1i(samplerLoc, i)
, где i
- единица изображения),Это число, которое вы использовали с glActiveTexture
.Сэмплер выберет цель в зависимости от типа сэмплера.Таким образом, sampler2D
выберет цель GL_TEXTURE_2D
.Это одна из причин, по которой сэмплеры имеют разные типы.
Теперь это звучит подозрительно, как будто у вас может быть два сэмплера GLSL с разными типами , которые используют один и тот же текстурный блок изображения.Но ты не можешь;OpenGL запрещает это и выдает ошибку при попытке рендеринга.