Рисуем огромное количество тайлов в Monogame (XNA)

8

Я задаю этот вопрос, потому что я не нашел однозначного ответа на него.

Позвольте мне прежде всего высказать несколько вещей об игре и о том, что я уже сделал. Игра станет RTS-сетом в процедурном мире с использованием симплексного шума. Мир состоит из фрагментов размером 16 x 16, созданных из спрайтов размером 64 x 64. Мне удалось динамически загружать и выгружать фрагменты, что прекрасно работает. Мир будет немного похож на Rimworld, поэтому сверху вниз расположены различные слои спрайтов (сначала ландшафт, спрайты перехода, деревья, наклейки и т. Д.). Вновь созданные миры могут содержать сущности, которые могут влиять на окружающую среду (например, деревня, ставшая городом) и тем самым кусок. Я уверен, что это можно рассчитать с помощью какой-то функции, но это то, на что нужно обратить внимание.

Основной вопрос у меня есть, когда я уменьшить, все больше и больше плиток нарисованы , которые сильно влияют на производительность. При примерно 30000 спрайтах секция рисования занимает 8 мс, что составляет половину от того, что требуется для работы при 60 FPS. И это только местность. Я использую атласы текстур, чтобы ограничить количество отрисовок (30000 спрайтов, нарисованных за 6 отсчетов).

Целью является , чтобы иметь возможность отдалить города / села / города уровень всего пути , чтобы быть в состоянии увидеть всю страну. Это имеет быть сделано динамически (например, не нажав на иконку миникарты, а просто прокрутить назад так же , как в Supreme Commander).

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

  • Грязные прямоугольники, как описано здесь , где вы рисуете только новые вещи, а остальное оставляете в буфере. Это имеет большой смысл, но я не знаю, как реализовать это в Monogame.
  • Мой предпочтительный выбор: широко использовать RenderTargets, как описано здесь , где вы рисуете в RenderTarget, а затем сохраняете его как текстуру. В моем случае блок 16 x 16, состоящий из 64 x 64, создал бы текстуру 1024 x 1024. Я действительно сомневаюсь, что это будет работать с точки зрения производительности, но результат будет состоять из высокодетализированных текстур, а также будет полезен для использования, учитывая тот факт, что большинство из них статические (рельеф / деревья и т. Д.) И не меняются так сильно. Но это также означает, что каждый раз, когда вносятся изменения в чанк, необходимо изменять Texture2D с помощью SetData, что, как я понял, довольно сильно загружает процессор. Однако, если бы текстуры были 16 х 16, они могут действительно работать, а также уменьшат использование памяти и диска.
  • Наклоните текстуры, указав SourceRectangle в SpriteBatch. Для огромных лугов / океанов это плюс, так как рисуется только один спрайт. Однако для детализированной местности с разными цветами и разными спрайтами (биомы и биомы) я боюсь, что это не будет иметь большого значения.
  • Мое собственное решение, которое ставит под угрозу детализацию, состояло в том, чтобы использовать стандартную белую плитку размером 64 x 64, придать ей цвет окружающих 4 плиток и затем масштабировать их так, чтобы они покрывали 4 предыдущих плитки. Различие здесь (помимо плитки, имеющей простой цвет) в том, что ландшафт заметно изменился. Я должен также упомянуть, что леса все еще должны быть нарисованы индивидуально, так как они не идеально квадратные.

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

Guguhl Pluhs
источник
Не позволяйте пользователю так сильно уменьшать масштаб
Bálint
У меня также возникли проблемы с производительностью в моем процедурно сгенерированном проекте MonoGame. Мое решение состояло в том, чтобы ограничить то, что было визуализировано (ограничить диапазон уменьшения и рисовать только то, что на экране). Кажется, что MonoGame разработан с мантрой «рисовать все в каждом кадре», но я надеюсь, что кто-то более опытный с MonoGame исправит меня.
Логическая ошибка
Как вы визуализируете плитки? Более конкретно - вы используете SpriteBatch и как вы его используете?
loodakrawa

Ответы:

3

Графические карты оптимизированы для рисования нескольких вещей много раз, а не наоборот.

В вашем случае у вас есть много вещей (разных текстур), которые вы хотите рисовать много раз (количество плиток).

IMO, простейшая оптимизация, которую вы можете сделать, чтобы получить значительный прирост производительности:

  1. Объедините вашу текстуру в атласы. Это должно значительно ускорить процесс, поскольку ваша видеокарта теперь рисует только одну (или пару) текстур вместо множества маленьких.
  2. Пакетные плитки с SpriteBatch в режиме SpriteSortMode.Texture. Это позволит отсортировать ваши спрайты по текстуре и минимизировать количество переключателей текстур до фактического количества различных текстур. Недостатком этого подхода является то, что ваши спрайты не будут отсортированы по глубине. Что, вероятно, хорошо, так как все ваши плитки находятся на одной глубине. Я предполагаю, что у вас есть и другие вещи, так что имеет смысл иметь отдельный SpriteBatch для плиток и еще один для всего остального (установлен в SpriteSortMode.BackToFront).

Я бы порекомендовал делать обе вещи. Удачи.

loodakrawa
источник
2

Я обнаружил, что SpriteBatch не подходит для рисования плиток. Одна из причин заключается в том, что спрайты предназначены для динамических объектов и используются для нескольких целей. Другая причина в том, что он невероятно загружен процессором , например. как объяснено в описании чертежа, 30 000 объектов стоят 8 мс (в то время как мой набор микросхем увеличен до 30%). Кроме того, до 3000 спрайтов может быть отрисовано до того, как новый вызов отрисовки должен быть сделан на графический процессор (пакетный и все).

Решением было нарисовать мои собственные плитки, используя текстурированные квадраты . Это в сочетании с VertexBuffer позволило мне рисовать до 500 000 текстурированных плиток за один вызов, когда метод рисования занимал примерно 1/20 миллисекунды. Конечно, мне, вероятно, не придется рисовать так много, но в любом случае я получил, чтобы мой GPU имел нагрузку 75% при стабильных 60 кадрах в секунду.

Guguhl Pluhs
источник
Звучит очень хорошо. Я хотел бы узнать больше об этом - следовали ли вы каким-либо учебным пособиям или у вас есть ссылки с более подробным описанием этого процесса?
loodakrawa
2
Я узнал большинство из них везде, где упоминались XNA / Monogame и текстурированные четырехугольники. Тем не менее, riemers.net очень мне помог, поскольку он нацелен на людей, которые просто хотят быстро настроить что-то, что работает. Вы хотите точно следовать его учебнику по 3D рельефу, поскольку он также охватывает VertexBuffers и IndexBuffers. Превращение его в плитку типа местности не должно быть проблемой.
Guguhl Pluhs
Если это именно то, что отвечает на ваш вопрос, вы можете пометить его как принятый :)
Vaillancourt
@GuguhlPluhs Я знаю, что уже поздно, но у вас еще есть какая-то информация по этому вопросу? Я сталкиваюсь с той же проблемой.
Jelle