Разброс растительности Megascans в Solaris
2026-10-27
Разброс растительности Megascans в Solaris
Пошаговая техника: мультивариантные USD-компонент-ассеты, разбросанные по точкам на хайтфилде и отрендеренные в Karma. Написано для Houdini 22 (Solaris / MaterialX / Karma XPU).
0. Обзор
Техника состоит из четырёх независимых частей. Каждый тип растительности (трава, кусты и т.д.) получает свою копию этих частей, так что типы можно настраивать, отключать или рендерить по отдельности.
SOP /obj/<terrain> LOP /stage
------------------- ----------------------------------------
heightfield (слои: height, mask) BUILD_<asset> (subnet, пишет <asset>.usd)
-> heightfield_scatter PROTO_<asset> (subnet, 8 прототипов)
-> patches (wrangle) |
-> attribs (wrangle) | input 2
-> POINTS_<asset> (null) ----- SOP Path --> <asset> (PointInstancer)
^ input 1
scene -> ... -> предыдущий instancer
1. Именование и пути
| Плейсхолдер | Значение | Пример |
|---|---|---|
<asset> |
Короткое имя ассета в snake_case | wild_grass |
<src_dir> |
Скачанная папка Megascans | wild_grass_abc123_mid |
<mesh_prefix> |
Имя FBX до _VarX_LODn |
Wild_Grass_abc123_Mid_abc123 |
<tex_prefix> |
Имя текстуры до суффикса карты | Wild_Grass_abc123_Mid_2K |
<terrain> |
SOP-объект с хайтфилдом | /obj/terrain |
| Элемент | Имя / путь |
|---|---|
| Исходные файлы | $HIP/models/plants/<src_dir>/ |
| Subnet сборки ассета (LOP) | BUILD_<asset> |
| Экспортированный ассет | $HIP/usd/assets/<asset>/<asset>.usd |
| Subnet прототипов (LOP) | PROTO_<asset> |
| Выход разброса (SOP) | /obj/<terrain>/POINTS_<asset> |
| Нода / прим инстансера | <asset> / /vegetation/<asset> |
| Варианты геометрии | VarA ... VarH (должны совпадать с именами вариантов Megascans) |
Совет. Держи каждую сборку
BUILD_иPROTO_внутри своего subnet. Имена нод должны быть уникальны в пределах сети, аVarA...VarHбудут переиспользоваться для каждого ассета. Внутри subnet имена локальные, поэтому ничего не переименуется автоматически вVarA1.
2. Сборка USD-ассета (BUILD_)
В /stage создай Subnet с именем BUILD_<asset>, зайди внутрь (I) и брось Component Builder. Переименуй ноду Component Output в <asset> (её Root Prim равен /$OS, так что прим и файл получат имя ноды).
2.1 Геометрия: один Component Geometry на вариант
Переименуй ноду Component Geometry в VarA и зайди внутрь. Используй имя ноды в путях к файлам, чтобы дубликаты требовали только переименования. ../../.. разрешается как file -> geo -> sopnet -> VarA.
file1 -> выход default (рендер-геометрия):
$HIP/models/plants/<src_dir>/<mesh_prefix>_`opname("../../..")`_LOD1.fbx
file2 -> выход proxy (только вьюпорт, LOD3 у Megascans обычно биллборд):
$HIP/models/plants/<src_dir>/<mesh_prefix>_`opname("../../..")`_LOD3.fbx
- При необходимости добавь Transform после каждой File SOP. FBX из Megascans часто в сантиметрах (Uniform Scale 0.01) и иногда Z-up (Rotate X -90). Растение должно быть примерно 0.3-1.5 м и стоять вдоль Y.
- Оставь
simproxyиalternativeпустыми. Если выходы отсутствуют, нажми Fix SOP Outputs на ноде. - На уровне subnet: продублируй VarA семь раз и переименуй копии в
VarB ... VarH. Пути обновятся автоматически.
2.2 Объединение вариантов
Подключи все восемь в Component Geometry Variants, а его выход — в input 1 ноды Component Material.
Внимание. Используй именно Component Geometry Variants, а не обычный Merge. Merge наслаивает все варианты на один и тот же прим, из-за чего у ассета не создаётся набор вариантов и все прототипы выглядят одинаково.
2.3 Материал (MaterialX, внутри Material Library)
Подключи Material Library в input 2 ноды Component Material. Внутри создай MtlX Standard Surface с именем <asset>_mtl и по одному MtlX Image на каждую карту. В каждое поле File впиши полный путь:
$HIP/models/plants/<src_dir>/<tex_prefix>_<Map>.jpg
| Карта | Тип | File Color Space | Подключить к |
|---|---|---|---|
| BaseColor | Color | sRGB - Texture | base_color |
| Roughness | Float | Raw | specular_roughness |
| Normal | Vector | Raw | MtlX Normal Map -> normal |
| Opacity | Float | Raw | opacity |
| Translucency | Color | sRGB - Texture | subsurface_color |
Настройки Standard Surface:
- Thin Walled: вкл (группа Geometry). Листья и травинки — одиночные карты (cards).
- Subsurface: 0.4-0.6 для травы, ~0.3 для сухих кустов. В режиме thin-walled это работает как просвечивающая полупрозрачность; Radius игнорируется. Оставь Scale на значении по умолчанию 1, а не 0.
- Specular: 0.2-0.4. Растительность не блестящая.
- Неиспользуемые карты: Specular, Gloss, Cavity, Bump, Displacement, AO. Карты
Billboard_*нужны только для биллборд-LOD.
Совет. Всегда задавай File Color Space явно. Пустое поле заставляет Houdini угадывать, что может затемнить цвета. Никогда не выбирай Display- или Output-трансформ для текстур.
2.4 Назначение и экспорт
- Component Material: Primitives
*, Material Path -><asset>_mtl. - На ноде Component Output нажми Save to Disk. Результат:
<asset>.usd,geo.usdc,mtl.usdc,payload.usdcи папка textures. - Проверка: выдели выходную ноду; в Scene Graph Tree у прима ассета должен быть набор вариантов
geoсо значениямиVarA...VarH. - После любого изменения геометрии или материала: снова Save to Disk, затем Reload Files на нодах Reference ниже по потоку.
3. Прототипы (PROTO_)
Создай Subnet PROTO_<asset>. Внутри собери по одной паре на вариант:
Reference LOP, нода с именем VarA:
Reference File : $HIP/usd/assets/<asset>/<asset>.usd
Primitive Path : /prototypes/$OS
Reference Prim : default (Component Output уже задал defaultPrim)
Set Variant LOP сразу за ней (вставь точно как есть, выражение читает имя входной ноды):
Primitives : /prototypes/`opinput(".", 0)`
Variant Set : geo
Variant Name: `opinput(".", 0)`
- Продублируй пару семь раз и переименуй только ноды Reference в
VarB ... VarH. - Слей все восемь нод Set Variant в Merge с именем
PROTOTYPES. - Подключи PROTOTYPES в output0 этого subnet. После сворачивания (Shift+C) output0 может оказаться привязан к случайной внутренней ноде: проверь, что он показывает
/prototypes/VarA... (8). - Эта ветка никогда не сливается в сцену. Она только питает input 2 своего инстансера, поэтому
/prototypes/VarAв нескольких subnet PROTO_ не конфликтует.
4. Разброс точек (SOP, /obj/)
4.1 Маска (опционально): инвертировать и оставить только низины
Чтобы разместить второй тип растений там, где нет первого, и только на ровной земле, ответвись от размытой маски Volume Wrangle с именем mask_floor_inv и используй его как вход mask нового разброса. @mask и @height привязываются к слоям хайтфилда по имени.
// инвертируем существующую маску
float inv = 1.0 - @mask;
// 1 на ровной земле, затухает к 0 между h_min и h_max
float floor_mask = 1.0 - smooth(chf("h_min"), chf("h_max"), @height);
@mask = clamp(inv * floor_mask, 0, 1);
Подбирай h_min (верх ровной зоны) и h_max (где маска доходит до 0), отображая ноду: маска рисуется красным во вьюпорте. Больший зазор даёт более мягкий переход на склоны. Убери 1.0 - в первой строке, чтобы сохранить исходную маску вместо инвертирования.
4.2 HeightField Scatter
| Параметр | Значение | Зачем |
|---|---|---|
| Scatter Method | By Coverage using Mask Layer | Разбрасывать только там, где есть маска |
| Coverage | 1 | Плотность дальше задаётся через Outer Radius |
| Outer Radius | 0.15-0.3 трава, 0.3-1 кусты | Меньше = плотнее |
| Relax Points | выкл | Relax + Self Overlap дают заметную ровную сетку |
| Keep Incoming Terrain | выкл | Иначе точки объёма хайтфилда (в 0,0,0) проходят дальше и получают инстансы |
4.3 Patches wrangle (Attribute Wrangle, Points)
// крупные пятна растительности
float n = noise(@P * chf("freq") + chf("offset"));
n = fit(n, chf("lo"), chf("hi"), 0, 1);
// чем ниже шум, тем больше точек удаляется
if (rand(@ptnum + 77) > n * chf("density"))
removepoint(0, @ptnum);
| Параметр | Старт | Эффект |
|---|---|---|
| freq | 0.005 | Размер пятен. Ниже = крупнее пятна (0.005 ~ 200 м, 0.02 ~ 50 м, 0.1 = мелкая рябь). |
| offset | 0 | Сдвигает рисунок шума. Задавай разное значение для каждого типа растений, чтобы их пятна не совпадали. |
| lo / hi | 0.35 / 0.65 | Контраст пятен. Узкий диапазон = жёсткие края; широкий = мягкие. Оба вверх = больше голой земли; оба вниз = плотнее в целом. |
| density | 1 | Глобальный множитель выживания внутри пятен. 0.5 прореживает даже самые плотные зоны вдвое. |
4.4 Attributes wrangle (Attribute Wrangle, Points)
float seed = chf("seed");
// вариант как СТРОКА, совпадающая с именами прототипов
string vars[] = {"VarA","VarB","VarC","VarD","VarE","VarF","VarG","VarH"};
s@variant = vars[int(rand(@ptnum + seed) * len(vars))];
// масштаб
f@pscale = fit01(rand(@ptnum + seed + 1), chf("scale_min"), chf("scale_max"));
// случайный поворот по Y + лёгкий наклон
float yaw = rand(@ptnum + seed + 2) * 2 * PI;
float tilt = radians(fit01(rand(@ptnum + seed + 3), -chf("max_tilt"), chf("max_tilt")));
p@orient = qmultiply(quaternion(yaw, {0,1,0}), quaternion(tilt, {1,0,0}));
Нажми кнопку Create spare parameters. Стартовые значения: scale_min 0.4-0.5, scale_max 1.4-1.6, max_tilt 5 (кусты) до 10 (трава). Опционально, более крупные растения внутри плотных пятен (добавь после строки с pscale):
f@pscale *= fit(noise(@P * chf("freq")), 0.35, 0.65, 0.6, 1.3);
Заверши цепочку нодой Null с именем POINTS_<asset>.
Совет. Если у самого разброса есть Variability (диапазон масштаба), pscale из wrangle перезапишет его. Используй что-то одно.
5. PointInstancer (LOP)
Один PointInstancer на каждый тип растений, соединённые цепочкой друг за другом в основном потоке.
| Параметр | Значение |
|---|---|
| Имя ноды | <asset> |
| Input 1 | Основная сцена (террейн, камни, предыдущий инстансер) |
| Input 2 | PROTO_<asset> |
| Primitive Path | /vegetation/<asset> (держит всю растительность под одной веткой) |
| Prototype Source | Second Input |
| Primitives | /prototypes/* (надёжнее дефолтного lopinputprims через subnet) |
| Import SOP Points > SOP Source | External SOP |
| SOP Path | /obj/<terrain>/POINTS_<asset> (без слеша в конце) |
| Prototype Assignment > Prototype Index | String Attribute, атрибут variant |
Ориентация (orient) и масштаб (pscale) подхватываются автоматически. Предупреждения (!) на скопированных прототипах в Scene Graph Tree — это уведомления об иерархии kind, на рендер они не влияют.
6. Свет и заметки по рендеру
- Тёмная растительность против света: проверь Thin Walled, Subsurface 0.4-0.6, Translucency в sRGB - Texture. Затем подними Diffuse Limit до 2-3 в Karma Render Settings, чтобы растения ловили отражённый свет от яркой земли.
- Подсвеченные сзади растения всё равно будут темнее освещённого солнцем песка — это физика. Слабый заполняющий Distant Light со стороны камеры помогает, если нужно.
- Оценивай opacity и translucency только после схождения рендера; первые сэмплы Karma XPU на тонкой геометрии всегда выглядят призрачно.
- Сравнивай варианты в Houdini GL для мгновенного фидбэка, затем проверяй финальный вид в Karma.
- Вьюпорт показывает ноду с флагом display, а не выделенную (подпись в левом верхнем углу). Scene Graph Tree следует за выделением.
7. Траблшутинг
| Симптом | Причина / решение |
|---|---|
Unable to find SOP attribute: variant |
Нет s@variant на точках, или он int. Используй строковую версию wrangle. |
| Все инстансы выглядят одинаково | Варианты объединены через Merge вместо Component Geometry Variants; или прототипы не перезагружены после Save to Disk. |
| Кластер инстансов в начале координат | Включён Keep Incoming Terrain в HeightField Scatter. |
| Растения выстроены сеткой | Включён Relax Points. |
| Слишком редко / слишком густо | Outer Radius в разбросе, затем density в patches wrangle. |
| Листья полупрозрачные | Карта Opacity не задана как Float, или рендер не сошёлся. Опционально отсеки opacity через MtlX Range (0.45-0.55, clamp). |
| Растения лежат или гигантские | Поправь Transform внутри Component Geometry (scale 0.01, rotate X -90), пересохрани. |
| Приходит только один прототип | output0 subnet PROTO_ подключён к одной ноде Set Variant вместо Merge. |
Нода переименована в VarA1 |
Конфликт имён в одной сети. Собирай внутри subnet BUILD_ / PROTO_. |
| Цвета слишком тёмные / выцветшие | File Color Space пустой или неверный: цветовые карты sRGB - Texture, карты данных Raw. |
8. Чек-лист для нового типа растений
- Скачай набор Megascans в
$HIP/models/plants/<src_dir>/. BUILD_<asset>: Component Builder, VarA с путями через opname, проверь масштаб/оси, продублируй до VarH, Component Geometry Variants.- Материал: 5 MtlX Image с явными цветовыми пространствами, Opacity как Float, Thin Walled, Subsurface.
- Переименуй выход в
<asset>, Save to Disk, проверь набор вариантов geo. PROTO_<asset>: Reference + Set Variant x8, Merge, подключи к output0.- SOP: маска (опционально инверт/низины), разброс (Relax выкл, Keep Terrain выкл), patches wrangle с новым offset, attributes wrangle,
POINTS_<asset>. - PointInstancer
<asset>в цепочке за предыдущим, String Attributevariant. - Проверка рендера в Karma; настрой плотность, пятна и просвечивание.