Все про об’єкти 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 забороняє це і надасть помилку при спробі візуалізації.
GL_TEXTURE0 + i- я мав на увазі перевірити значення перерахунків, щоб побачити, чи це дійсно чи ні. І останній абзац - не знав, законно це чи ні. Відмінно! Я закладаю закладки на всі ваші відповіді, щоб я могла звернутися до них ще раз.