Pull to refresh

Comments 15

По первому примеру glDrawTexfOES

Чем обусловлен выбор glDrawElements с передачей индексов квада а не glDrawArrays?

Не проще было бы использовать glOrtho до вместо кучи излишних вычислений для подгонки на каждый вызов?
Глобально glDrawElements обусловлен тем, что его скорости для этого примера более, чем достаточно. Технически тут не будет бутылочного горлышка даже при glEnd/glBegin, потому что мобильные 2D игры рисуют уж совсем мало элементов и на ПК видеокартах просесть тут может что-либо из-за Fill Rate, а не передачи всех наших 20-50 треугольников на всю сцену.
glOrtho, кстати, тут обязателен для текущей Projection матрицы. Использовать же его для каждого вызова тоже не имеет смысла.

Воспринимайте этот кусок кода как пример, во что можно развернуть glDrawTexfOES c максимальной читабельностью, а оптимизировать его есть смысл лишь если нагрузка возрастет до десятков тысяч треугольников в кадре.
Для интересующихся, что за игра, да и просто для уверенных, что на «древних» технологиях нельзя ничего сделать:
www.youtube.com/watch?v=vZFxN7Yq8Cc
А все же — зачем это все? Зачем делать уникальный велосипед, подходящий только для наипростейших 2D-игр? Вы в итоге получили кучу тяжелоподдерживаемого кода, потратили на это кучу времени и потратите еще очень много, если потребности выйдут хоть немного за границу текущих.
Что вы сэкономили по сравнению с тем же MonoGame — мегабайт веса? Что получили взамен?
И хотя я еще могу понять фан написания движка, но использовать при этом OpenGL 1.1 с аргументацией «все что выше — для Дон Кихотов и теоретиков» это как-то слегка… недальновидно. Старый конь борозды не испортит, но и глубоко не вспашет.
К сожалению я не могу в принципе представить мобильную 2D игру, для которой этого не достаточно. Все разновидности злых птиц, загадочных домов, хидден обжекты и т.д. реализованы выводом спрайтов. Конечно, есть исключения, есть псевдо-2D платформеры со светом и другие гибриды, где это сделано для пущей красивости, но по большому счету более половины современных игр в маркетах занимаются выводом спрайтов и требуют от видеокарты аж одну функцию — рисовать прямоугольник с текстурой. И вот тут выбор у меня очень прост, на iOS и Android я ничего не выиграю при использовании огрызков XNA c SpriteBatch и корявой архитектурой против велосипеда, который ближе к телу.
Даже спрайты можно выводить очень по-разному. Казуалки — это хорошо, но как думаете, ваш движок осилит отрисовать нечто вроде этого на мобильных девайсах?
www.youtube.com/watch?v=XD_EBXpUYX0&spfreload=10
(Причем в трейлере не показано, что ближе к середине игры весь экран равномерно заполняется несколькими тысячами монстров одновременно)
Осилит без особых проблем. Правда, Crimsonland это все-таки 3D, но если перевести в 2D и рисовать именно спрайты — не вижу никаких трудностей. Собственно, как и можно то же самое сделать обычными CGLayer в iOS, и SpriteBatch в MonoGame, и SpriteRender в Unity. Самое смешное, что еще и принципиальной разницы в скорости не будет между этими подходами, потому что транслируются они примерно в один и тот же шейдерный код, как и glDrawTexfOES в OpenGL ES 1.1 на современном железе.
А вот шейдерную воду, огонь, отражения, свет и прочие радости я в самопальном движке не сделаю, тут вы правы на все 100%. Правда, игру с ними и не потяну, наверное, своими силами.
Не, Crimsonland это чистая спрайтовая 2D-графика, только камера перспективная и слегка наклонена.
Основную проблему я вижу в количестве вызовов отрисовки. SpriteRenderer и SpriteBatch объединят 1000 одинаковых спрайтов в один большой меш и отрисуют за один вызов. В то время как в вашем случае, как я понимаю, каждый спрайт рисуется отдельным вызовом? Мобильные девайсы крайне чувствительны к количеству вызовов отрисовки. Банальная отрисовка 500 квадов 500 вызовами убьет производительность. CGLayer по этой же причине тоже не осилит Crimsonland.
А вот шейдерную воду, огонь, отражения, свет и прочие радости я в самопальном движке не сделаю, тут вы правы на все 100%. Правда, игру с ними и не потяну, наверное, своими силами.
Про то и речь. Использовался бы OpenGL 2.0+ — это все делалось бы на раз-два, и уж всяко на порядок проще, чем городить странные костыли, пытаясь выжать из 1.1 все, что возможно. Даже не вдаваясь в особо крутые эффекты — в том же хидденобжекте может понадобиться сделать спрайт, скажем, пульсирующим от полноцветного до оттенков серого (первое, что в голову пришло). На шейдерах это пара строк кода, с GL 1.1 — нереально, насколько я знаю.
Если камера перспективная, а не ортогональная, то это все же уже не совсем 2D графика.
Количество Draw Calls для glDrawTexfOES равно количеству используемых текстур, это 1в1 то же самое, что используется в «больших» движках внутри. Даже CGLayer вроде как умеет так делать, хотя поручиться не могу.
это все делалось бы на раз-два, и уж всяко на порядок проще, чем городить странные костыли

