Я давно хотел печатать на 3D-принтере то, что придумал сам. Скачать готовую модель и отправить её в печать — дело пяти минут, но такая же вещь есть у каждого, кто её скачал. Хотелось своего, уникального: такого, что можно только вообразить.
Нейросети, которые превращают картинку в 3D-модель, наконец сделали это реальным. Онлайн-сервисы берут за это деньги: у Tripo тариф Pro стоит $20 в месяц примерно за 200 моделей, у Meshy — около $20 за 1000 кредитов, это до 100 моделей. Бесплатных тарифов хватает на несколько попыток в достаточно низком качестве. Я пошёл другим путём: TRELLIS.2 от Microsoft бесплатно работает на моём ПК с RTX 5060 Ti 16 ГБ, и попыток можно делать сколько угодно.
Так у меня появились два светильника: орочий череп, который светится изнутри, и планета-литофания на подставке из корней. Между красивой картинкой и вещью, которая собирается и светится, оказалась куча задач: полость под лампу, патрон, посадка, поддержки. А нейросеть, как выяснилось, нужна не везде: гладкую планету проще построить формулой.


Скрипты, пример карты и инструкция — на GitHub, готовые STL — в релизе. Можно взять детали как есть или повторить процесс со своим рисунком.
1. Череп: нейросеть дала форму, обработка чуть не стёрла лицо
Начну с черепа: на нём лучше всего видно, что сырой результат нейросети — ещё не модель для печати. В режиме 1536_cascade TRELLIS.2 не уместился в 16 ГБ видеопамяти на декодировании. В 1024_cascade без текстур прогон занял 181 секунду с пиком 12 ГБ, на выходе — около 24 млн треугольников.

Дальше я сделал то, что делают обычно: замкнул сырой меш в сплошное тело, чтобы потом вырезать полость под лампу. Обработка закрыла глазницы и височные отверстия плоскими заплатками, и лицо стало чужим. Достраивать механику на испорченной основе смысла не было, и я вернулся к исходной поверхности.
В build_preserved_shell.py вокселизуется только поверхность: вокруг неё наращивается узкая полоса материала, и из неё заново извлекается сетка. Внутренний объём не заливается, поэтому глазницы и височные окна остаются открытыми. Мелкие щели при этом могут закрыться, так что радиус утолщения подбирается под размер деталей.

Потом всплыли две ошибки, которых не видно на красивом рендере. После утолщения часть оболочки ушла ниже нуля, и при переносе модели на стол опорное кольцо поднялось примерно на 1,2 мм — принтер начинал бы печать не с него. Помогла обрезка по плоскости стола до финального выравнивания. Вторая ошибка: короткое отверстие под лампу упиралось внутри черепа в плоский потолок. Я продолжил полость выше и закрыл её сужающимся сводом.

С поддержками не помогло просто сделать стойки толще: некоторые участки всё равно начинались в воздухе. Тогда скрипт стал проходить модель снизу вверх, искать участки, которым не на что опереться на предыдущем слое, и отмечать длинные мосты. Так нашлось 12 зон, которым добавили опоры.
Скрипт черепа заточен под эту модель: координаты опоры и полости привязаны к нему. В репозитории он лежит как пример метода — для другой модели их придётся пересчитать. Своего провода у черепа пока нет, поэтому светится он на подставке от планеты. О ней дальше.
2. Планета: сначала размеры, потом рисунок
Планету я строил с другого конца — от покупных деталей. Светильник состоит из плафона, подставки и обычного узла с лампой, патроном и проводом. E27 — это резьба лампы, наружный диаметр корпуса патрона из неё не узнать. А колба должна пройти в нижнее отверстие плафона: в 58,4 мм лампа диаметром 60 мм не войдёт.
Деталь или сопряжение | Размер в этой сборке | Зачем его знать |
|---|---|---|
Планета | Диаметр 140 мм | Задаёт свободное место между корнями |
Нижнее отверстие цельной планеты | 58,4 мм | Через него проходят лампа и центрирующий пояс |
Опорное кольцо планеты | Наружный диаметр 67,6 мм, высота 4 мм | Стоит на столе при печати, затем опирается на подставку |
Полка и центрирующий пояс подставки | 68 и около 57,6 мм | Полка несёт вес, пояс удерживает шар от бокового смещения |
Корпус патрона | Не шире 46 мм и не длиннее 55 мм | Под него сделано гнездо 46,4 мм |
Лампа | LED с колбой около 45 мм | Проходит через отверстие с запасом |
Зазор между поясом и отверстием — (58,4 − 57,6) / 2 = 0,4 мм. Реальная печать его съедает, поэтому сначала лучше напечатать короткую посадочную часть planet_base_test_10mm.stl и примерить её, а потом уже весь шар.
3. Как плоская картинка становится светящимся шаром
Нейросеть здесь не нужна. Планета — литофания: рисунок появляется за счёт толщины пластика, тонкое светится ярче, толстое — темнее. Внешняя поверхность остаётся гладкой сферой, а рисунок меняет внутреннюю. На вход скрипту подаётся плоская развёртка 2:1: горизонталь становится долготой, вертикаль — широтой. Левый и правый края встретятся на шаре, поэтому разрыв между ними превратится в заметный меридиан.

