Атлас и тайлинг. Один материал, чтобы править драуколами

Итак, многие из нас знают о трёх подходах работы с материалами и текстурами. Коснёмся преимуществ и недостатков каждого из них.

Я знаю три рабочих процесса:

1.Индивидуальное текстурирование.

2. Множество материалов.

3. Атласы.

I: Самый распространённый рабочий процесс - индивидуальная покраска 3д моделей в Substance Painter.

Преимущества такого подхода таковы:
1. Минимальное число полигонов, так как детали можно запечь в нормалмапу.

2. Детализированные текстуры. Благодаря Substance модель можно реалистично запачкать, заржавить и тд и тп. У меня это за отсутствием художественных навыков выходило ужасно, тем не менее у людей получается очень круто.

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

А теперь перейдём к недостаткам:

1. Каждая модель может иметь даже несколько материалов, в зависимости от детализации. Материалы индивидуальны, их почти невозможно переиспользовать.

2. Требуются значительные усилия в рабочем процессе. Создание хай и лоуполи, запекание нормалей (что тоже тот ещё квест, надо слегка преувеличивать кривизну и помнить, что с перпендикулярных поверхностей нормали не запекаются.) А также требуется оптимизация развёртки, дабы повысить texel density и вписаться в лимит текстуры. Надо применять оверлаппинг и упаковку UV - островов в борьбе за качество. Играть с pixel density на редко видимых поверхностях и тд и тп. Для инди-разработчика, который сам себе художник, моделлер, аниматор, программист, звукач и издатель это всё занимает весьма продолжительное время. А если ты, не дай Бог, перфекционист, то будешь переделывать это всё вечно.

3. Платный вход. Нужен, как минимум substance painter и UV packmaster. Слава Богу, это не так дорого, но таки не бесплатно.

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

II. Второй подход - множество материалов.

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

Лично я довожу лоуполи модель до более-менее адекватного состояния (многие проблемы с шейдингом уберут текстуры, так что идеал не нужен) с помощью рёбер поддержки и/или модификатора Edge Split и считаю это мидполью. Можно использовать autosmooth, но когда начинаешь объединять модели автосмус слетает. К тому же не смотря на то, что автосмус не пишет, что создаёт новые вершины, он таки их создаёт. Только вот применить его, в отличии от эйдж сплит, нельзя.

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

III. Атласы.

Атласами называют несколько материалов на одном текстурном пространстве. От тупейшего "запечём на одну текстуру два меша" (сам так любил, старался ассетпаки запекать на одну текстуру, но они всё равно не влезали) до множества материалов на одном текстурном пространстве. Атласы отлично подходят для игровой индустрии. Основная идея атласов - текстура одна, а мешей много. И такой подход очень распространён в геймдеве, насколько я слышал, юбисофт требует разворачивать новые модели на готовые атласы. Но атласы имеют свои недостатки. Самый главный - они не тайлятся. Если ты хочешь увеличить стену вдвое - будь добр, добавь полигонов. Потому что повторение у атласов не работает. Как пример классического использования атласов - шикарнейший плагин для блендера Sprytile.

Тут же видно и его недостатки. Это то, что для повторения тайла надо повторять сетку. И сетка растёт. Бесконтрольно растёт. Без всякой пользы для формы. И мы размениваем драуколы на трианглы.

Но самый главный недостаток таких атласов то, что в них трудно заложить паддинг. То есть отступ материалов друг от друга, заполненный цветом материала. Для чего он нужен? А чтобы избежать edge bleeding. Движок рассматривает текстуру как целостную картинку. И при фильтрации, например, начинает использовать пиксели с соседнего материала. Он же не знает о ваших наполеоновских планах. В итоге по краям появляются белые полосы. А ещё есть такая штука, как мипмаппинг. Она уменьшает исходную текстуру вдвое и тд, чтобы показывать её на расстоянии. Без этого картинка сыпет артефактами на удалённых предметах. И мипмаппинг тоже работает со всей картинкой. И при удалении от стены у нас тоже появятся белые полосы. Борятся с этим так - просто делают развёртку тайла чуть меньше и материал записывают уже с паддингом. Sprytile же вообще ориентируется на пиксель-арт в зд, так что просто советует вырубить эти ваши фильтры с мипмаппингом. И это тоже вариант, тогда паддинг роли не играет и можно разворачивать плоскость на весь тайл.