Повторюсь, а зачем это в 2D играх того уровня, что может сделать команда в 1-3 человека за 3-6 месяцев?
в том же хидденобжекте может понадобиться сделать спрайт, скажем, пульсирующим от полноцветного до оттенков серого (первое, что в голову пришло)

Плохой пример, в шейдере это сложнее, чем без него, потому что вариант с l = (color.r+color.g+color.b)/3 даст откровенно плохой эффект. Можно сделать так, почти откидывая синий цвет (коэффициенты уперты где-то в инете):
float luminance = dot(vec3(0.2126,0.7152,0.0722), color);
color = mix(color, luminance, mix_amount);

Но все-равно контрастность и яркость пострадает. Если графикой занимается арт-лид, а не программист, то он в этом месте просто скажет художнику сделать еще один спрайт, обесцвеченный вручную, с соблюдением правильного цветового разделения и будет этот спрайт блендиться с мигающей альфой поверх оригинального. Ну а скорость такого решения может оказаться на микросекунду выше, потому что пиксельный конвейер отлично воспринимает ситуации с OUT.color = IN.color, а вот попиксельная обработка не бесплатна, пусть и скорости там порядка миллиона пикселей в секунду.
Если камера перспективная, а не ортогональная, то это все же уже не совсем 2D графика.
Перефразирую — под 2D игрой я имею в виду ту, где все спрайты находятся в одной плоскости. Какая при этом камера — дело десятое, если все равно все плоское и рисуется точно так же, как и при ортогональной камере.
Количество Draw Calls для glDrawTexfOES равно количеству используемых текстур
Простите, откуда вы это взяли? Это не так. Биндинг текстуры это не то же самое, что и отрисовка. Документация говорит, что «glDrawTexOES draws a texture rectangle to the screen.», т.е. каждый вызов glDrawTexOES — это новая операция отрисовки, которая просто берет текущую текстуру и как-то ее рисует (вполне возможно, что через эмуляцию при помощью двух треугольников и все того же glDrawElements). Таким образом, для отрисовки 1000 одинаковых спрайтов вы 1 раз биндите текстуру и 1000 раз отрисовываете квад с этой текстурой. В случае с батчингом, как в нормальных движках — 1 раз биндите текстуру, 1 раз отрисовываете 1 объединенный меш. Это принципиальное различие влияет на производительность при большом количестве объектов не меньше, чем филлрейт.
Повторюсь, а зачем это в 2D играх того уровня, что может сделать команда в 1-3 человека за 3-6 месяцев?
Для красоты, очевидно же. А какая вообще связь между фактом использования шейдеров и количеством людей в команде?.. Шейдеры при правильном применении как раз и позволяют сэкономить кучу времени и ресурсов.
Пример на то и пример, он не обязан соответствовать всем ситуациям. Понятно, что усреднять цвет — плохо, но чтоб рисовали обесцвеченные спрайты вручную — я лично еще не видел. Но ок, предположим, что рисуем обесцвеченные спрайты вручную.
будет этот спрайт блендиться с мигающей альфой поверх оригинального.
А теперь представим, что этот спрайт рисуется во весь экран (или занимает большую его часть). Отрисовка обесцвеченного спрайта поверх обычного будет означать практически удвоение филлрейта. В шейдере же я мог бы забиндить две текстуры и сблендить их прямо в шейдере разом, тем самым не потеряв в филлрейте. Это банальный пример того, как использование шейдера позволяет увеличить производительность того же самого эффекта просто за счет большей гибкости. В 1.1 же действительно просто ничего не оставалось бы, кроме как рисовать два спрайта поверх друг друга.
под 2D игрой я имею в виду ту, где все спрайты находятся в одной плоскости. Какая при этом камера — дело десятое, если все равно все плоское и рисуется точно так же, как и при ортогональной камере.

