OpenGL ES 1.1 в Windows 8 и Windows Phone 8.1

    В далеком 1998 году я пытался сделать свою игру с OpenGL. Разработка с трудом дошла до альфы и была заброшена, но что особо запомнилось, так это как удобно было делать под GL интерфейсы — ортогональная проекция, пара трансформаций, биндинг нескольких вершин с GL_TRIANGLE_STRIP и у нас уже есть кнопка. И вот, спустя шестнадцать лет и занимаясь мобильным игростроем я столкнулся с таким же подходом в OpenGL ES 1.*, разве что 2D текстуры без вращений можно теперь рисовать через glDrawTexfOES.
    Я поддерживал несколько проектов, сделанных по этому принципу и понемногу в голове выстроился коварный план: сделать кросс-платформенную 2D игру на мобильных с OpenGL ES и на C#, а на десктопах с обычным OpenGL. Цели я добился не с первого раза и было с этим много проблем, но в результате очередной проект у меня работает без изменений бизнес-логики на iOS, Android, BlackBerry, Windows XP/7, Mac OS X, Linux, ReactOS, Windows 8, Windows Phone 8.1. Материала набралось на много статей, но в этот раз я расскажу именно о поддержке Windows Runtime.

    OpenTK


    Можно много спорить на счет удобства OpenGL именно для 2D, до хрипоты в горле убеждать себя, что для полноценной игры необходимы шейдеры и многопроходный рендеринг, а заодно и находить подтверждения, что устаревший OpenGL ES 1.1 часто реализован именно на уровне эмуляции через шейдеры. Это я оставлю для Дон Кихотов и теоретиков. Меня же волновало, что это самый простой способ написать единожды код 2D отрисовки и запускать его на разных платформах, причем не используя монструозные Unity, MonoGame и другие движки.
    На iOS и Android под Xamarin все прошло гладко, работа с GL делается через библиотеку OpenTK с неймспейсом OpenGL.Graphics.GL11, константы и методы на обеих платформах одинаковы. На десктопах я решил использовать OpenTK.Graphics.OpenGL, т.е. обычный десктопный OpenGL с C# оберткой. Там в принципе нет glDrawTexfOES, но без проблем можно сделать замену для него и рисовать два треугольника через GL_TRIANGLE_STIP/GL_TRIANGLES и glDrawElements — по сравнению с мобильными, производительности хватает с лихвой и VBO тут не нужны.
    Пример враппера с GL_TRIANGLES
            private static readonly int[] s_textureCropOesTiv = new int[4];
            private static readonly short[] s_indexValues = new short[] { 0, 1, 2, 1, 2, 3 };
            private static readonly float[] s_vertexValues = new float[] { -0.5f, 0.5f, 0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f };
            
            public void glDrawTexfOES(float x, float y, float z, float w, float h)
            {
                glPushMatrix();
                glLoadIdentity();
    
                glTranslatef(w / 2.0f + x, h / 2.0f + y, 0.0f);
    
                glScalef(w, -h, 1.0f);
    
                int[] tiv = s_textureCropOesTiv; // NOTE: clip rectangle, should be set before call
    
                int[] texW = new int[1];
                glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, texW);
                int[] texH = new int[1];
                glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, texH);
    
                float[] texCoordValues = new float[8];
    
                float left = 1.0f - (tiv[0] + tiv[2]) / (float)texW[0];
                float bottom = 1.0f - tiv[1] / (float)texH[0];
                float right = 1.0f - tiv[0] / (float)texW[0];
                float top = 1.0f - (tiv[1] + tiv[3]) / (float)texH[0];
    
                texCoordValues[0] = right;
                texCoordValues[2] = left;
                texCoordValues[4] = right;
                texCoordValues[6] = left;
    
                texCoordValues[1] = bottom;
                texCoordValues[3] = bottom;
                texCoordValues[5] = top;
                texCoordValues[7] = top;
    
                glEnableClientState(GL_VERTEX_ARRAY);
                glEnableClientState(GL_TEXTURE_COORD_ARRAY);
                glVertexPointer(2, GL_FLOAT, 0, s_vertexValues);
                glTexCoordPointer(2, GL_FLOAT, 0, texCoordValues);
                glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, s_indexValues);
    
                glPopMatrix();
            }
    

    Учтите, что копипастить себе этот код не стоит — он не будет работать там, где нет констант GL_TEXTURE_WIDTH/GL_TEXTURE_HEIGHT. Заодно переменная s_textureCropOesTiv должна быть заполнена до вызова, а сам код не выполняет переворот вьюпорта по оси ординат.


    XAML


    Некоторое количество магии понадобилось, чтобы проект запускался на актуальных версиях Mono, .Net 2.0-4.5, Wine, а заодно и под ReactOS, но в целом кроме зоопарка с текстурами особых проблем тут не было. А вот проблемы начались на Windows 8 и Windows Phone, где OpenGL отсутствует в принципе. С начала я пробовал решить это малой кровью, буквально дописав свою версию glDrawTexfOES, которая бы внутри вызывала что-то специфичное для этих систем. В ходе экспериментов я использовал XAML элемент Canvas, а в нем рисовал Rectangle, у которого в Brush использовалась нужная трансформация для отображения только части текстуры.
    Код трансформации в XAML
                    TransformGroup group = new TransformGroup();
    
                    ScaleTransform scale = new ScaleTransform();
                    scale.ScaleX = (double)texture.PixelWidth / (double)clipRect.Width;
                    scale.ScaleY = (double)texture.PixelHeight / (double)clipRect.Height;
                    group.Children.Add(scale);
    
                    TranslateTransform translate = new TranslateTransform();
                    translate.X = -scale.ScaleX * (double)clipRect.X / (double)texture.PixelWidth;
                    translate.Y = -scale.ScaleY * (double)clipRect.Y / (double)texture.PixelHeight;
                    group.Children.Add(translate);
    
                    imageBrush.RelativeTransform = group;
    

    clipRect — прямоугольник с параметрами обрезки, аналог s_textureCropOesTiv из примера выше
    texture — BitmapSource с самой текстурой

    Этот метод кажется странным, но надо помнить, что XAML зачастую hardware accelerated и довольно быстр. Я портировал с таким подходом несколько мобильных OpenGL ES игр на Windows 8 и работают они приемлемо, только нет возможности изменять цвет текстур, как в GL через glColor. Т.е. в принципе в XAML разрешается менять прозрачность элемента, но никак нельзя менять его Color Tint. Например, если у вас используются белые шрифты и потом раскрашиваются в разные цвета, то с этим подоходом они так и останутся белыми.

    В целом, вариант с XAML достаточно сомнителен и не совсем соответствовал изначальному плану, да и без цветовой дифференциации штанов модуляции, потому когда игра была на 80% готова и уже работала на мобильных и стационарном .Net/Mono, я начал искать более приемлемые варианты для Windows 8. Много было слухов и восторгов вокруг порта библиотеки Angle, но на тот момент она была уж очень сырая и без поддержки C#. Напрямую из C# работать с DirectX оказалось тоже не возможно, а сама Microsoft предлагает разработчику несколько «простых» путей: переделать весь C# код на C++, использовать стороннюю библиотеку SharpDX (C# биндинг над DirectX), либо перейти на MonoGame. Библиотека MonoGame это наследник XNA, использующий ту же SharpDX для вывода графики на Windows 8, она довольно неплоха, но довольно специфична и переходить на нее в моем проекте было поздновато. SharpDX выглядел не менее монструозным, ведь тянет за собой все существующие возможности DirectX, хотя и довольно близок к тому, что мне было надо. Я уже начал проводить с ним серьезные беседы с паяльником и мануалом, когда наткнулся на проект gl2dx.

    GL2DX


    Библиотека эта была выложена юзером average на CodePlex несколько лет назад и больше не обновлялась. Это С++ библиотека, которая объявляет такие же функции, как в OpenGL, а внутри транслирует их в вызовы D3D11. К библиотеке шел пример на C++/CX, который создавал XAML страницу с SwapChainBackgroundPanel и инициализировал ее через D3D11CreateDevice для работы с С++ частью. Проект был бы хорош, если бы хоть немного вышел из стадии прототипа. Технически, в нем работает всего несколько процентов OpenGL методов, а в остальных стоят ассерты. С другой стороны, она справляется с выводом 2D текстур, трансформацией и простейшей геометрией. На этом этапе я взялся за библиотеку и довел ее до состояния продукта, который подключается к C# проекту как Visual Studio Extension и позволяет писать подобный код:
    Код
     GL.Enable(All.ColorMaterial);
                GL.Enable(All.Texture2D);
                GL.Color4(1.0f, 1.0f, 1.0f, 1.0f);
    
                GL.TexParameter(All.Texture2D, All.TextureCropRectOes, new int[] { 0, 0, 1024, 1024 });
                GL.BindTexture(All.Texture2D, m_textureId1);
                GL.DrawTex(0, - (m_width - m_height) / 2, 0, m_width, m_width);
                
                for (int i = 0; i < 10; i++)
                {
                    if (i % 2 == 0)
                    {
                        GL.BindTexture(All.Texture2D, m_textureId2);
                        GL.TexParameter(All.Texture2D, All.TextureCropRectOes, new int[] { 0, 0, 256, 256 });
                    }
                    else
                    {
                        GL.BindTexture(All.Texture2D, m_textureId2);
                        GL.TexParameter(All.Texture2D, All.TextureCropRectOes, new int[] { 256, 0, 256, 256 });
                    }
                    
                    int aqPadding = 20;
                    int fishSize = 128;
                    int aqWidth = (int)m_width - aqPadding * 2 - fishSize;
    
                    float x = (Environment.TickCount / (i + 10)) % aqWidth;
                    float alpha = 1.0f;
                    if (x < fishSize)
                        alpha = x / fishSize;
                    else
                        if (x > aqWidth - fishSize)
                            alpha = (aqWidth - x - fishSize) / 128.0f;
    
                    GL.Color4(1.0f, 1.0f, 1.0f, alpha);
                    GL.DrawTex(x + aqPadding, m_height / 20 * (i + 5), 0, fishSize, fishSize);
                }
    

    P.S. Код в формате вызовов OpenTK, что немного сбивает с толку тех, что привык писать glColor4f вместо GL.Color4.
    Сие поделие получило от меня гордое название MetroGL.

    MetroGL


    Пример на C++/CX трансформировался в библиотеку на этом же птичьем современном языке, оброс большим количеством дополнительных функций, а C++ получила реализацию многих OpenGL методов, блендинги, оптимизацию внутренного VertexBuilder, загрузку произвольных изображений и DDS текстур, а главное — точную имитацию glDrawTexfOES, дающую 1в1 такую же картинку, как на OpenGL ES, а заодно соединяющую последовательные операции с одной текстурой в единый DrawCall. Кое-что пришлось доводить напильником, сам код местами грязноват (как до меня, так и после), а для создания VSIX расширения надо пересобирать проект вручную под каждую архитектуру (x86, x64, ARM) и лишь потом билдить VSIX проект. Главное, что если у вас есть OpenGL ES 1.* код с 2D интерфейсом или не сложным 3D, то с этой библиотекой его можно использовать прямо из C#, не думая о внутренностях, С++ коде, D3D11 контекстах и других гадостяхрадостях. Заодно сделан пример с рыбками справа и кодом из под ката. Конечно, если код у вас на OpenGL 2.0+ с шейдерами и экстеншенами, то ни о каком портировании речи и не будет.
    Другой неприятный момент в том, что у меня нет настроения и желания доводить библиотеку до уровня 50-100% совместимости с OpenGL, а значит под ваши конкретные задачи ее придется затачивать своими силами. Благо, весь код выложен на github и я пока никуда не исчезаю и буду рад коммитам или вообще желающим взвалить на себя этот груз. Библиотека собирается под Windows 8.0 и Windows Phone 8.1, для VSIX может понадобиться не-Express версия Visual Studio.

    Эпилог


    Ну и в конце-концов немного об играх. Проект свой я на 100% закончил и именно комбинация C# и OpenGL дала возможность сделать высокоуровневый код вообще не изменяемым — это библиотека без единого дефайна, не использующая каких-либо системных вызовов. Затем идет код среднего уровня: рисование через OpenGL в 2D, с вращением, трансформацией и цветовой модуляцией, тут немного код отличается на разных платформах — разные текстуры, по-разному хранятся данные. Низкоуровневая часть уже для каждой платформы разная, это создание окна, инициализация контекста, вывод звука. В любом случае, девять платформ, перечисленные в начале статьи, реально работают, и пусть C# в связке с OpenGL пока нельзя использовать в вебе или на Firefox OS, но все-равно разве это не отблеск кроссплатформенного будущего, господа?


    Поделиться публикацией

    Похожие публикации

    Комментарии 15

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

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

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

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

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

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

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

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

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

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

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

                        Опять же спор зашел в странную плоскость. Судя по всему, вы сразу начали работать с шейдерными технологиями и считаете все, что было до них анахронизмом. Но это не так, и разница тут совсем не такая, как у языков высокого уровня против ассемблера. Более того, режим без шейдеров еще и может быть быстрее в некоторых задачах. Шейдеры же дают гибкость, возможность принимать решения на GPU без CPU и прочие радости, а не скорость в примитивных действиях. На сим я откланиваюсь, ибо свою точку зрения высказал и аргументировал, а дальше уже не за чем хабру марать.
                          0
                          ибо свою точку зрения высказал и аргументировал
                          Простите, это чушь.
                          Очень аргументировано, действительно.
                          У вас есть какое-то объективное определение 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. Если не нужны шейдеры — ну дык использовать по умолчанию примитивный шейдер, который рисует все «как есть». Зато появится возможность безболезненно использовать шейдеры, когда они будут нужны.
            +2
            Ничего не имею против статьи, но, все же, вынужден заметить, что список
            iOS, Android, BlackBerry, Windows XP/7, Mac OS X, Linux, ReactOS, Windows 8, Windows Phone 8.1
            почти полностью перекрывается Unity. А делать игру на Unity намного быстрее и удобнее, чем писать свой движок. И для 2d игр там в бесплатной версии есть практически все, что необходимо, даже GUI они наконец сделали приличный. И скриптовый язык основной там тоже C# (в то время как сам движок написан на C++).
              0
              На 100% верно. Проблема в том, что я пошел с обратной стороны — сначала сделал большую часть для на iOS и Android, а потом подумал о Unity. И переделывать уже не было смысла.
              0
              пусть C# в связке с OpenGL пока нельзя использовать в вебе или на Firefox OS
              Кстати да, чуть не забыл. Обратите внимание на проект JSIL. По сути, это транслятор IL-кода сборок .NET в JS, который работает в браузере. Конечно, сама по себе трансляция кода не даст совместимости с OpenGL, но под JSIL для простых игр уже довольно неплохо работает враппер MonoGame. Думаю, и враппер OpenGL <-> WebGL написать при большом желании можно. Во всяком случае это, наверное, самый простой путь портирования под браузеры для вашего случая.

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое