Различия и взаимосвязь между glActiveTexture и glBindTexture

Из того, что я собираю, glActiveTexture устанавливает активную "текстурную единицу". Каждый текстурный блок может иметь несколько объектов текстуры (обычно это GL_TEXTURE_1D, 2D, 3D или CUBE_MAP).

Если я правильно понял, вам нужно позвонить glActiveTexture, чтобы сначала установить блок текстуры (инициализирован на GL_TEXTURE0), а затем вы привязываете (один или несколько) "объектов текстуры" к этой текстурной единице?

Количество доступных текстурных единиц зависит от системы. Я вижу перечисления до 32 в моей библиотеке. Я предполагаю, что это по существу означает, что у меня может быть меньше предела моего GPU (который, я думаю, 16 8) и 32 текстуры в памяти GPU в любой момент времени? Я предполагаю, что существует дополнительный предел, который не превышает максимальную память GPU (предположительно 1 ГБ).

Я правильно понимаю взаимосвязь между объектами текстуры и текстурными блоками? Пусть говорят, что мне разрешено 16 единиц и 4 цели каждый, значит ли это место для 16 * 4 = 64 целей, или это не работает?

Затем вы обычно хотите загрузить текстуру. Вы можете сделать это через glTexImage2D. Первый аргумент которого - цель текстуры. Если этот работает как glBufferData, мы по существу привязываем "handle" / "имя текстуры" к цели текстуры, а затем загружаем данные текстуры в эту цель, и таким образом косвенно связывать его с этим дескриптором.

Как насчет glTexParameter? Нам нужно связать цель текстуры, а затем снова выбрать ту же цель, что и первый аргумент? Или цель текстуры не должна быть привязана, если у нас есть правильный активный блок текстур?

glGenerateMipmap также работает с целью... эта цель должна по-прежнему привязываться к имени текстуры для успеха?

Затем, когда мы хотим нарисовать наш объект с текстурой на нем, нужно ли нам выбрать активный блок текстур, а затем цель текстуры? Или мы выбираем блок текстуры, а затем мы можем захватывать данные из любой из 4 целей, связанных с этим устройством? Это та часть, которая меня действительно сбивает с толку.

Ответ 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 запрещает это и даст вам ошибку при попытке рендеринга.

Ответ 2

Я попробую! Все это не так сложно, просто вопрос терминов, надеюсь, что я дам себе понять.


Вы можете создать примерно столько объектов Texture, сколько в вашей системе имеется доступная память. Эти объекты содержат фактические данные (тексели) ваших текстур вместе с параметрами, предоставляемыми glTexParameter (см. FAQ).

При создании вам необходимо назначить один объект текстуры одному текстурному объекту, который представляет тип текстуры (GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE,...).

Эти два объекта, объект текстуры и цель текстуры представляют данные текстуры. Мы вернемся к ним позже.

Текстурные единицы

Теперь OpenGL предоставляет массив текстурных блоков, которые могут использоваться одновременно при рисовании. Размер массива зависит от системы OpenGL, у вас есть 8.

Вы можете привязать объект текстуры к текстурной единице, чтобы использовать данную текстуру во время рисования.

В простом и легком мире, чтобы рисовать с заданной текстурой, вы привязываете объект текстуры к текстурному блоку, и вы будете делать (псевдокод):

glTextureUnit[0] = textureObject

Поскольку GL является конечным автоматом, он, увы, не работает таким образом. Предположим, что наш textureObject имеет данные для целевой текстуры GL_TEXTURE_2D, мы будем использовать предыдущее назначение как:

glActiveTexture(GL_TEXTURE0);                   // select slot 0 of the texture units array
glBindTexture(GL_TEXTURE_2D, textureObject);    // do the binding

Обратите внимание, что GL_TEXTURE_2D действительно зависит от типа текстуры, которую вы хотите привязать.

Объекты текстуры

В псевдокоде, чтобы задать данные текстуры или параметры текстуры, выполните следующие действия:

setTexData(textureObject, ...)
setTexParameter(textureObject, TEXTURE_MIN_FILTER, LINEAR)

OpenGL не может напрямую манипулировать текстурными объектами, обновлять/устанавливать их содержимое или изменять их параметры, вы должны сначала привязать их к активной текстурной единице (в зависимости от того, что она есть). Эквивалентный код будет:

glBindTexture(GL_TEXTURE_2D, textureObject)       // this 'installs' textureObject in texture unit
glTexImage2D(GL_TEXTURE_2D, ...)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)

Shaders

Шейдеры имеют доступ ко всем текстурным блокам, они не заботятся об активной текстуре.

Форма сэмплера - это int значения, представляющие индекс блока текстуры для использования для сэмплера (а не используемый объект текстуры).

Таким образом, вы должны привязать свои объекты текстуры к единицам, которые вы хотите использовать.

Тип сэмплера будет соответствовать совпадению с целевой текстурой, которая используется в блоке текстуры: Sampler2D для GL_TEXTURE_2D и т.д.

Ответ 3

Представьте себе графический процессор, например, какой-то завод по обработке краски.

Существует несколько резервуаров, которые доставляют красители на некоторые машины для окраски. В машине для окрашивания краска затем наносится на объект. Эти резервуары являются единицами текстуры

Эти резервуары могут быть оснащены различными красками. Каждый вид красителя требует какого-либо другого растворителя. "Растворитель" - это цель текстуры. Для удобства каждый бак подключается к некоторому растворителю, и только один вид растворителя может использоваться одновременно в каждом резервуаре. Итак, есть клапан/переключатель TEXTURE_CUBE_MAP, TEXTURE_3D, TEXTURE_2D, TEXTURE_1D. Вы можете одновременно заполнить все типы красителей в баке, но, поскольку вступает только один вид растворителя, он будет "разбавлять" только вид соответствия красителя. Таким образом, вы можете связать каждый вид текстуры, но привязка с "самым важным" растворителем действительно войдет в резервуар и смешана с типом красителя, которым он принадлежит.

И вот там сам краситель, который приходит со склада и заполняется в бак, "привязывая" его. Это ваша текстура.