Также есть некая помесь этих подходов подходов с материалами и атласами, когда материалы тайлятся только в одну сторону. Так называемые trimsheets. Вот отличное видео по ним

Так вот, всё это присказка. Сказка, которую я хочу рассказать, впереди.

Так, о чём это я? А, о том, что было бы круто иметь возможность использовать атлас текстур, например, с 64 материалами (чего мелочиться? 4096 текстура, 512Х512 каждый материал). И чтобы не просто атлас, а чтобы каждый из материалов тайлился, как я хочу. Да, именно так. Чтобы не плодить лишней геометрии. Это, конечно, нельзя так. Мироздание может пострадать. Но если очень хочется... То чуть-чуть можно. Хоть не всё и не сразу.

Итак, основательно подумав (минут так несколько) и посмотрев пару сезонов аниме (прокрастинация, она такая) всё в голове устаканилось и я начал работу.

Для начала нужно понимать, что ты делаешь и продумать сам рабочий процесс. Идея была такова - обычный канал UV используется для обычной развёртки, а второй канал UV для указания тайла, в котором эта развёртка будет. На первом канале я подгоняю развёртку точно и могу её масштабировать, чтобы тайлилось, а на втором канале я просто уменьшаю этот островок и кидаю его в нужный мне квадратик. По счастью, если в блендере добавить новый канал UV, в него скопируется UV с первого канала и мне достаточно будет просто раскидать островки по квадратикам. А потом уже финально доработать первый канал.

Шейдер для блендера выглядит так

Атлас и тайлинг. Один материал, чтобы править драуколами
//Псевдокод, кому впадлу рассматривать ноды. //8 это количество строк и столбцов vec2 uv1=fract(UV*8.0)/8.0; //Сначала масштабируем остров в 8 раз, чтобы он занимал весь квадрат UV или больше. Берём дробную часть, чтобы если UV стало больше 1, сработал тайлинг. Приводим к первоначальному размеру поделив на 8.0 vec2 uv2=floor(UV2*8.0)/8.0; // Тоже самое, что и выше, но теперь мы берём только целую часть, чтобы вычислить смещение до нужного тайла. vec2 uv_final = uv1+uv2;// смещаем вершину в координаты тайла.

Подводим вывод этой конструкции к вводу UV текстур материала... И видим, что на границах наблюдается белый крестик. Ставим всем текстурам режим interpolation в состояние closest. Это отключает фильтрацию. И вуаля, всё красиво и великолепно. Да, в упор можно наблюдать пиксели, но картинка более чем приемлемая. А мипмаппинг работает без нареканий и артефактов.

А теперь идём в годот. Сразу скажу, что на данный момент использование UV2 лишает нас возможности использовать lightmaps, что весьма нехорошо и вообще. Но что поделать. Issue на github висит давно и давно обещано, что эту мегафичу скоро прикрутят. Да вообще я собирался делать цикл дня и ночи, так что запеканка света мне не подходит.

А ещё в годот есть texture array. Его смысл в том, что он таки как раз режет текстуру на куски и может запекать mipmaps для каждого кусочка отдельно. Только в UV должно быть три значения - UV и номер тайла. Для него шейдерный код таков:

shader_type spatial; render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlick_ggx; uniform sampler2DArray texture_albedo : hint_albedo; uniform sampler2DArray texture_ORM : hint_white; uniform sampler2DArray texture_normal : hint_normal; varying vec3 vertex_color; //uniform vec4 test_color: hint_color; uniform float subdivision: hint_range (1.0,16.0,1.0); void vertex() { vertex_color = COLOR.rgb; } void fragment() { lowp vec2 tiles_coord = trunc(UV2*subdivision); lowp vec3 uv= vec3 (fract(UV*subdivision),tiles_coord.x+subdivision*(tiles_coord.y)); ALBEDO = texture(texture_albedo,uv).rgb*vertex_color; NORMALMAP =texture(texture_normal,uv).rgb; vec3 ORM = texture(texture_ORM,uv).rgb; METALLIC = ORM.b; ROUGHNESS = ORM.g; }

