LINUX.ORG.RU

Оффлайн 2D рендеринг в Metal/OpenGL

 , , , ,


0

2

Задал вопрос на stackoveflow про то, каким лучше способом рендерить анимированный 2D треугольник (у которого шейдером будут меняться цвета), закешировать это в виде дюжины спрайтов, и затем результат рендеринга подсовывать игровому движку в виде спрайтов из кеша. Либо сразу рисовать поверх картинки игрового движка этот несчастный треугольник с анимацией, но только не понятно каким путём лучше это делать, учитывая, что нативные движки для Apple, к примеру, spriteKit/sceneKit, скрывают Metal/OpenGL под капотом. Правильным ли будет рендерить в отдельный “layer”, а затем композитить всё в единый UI?

Насколько я знаю, метал рендерит свой мир в текстуру, затем можно скопировать данные из этой текстуры в пустую картинку, из неё слепить спрайт и уже им оперировать в 2D движках. Правильно ли я мыслю?

За этот вопрос мне стали сливать на SO карму и никто ничего не написал мне в ответ. WTF? :-D

★★★★★

Я ничего не понял, суёшь кадры в 1 текстуру вот так https://cdn.atomisystems.com/uploads/2017/11/sprite-sheet-image.png допустим в одной текстуре 8 на 8 кадров 64 кадра анимации 1 делить на 8 = uv этой текстуры в шейдере опять же в него передаёшь смещение UV что бы получить нужный кадр. У меня вот фиксированная UV анимация отправляю в шейдер x,y позицию кадра в текстуре и скейл (сколько кадров строке с учётом что всё квадратное)

Выглядит это вот так –> https://youtu.be/1YZjs6HU92w.

vec2 uv_frame(int scale_factor,int frame)
{ 
    vec2 uvn = vec2_zero();
    static vec2 uv2  [4+1]  =
    {  {0,0},//first frame unused
       {0,0},{1,0},
       {0,1},{1,1}
    };
    static vec2 uv3  [9+1]  =
    {  {0,0},//first frame unused
       {0,0},{1,0},{2,0},
       {0,1},{1,1},{2,1},
       {0,2},{1,2},{2,2}
    };
    static vec2 uv4  [16+1] =
    {  {0,0},//first frame unused
       {0,0},{1,0},{2,0},{3,0},
       {0,1},{1,1},{2,1},{3,1},
       {0,2},{1,2},{2,2},{3,2},
       {0,3},{1,3},{2,3},{3,3}
    };

    static vec2 uv5 [25+1] = 
    {   {0,0},//first frame unused
        {0,0},{1,0},{2,0},{3,0},{4,0},
        {0,1},{1,1},{2,1},{3,1},{4,1},
        {0,2},{1,2},{2,2},{3,2},{4,2},
        {0,3},{1,3},{2,3},{3,3},{4,3},
        {0,4},{1,4},{2,4},{3,4},{4,4}
    };

    static vec2 uv6 [36+1] = 
    {   {0,0},//first frame unused
        {0,0},{1,0},{2,0},{3,0},{4,0},{5,0},
        {0,1},{1,1},{2,1},{3,1},{4,1},{5,1},
        {0,2},{1,2},{2,2},{3,2},{4,2},{5,2},
        {0,3},{1,3},{2,3},{3,3},{4,3},{5,3},
        {0,4},{1,4},{2,4},{3,4},{4,4},{5,4},
        {0,5},{1,5},{2,5},{3,5},{4,5},{5,5}
    };

    static vec2 uv7 [49+1] = 
    {   {0,0},//first frame unused
        {0,0},{1,0},{2,0},{3,0},{4,0},{5,0},{6,0},
        {0,1},{1,1},{2,1},{3,1},{4,1},{5,1},{6,1},
        {0,2},{1,2},{2,2},{3,2},{4,2},{5,2},{6,2},
        {0,3},{1,3},{2,3},{3,3},{4,3},{5,3},{6,3},
        {0,4},{1,4},{2,4},{3,4},{4,4},{5,4},{6,4},
        {0,5},{1,5},{2,5},{3,5},{4,5},{5,5},{6,5},
        {0,6},{1,6},{2,6},{3,6},{4,6},{5,6},{6,6}
    };

    static vec2 uv8  [64+1] =
    {  {0,0},//first frame unused
       {0,0},{1,0},{2,0},{3,0},{4,0},{5,0},{6,0},{7,0},
       {0,1},{1,1},{2,1},{3,1},{4,1},{5,1},{6,1},{7,1},
       {0,2},{1,2},{2,2},{3,2},{4,2},{5,2},{6,2},{7,2},
       {0,3},{1,3},{2,3},{3,3},{4,3},{5,3},{6,3},{7,3},
       {0,4},{1,4},{2,4},{3,4},{4,4},{5,4},{6,4},{7,4},
       {0,5},{1,5},{2,5},{3,5},{4,5},{5,5},{6,5},{7,5},
       {0,6},{1,6},{2,6},{3,6},{4,6},{5,6},{6,6},{7,6},
       {0,7},{1,7},{2,7},{3,7},{4,7},{5,7},{6,7},{7,7}
    };
    switch (scale_factor)
    {
        case 2: uvn = uv2[(frame >  4+1 )?0:frame]; break;
        case 3: uvn = uv3[(frame >  9+1 )?0:frame]; break;
        case 4: uvn = uv4[(frame > 16+1 )?0:frame]; break;
        case 5: uvn = uv5[(frame > 25+1 )?0:frame]; break;
        case 6: uvn = uv6[(frame > 36+1 )?0:frame]; break;
        case 7: uvn = uv7[(frame > 49+1 )?0:frame]; break;
        case 8: uvn = uv8[(frame > 64+1 )?0:frame]; break;
        default: break;
    }
    return uvn;
}


Передаёшь в шейдер текстуру делитель (кадров в строке) и vec2 значения положения кадра и собственно получаешь кадр для отрисовки


uniform vec2  frame_pose;     //x=0:y=0 для первого кадра к примеру
uniform float frame_line_nums;//8 для 8x8=64 кадров в текстуре
uniform sampler2D texture;
vec4 texcolor =  texture2D(texture, vec2(fTexcoord.x + frame_pose.x ,fTexcoord.y + frame_pose.y ) / frame_line_nums );

Ну и всё раскрашивай texcolor и отправляй на отрисовку или рисуй в буфер
LINUX-ORG-RU ★★ ()

Насколько я знаю, метал рендерит свой мир в текстуру, затем можно скопировать данные из этой текстуры в пустую картинку, из неё слепить спрайт и уже им оперировать в 2D движках. Правильно ли я мыслю

Тут нельзя мыслить правильно,как там живёт метал я не в курсе вообще, а вот на счёт движков, тут надо смотреть в доки движка, где как и что там можно и какие нюансы, работа с текстурами там порой вообще ничего общего с обычным кодом на API низкоуровневом не имеет, не которые твои ресурсы автоматом батчат в одну больную и когда ты запрашиваешь нужную тебе то выдают срез от большой ну типа id виртуальный.

Твой вопрос слишком общий, я зх что отвечать =)

Мне кажется тебе надо идти с другой стороны, сначала типа ага мне нужна анимировання текстура, я могу сделать вот так и вот так, ага, а теперь я погляжу что мне доступно в движке от эппла и ли в OpenGL для реализации моей идеи.

Хотя хз.

LINUX-ORG-RU ★★ ()

За этот вопрос мне стали сливать на SO карму и никто ничего не написал мне в ответ. WTF? :-D

Там серые люди сидят и пальцы веером. Забей.

Чво за игрушку делаешь? ^.^/ :)

LINUX-ORG-RU ★★ ()
Ответ на: комментарий от LINUX-ORG-RU

Понимаешь, у меня нет опыта рендеринга в оффлайн изображения (без вывода изображения на экран). И я не знаю как мне отрендерить треугольник (примитив) в Metal/OpenGL для получения Quartz 2D картинки, это CGImage

https://developer.apple.com/documentation/coregraphics/cgimage

menangen ★★★★★ ()
Ответ на: комментарий от anonymous

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

menangen ★★★★★ ()
Ответ на: комментарий от anonymous

Мне просто интересно, в чём причина слива на SO? Люди не поняли мой вопрос? Как оффлайн рендерить из Metal в буфер и из него лепить картинку под эпл платформой? Я ждал описания по шагам как что делать, а получил тупо минусы, причём без единого коммента, это и удивляет.

menangen ★★★★★ ()
Ответ на: комментарий от menangen

Что за CGImage? В опенжоеле такого нет, зачем он? Не следует что-то забирать в память cpu без реальной необходимости, особенно чтобы опять обратно на видеокарту заливать. По-идее можно сразу на видеокарте сгенерировать в её же видеопамять что там тебе нужно.

anonymous ()
Ответ на: комментарий от anonymous

Согласен, что гонять графику плохой способ. Спс за ссылку, буду пробовать через композитинг слоёв, подготавливая 2 слоя: один слой из нативных фреймворков, а вторым слоем нарисовать треугольник через gl/metal с прозрачным background

menangen ★★★★★ ()

В данном конкретном случае не вижу смысла в кэшировании, ибо когда ты будешь рендерить свой спрайт движок всё равно будет рисовать треугольник, или два. А сэмплирование текстуры, да ещё и переключение этих текстур между кадрами, скорее всего вычислительно более накладно, чем напрямую отрендерить что нужно с шейдером.

нативные движки для Apple, к примеру, spriteKit/sceneKit, скрывают Metal/OpenGL под капотом

Так используй средства движка, нахрена тебе вообще опускаться до Metal/OpenGL? Рендерить треугольники шейдерами любой движок должен уметь.

slovazap ★★★★★ ()
Ответ на: комментарий от slovazap

Шейдеры - они не для рендеринга треугольника. А для обработки уже отрендериной картинки. Для всяих эффектов. Треугольник рендерят как раз таки opengl/metal, так как это быстро. Metal позволяет миллионы треугольников отрендерить за миллисекунды. Ну, не суть. Смысл в том, что их движки заточены под готовую графику и готовые модели. Мысли твои я понял, и понятно, что способов много, но людей знающих конкретно какой лучше - очень мало.

menangen ★★★★★ ()
Ответ на: комментарий от menangen

Шейдеры - они не для рендеринга треугольника. А для обработки уже отрендериной картинки. Для всяих эффектов.

А это что, не ты писал:

анимированный 2D треугольник (у которого шейдером будут меняться цвета)

Вообще-то шейдеры для всего - и для изначального рендеринга примитивов, и для пост-эффектов. Им вообще без разницы.

Треугольник рендерят как раз таки opengl/metal, так как это быстро

Ещё раз: у тебя всега всё рендерит opengl/metal, даже когда сверху движок.

Смысл в том, что их движки заточены под готовую графику и готовые модели

Но шейдеры-то они умеют? Если умеют, тебе больше ничего и не нужно. Возьми готовую модель треугольника, повесь на неё шейдер, и меняй себе цвет.

slovazap ★★★★★ ()
Ответ на: комментарий от menangen

Шейдеры - они не для рендеринга треугольника. А для обработки уже отрендериной картинки. Для всяих эффектов.

Нет,

             (вершинный шейдер)                       (пиксельный шейдер)
             координата модели -> uv ->  цвет uv координаты пикселя по координате модели -> вывод пикселя на экран/буфер    
                         |
                         |
буферы данных -> шейдерный конвеер -> отрисовка на эран (прямой рендеринг)
      |                  |
      |                  |
(изображения,)           `--- отрисовка в буфер данных -> шейдерный конвеер -> отрисовка на экран (отложенный рендеринг)
  (модели)
  (любые массивы данных в нужном представлении)

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

Мне кажется ты просто излагаешь по своему и вот всем и непонятно.

У тебя есть

  • набор картинок

Тебе надо

  • загрузить картинки и рисовать их по очереди на другой картинке для вывода

Перед выводом тебе нужно

  • передать следующую картинку в шейдер, разукрасить её и получить разукрашенный вариант

Я вот так понял что тебе надо в истине.

Если ты используешь движки - просто забываешь о том что такое opengl и metal ибо ты работаешь через слой абстракци, язык шейдеров там порой тоже свой из которого уже генерируется внутри движка целевой

Твой порядок обработки (сгенерировать/загрузить изображение) -> (передать его шейдеру) -> (отрисовать сразу их него куда нужно)

Теперь смотришь, как заглузить изображения (реализуешь загрузку)

Далее смотришь как обработать изображение шейдером (реализуешь передачу изображения в шейдер и обрабатываешь его там)

Далее смотришь как получить результат в виде просто изображения и как сразу отрисовать.

В первом случае получить именённую щейдером картинку для сохранения или для передачи другой части программы, второе для вывода, во втором случае рисуешь одно над другим.

У тебя apple + движок какой то, просто на бумаге составь план, как ты видишь процесс обработки и вывода прям нарисуй его , а затем последовательно реализуй. Просто осмысливая задачу целиком ты запутаешься к чертям.

                          .---- сохранить и передать другой части программы -> нарисовать
                          |
загрузить -> обработать ->|
                          |
                          `--- нарисовать
LINUX-ORG-RU ★★ ()
Ответ на: комментарий от menangen

Понимаешь, у меня нет опыта рендеринга в оффлайн изображения (без вывода изображения на экран).

Аааааа, тьфу. Читай про отложенный рендеринг в буфер вот для затравки, важно саму суть понять https://habr.com/ru/post/420565/ а дальше гугли как отрисовать текстуру в буфер и как из буфера получить image data

генерируешь буфер
пишешь шейред
биндишь буфер, включаешь шейдер, передаёшь в шейдер текстуру, исполняешь шейдер
результат отрисовки теперь лежит в буфере который ты забиндил, а не на экране
получаешь картинку из буфера, делаешь что то с ней дальше

И я не знаю как мне отрендерить треугольник (примитив) в Metal/OpenGL

http://www.opengl-tutorial.org/ru/intermediate-tutorials/tutorial-14-render-to-texture/ https://stackoverflow.com/questions/32149512/opengl-how-to-read-back-texture-buffer

LINUX-ORG-RU ★★ ()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)
Ответ на: комментарий от LINUX-ORG-RU

Не, ты не так понял. У меня есть 2D сцена. Она разбита на ноды. Ноды - это спрайты. На спрайты можно накладывать по отдельности тока пиксельные шейдеры, и точка. У меня стоит задача поверх этой сценки рисовать кучу треугольников посредством Metal (вершинным шейдером ясен пень). В определенном положении относительно 2D нод. И раскрашивать полученный треугольник/и через пиксельный Metal shader. Мне нужны были примеры кода КАК ЛУЧШЕ (совместить 2D «spriteKit» и низкоуровневый Metal), как быстрее, как удобнее и т.п.

Я пришел к выводу, что нужно рисовать через Metal отдельным слоем, и не гонять текстуру, но это не так удобно, как мне бы хотелось. Так как 2D spriteKit движок требует именно обычное представление картинки (нативное для Mac) и хранящееся в абстракции CGImage (память CPU).

menangen ★★★★★ ()
Ответ на: комментарий от LINUX-ORG-RU

просто на бумаге составь план, как ты видишь процесс обработки и вывода прям нарисуй его

Да я хотел советов на SO какой путь выбрать для решения задачи микса результата отрисовки метал + движка спрайт кит.

Apple рекомендует создать базовую Metal приложуху, и в неё присобачить 2D сцену с кишками 2D движка - тоже вариант, но примеров как это будет работать, как всем этим управлять (в плане очередности прорисовки, слоев и прочего - нигде нет).

Я знал какими путями можно это сделать (в общих чертах, на очень базовом уровне), но на SO хотел найти совет как люди делали, и каким путем пойти лучше, а результат - молчание.

menangen ★★★★★ ()
Ответ на: комментарий от slovazap

Но шейдеры-то они умеют?

SpriteKit умеет тока пиксельные OpenGL ES шейдеры. Никаких вершинных. Но можно создать голое Metal приложения и туда впендюрить эту SpriteKit сцену, но некоторые плюшки движка теряются (похер на них)

menangen ★★★★★ ()
Ответ на: комментарий от menangen

(совместить 2D «spriteKit» и низкоуровневый Metal)

Во! Понял, ну промолчу, дела не имел.

Так как 2D spriteKit движок требует именно обычное представление картинки (нативное для Mac) и хранящееся в абстракции CGImage (память CPU).

Значит придётся рисовать отдельно или встраивать одно в другое, а потом забирать из GPU на CPU раз именно требует. Ну или хак искать =)

Ну удачи чё =)

LINUX-ORG-RU ★★ ()

рендеришь спрайты в текстуру/текстуры, любым способом. metal умеет это. дальше, например, используй вот это чтобы нарисовать их через metal: https://developer.apple.com/documentation/scenekit/scnnoderendererdelegate?la...

вроде понятно и просто же все? нафига забирать результаты рендера в main ram?

waker ★★★★★ ()
Ответ на: комментарий от waker

со spritekit такое вероятно не получится.

но отрендерить в текстуру и забрать из нее в CGImage тривиально.

дергаешь вот это: https://developer.apple.com/documentation/metal/mtltexture/1516318-getbytes?l...

а потом из результата создаешь CGImage. гугли «CGImage from byte array».

waker ★★★★★ ()
Ответ на: комментарий от waker

Спасибо, я пока решил не гонять текстуру туда-сюда, а рисовать отдельным слоем. Спасибо за идеи. Пока всё ок, удалось отрендерить что я хотел с прозрачностью, наложением и прочим, так что всё нормуль. В движок spriteKit я не стал это интегрировать, так как у него есть 3D нода, надо будет попробовать в ней порисовать через SceneKit движок (там высокоуровневая абстракция над metal/opengles, опять таки)

menangen ★★★★★ ()