Відмінності та взаємозв'язок між glActiveTexture та glBindTexture


137

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

Якщо я правильно розумію, вам потрібно зателефонувати, glActiveTextureщоб спершу встановити блок текстури (ініціалізований до GL_TEXTURE0), а потім ви прив’язуєте (одну або більше) "цільових текстур" до цієї одиниці текстури?

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

Чи правильно я розумію взаємозв’язок між цільовими текстурами та текстурними одиницями? Скажімо, мені дозволено 16 одиниць і 4 цілі, чи це означає, що є місце для 16 * 4 = 64 цілі, чи це не працює так?

Далі ти зазвичай хочеш завантажити текстуру. Це можна зробити через glTexImage2D. Перший аргумент - це цільова текстура. Якщо це працює на зразокglBufferData , ми, по суті, прив'язуємо "ручку" / "ім'я текстури" до цілі текстури, а потім завантажуємо дані текстури в цю ціль і тим самим опосередковано пов'язуємо її з цією ручкою.

Про що glTexParameter? Ми повинні прив’язати цільову текстуру, а потім знову вибрати ту саму ціль, що й перший аргумент? Або не потрібно прив'язувати цільову текстуру, якщо у нас є правильна одиниця текстури?

glGenerateMipmap також працює над ціллю ... для досягнення успіху цільова ціль ще повинна бути пов'язана з назвою текстури?

Тоді, коли ми хочемо намалювати наш об’єкт з текстурою на ньому, чи потрібно обидва обирати активну одиницю текстури, а потім цільову текстуру? Або ми вибираємо одиницю текстури, і тоді ми можемо захопити дані з будь-якої з 4 цілей, пов’язаних із цим блоком? Це та частина, яка мене справді бентежить.

Відповіді:


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параметр вище.

Текстури особливі, тому що, коли ви вперше прив'язуєте їх до цілі, вони отримують спеціальну інформацію. Коли ви вперше прив'язуєте текстуру як a GL_TEXTURE_2D, ви фактично встановлюєте особливий стан текстури. Ви говорите, що ця текстура є двовимірною. І це завжди буде 2D текстура; цей стан неможливо змінити ніколи . Якщо у вас є текстура, яка спочатку була пов'язана як a GL_TEXTURE_2D, ви завжди повинні пов'язувати її як a 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 текстуру, яка завжди є двовимірною текстурою. Зараз я будую обгортку навколо деяких з цих речей, і я не був впевнений, чи варто мені залишати це відкритим для змін. І частина про GL_TEXTURE0 + i- я мав на увазі перевірити значення перерахунків, щоб побачити, чи це дійсно чи ні. І останній абзац - не знав, законно це чи ні. Відмінно! Я закладаю закладки на всі ваші відповіді, щоб я могла звернутися до них ще раз.
mpen

6
@Nicol Bolas: Це дійсно добре пояснено. Ви повинні скопіювати частину цього у розділ текстури у вашій онлайн-книзі opengl. Я думаю, що це набагато зрозуміліше, і це би добре компліментувало главу.
WesDec

3
@Nicol Bolas Я тільки починаю вивчати OpenGL, і ця відповідь мені дуже допомогла. Дякую!
inline

2
Привіт Ніко, просто хочу вказати на ваш маленький помилковий помилок: це GL_DRAW_FRAMEBUFFER, а не GL_WRITE_FRAMEBUFFER
Невдаче

3
@Nicol: Ого, найкраще визначення, яке я мав до цього, було з ваших навчальних посібників з аркосинтезу, тепер ви перевершили навіть це геніальне джерело. Thankyou
Baggers

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пробовідбірника - це значення, що представляють індекс текстурної одиниці, яку слід використовувати для вибірки (а не об'єкт текстури, який слід використовувати).

Отже, ви повинні прив’язати свої текстурні об’єкти до одиниць, які ви хочете використовувати.

Тип пробовідбірника буде відповідати цільовій текстурі, яка використовується в одиниці текстури: Sampler2Dдля GL_TEXTURE_2Dтощо.


Я не розумію одне. Припустимо, у мене є деяка текстура, і вона використовується в багатьох шейдерах на різних одиницях текстури. Припустимо, я хочу змінити фільтрацію текстури під час виконання. Яку одиницю текстури я повинен використовувати? Чи можу я змінити стан текстури на блоці 0, а потім використовувати цю текстуру на різних одиницях?
маяктхекодер

@majakthecoder У своїй відповіді я вважаю фільтрування властивістю об'єкта текстури - це означає, що ви не можете змінити його конкретно в одиниці текстури. Залежно від аромату OpenGL, на який ви орієнтуєтесь, ви зможете відібрати об'єкти для вибірки, щоб вирішити цю проблему ( opengl.org/wiki/Sampler_Object ), інакше вам, можливо, доведеться дублювати текстурний об’єкт, щоб мати кілька одночасних фільтрувань.
rotoglup

12

Уявіть собі GPU, як якийсь завод з обробки фарби.

Є ряд цистерн, які доставляють барвник на якусь фарбувальну машину. Потім у фарбувальній машині барвник наноситься на предмет. Ці резервуари є одиницями текстури

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

А далі є самий барвник, який надходить зі складу і заповнюється в резервуар, "зв'язуючи" його. Це ваша текстура.


2
Якась дивна аналогія ... Я не впевнений, що насправді щось очищає. Особливо частина про "розведення" та "найважливіший розчинник". Ви говорите, якщо я пов'язую як 2d текстуру, так і 3d текстуру, я можу використовувати лише одну з них, чи що? Який із них вважатиметься найважливішим?
mpen

2
@ Марк: Ну, я намагався говорити з точки зору художника, який працює з буквальним барвником (скажімо, на олійній основі та на водній основі). У будь-якому випадку, так, якщо ви прив'язуєте та вмикаєте кілька цільових текстур, є пріоритет: 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.