И да, фильтрация теперь работает корректно. Однако, мипмапы, по краям текут кровью убитых пикселей.

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

Там мне сказали "у нас всё работает". В общем, оказалось, что вся проблема в том, что при использовании fract(), дабы повторять текстуру своими силами, таки возникают некоторые неопределённости (у альбедо почему-то не возникают, а у мипмапа возникают) и разрывы непрерывности материи UV. На самом деле это связано с вездесущей ошибкой флоата, я думаю. Но если при импорте texture array включить repeat enabled и потом включить его в настройках материала и обязательно перезагрузить годот (вот тут я не ожидал), то можно просто использовать UV больше 1 для конкретного тайла. И всё работает. Так что корректный код шейдера вот:

shader_type spatial; render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlick_ggx; uniform sampler2DArray texture_albedo : hint_albedo; uniform sampler2DArray texture_ORM : hint_white; uniform sampler2DArray texture_normal : hint_normal; varying vec3 vertex_color; uniform float subdivision: hint_range (1.0,16.0,1.0); void vertex() { vertex_color = COLOR.rgb; } void fragment() { lowp vec2 tiles_coord = trunc(UV2*subdivision); lowp vec3 uv= vec3 (UV*subdivision,tiles_coord.x+subdivision*(tiles_coord.y)); ALBEDO = texture(texture_albedo,uv).rgb*vertex_color; NORMALMAP =texture(texture_normal,uv).rgb; vec3 ORM = texture(texture_ORM,uv).rgb; METALLIC = ORM.b; ROUGHNESS = ORM.g; }

Так что Texture Array прекрасно работает и позволяет использовать атлас с удобством материалов.

Также в стремлении разнообразить наличные материалы, как вы уже могли заметить в шейдере, я использую вертекс-колор. Я стараюсь подбирать белые материалы и просто могу умножать на цвета вершин. Что очень помогает, так если у вершины цветов нет, так по умолчанию получается белый цвет. А умножение на белый, то есть на 1.0, не меняет цвета текстуры. Кроме того, один тайл у меня занимает палитра с разными значениями раффнес для металлов и такая же для не металлов, дабы я мог создавать простые однотонные материалы, вроде голого железа или простого пластика.

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

Вот исполняемый файл с демонстрашкой для любопытных, там можно полетать, посмотреть материалы. Делать видосик мне в лом.

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

Впрочем, второй материал не менее любопытен (компьютеры и хавчик). Он сделан по другому рабочему процессу, не упомянутому выше. Это когда ты находишь фото объектов и разворачиваешь UV на эти фото так, чтобы оно хоть как-то подходило. Тут очень помогают project from view и mirror. Но этот процесс описать немного сложнее. Кроме того, вы получите не PBR материалы, а просто фото. Тем не менее это тоже можно использовать в некоторых случаях и не сильно выбиваются в движке из вида.

А теперь подведём итог: Если очень хочется, то можно. Даже отключив мипмаппинг с фильтрацией. А если уж в движке есть texture array, то нужно.

5353
40 комментариев

Комментарий недоступен

4
Ответить

драуколами? управлять драуколами? я смирился с «отрисовкой», но, сука, драуколы.. 😭

2
Ответить

Терпи. Английский язык продолжает доминировать над русским в технической сфере. Заимствования неизбежны.

2
Ответить

Спасибо за статью, идея прикольная надо будет попробовать такое сделать на ue4. Интересно сравнить на сколько сильным будет буст по сравнению с кучей материалов на масках.

1
Ответить

Не за что. Надеюсь на уменьшение драуколов. У себя собираюсь таким образом домики в городе сделать, чтобы на улице был только материал ландшафта и этот.

1
Ответить

случайный дубль.

Ответить

Кроме фасок ещё нужно много дополнительных лупов для корректного шейдинга. Ели есть нормалка с хайполи, то она вытянет шейдинг. Это когда на модели непонятные тени тд и тп. Но да, основные вопросы с шейдингом снимаются фасками.

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

Ответить