После деталей приступила к траве! Генерация травы очень похожа на генерацию деталей, но есть и отличия, так что придется хорошенько поработать 💃
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5⚡1
Media is too big
VIEW IN TELEGRAM
Первая версия травы! 🙌 Выглядит кривовато, буду еще дорабатывать)
За счет уже готового процесса добавления новых модулей генерации, это заняло всего один день. Правда много т.н. бойлерплейта, хмм, есть о чем подумать😶
За счет уже готового процесса добавления новых модулей генерации, это заняло всего один день. Правда много т.н. бойлерплейта, хмм, есть о чем подумать
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12❤1
Чуть ранее я немного писала про архитектуру генератора и упомянула, но не рассказала, т.н. процессоры данных. Что это такое? Попробую объяснить.
Генератор и модули генерации отвечают только за генерацию данных в каком-либо формате. Генератор ландшафта не генерирует Mesh, он генерирует только набор вершин и описание треугольников. Генератор деталей или травы не генерирует сами детали или траву, он генерирует только матрицы трансформаций. То есть генератор - это речь только про самый минимум данных.
А как тогда получать ландшафт? Камушки? Травку и остальные объекты окружения? Да еще и с материалами!
Вот тут и вступают в игру эти процессоры данных, которые умеют переводить данные из формата от генератора в формат, который нужен для представления. Например, для ландшафта процессинг занимает несколько шагов:
- конвертирование данных о треугольниках в
- генерация материала для каждого чанка и всех данных которые необходимы для этого материала (текстурные массивы, различные буферы и т.п.);
- отправка данных отображения (меш, материал, настройки рендеринга и т.п.) в различные системы из мира ECS, которые уже знают как из этого собрать сущность.
Процессор создать достаточно просто: наследуемся от базового класса, указываем какие данные мы хотим обрабатывать и описываем логику обработки.
Потом указываем в шаблоне генерации (на скриншоте) нужный процессор.
Генератор и модули генерации отвечают только за генерацию данных в каком-либо формате. Генератор ландшафта не генерирует Mesh, он генерирует только набор вершин и описание треугольников. Генератор деталей или травы не генерирует сами детали или траву, он генерирует только матрицы трансформаций. То есть генератор - это речь только про самый минимум данных.
А как тогда получать ландшафт? Камушки? Травку и остальные объекты окружения? Да еще и с материалами!
Вот тут и вступают в игру эти процессоры данных, которые умеют переводить данные из формата от генератора в формат, который нужен для представления. Например, для ландшафта процессинг занимает несколько шагов:
- конвертирование данных о треугольниках в
UnityEngine.Mesh через Extended Mesh API;- генерация материала для каждого чанка и всех данных которые необходимы для этого материала (текстурные массивы, различные буферы и т.п.);
- отправка данных отображения (меш, материал, настройки рендеринга и т.п.) в различные системы из мира ECS, которые уже знают как из этого собрать сущность.
Процессор создать достаточно просто: наследуемся от базового класса, указываем какие данные мы хотим обрабатывать и описываем логику обработки.
public class SurfaceDataProcessor : BaseDataProcessor<SurfaceData>, ISurfaceDataProcessor
{
protected sealed override void OnDataAdded(in Chunk chunk, ref SurfaceData data){}
protected sealed override void OnDataRemoved(in Chunk chunk){}
}
Потом указываем в шаблоне генерации (на скриншоте) нужный процессор.
👍8👎1
Разработка игр это часто не просто использование одного инструмента. В моем случае приходится залезать и в С++, чтобы дописывать то, чего не хватает 🧂
Все думаю, как бы перевести это на Unity.Mathematics (и на C# соответственно), и под Burst. А еще кодоген! Чтобы количество вызовов уменьшить и убрать абстракции. Пока это только мечта...😢
Все думаю, как бы перевести это на Unity.Mathematics (и на C# соответственно), и под Burst. А еще кодоген! Чтобы количество вызовов уменьшить и убрать абстракции. Пока это только мечта...
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9😢3🌚1
Все еще вожусь с травой 👏
Поняла, что не хватает LOD для деталей и травы. Надо будет запланировать добавить поддержку в LOD для BRG.
Также очень не хватает смешивания травы и ландшафта, слишком резкая граница как по мне. Но это визуал, над которым я еще совсем не работала)
Поняла, что не хватает LOD для деталей и травы. Надо будет запланировать добавить поддержку в LOD для BRG.
Также очень не хватает смешивания травы и ландшафта, слишком резкая граница как по мне. Но это визуал, над которым я еще совсем не работала)
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10
Media is too big
VIEW IN TELEGRAM
Трава кстати уже шевелится 💃 Я взяла ассеты из сэмплов HDRP, чтобы сейчас немного укориться ☺️
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10❤3👎1
В комментариях я обещала рассказать про основные структуры данных, о которых я уже упоминала 💃
Воксель - просто информация о пространстве: density и gradient + информация по визуализации (индексы материалов).
Позицию в вокселе я не храню, так как доступ к ним и так через позицию, и хранятся они в отношении позиция вокселя -> воксель. Но это не HashMap! А обычный массив с вычислением индекса из позиции:
Чанк - тут еще меньше информации: позиция и lod!
Вся идея состоит в том, чтобы из любой точки кода можно было сказать: "я хочу сгенерировать пространство для вот этой позиции с таким-то уровнем детальности". Ну и еще отсюда вытекает множество плюсов в виде хранения данных в формате AOS (кэш-able), легковесность, расширяемость (сам чанк не знает, что в нем хранится, он выступает только в качестве ключа).
Octree - фактически, дерево у меня представлено в виде HashMap, где ключ - просто число без знака, а значение - нода с какой-либо информацией.
Ключ - просто набор бит (причем используются первые 30 бит). Зная, что каждая нода разбивается на 8 нод, на каждый уровень можно выделить по 3 бита (2^3 = 8). Максимальная глубина соответственно равна 10, чего мне хватает с запасом. Чтобы получить значение ребенка:
Немного побитовых операций, а уже не надо в ноде хранить ссылки на другие ноды☺️ Я после этого начала использовать побитовые операции везде где надо и не надо 🤭
Воксель - просто информация о пространстве: density и gradient + информация по визуализации (индексы материалов).
public readonly struct Voxel : IEquatable<Voxel>
{
public readonly float3 Gradient;
public readonly half Density;
...
public readonly FixedVoxelType4 VoxelTypes;
...
}
Позицию в вокселе я не храню, так как доступ к ним и так через позицию, и хранятся они в отношении позиция вокселя -> воксель. Но это не HashMap! А обычный массив с вычислением индекса из позиции:
math.dot(coordinate, new int3(1, dimension.x, dimension.x * dimension.y)); (когда математику вдруг можно применить и в таких случаях).Чанк - тут еще меньше информации: позиция и lod!
public readonly struct Chunk : IEquatable<Chunk>
{
public readonly int3 Position;
public readonly ChunkLod Lod;
}
Вся идея состоит в том, чтобы из любой точки кода можно было сказать: "я хочу сгенерировать пространство для вот этой позиции с таким-то уровнем детальности". Ну и еще отсюда вытекает множество плюсов в виде хранения данных в формате AOS (кэш-able), легковесность, расширяемость (сам чанк не знает, что в нем хранится, он выступает только в качестве ключа).
Octree - фактически, дерево у меня представлено в виде HashMap, где ключ - просто число без знака, а значение - нода с какой-либо информацией.
public readonly struct LocationCode : IEquatable<LocationCode>
{
public readonly uint Value;
}
Ключ - просто набор бит (причем используются первые 30 бит). Зная, что каждая нода разбивается на 8 нод, на каждый уровень можно выделить по 3 бита (2^3 = 8). Максимальная глубина соответственно равна 10, чего мне хватает с запасом. Чтобы получить значение ребенка:
(source.Value << 3) | childIndex, где childIndex - значение от 0 до 7. Чтобы получить значение родителя: code >> 3. Немного побитовых операций, а уже не надо в ноде хранить ссылки на другие ноды
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥16❤9👏3🤯2
Когда немного переборщила с травой 🤭
Пофиксила заполнение буфера, теперь рисуется вся трава, причем довольно быстро (30+ фпс есть). Но если глянуть в Frame Debugger, то можно увидеть сколько на самом деле рисуется инстансов - 26473! А вершины и индексы определяются десятками миллионов!
Как рисовать такие объемы? Например с помощью GPU Instancing. Но еще лучше - сгруппировать все меши и материалы и использовать Batch Renderer Group! Я использую свой инструмент - BRG Container.
Следующий шаг - добавить поддержку LOD. А то и выглядит так себе при движении, и треугольников нууу слишком уж много😔
Пофиксила заполнение буфера, теперь рисуется вся трава, причем довольно быстро (30+ фпс есть). Но если глянуть в Frame Debugger, то можно увидеть сколько на самом деле рисуется инстансов - 26473! А вершины и индексы определяются десятками миллионов!
Как рисовать такие объемы? Например с помощью GPU Instancing. Но еще лучше - сгруппировать все меши и материалы и использовать Batch Renderer Group! Я использую свой инструмент - BRG Container.
Следующий шаг - добавить поддержку LOD. А то и выглядит так себе при движении, и треугольников нууу слишком уж много
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥16😱4❤2✍1
Infinity World (Дневники разработчицы) pinned «Меня в комментариях спрашивали, на чем базируется копание ландшафта, поэтому я решила немного рассказать о своем фреймворке прежде, чем начну вести сами дневники разработки по текущим задачам. Я не использую уже готовые решения по той причине, что они или…»
Генерация и ECS
Наверное многие задумывались: "она говорит про ECS, DOTS, но показывает ООП код, что-то тут не так"?👍 И ведь правда, причем тут ECS?
Вся генерация, как я писала в прошлых постах, реализована на самом обычном ООП по множеству причин. Но есть также и ECS часть в моем процессе генерации - представление.
Все, что генерируется и видимо игроком, должно каким-либо образом рисоваться. Для этого я разбила представление окружающих объектов на две части:
- окружающие объекты без физики;
- окружающие объекты с физикой.
Почему именно такое распределение? Потому что объекты без физики я могу рисовать самостоятельно без создания самих объектов (GameObject или Entity). Мне нужно знать только что рисовать и где рисовать.
А вот объекты с физикой (или любые другие объекты, с которыми игрок может взаимодействовать) необходимо уже собирать из кусочков. Тут и компоненты отображения, и компоненты физики, и компоненты взаимодействия вместе с состоянием. А еще они должны быть представлены и на сервере!
По этой причине, на этапе процессинга сгенерированных данных я подготавливаю их в требуемом виде (например, меши ландшафта уже представлены в виде UnityEngine.Mesh) и отправляю в соответствующие ECS-системы. Не могу сказать, что именно такая связь правильная, возможно было бы лучше, если бы ECS-системы сами собирали информацию.🤷
ECS-системы, в свою очередь, создают из этих данных сущности с компонентами. Например, есть система, которая создает сущности чанков. Другая система - создает уже отображение этих чанков. Третья - коллайдеры, с ними кстати все не так просто оказалось в UECS, надеюсь когда-нибудь расскажу. И вот на выходе у меня уже не просто меши, а самые настоящие ентити с набором компонентов.
Тоже самое происходит и на серверной стороне, только в существенно меньшем масштабе, потому что на сервере визуализация не нужна. Поэтому там например создаются чанки только с коллайдерами, да и то не все, а только необходимый минимум для симуляции.
Наверное многие задумывались: "она говорит про ECS, DOTS, но показывает ООП код, что-то тут не так"?
Вся генерация, как я писала в прошлых постах, реализована на самом обычном ООП по множеству причин. Но есть также и ECS часть в моем процессе генерации - представление.
Все, что генерируется и видимо игроком, должно каким-либо образом рисоваться. Для этого я разбила представление окружающих объектов на две части:
- окружающие объекты без физики;
- окружающие объекты с физикой.
Почему именно такое распределение? Потому что объекты без физики я могу рисовать самостоятельно без создания самих объектов (GameObject или Entity). Мне нужно знать только что рисовать и где рисовать.
А вот объекты с физикой (или любые другие объекты, с которыми игрок может взаимодействовать) необходимо уже собирать из кусочков. Тут и компоненты отображения, и компоненты физики, и компоненты взаимодействия вместе с состоянием. А еще они должны быть представлены и на сервере!
По этой причине, на этапе процессинга сгенерированных данных я подготавливаю их в требуемом виде (например, меши ландшафта уже представлены в виде UnityEngine.Mesh) и отправляю в соответствующие ECS-системы. Не могу сказать, что именно такая связь правильная, возможно было бы лучше, если бы ECS-системы сами собирали информацию.
ECS-системы, в свою очередь, создают из этих данных сущности с компонентами. Например, есть система, которая создает сущности чанков. Другая система - создает уже отображение этих чанков. Третья - коллайдеры, с ними кстати все не так просто оказалось в UECS, надеюсь когда-нибудь расскажу. И вот на выходе у меня уже не просто меши, а самые настоящие ентити с набором компонентов.
Тоже самое происходит и на серверной стороне, только в существенно меньшем масштабе, потому что на сервере визуализация не нужна. Поэтому там например создаются чанки только с коллайдерами, да и то не все, а только необходимый минимум для симуляции.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤1
Добавляю поддержку лодов в BRG, алгоритм уже работает (но еще не протестировала до конца), начала интегрировать в проект, скоро будет видео)
Редактор конечно кривоватый, но и так сойдет пока что🫥
Редактор конечно кривоватый, но и так сойдет пока что
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥15❤1
This media is not supported in your browser
VIEW IN TELEGRAM
Обещанное видео) Конечно тут кубики, с разными цветами, обозначающие уровень детализации 🤷
С LOD оказалось все чуть сложнее, чем я думала. Но все же победа вот-вот будет за мной! Осталось откинуть те кубики, которые слишком далеко от камеры👏
С LOD оказалось все чуть сложнее, чем я думала. Но все же победа вот-вот будет за мной! Осталось откинуть те кубики, которые слишком далеко от камеры
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥17👍3
This media is not supported in your browser
VIEW IN TELEGRAM
А вот теперь есть и поддержка "пустых" лодов, которые не рендерят ничего 💃
Стоит заметить, что LOD выбирается не просто по дистанции до камеры, а по эвристике, определяющей, сколько объект занимает в процентном соотношении места на экране! Это позволяет более точно выбирать LOD и рисовать только то, что реально видно и имеет шанс быть замеченным (поэтому маленькие объекты даже в близи могут быть еще скрыты, тогда как большие - уже рисоваться).
Следующий шаг - добавление cross fade, чтобы лоды плавно смешивались.
Стоит заметить, что LOD выбирается не просто по дистанции до камеры, а по эвристике, определяющей, сколько объект занимает в процентном соотношении места на экране! Это позволяет более точно выбирать LOD и рисовать только то, что реально видно и имеет шанс быть замеченным (поэтому маленькие объекты даже в близи могут быть еще скрыты, тогда как большие - уже рисоваться).
Следующий шаг - добавление cross fade, чтобы лоды плавно смешивались.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥19❤3
Хочу немного рассказать, как я выбираю LOD. Это не очень сложно на самом деле, как бы не звучало в моем предыдущем посте 🤭
Самый простой способ выбирать LOD - зная позицию камеры и для каждого инстанса матрицу трансформации, можно взять дистанцию между ними. Потом сравнить дистанцию с теми значениями диапазонов, которые мы указали в редакторе. Получается линейная выборка, которая никак не учитывает размер объекта.
А размер объекта на самом деле очень важен. Цель LOD - уменьшить не только кол-во треугольников, но и кол-во инстансов. Маленькие объекты вдалеке человеку чаще не заметны, тогда зачем их рисовать?
Диапазоны расстояний в итоге заменяются на процентное соотношение относительно экрана. То есть при настройке LOD мы указываем уже не дистанцию, а процентное соотношение. Например, 100% - виден когда занимает весь экран, 60% - виден, когда занимает примерно 60% площади экрана, 15% - когда объект занимает совсем небольшую площадь экрана, то есть он или маленький или находится далеко от нас.
В BRG у нас на руках есть матрица трансформации (с поворотом и скейлом в том числе) для каждого инстанса, а также размер меша per LOD в world space. То есть вычислить фактический размер в world space - уже не проблема, для удобства берем самое большее значение по одной из осей.
Зная размер меша и зная процентное соотношение, можно вычислить для него диапазон уже дистанций:
Но дистанция от объекта до камеры тоже не просто
Самый простой способ выбирать LOD - зная позицию камеры и для каждого инстанса матрицу трансформации, можно взять дистанцию между ними. Потом сравнить дистанцию с теми значениями диапазонов, которые мы указали в редакторе. Получается линейная выборка, которая никак не учитывает размер объекта.
А размер объекта на самом деле очень важен. Цель LOD - уменьшить не только кол-во треугольников, но и кол-во инстансов. Маленькие объекты вдалеке человеку чаще не заметны, тогда зачем их рисовать?
Диапазоны расстояний в итоге заменяются на процентное соотношение относительно экрана. То есть при настройке LOD мы указываем уже не дистанцию, а процентное соотношение. Например, 100% - виден когда занимает весь экран, 60% - виден, когда занимает примерно 60% площади экрана, 15% - когда объект занимает совсем небольшую площадь экрана, то есть он или маленький или находится далеко от нас.
В BRG у нас на руках есть матрица трансформации (с поворотом и скейлом в том числе) для каждого инстанса, а также размер меша per LOD в world space. То есть вычислить фактический размер в world space - уже не проблема, для удобства берем самое большее значение по одной из осей.
Зная размер меша и зная процентное соотношение, можно вычислить для него диапазон уже дистанций:
worldSpaceSize / screenRelativeTransitionHeight[i]. С этими дистанциями уже можно сравнивать дистанцию от объекта до камеры.Но дистанция от объекта до камеры тоже не просто
math.distance, так как для перспективной камеры у нас есть искажение, а для ортогональной его нет. Также необходимо еще учитывать LOD Global Bias из QualitySettings,Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤3🔥2🦄1
Вот и LOD Cross Fade получился! 🙌
Есть о чем рассказать, есть интересные моменты😧 Завтра соберу все мысли и опишу в виде поста 💃
Хотя может даже в виде статьи?🚪
Есть о чем рассказать, есть интересные моменты
Хотя может даже в виде статьи?
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
❤11🔥9
https://telegra.ph/Level-of-Detail-v-Unity-BRG-04-10
Написала статью про LOD в BRG🚪 Весь код можно найти в моем репозитории по BRG (MIT-лицензия) 😶 Документацию там тоже скоро обновлю.
Написала статью про LOD в BRG
Please open Telegram to view this post
VIEW IN TELEGRAM
Telegraph
Level of Detail в Unity BRG
Что такое BRG Для того, чтобы описать что же такое Batch Renderer Group (BRG) нужна отдельная статья, поэтому тут я только кратко расскажу про основные его принципы и больше расскажу про интеграцию поддержки Level of Detail (LOD).
🔥28❤6👏3😍3❤🔥1
Media is too big
VIEW IN TELEGRAM
Видео с камушками! 🤩 (и с LOD вместе с CrossFade). 🙌
Как по мне, выглядит неплохо, очень нравится, что камушки плавненько появляются и исчезают, ради этого я и добавила поддержку LOD (а не ради оптимизации, как вы могли подумать☺️ )
Чтобы не делать много постов, скриншот из Frame Debugger приложу в комментариях🚪
Как по мне, выглядит неплохо, очень нравится, что камушки плавненько появляются и исчезают, ради этого я и добавила поддержку LOD (а не ради оптимизации, как вы могли подумать
Чтобы не делать много постов, скриншот из Frame Debugger приложу в комментариях
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥17❤12
Media is too big
VIEW IN TELEGRAM
Протестировала на траве! 🥔
К сожалению не нашла в HDRP ассетах от Unity лоды для травы, они ее просто скрывают плавно по дистанции (ага, и десятки и сотни млн треугольников в итоге в кадре, хоть и не видно их). Пришлось также взять просто Lit шейдер, чтобы не писать сейчас поддержку для Cross Fade для их шейдера. В итоге трава не шевелится :(
Но ничего, то, что мы не видим, то не рисуется, а это главное!🙌 Хотя при этом трава сгенерирована и находится в памяти.
P.S.: Я думала, думала, и решила немного уточнить момент с графикой. Это не стиль будущей игры, и она не будет в реализме, поэтому я не работаю сейчас над визуальной составляющей (некоторые подвижки в эту сторону скоро начнутся). Это пока что просто технические решения и некоторые стресс тесты для них (и для меня😄 ).
К сожалению не нашла в HDRP ассетах от Unity лоды для травы, они ее просто скрывают плавно по дистанции (ага, и десятки и сотни млн треугольников в итоге в кадре, хоть и не видно их). Пришлось также взять просто Lit шейдер, чтобы не писать сейчас поддержку для Cross Fade для их шейдера. В итоге трава не шевелится :(
Но ничего, то, что мы не видим, то не рисуется, а это главное!
P.S.: Я думала, думала, и решила немного уточнить момент с графикой. Это не стиль будущей игры, и она не будет в реализме, поэтому я не работаю сейчас над визуальной составляющей (некоторые подвижки в эту сторону скоро начнутся). Это пока что просто технические решения и некоторые стресс тесты для них (и для меня
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥14❤3👍2
https://telegra.ph/Shading-landshafta-04-13
Решила сегодня попробовать формат не просто поста, а поста-статьи, так как в телеграмме не получится написать такой длинный пост😢 Но это не статья как в прошлый раз! Просто чуть длинненький пост)
Зато там получилось выложить побольше информации и побольше скриншотов🙌
Решила сегодня попробовать формат не просто поста, а поста-статьи, так как в телеграмме не получится написать такой длинный пост
Зато там получилось выложить побольше информации и побольше скриншотов
Please open Telegram to view this post
VIEW IN TELEGRAM
Telegraph
Shading ландшафта
Как же я рисую ландшафт?
🔥16❤4👍3