Различия и отношения между glActiveTexture и glBindTexture - PullRequest
124 голосов
/ 15 января 2012

Насколько я понимаю, 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, то мы, по сути, привязываем «дескриптор» / «имя текстуры» к цели текстуры, а затем загружаем данные текстуры в эту цель и тем самым косвенно связываем ее с этим дескриптором. .

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

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

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

Ответы [ 4 ]

240 голосов
/ 17 января 2012

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

17 голосов
/ 17 января 2012

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


Вы можете создать примерно столько же объектов текстуры , сколько имеется свободной памяти в вашей системе. Эти объекты содержат фактические данные (тексели) ваших текстур, а также параметры, предоставляемые 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)

шейдеры

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

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

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

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

12 голосов
/ 15 января 2012

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

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

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

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

1 голос
/ 20 июля 2018

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