Рисование огромного количества текстурированных кубов в XNA 4.0 - PullRequest
5 голосов
/ 15 января 2011

Я пытаюсь написать собственный многопользовательский клиент Minecraft Classic для XNA 4.0, но я совершенно ошарашен, когда дело доходит до рисования мира в игре. Каждый блок представляет собой куб в трехмерном пространстве, и он может иметь разные текстуры на каждой стороне. Я читал в Интернете и обнаружил, что для того, чтобы у куба была разная текстура на каждой стороне, каждому лицу нужен свой набор вершин. Это дает в общей сложности 24 вершины для каждого куба, и если у вас есть мир, состоящий из 64 * 64 * 64 кубов (или, возможно, даже больше!), То получается много вершин.

В своем исходном коде я разделил карту текстур на отдельные текстуры и применил их перед рисованием каждой стороны каждого куба. Мне сказали, что это очень дорогой подход, и я должен держать текстуры в одном файле и просто использовать UV-координаты для отображения определенных субтекстур на куб. Это не сильно повлияло на производительность, поскольку количество вершин просто слишком велико. Мне также сказали собрать вершины в VertexBuffer и нарисовать их все сразу, но это тоже мало помогло, и иногда вызывает исключение, когда число вершин превышает максимальный размер буфера. Любая попытка заставить кубы делить вершины также потерпела неудачу, что привело к значительному замедлению и глючным кубам.

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

Вот метод, который я использую для рисования кубов. У меня есть два глобальных списка List<VertexPositionTexture> и List<int>, один для вершин и один для индексов. При рисовании я перебираю все кубы в мире и выполняю RenderShape на тех, которые не пусты (например, Air). Класс формы, который я имею, вставлен ниже. Закомментированный код в методе AddVertices - это попытка заставить кубы совместно использовать вершины. Когда все вершины кубов добавлены в список, данные вставляются в VertexBuffer и IndexBuffer и вызывается DrawIndexedPrimitives.

Честно говоря, я, вероятно, делаю это совершенно неправильно, но я действительно не представляю, как это сделать, и нет учебных пособий, которые на самом деле описывают, как рисовать много объектов, только очень простые. Мне пришлось выяснить, как переделать BasicShape, чтобы иметь несколько текстур самостоятельно.

Форма: http://pastebin.com/zNUFPygP

Ответы [ 5 ]

3 голосов
/ 12 декабря 2011

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

http://techcraft.codeplex.com

Его бесплатный и открытый исходный код.Он должен показать вам, как создать двигатель, похожий на Minecrafts.

1 голос
/ 15 января 2011

Вам нужно подумать о сокращении размера проблемы. Как вы можете создать то же изображение, выполнив меньше работы?

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

Если ваши кубы что-то скрывают за ними, вам нужно только нарисовать самые передние кубы - что-нибудь позади них просто не видно. Естественным подходом для этого было бы использование структуры данных октодерева, которая делит трехмерное пространство на воксели (кубы). С помощью октодерева вы можете быстро определить, какие кубы видимы, и просто нарисовать эти кубы - поэтому вместо рисования кубов 64x64x64 вам может понадобиться нарисовать несколько сотен на кадр. Вы также обнаружите, что по мере движения камеры набор видимых кубов не сильно изменится, поэтому вы можете использовать эту «временную согласованность» для обновления ваших структур данных, чтобы минимизировать работу, которую необходимо выполнить, чтобы решить, какие кубы видны.

1 голос
/ 15 января 2011

Есть много вещей, которые вы можете сделать, чтобы ускорить это:

То, что вы хотите сделать, это запечь область кубов в буфер вершин. Под этим я подразумеваю взять все кубы в небольшой области и поместить их все в один буфер вершин. Обновляйте этот буфер только при изменении куба.

В мире, подобном Minecraft, Множество лиц закрывают друг друга. Самое большое, что вы можете сделать, это скрыть лица, которые разделены между двумя кубами. Представьте, что два кубика сидят рядом друг с другом, вам не нужно рисовать лицо между ними, так как его никогда не будет видно. В нашем движке это привело к уменьшению вершин в 20 раз.

 _ _      _ _
|_|_| == |_ _|

Что касается ваших текстур, то, как вы сказали, неплохо использовать атлас текстур. Это значительно уменьшает ваши колл-колы.

Удачи! А если вы хотите обмануть, посмотрите на Infiniminer . Infiniminer - игра, основанная на Minecraft. Он написан на XNA и имеет открытый исходный код!

0 голосов
/ 20 августа 2016

Я недавно написал движок для этого в XNA, метод, который вы хотите изучить, называется аппаратным инстансингом и позволяет вам передавать одну модель в шейдер с потоком мировых позиций, чтобы «создать экземпляр» этой модели сотен (даже тысячи раз) по всему игровому миру.

Я построил свой движок поверх этого примера, заменив инстансированную модель своей собственной.

http://xbox.create.msdn.com/en-US/education/catalog/sample/mesh_instancing

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

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

0 голосов
/ 15 января 2011

Я не думаю, что Minecraft все время рисует все кубы.Большинство из них являются внутренними, и вам нужно рисовать только те, что на поверхности.В общем, вам нужен эффективный воксел рендер.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...