После перевода карты в серый и нормализации яркости толщина считается одной строкой. У меня минимум — 0,8 мм, максимум — 3 мм:
thickness = 0.8 + (1.0 - brightness) * (3.0 - 0.8) # brightness: 0 — чёрное, 1 — белое
Для каждой точки сферы скрипт строит наружную и внутреннюю вершины, соединяет соседние точки треугольниками и замыкает нижний край. Возле основания переход устроен отдельно: узкая нижняя кромка должна устойчиво печататься. Всё это видно в make_clean_whole_lithophane.py.
Первую планету я печатал двумя половинами: их удобно класть на стол широкой стороной. Но на просвет клеевой шов стал частью рисунка. Цельный шар убирает шов, зато начинается с узкой нижней части. Поэтому у него кольцевой воротник шириной 4,6 мм и высотой 4 мм вместо тонкой кромки или однослойной «юбки».
Чтобы повторить пример в Windows с Python 3.12, скачайте репозиторий и выполните в его папке:
python -m venv .venv .venv\Scripts\python.exe -m pip install -r requirements.txt .venv\Scripts\python.exe tools\rebuild_example.py planet
В build появятся planet.stl, отчёт planet_metrics.json и расчётная карта светопропускания. Повторный запуск собирает тот же STL, что у напечатанной планеты, байт в байт.
Для своего рисунка достаточно передать карту через --lightmap: на входе развёртка 2:1, на выходе сферический плафон. Для первой попытки лучше заменить только картинку, оставив проверенные размеры и толщины. Полная команда со всеми параметрами — в README. Форму генератор не меняет: им делаются шары с разными рисунками, череп так не построить.
После экспорта скрипт проверяет замкнутость сетки, число компонентов и 125 сечений нижних 25 мм. Проверка строгая: на подробной карте она может ругаться на внутренние полости, так что счётчик bad_sections — повод открыть сечения и посмотреть. Последнее слово за слайсером: нижние слои, нависания, траектории стенок.
4. Подставка: нейросеть рисует корни, скрипт освобождает место
Для корней порядок обратный: сначала образ, потом механика. Эскиз сделал встроенный генератор картинок Codex; локальная FLUX.1 Krea dev через ComfyUI тоже давала подходящие эскизы на том же ПК. Для 3D важнее читаемая форма, однотонный материал и чистый фон, чем выбор генератора. На слово «подставка» генераторы упорно рисовали глубокую чашу, в которой планета утонула бы наполовину, поэтому я просил открытое место между корнями. Дальше TRELLIS.2 превратил эскиз в объём.
Но фронтальный рисунок ничего не говорит о глубине. В 3D верхушки корней оказались в 30–38 мм от оси при радиусе шара 70 мм: вычитание сферы срезало бы ветви. Я попробовал поднять шар выше — и вместо планеты в корнях получилась планета на трубе.

Решение — сначала плавно развести ветви, а потом вычитать место для шара. Основание остаётся на месте; смещение начинается на высоте 45 мм и достигает 30 мм к высоте 125 мм. Для перехода используется smoothstep: при нормированной высоте u коэффициент равен 3u² − 2u³. Поэтому у корней не появляется резкого излома на границе деформации. Центральная область под патрон из этого смещения исключена.

