Ответ 1
Все о объектах OpenGL
Стандартная модель для объектов OpenGL выглядит следующим образом.
Объекты имеют состояние. Думайте о них как о struct
. Таким образом, у вас может быть объект, определенный следующим образом:
struct Object
{
int count;
float opacity;
char *name;
};
Объект имеет определенные значения, хранящиеся в нем, и он имеет состояние. Объекты OpenGL также имеют состояние.
Изменение состояния
В C/С++, если у вас есть экземпляр типа Object
, вы измените его состояние следующим образом: obj.count = 5;
Вы непосредственно ссылаетесь на экземпляр объекта, получаете конкретное состояние, которое хотите изменить, и вставьте в него значение.
В OpenGL вы не делаете этого.
По старым причинам лучше оставить необъяснимым, чтобы изменить состояние объекта OpenGL, вы должны сначала привязать его к контексту. Это делается с некоторыми из вызова glBind*
.
эквивалент 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_WRITE_FRAMEBUFFER
). Это влияет на то, как OpenGL использует буфер. Вот что представляет собой параметр loc
.
Текстуры являются особенными, потому что, когда вы сначала привязываете их к цели, они получают специальную информацию. Когда вы сначала привязываете текстуру как GL_TEXTURE_2D
, вы фактически устанавливаете особое состояние в текстуре. Вы говорите, что эта текстура является двумерной текстурой. И это всегда будет 2D-текстура; это состояние невозможно изменить. Если у вас есть текстура, которая была сначала связана как GL_TEXTURE_2D
, вы всегда должны привязывать ее как GL_TEXTURE_2D
; попытка связать его как GL_TEXTURE_1D
приведет к ошибке (во время выполнения).
Как только объект привязан, его состояние можно изменить. Это делается через общие функции, специфичные для этого объекта. Они также берут местоположение, которое представляет, какой объект изменить.
В 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/С++, то у нас есть следующее:
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
s, но также есть понятие текущего объекта. У нас есть функция для установки текущего объекта, у нас есть концепция максимального количества текущих объектов, и все наши функции манипуляции с объектом настраиваются для выбора из текущего объекта.
При изменении текущего активного объекта вы изменяете весь набор целевых местоположений. Таким образом, вы можете связать что-то, что входит в текущий объект 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
.
Rendering
Так что же все это связано с рендерингом?
При использовании шейдеров вы устанавливаете униформу сэмплера в единицу изображения текстуры (glUniform1i(samplerLoc, i)
, где i
- единица изображения). Это означает номер, который вы использовали с glActiveTexture
. Сэмплер будет выбирать цель на основе типа сэмплера. Таким образом, sampler2D
будет выбирать из цели GL_TEXTURE_2D
. Это одна из причин, почему семплеры имеют разные типы.
Теперь это звучит подозрительно, так как у вас могут быть два пробоотборника GLSL, с разными типами, которые используют один и тот же блок изображения текстуры. Но вы не можете; OpenGL запрещает это и даст вам ошибку при попытке рендеринга.