Все про об’єкти 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;
}
Зауважте, що зараз у нас є не тільки двовимірний список 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
.
Візуалізація
Отже, що все це має відношення до візуалізації?
Під час використання шейдерів ви встановлюєте уніформа для зразка на одиницю текстурного зображення ( glUniform1i(samplerLoc, i)
де i
є одиниця зображення). Це число, яке ви використовували glActiveTexture
. Пробовідбірник вибере ціль на основі типу вибірки. Тож sampler2D
вибір буде від GL_TEXTURE_2D
цілі. Це одна з причин, чому вибірки мають різні типи.
Тепер це звучить підозріло, як у вас можуть бути два пробовідбірники GLSL, з різними типами, які використовують однакові зображення текстури. Але ти не можеш; OpenGL забороняє це і надасть помилку при спробі візуалізації.
GL_TEXTURE0 + i
- я мав на увазі перевірити значення перерахунків, щоб побачити, чи це дійсно чи ні. І останній абзац - не знав, законно це чи ні. Відмінно! Я закладаю закладки на всі ваші відповіді, щоб я могла звернутися до них ще раз.