Различия и отношения между glActiveTexture и glBindTexture

137

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

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

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

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

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

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

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

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

mpen
источник

Ответы:

259

Все об объектах 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;
}

Обратите внимание, что теперь у нас есть не только двухмерный список Objects, но и концепция текущего объекта. У нас есть функция для установки текущего объекта, у нас есть концепция максимального количества текущих объектов, и все наши функции манипулирования объектами настроены для выбора из текущего объекта.

Когда вы меняете текущий активный объект, вы меняете весь набор целевых местоположений. Таким образом, вы можете привязать что-то, что входит в текущий объект 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 запрещает это и выдает ошибку при попытке рендеринга.

Николь Болас
источник
12
Вот Это Да! Еще один замечательный ответ - спасибо, Николь! Мне особенно нравится этот абзац о том, что 2D-текстура всегда является 2D-текстурой. Сейчас я создаю обертку вокруг некоторых из этих вещей, и я не был уверен, должен ли я оставить это открытым для изменений. И часть о GL_TEXTURE0 + i- я хотел проверить значения перечисления, чтобы увидеть, было ли это правильно или нет. И последний абзац - не знал, законно это или нет. Превосходно! Я добавляю в закладки все ваши ответы, чтобы снова обратиться к ним.
mpen
6
@ Никол Болас: Это действительно хорошо объяснено. Вы должны скопировать кое-что из этого в главу по текстуре в своей онлайн-книге opengl. Я думаю, что это намного яснее и будет хорошо дополнять главу.
WesDec
3
@Nolol Bolas Я только начинаю изучать OpenGL, и этот ответ мне очень помог. Спасибо!
встроенный
2
Эй, Нико, просто хочу указать на твою маленькую опечатку: это GL_DRAW_FRAMEBUFFER, а не GL_WRITE_FRAMEBUFFER
Defd
3
@Nicol: Ух ты, лучшее определение, которое я имел до этого, было из твоих уроков по арсинтезу, теперь ты превзошел даже этот блестящий источник. Спасибо
Баггерс
20

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


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

Когда создаются, вы должны назначить один Texture объект на один объект текстуры, который представляет тип текстуры ( 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)

шейдеры

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

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

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

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

rotoglup
источник
Одна вещь, которую я не понимаю. Давайте предположим, что у меня есть текстура и она используется во многих шейдерах на разных текстурных единицах. Давайте предположим, что я хочу изменить текстуру фильтрации во время выполнения. Какой текстурный блок я должен использовать? Могу ли я изменить состояние текстуры на устройстве 0, а затем использовать эту текстуру на другом устройстве?
majakthecoder
@majakthecoder В своем ответе я рассматриваю фильтрацию как свойство объекта текстуры - это означает, что вы не можете изменить его конкретно в текстурном блоке. В зависимости от разновидности OpenGL, на которую вы нацелены, вы можете выбрать объекты- сэмплеры для решения этой проблемы ( opengl.org/wiki/Sampler_Object ), в противном случае вам может потребоваться продублировать объект текстуры, чтобы иметь несколько одновременных фильтров.
rotoglup
12

Представьте себе GPU как какой-то завод по переработке краски.

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

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

И еще есть сам краситель, который поступает со склада и заполняется в резервуаре, «связывая» его. Это твоя текстура.

datenwolf
источник
2
Какая-то странная аналогия ... Я не уверен, что это действительно что-то проясняет. Особенно часть о "разбавлении" и "самом важном растворителе". Вы говорите, что если я связываю как 2d текстуру, так и 3d текстуру, я могу использовать только одну из них, или как? Какой из них будет считаться наиболее важным?
mpen
2
@Mark: Ну, я пытался говорить с точки зрения художника, который работает с буквальным красителем (скажем, на масляной и водной основе). В любом случае, да, если вы связываете и активируете несколько текстурных целей, есть приоритет: CUBE_MAP> 3D> TEXTURE_ARRAY> 2D> 1D.
datenwolf
1
Ухоженная! Я не знал о приоритете. Имеет больше смысла теперь, когда я знаю, что только одна текстурная цель может использоваться на единицу текстуры.
mpen
1
@ legends2k: Ну, теперь это становится интересным. Мы говорим о ядре или профиле совместимости. Предполагаем ли мы идеальные или глючные драйверы? Теоретически тип униформы выбирает, какую цель текстурного блока выбрать. На практике это происходит в профиле ядра. В профиле совместимости ожидайте, что некоторые глючные драйверы предоставят вам всю белую текстуру по умолчанию, если предыдущая цель текстурного блока не соответствует типу сэмплера.
datenwolf
1
@ legends2k: Кроме того, подумайте о том, что произойдет, если бы 2D и 3D текстуры были связаны с одним и тем же модулем, а у вас была форма 2D и 3D сэмплера, которую вы связали с одним и тем же модулем? Вы можете вызвать все виды странных ошибок драйвера с этим. На практике мышление в старой модели приоритетов с фиксированными функциями сохраняет разум и работу вашей программы, потому что именно так большинство драйверов будут вести себя предсказуемо.
datenwolf
2

Если в вашем шейдере вам нужен поиск по 2 текстурам:

uniform sampler2D tex1;  
uniform sampler2D tex2;  

для tex1 и tex2 необходимо указать их источники следующим образом:

tex1 = gl.createTexture();  
gl.activeTexture(gl.TEXTURE3);  
gl.bindTexture(gl.TEXTURE_2D, tex1); 
gl.texParameteri(gl.TEXTURE_2D, ...);  
....


tex2 = gl.createTexture();  
gl.activeTexture(gl.TEXTURE7);  
gl.bindTexture(gl.TEXTURE_2D, tex2); 
gl.texParameteri(gl.TEXTURE_2D, ...);  
....  
var tex1Loc  = gl.getUniformLocation(your_shader,"tex1");  
var tex2Loc  = gl.getUniformLocation(your_shader,"tex2");

в цикле рендеринга:

gl.uniform1i(tex1Loc, 3);  
gl.uniform1i(tex2Loc, 7);  
// but you can dynamically change these values

С gl_bindtexture невозможно сделать такую ​​вещь. С другой стороны, возможное использование привязки в цикле рендеринга, когда вы передаете текстуру с контентом в потоке (видео, веб-камера):

gl.bindTexture(gl.TEXTURE_2D, tex1);  
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, video);  
// in the render loop
Филипп Осеангерманик
источник