SashkoSoft

procedural-ground-surfaces

Покрытия больших поверхностей — асфальт, тротуарная плитка, бордюр, земля, двор, площадь — процедурой во фрагментном шейдере от мировых координат, без текстур и без тайла. Поперечная параметризация полосы, без которой не сделать ни колеи, ни разметку, ни грязь у бордюра; перевязка рядов; хребтовый шум для сетки трещин; глушение мелкого зерна с расстоянием против муара; цена всего этого в кадре. Используй, когда дорога или тротуар «полосатые», когда видно повтор текстуры на большой площади, когда поверхность в сотни метров надо покрыть без библиотеки текстур, или когда плоскость выглядит крашеным картоном.

SashkoSoft 0 Actualitzat fa 4 setm.

Recursos

2
GitHub

Instal·la

npx skillscat add sashkosoft/godot-skilled/procedural-ground-surfaces

Instal·la des del registre de SkillsCat.

SKILL.md

Покрытия без тайла: дорога, тротуар, земля

Собран на улице вокруг панельного дома в изометрической игре: полосы длиной
300 м, камера и вплотную, и с 80 м (см. ORIGIN.md). Рабочий шейдер целиком —
references/surface-shader.md.

Главное правило

Любая тайловая текстура на поверхности в сотни метров рано или поздно
начинает дышать сеткой.
Не помогает ни размер тайла, ни стохастическая
выборка: период всё равно находится глазом, особенно на скользящей камере.

Считай покрытие от мировой точки в фрагментном шейдере. Тогда повтора нет
ни на каком масштабе, потому что рисунок уникален в каждой точке улицы.

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

Поперечная ось — то, без чего покрытие остаётся серым полем

Одного шума мало. Дорога отличается от бетонной площадки тем, что у неё есть
поперечная структура: колеи там, где ездят, разметка по оси, намытый песок
у бордюра, крокодилья сетка по краям, где нет нагрузки.

Дай шейдеру два уникформа:

uniform float lat_center;   // мировая координата середины полосы
uniform float lat_half;     // её полуширина
...
float lat  = clamp((w.y - lat_center) / max(lat_half, 0.001), -1.0, 1.0);
float edge = abs(lat);      // 0 по оси, 1 у кромки

Дальше всё строится на lat:

float track = exp(-pow((edge - 0.46) / 0.15, 2.0));   // две накатанные полосы
float silt  = smoothstep(0.72, 1.0, edge);            // песок у бордюра
float line  = 1.0 - smoothstep(0.045, 0.075, abs(lat * lat_half));

Важно: у каждой полосы своя ось, значит своя копия материала. Один
общий материал на все полосы означает один lat_center — и колеи посчитаются
от чужой середины. Копий получается по одной на полосу, это единицы.

Приёмы, которые решают

Перевязка. Прямая сетка «клетка в клетку» сразу читается фальшивкой, так
плитку не кладут. Сдвигай каждый второй ряд на полплиты:

float row   = floor(w.y / plate);
vec2  cell  = vec2(w.x / plate + mod(row, 2.0) * 0.5, w.y / plate);

Хеш на ячейку — не только на тон. От h21(id) должны зависеть тон плиты,
ширина шва, просадка и наличие трещины. Одинаковый шов по всей площади —
второй по заметности признак процедурности после прямой сетки.

Хребтовый шум для трещин. Обычный fbm с порогом по |n − 0.5| рисует ровно
одну мажущую полосу на октаву, и асфальт выглядит заляпанным, а не
потрескавшимся. Ветвящуюся сетку даёт хребет:

float n = 1.0 - abs(vnoise(p) * 2.0 - 1.0);
s += a * n * n;

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

Почти стёртая разметка. Она делает полосу дорогой за один приём, а её
состояние — заброшенной дорогой. Пунктир от fract(w.x / шаг), поверх —
износ крупным шумом.

Две вещи, которые сломают картинку

Муар на дальнем плане. Мелкое зерно на расстоянии превращается в
регулярный узор — ту же сетку, от которой уходили. Глуши высокие частоты по
глубине:

float fine = clamp(1.0 - length(VERTEX) / 55.0, 0.0, 1.0);
col *= 1.0 - (grain - 0.5) * 0.20 * fine;

Боковые грани. Полоса — это бокс, и у него есть торцы. Им не нужны ни
колеи, ни разметка. В Godot return во фрагментной функции запрещён
(SHADER ERROR: Using 'return' in the 'fragment' processor function is incorrect), поэтому флаг и подмена результата в конце:

bool is_side = NORMAL.y < 0.5;
...
if (is_side) { col = col_b * (0.80 + se * 0.30); rough = 1.0; }

Цена

Это полноэкранные плоскости: на каждый пиксель приходятся десятки выборок
шума. Два правила окупаются сразу:

  • хеш без sin. fract(sin(dot(p, ...)) * 43758.5453) — самое дорогое,
    что тут может быть. Бери fract-хеш на трёх множителях;
  • переиспользуй поля шума. Посчитай два-три (крупные разводы, средний,
    зерно) и строй из них всё остальное — грязь, износ разметки, намыв.
    Каждый лишний fbm — это четыре выборки на октаву на каждом пикселе.

Щебень, дроблёное зерно и подобное — одна выборка vnoise, не fbm: на
таком масштабе октавы неразличимы.

Мерить цену только секундомером. На этом проекте число FPS из
скриншот-харнесса на одинаковой работе давало 83, 4, 15 и 1 — и целый заход
оптимизации был сделан по ложной тревоге. Честный замер: фиксированное число
кадров, обычный запуск, часы снаружи. Подробно — скил measurement-sanity.

Результат на реальной сцене: с улицей 82 FPS, без улицы 73 — покрытие не
стоило ничего измеримого.

Трава и мелкая растительность

Дёшево и красиво для изометрической камеры — двумя слоями по одной карте
«где растёт»: цвет травы в шейдере земли издали и настоящие травинки
(без текстур и альфы) в участке вокруг камеры. Там же подорожник, клевер
и цветы — без новых отрисовок. Подробно, с ловушками —
references/grass.md.

Рабочие привычки

  • На каждую спорную поверхность — флаг, который её снимает (--no-street).
    Два кадра рядом отвечают за минуту.
  • Заведи процедурной поверхности kind: плита, асфальт, земля — один шейдер,
    ветка на вид. Материалов от этого меньше, а править в одном месте.
  • Земля без геометрии остаётся крашеным картоном, сколько шума ни клади.
    Это честный предел приёма: под неё нужен рельеф, а не пиксели.

Пол квартала из слоёв

Слои по правилам от данных квартала + тайловые наборы, смешанные по высоте; формат наборов для поставщика — references/floor-layers.md.