Простите, это чушь.

Простите, откуда вы это взяли? Это не так. Биндинг текстуры это не то же самое, что и отрисовка.

Взял из ранних презентаций, где говорилось и показывалось, как рисовать тысячи спрайтов в кадре этой функцией. Ищите, это было в сети на заре OpenGL ES 1.1. Что у функции внутри сказать сложно, потому что работает она всегда вне зависимости от текущей камеры в screen space и сделана сугубо 2D; не удивлюсь, если она копирует текстуру напрямую из видеопамяти в фреймфбуфер на некоторых устройствах. Заодно тогда проводилось немало тестов и производительность этой функции подтверждалась не раз.

Для красоты, очевидно же. А какая вообще связь между фактом использования шейдеров и количеством людей в команде?

На счет красоты это вопрос вкуса, запрограммировать эффект такой же красивый, как может нарисовать хороший художник очень и очень сложно, а зачастую это займет еще и больше времени. Опять же, посмотрите на современные 2D мобильные игры, там шейдеры не используются, а выглядят они отлично. Корреляция с размером команды, конечно, условная, но прослеживается четко — небольшому проекту для небольшой команды не нужны FSAA, SSS, Deferred Lighting, пост-эффекты и прочие навороты. Есть исключения, но не существенные.

А теперь представим, что этот спрайт рисуется во весь экран (или занимает большую его часть). Отрисовка обесцвеченного спрайта поверх обычного будет означать практически удвоение филлрейта. В шейдере же я мог бы забиндить две текстуры и сблендить их прямо в шейдере разом, тем самым не потеряв в филлрейте

Извините, неверно. Два texture fetch в шейдере вдвое уменьшат филрейт (+- пара процентов). Выигрыш у вас может быть только на спичках вроде discard при прозрачности и т.п. Но я все-равно не могу представить задачи, эта скорость будет что-либо значить.

