Изобразете множество модели в OpenGL с едно извикване на чертеж

Създадох 2D графичен двигател и създадох система за групиране за него, така че ако имам 1000 спрайта с една и съща текстура, мога да ги нарисувам с едно извикване на openGl.

Това се постига чрез поставяне в един vbo върхов масив на всички върхове на всички спрайтове с една и съща текстура.

Вместо „отпечатайте тези върхове, отпечатайте тези върхове, отпечатайте тези върхове“, аз правя „съберете всички върхове заедно, отпечатайте“, просто за да бъда много ясен. Достатъчно лесно, но сега се опитвам да постигна същото в 3D и имам голям проблем.

Проблемът е, че използвам матрица за проекция на изглед на модел, за да поставя и изобразявам моите модели, което е общият подход за изобразяване на модел в 3D пространство.

За всеки модел на екрана трябва да предам MVP матрицата на шейдъра, така че да мога да я използвам, за да трансформирам всеки връх в правилната позиция.

Ако направя трансформацията извън шейдъра, тя ще бъде изпълнена от процесора, което не е добра идея по очевидни причини.

Но проблемът е там. Трябва да предам матрицата на шейдъра, но за всеки модел матрицата е различна.

Така че не мога да направя същото, което направих с 2d спрайтове, защото смяната на униформата на шейдър изисква равенство всеки път.

Надявам се, че бях ясен, може би имате добра идея, която аз нямах или вече сте имали същия проблем. Знам със сигурност, че някъде има решение, защото в двигател като Unity можете да използвате един и същ шейдър за множество модели и да се измъкнете с едно повикване за изтегляне


person Ryno    schedule 14.06.2015    source източник


Отговори (3)


Съществува функция, точно като тази, която търсите, и се нарича инстанциране. С инстанцирането съхранявате n матрици (или каквото друго ви трябва) в единен буфер и извиквате glDrawElementsInstanced, за да начертаете n копия. В шейдъра получавате допълнителен вход gl_InstanceID, с който индексирате в Uniform Buffer, за да извлечете матрицата, от която се нуждаете за този конкретен екземпляр.

Можете да прочетете повече за инстанцирането тук: https://www.opengl.org/wiki/Vertex_Rendering#Instancing

person orost    schedule 14.06.2015
comment
Страхотен отговор! Учех да внедря нещо подобно сам, но наистина не знаех как, точно това търсех - person Ryno; 15.06.2015

Отговорът зависи от това дали данните за върха за всеки елемент са идентични или не. Ако е така, можете да използвате инстанциране, както в отговора на @orost, като използвате glDrawElementsInstanced и gl_InstanceID в шейдъра на върхове и този метод трябва да бъде предпочитан.

Въпреки това, ако всеки 3D модел изисква различни данни за върха (което често се случва), все още можете да ги изобразите с помощта на едно извикване на чертеж. За да направите това, бихте добавили друг поток към вашите данни за върха с glVertexAttribPointerglEnableVertexAttribArray). Този допълнителен поток ще съдържа индекса на матрицата в рамките на унифицирания буфер, който върхът трябва да използва при рендиране - така че всяка мрежа в рамките на VBO ще има идентичен индекс в допълнителния поток. Униформеният буфер съдържа същите данни като в инстанционната настройка.

Обърнете внимание, че този метод може да изисква допълнителна обработка на процесора, ако трябва да повторите групирането - например обект в рамките на пакет не трябва повече да се изобразява. Ако този процес се изисква често, трябва да се определи дали групирането на артикули е действително полезно или не.

