Все об объектах 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*
звонки имеют целевой параметр. Это представляет различные местоположения в контексте OpenGL, где объекты этого типа могут быть связаны. Например, вы можете связать объект framebuffer для чтения ( GL_READ_FRAMEBUFFER
) или для записи ( GL_DRAW_FRAMEBUFFER
). Это влияет на то, как OpenGL использует буфер. Это то, что представляет loc
параметр выше.
Текстуры особенные, потому что когда вы впервые связываете их с целью, они получают особую информацию. Когда вы впервые связываете текстуру как 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;
}
Обратите внимание, что теперь у нас есть не только двухмерный список 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
.
оказание
Так, что все это имеет отношение к рендерингу?
При использовании шейдеров вы устанавливаете форму сэмплера в единицу текстуры изображения ( glUniform1i(samplerLoc, i)
где i
находится единица изображения). Это представляет число, которое вы использовали с glActiveTexture
. Сэмплер выберет цель в зависимости от типа сэмплера. Так sampler2D
что выберут из GL_TEXTURE_2D
цели. Это одна из причин, почему сэмплеры имеют разные типы.
Теперь это звучит подозрительно, как будто у вас может быть два сэмплера GLSL, с разными типами, которые используют одну и ту же текстурную единицу изображения. Но ты не можешь; OpenGL запрещает это и выдает ошибку при попытке рендеринга.
GL_TEXTURE0 + i
- я хотел проверить значения перечисления, чтобы увидеть, было ли это правильно или нет. И последний абзац - не знал, законно это или нет. Превосходно! Я добавляю в закладки все ваши ответы, чтобы снова обратиться к ним.Я попробую! Все это не так сложно, просто вопрос сроков, надеюсь, я все проясню.
Вы можете создать примерно столько объектов текстуры, сколько есть свободной памяти в вашей системе. Эти объекты содержат фактические данные (тексели) ваших текстур, а также параметры, предоставляемые glTexParameter (см. FAQ ).
Когда создаются, вы должны назначить один Texture объект на один объект текстуры, который представляет тип текстуры (
GL_TEXTURE_2D
,GL_TEXTURE_3D
,GL_TEXTURE_CUBE
...).Эти два элемента, текстуры объекта и целевые текстуры представляют данные текстуры. Мы вернемся к ним позже.
Единицы текстуры
Теперь OpenGL предоставляет массив текстурных блоков , которые можно использовать одновременно во время рисования. Размер массива зависит от системы OpenGL, у вас 8.
Вы можете привязать объект текстуры к текстурному блоку, чтобы использовать данную текстуру во время рисования.
В простом и легком мире, чтобы рисовать с заданной текстурой, вы привязываете текстурный объект к текстурному блоку и делаете (псевдокод):
Поскольку GL является конечным автоматом, он, увы, не работает таким образом. Предположим, что у нас
textureObject
есть данные дляGL_TEXTURE_2D
цели текстуры, мы будем выражать предыдущее назначение как:Обратите внимание, что на
GL_TEXTURE_2D
самом деле зависит от типа текстуры, которую вы хотите связать.Текстурные объекты
В псевдокоде, чтобы установить данные текстуры или параметры текстуры, вы должны сделать, например:
OpenGL не может напрямую манипулировать текстурными объектами, обновлять / устанавливать их содержимое или изменять их параметры, вы должны сначала связать их с активным текстурным модулем (в зависимости от того, что это). Эквивалентный код становится:
шейдеры
Шейдеры имеют доступ ко всем текстурным блокам, им нет дела до активной текстуры.
Униформа сэмплера - это
int
значения, представляющие индекс единицы текстуры, используемой для сэмплера (а не объекта текстуры, который нужно использовать).Таким образом, вы должны привязать свои текстурные объекты к единицам, которые вы хотите использовать.
Тип сэмплера будет соответствовать цели текстуры, используемой в текстурном блоке:
Sampler2D
forGL_TEXTURE_2D
и так далее ...источник
Представьте себе GPU как какой-то завод по переработке краски.
Есть несколько резервуаров, которые доставляют краситель к какой-нибудь покрасочной машине. В покрасочной машине краситель затем наносится на объект. Эти танки являются текстурными единицами
Эти баки могут быть оснащены различными видами красителей. Каждый вид красителя требует другого вида растворителя. «Растворитель» является целью текстуры . Для удобства каждый резервуар подключен к некоторому источнику растворителя, но одновременно в каждом резервуаре можно использовать только один вид растворителя. Таким образом , есть клапан / выключатель
TEXTURE_CUBE_MAP
,TEXTURE_3D
,TEXTURE_2D
,TEXTURE_1D
. Вы можете заполнить все типы красителей в резервуаре одновременно, но, поскольку в него входит только один вид растворителя, он будет "разбавлять" только тот тип красителя, который соответствует. Таким образом, вы можете связать каждый вид текстуры, но связывание с «наиболее важным» растворителем будет фактически попадать в резервуар и смешиваться с типом красителя, которому он принадлежит.И еще есть сам краситель, который поступает со склада и заполняется в резервуаре, «связывая» его. Это твоя текстура.
источник
Если в вашем шейдере вам нужен поиск по 2 текстурам:
для tex1 и tex2 необходимо указать их источники следующим образом:
в цикле рендеринга:
С gl_bindtexture невозможно сделать такую вещь. С другой стороны, возможное использование привязки в цикле рендеринга, когда вы передаете текстуру с контентом в потоке (видео, веб-камера):
источник