Дальше важная операция, которую легко пропустить: освободить не только конечное положение шара, но и весь путь установки сверху. Скрипт строит объём, занимаемый движущимся шаром, и вычитает его из корней. Центральный проход внутри нижнего отверстия оставляет — там должен находиться патрон. Если вычесть сплошную сферу без этого исключения, она срежет и допустимую центральную часть.
Для геометрии — Python, NumPy и trimesh, для булевых операций — manifold3d. make_cradle.py получает уже замкнутую декоративную сетку. Чтобы повторить этап без TRELLIS, я положил в архив промежуточную stand_solid_04.stl: распакуйте его в папку reference и выполните:
.venv\Scripts\python.exe tools\rebuild_example.py stand
Пресет рассчитан на прежний шар с отверстием 59,2 мм. Для печати готовой сборки берите stand_print.stl, а под свои размеры пересчитывайте посадку целиком: параметры скрипта описаны в README.
Полку тоже пришлось переделать: плоское кольцо снизу давало нависание. Вместо поддержек я сделал нижнюю поверхность наклонной — полка 68 мм с фаской высотой 8 мм. Скрипт следит, чтобы высота фаски была не меньше радиального вылета.

Ещё одна ловушка — облегчение модели. Два способа уменьшить число треугольников после булевых операций испортили топологию. Печатную версию пришлось собрать из более грубой промежуточной сетки с шагом 0,4 мм и повторить точные операции.
А если вместо корней — другая подставка? Я прогнал тот же make_cradle.py на каменной композиции: код менять не пришлось, только заготовку и параметры посадки. Обработка заняла 14 секунд, проверка — ещё 27. Сетка замкнута, с плафоном не пересекается, Bambu Studio её нарезал; этот вариант я пока не печатал. Заготовка и пошаговая инструкция — в README. На входе нужна уже замкнутая модель: сырой выход нейросети сначала придётся замкнуть.
5. Что проверить в слайсере, прежде чем тратить катушку
Самая неприятная ошибка литофании — разреженное заполнение внутри стенки. На обычной детали решётку не видно, а здесь она светится вместе с рисунком. Поэтому я смотрю на нарезку: в стенке плафона должны быть только периметры и сплошные линии, без Sparse infill.
Параметр | Цельная планета | Тёмная подставка |
|---|---|---|
Сопло | 0,4 мм | 0,4 мм |
Слой | 0,10 мм | 0,16–0,20 мм |
Стенки | 8, генератор Arachne | 3–4 |
Заполнение | 100%, проверка отсутствия решётки в стенке | 20–25% |
Внешняя кайма | Проверить по короткому тесту основания | 5 мм |
Старый 3MF с 0,20 мм, пятью стенками и 15% заполнения я бы без просмотра траекторий не переносил. Профиль материала — под свою катушку. Поддержек в STL нет: нужны ли они, видно по нарезке.
Порядок печати: сначала planet_base_test_10mm.stl тем же профилем, примерка к подставке, потом полный плафон. В полном STL стоит отдельно пройти первые слои, переход воротника в сферу и нависания, а у подставки — канал провода и обратные стороны ветвей.
Собирать при отключённом питании: патрон, лампа, затем шар вертикально на пояс — он должен садиться без продавливания между корнями. Для сетевой части лучше взять готовый исправный узел с проводом: напечатанный канал не заменяет крепление кабеля. Лампа — только LED.
Что получилось и что можно забрать себе
Для повторения основной пример — планета с древесной подставкой, для каменной композиции есть отдельный пресет. Ниже — напечатанная раньше космическая подставка.


Готовая деталь | Примерная масса |
|---|---|
Планета | 140 г |
Череп | 157 г |
Древесная подставка | Около 120 г |
Космическая подставка | Около 85 г |
Массы примерные, без брака и поддержек. Всё остальное — в репозитории со скриптами и инструкцией, архиве эталонных деталей и результатах проверок.
Главное, что я вынес из этих светильников: нейросеть хороша там, где нужна фантазия, — корни, череп. Свет лучше строить через толщину стенки, а посадку и сборку задавать размерами и проверять отдельно. Тогда не приходится просить нейросеть одновременно придумать красивые корни, угадать ваш патрон и предсказать, как шар пройдёт между ветвями.
А вы печатаете своё или скачиваете готовое? Если тоже делаете модели из картинок — расскажите в комментариях, чем доводите сырую сетку до печати.