person MuertoExcobito    schedule 15.06.2015
comment
Най-добрият отговор, който имах досега, наистина ми помогна да разбера как да подходя към него...Може би ще се опитам да напиша система, която автоматично избира инстанциране или този метод...Мога да запазя заедно с мрежите броя на нейните върхове . След това, всеки път, когато добавя мрежа към системата, ако номерът е същият като предишната мрежа, запазвам bool isInstance = true, но веднага щом получа различен брой върхове, го настройвам на false. В края на рамката изобразявам, използвайки единия или другия метод, в зависимост от bool стойността - person Ryno; 16.06.2015
comment
Изглежда ли ви разумно? - person Ryno; 16.06.2015
comment
@user3578051 не само броят на върховете е важен, но и действителното съдържание. Инстанцирането основно просто рисува една и съща мрежа няколко пъти. Вероятно ще променяте унифицираните данни между екземплярите. При този метод съдържанието на данните от буфера на върховете може да бъде напълно различно между екземплярите. Система, която може да обработва и двете (и предпочита инстанциране), вероятно би била правилният подход, освен ако да кажем, че вашите партиди променят всеки кадър. - person MuertoExcobito; 16.06.2015
comment
ммм...не чакай, аз ще ти кажа какво разбрах, а ти ми кажи какво съм сгрешил. Идеята е да поставите всички върхове в един vbo (всеки връх съдържа позиция, цвят, нормал и каквото и да е друго). Да кажем, че добавя куб и след това огромна мрежа (общо 1000 върха). Моят vbo ще има 36+1000 върха. След това имам единен буфер, съдържащ MVP матриците, които в този случай са две. Това, което разбрах, е да има буфер с дължина 1036, където първите 36 са указатели към първата матрица, а другите указатели към втората. Вярно ли е? - person Ryno; 16.06.2015
comment
Ако това е правилно, идеята ми ще бъде: 1) Ще дам на всяка мрежа идентификатор (защото казахте, че броят на върховете сам по себе си не е достатъчен) 2) Всеки път, когато добавям към партидата, поставям модела в партидата със собствена текстура 3)Ако идентификаторът е различен обаче, ще превключа към неинстанциране 4)в края на рамката рендирам по двата начина в зависимост от метода, който трябва да използвам. добре ли е - person Ryno; 16.06.2015
comment
Мисля, че звучи така, сякаш ще работи. Просто исках да изясня, че можете да имате две мрежи, които са различни, но имат еднакъв брой върхове. - person MuertoExcobito; 17.06.2015
comment
Добре, перфектно, да, знаех факта, като научих, че с инстанцирането трябва също да подадете vbo с всички върхове за всички матрици, след което да посочите как да го изрежете до графичния процесор. Например за десет куба, предайте 360 върха vbo и кажете на GPU да вземе 36 върха наведнъж. В този случай мислех, че дори мрежи с еднакъв номер ще работят, но вероятно инстанцирането изисква само една мрежа като параметър и N число, за да свърши работата, все още не съм опитвал, така че не знаех. Благодаря за отговорите, сега идеите ми са много по-ясни - person Ryno; 17.06.2015
comment
Новини: внедрих пакетната система и инстанцията. Бих искал да добавя вашия метод към него, за да обработвам различни модели, затова направих допълнителния поток, предадох индексите, но как мога да ги използвам в шейдъра? Получавам матрица от инстанционния поток и индекс от вашия...но не мога да използвам този индекс, за да получа матрица от vbo, защото във вертекс шейдъра имам само една матрица...надявам се да имам беше ясно... - person Ryno; 18.06.2015
comment
Вашите матрици трябва да бъдат зададени като униформи в шейдъра, а не като VBO. - person MuertoExcobito; 18.06.2015

Освен инстанциране и добавяне на друг атрибут на връх като някакъв идентификатор на обект, бих искал да спомена и друга стратегия (която обаче изисква модерен OpenGL):

Разширението ARB_multi_draw_indirect (в основата от GL 4.3) добавя indirect команди за рисуване. Тези команди извличат своите параметри (брой върхове, начален индекс и т.н.) директно от друг буферен обект. С тези функции много различни обекти могат да бъдат начертани с едно извикване на чертеж.

Въпреки това, тъй като все още искате някакво състояние на обект като матрици за трансформация, тази функция не е достатъчна. Но в комбинация с ARB_shader_draw_parameters (все още не в основния GL), получавате параметърът gl_DrawID, който ще бъде увеличен с единица за всеки отделен обект в едно непряко повикване за множествено теглене. По този начин можете да индексирате в някои UBO, или TBO, или SSBO (или каквото и да е), където съхранявате данни за всеки обект.

person derhass    schedule 15.06.2015