вторник, 18 августа 2015 г.

Немного накопленного опыта в работе с трехмерной графикой.


Весьма забавно вышло с кодингом этого воксельного мирка, настолько, что я даже не решался пока что-то писать, а все-таки закончить на каком-то этапе, чтобы остановиться и проанализировать произошедшее.
Переход в 3D оказался неподъемной ношей для моего неопытного организма.

Во-первых, оказалось, что рисовать огромную кучу отдельных кубиков - неподъемная ноша для графического движка JMonkeyEngine. Да и, думаю, для любого другого. Поэтому все нормальные люди превращают кубики в большие наборы, которые называются в Майнкрафте "чанками". Это позволяет еще до рендеринга преобразовать массив кубиков в массив вершин, решить, какие стороны кубиков, в принципе, можно увидеть, преобразовать это в некий объект и загрузить для дальнейшей обработки шейдерами. Поэтому пришлось от "отдельных блоков" отказаться на этапе загрузки всего, что увидит игрок и вместо этого реализовать некоторый "менеджер чанков" и "чанки". Первый нужен для того, чтобы хранить и находить чанки вокруг игрока, решать, что их нет, запрашивать у сервера и удалять из памяти, когда они стали не нужны (игрок ушел за пределы "видимости", скажем). Второй, собственно, занимается хранением отдельных блоков и преобразованием их набора в "mesh", то есть некоторый объемный объект, который содержит несколько VBO, то есть массивов вершин, координат текстур и других данных для обработки в видеокарте.
Вот после их полной загрузки их, в принципе, можно и обновлять отдельными событиями вроде "удалить блок", "добавить блок", "изменить блок". Но тут тоже не все так гладко. В выбранном мной для простоты начинаний JMonkeyEngine3, да и, я думаю, где угодно еще, один Mesh не может содержать множества разных материалов. То есть, на него, в принципе, можно натягивать много разных текстур, используя атлас и натягивая на разные треугольники разные куски этого атласа, но сделать его часть прозрачной, а часть непрозрачной, например, уже сложнее. Все дело, видимо, в том что он целиком обрабатывается одним и тем же набором шейдерных программ. Чтобы это решить, нужно еще крепко подумать.
Первый вариант - выделять из чанка отдельно прозрачные и непрозрачные блоки, например и делать из них два разных "меша", Увеличивает количество VBO, потенциально снижая производительность рендера, зато делать с ними можно что угодно. Второй - считать весь чанк прозрачным и управлять прозрачностью отдельных блоков с помощью альфа-канала текстуры. До этого я еще не дошел и предстоит еще разобраться, какой позволит добиться правильного эффекта.

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

Как только эта штука будет способна быстро нарисовать хотя-бы длинное плоское полюшко-поле и позволять перемещаться над ним с подгрузкой/выгрузкой, я считаю, что можно будет это считать какой-то определенной вехой и загрузить код на GitHub после некоторой чистки. Сейчас она это уже умеет, но медленно и с некоторыми багами. Но умеет.
И приступить тогда к решению следующих задач, из коих есть что выбрать.

  • Как я уже говорил, мир состоит не из тупо блоков, а из каких-то веществ в каком-то состоянии. Значит, нужно думать, где и как сохранить этот набор веществ и правила, по которым они отображаются. Прозрачные они или нет, какие у них текстуры и все такое прочее. Потом нужно научиться это рендерить. Также важно, может ли игрок двигаться внутри блоков, или должен "сталкиваться" с их границей. Сейчас он просто пролетает сквозь них, это по-своему удобно для отладки одних вещей. но неудобно для отладки других. Значит, и на сервере и на клиенте нужно обнаруживать столкновения и запрещать дальнейшее движение.
  • Следующая проблема, это, конечно, тики сервера. Сейчас я их отключил совсем. Нужно их возродить и что-то обрабатывать. Так как блоки пока стали статичными, обрабатывать их нет смысла. Одним из важных параметров мира, как правило, является энергия, например, тепловая энергия. Ее хранение и перенос. Это непростая проблема и интересно она начинает работать только если блоки разные, в том числе, по поведению. Например, нагревающийся газ расширяется. Нагревающаяся вода испаряется.
  • Это значит, что без какой-то физической модели, кроме тепловой, обойтись нельзя. Нужно вводить какие-то понятия, во-первых, силы и давления (расширяющися газ), гравитации, импульса, массы, наконец. Появление таких вещей, как гравитации, заставляет задуматься над тем, могут ли двигаться блоки и какие правила при этом действуют. Очевидно, как минимум нетвердые блоки должны двигаться. Или, скажем, не сами блоки, а их содержимое. Пока я не столкнулся еще с проблемами производительности, я бы все-таки попробовал подумать, можно ли реализовать конечную воду, которая течет в соответствии с действием гравитации, пусть даже без "просачивания" в пористые/проницаемые вещества.
  • Но все эти вещи трудно смотреть и отлаживать без возможности для игрока ставить и убирать любые блоки. То есть, нужен какой-то "админский" или "божественный" режим, похожий на креатив в Майнкрафте. Для этого нужно, конечно же, реализовать не только такие мелочи как "узнать, в какой блок сейчас смотрит игрок и в какую его плоскость", но и некоторый GUI для выбора, какой вид блока поставить. Возможно, еще какой-то UI для того, чтобы понять, что перед тобой есть.
  • Для дальнейшей отладки очень удобно было бы все-таки заиметь текстовую консоль с командами на сервере и на клиенте. И какой-то инструмент для "замера параметров" блока с помощью наведения на него, так как нет смысла передавать все параметры каждого блока и некоторые стоит все-таки передавать по запросу.
  • Также ясно, что наличие движения требует синхронизации того, что игрок видит на экране и того, что сервер посчитал у себя. Игрок должен видеть это плавно, сервер должен делать это быстро (потому что игроков может быть более одного), так что это интересная задача, даже если думать только о движении самого игрока. Сейчас, для простоты, игрок просто передает вектор своего относительного движения на каждый апдейт (по сути - фрейм) внутри клиента, что накладно, так как это сотни-тысячи сообщений в секунду. С учетом того, что двигает игрока, на самом деле, сервер, отвечая ему новыми координатами. можно прикинуть, насколько это неоптимально. Конечно, когда FPS начнет проседать, автоматически решится и эта проблема, но зачем надеяться, что ты сделаешь слайд-шоу? :)
  • Ну и конечно, за всем этим следует работа над первым этапом генерации мира, на основе самого простого трехмерного шума. Почему так? Да потому что для всех остальных фич все намного сложнее и оно требует хотя-бы их наличия. Не хотелось бы иметь мир, который после генерации и загрузки в память вдруг оказывается нестабильным по действующим законам и требует еще кучу времени, чтобы устаканиться (такое можно иногда в Майнкрафте заметить, когда вдруг в новозагруженных чанках все осыпается и начинает течь с потолка поток лавы).

Комментариев нет:

Отправить комментарий