Опять же спор зашел в странную плоскость. Судя по всему, вы сразу начали работать с шейдерными технологиями и считаете все, что было до них анахронизмом. Но это не так, и разница тут совсем не такая, как у языков высокого уровня против ассемблера. Более того, режим без шейдеров еще и может быть быстрее в некоторых задачах. Шейдеры же дают гибкость, возможность принимать решения на GPU без CPU и прочие радости, а не скорость в примитивных действиях. На сим я откланиваюсь, ибо свою точку зрения высказал и аргументировал, а дальше уже не за чем хабру марать.
ибо свою точку зрения высказал и аргументировал
Простите, это чушь.
Очень аргументировано, действительно.
У вас есть какое-то объективное определение 2D-игры? Как по мне, если оно выглядит как 2D-игра и играется как 2D-игра, то это 2D-игра.
Взял из ранних презентаций, где говорилось и показывалось, как рисовать тысячи спрайтов в кадре этой функцией. Ищите, это было в сети на заре OpenGL ES 1.1
Искал, не нашел никаких подтверждений. Зато нашел в сети упоминания, что на PowerVR (iOS + множество Android-девайсов) эта функция именно через эмуляцию и реализована, например. Аналогично вашему же примеру с OpenTK.
На счет красоты это вопрос вкуса, запрограммировать эффект такой же красивый, как может нарисовать хороший художник очень и очень сложно, а зачастую это займет еще и больше времени.
Это вопрос не вкуса, это вопрос задачи, ибо обратное тоже справедливо — нарисовать эффект такой же красивый, который можно сделать очень быстро шейдером, может быть очень и очень сложно и долго, а зачастую и вообще невозможно.
Извините, неверно. Два texture fetch в шейдере вдвое уменьшат филрейт
Вы что-то очень сильно путаете. texture fetch в большинстве случаев никак не связан с филлрейтом. На то он и *fill* rate — этот параметр, грубо говоря, показывает, сколько раз в секунду GPU может полностью залить экран. Время выборки текселя тут вообще сбоку. В моем примере с шейдером происходит 2 выборки из текстуры и 1 рендеринг пикселя в backbuffer. В случае с рисованием двух спрайтов поверх друг друга — 2 выборки из текстуры, 2 рендеринга пикселя в backbuffer, т.е. филлрейт падает в 2 раза. Ситуаций, когда подобная оптимизация может зарулить — очень много, на самом деле. Мультитекстурирование тоже не просто так изобрели, хотя могли ведь 2 раза рисовать, проще же.
Судя по всему, вы сразу начали работать с шейдерными технологиями и считаете все, что было до них анахронизмом.
Отнюдь. Я очень долго работал с GL 1.1, но как только появилась возможность использовать шейдеры — я перешел на них просто потому, что это удобнее и быстрее. Дело не в том, что я считаю GL 1.1 анахронизмом, я не понимаю другого — почему просто не использовать 2.0?.. Вот правда, я не вижу ни одного объективного критерия на сегодня, почему кто-либо может предпочесть 1.1. Если не нужны шейдеры — ну дык использовать по умолчанию примитивный шейдер, который рисует все «как есть». Зато появится возможность безболезненно использовать шейдеры, когда они будут нужны.
Ничего не имею против статьи, но, все же, вынужден заметить, что список
iOS, Android, BlackBerry, Windows XP/7, Mac OS X, Linux, ReactOS, Windows 8, Windows Phone 8.1
почти полностью перекрывается Unity. А делать игру на Unity намного быстрее и удобнее, чем писать свой движок. И для 2d игр там в бесплатной версии есть практически все, что необходимо, даже GUI они наконец сделали приличный. И скриптовый язык основной там тоже C# (в то время как сам движок написан на C++).
На 100% верно. Проблема в том, что я пошел с обратной стороны — сначала сделал большую часть для на iOS и Android, а потом подумал о Unity. И переделывать уже не было смысла.
пусть C# в связке с OpenGL пока нельзя использовать в вебе или на Firefox OS
Кстати да, чуть не забыл. Обратите внимание на проект JSIL. По сути, это транслятор IL-кода сборок .NET в JS, который работает в браузере. Конечно, сама по себе трансляция кода не даст совместимости с OpenGL, но под JSIL для простых игр уже довольно неплохо работает враппер MonoGame. Думаю, и враппер OpenGL <-> WebGL написать при большом желании можно. Во всяком случае это, наверное, самый простой путь портирования под браузеры для вашего случая.
Sign up to leave a comment.

Articles