
На сайте появился новый раздел — «Четвёртое измерение». Это живые четырёхмерные тела, спроецированные в наше пространство, и справочник из одиннадцати глав с интерактивными виджетами. Задача была сформулирована одной фразой: сделать самую продвинутую и эффектную проекцию четырёхмерного пространства в трёхмерное, «чтоб прям вау». Ниже — как это устроено внутри: немного математики, немного шейдеров и честный список граблей.
Как вообще смотреть в четвёртое измерение
Главный трюк придумал ещё Эдвин Эбботт в 1884 году во «Флатландии». Двумерный Квадрат не может увидеть трёхмерный шар, но видит две вещи: его тень на своей плоскости и его сечение, когда шар проходит сквозь плоскость (точка, растущий круг, сжимающийся круг, снова точка). Нам с четырёхмерными телами доступно ровно то же самое: тень в наше пространство и сечение нашим пространством.
Всё, что показывает витрина, — это тень или сечение. Никакого «художественного впечатления»: вершины, рёбра и грани — точная геометрия, пересчитанная из четырёх измерений в три шестьдесят раз в секунду. Вращать можно и обычную трёхмерную камеру, и сам объект в четвёртом измерении: жест с Shift (или двумя пальцами) толкает ближнюю в 4D сторону тела туда, куда ведёте.
Кирпичики: шесть правильных многоячейников одним конвейером
В трёх измерениях пять правильных многогранников, в четырёх — шесть: 5-, 8-, 16-, 24-, 120- и 600-ячейник. У самого сложного из них, 120-ячейника, 600 вершин, 1200 рёбер, 720 пятиугольных граней и 120 ячеек-додекаэдров. Вбивать это таблицами не хотелось, поэтому все многоячейники строятся одним конвейером из двух списков: вершины и нормали к ячейкам.
- Ячейка — это вершины, самые дальние в направлении своей нормали.
- Грань — место, где две ячейки делят три и больше вершин.
- Рёбра — соседние вершины в каждой грани.
Нормали берутся у двойственного многогранника: у тессеракта это вершины 16-ячейника и наоборот, у 120-ячейника — вершины 600-ячейника. А сам 120-ячейник выводится из 600-ячейника. Вершины 600-ячейника — это 120 «икосианов», кватернионов, которые образуют группу. Ближайшие пары дают рёбра, четвёрки попарно соседних вершин — 600 тетраэдров, а центры этих тетраэдров, вытолкнутые на сферу, и есть 600 вершин 120-ячейника. Весь 120-ячейник собирается за 7 миллисекунд.
Тесты с первого раза подтвердили все счётчики вершин, рёбер, граней и ячеек и эйлерову характеристику V − E + F − C = 0. Упали только два моих собственных ожидания. Я думал, что сечение тессеракта через центр «вершиной вперёд» — октаэдр через середины рёбер, а оно проходит прямо через шесть вершин, и объём у него 4/3. А строки «−0.000000» и «0.000000» как ключи словаря разрезали кольца Хопфа пополам: вышло 24 кольца вместо 12.
Одна формула на три линзы
Проекций нужно было минимум три: ортогональная тень (четвёртая координата просто отбрасывается), перспектива (дальнее в 4D меньше) и стереографическая проекция (глаз стоит на самой трёхмерной сфере). Оказалось, что это одна формула:
X = (x, y, z) / (1 − k·w)
При k = 0 это тень, при 0 < k < 1 — перспектива из точки w = 1/k, при k = 1 глаз садится на полюс единичной сферы, и для её точек получается ровно стереографическая проекция. Переключатель «линзы» на витрине просто плавно анимирует k — и тессеракт на глазах превращается из «куба в кубе» в клубок дуг.

Рёбра-дуги и трубки, которые строит видеокарта
В стереографической проекции рёбра многоячейника, вписанного в сферу, становятся дугами окружностей. Классический способ получить точку на дуге — slerp с синусами. Но есть способ проще: взять точку на хорде и нормировать её, p(t) = normalize((1 − t)·a + t·b). Скорость движения по дуге получается неравномерной, но сама кривая та же, а в шейдере это одна строка.
Вообще вся четырёхмерная математика живёт в вершинном шейдере. Процессор один раз загружает в видеопамять концы рёбер (по два четырёхмерных вектора на ребро), а поворот, проекция и построение трубки вокруг ребра происходят на GPU для каждой вершины каждого кадра. Трубка — это один цилиндр-шаблон, размноженный инстансингом. Самое хитрое в трубке — ориентация её сечения: если брать «любой перпендикуляр», трубка перекручивается. Помогла геометрия. Проекция любого ребра в любой из наших линз — отрезок или дуга окружности, то есть плоская кривая. Нормаль её плоскости постоянна вдоль всего ребра и даёт идеальную рамку без скручивания.
Второй приём — толщина. В стереографии масштаб в точке равен 1/(1 − w), и если умножить на него радиус трубки, ячейки у полюса проекции становятся толстыми, как им и положено, а у центра — тонкими. Картинка перестаёт быть «проволочной» и обретает глубину.
Грабли у полюса проекции
Первый же кадр был таким:

Слишком сильное свечение (bloom) и полупрозрачные грани, которые складываются в белое. Это лечится настройкой. Интереснее было с тем, что около полюса проекции: там ячейки раздуваются до бесконечности и закрывают весь экран. Их нужно гасить. Попытка номер один — уменьшать радиус трубки до нуля. Получился морской ёж:

Попытка номер два — alpha-to-coverage, прозрачность через сэмплы сглаживания. Сортировать ничего не нужно, но при четырёх сэмплах на пиксель вместо плавного затухания видна растровая сетка. В итоге сработал самый прямолинейный вариант: та же геометрия рисуется дважды. Сначала непрозрачным проходом — всё, что видно полностью, потом прозрачным — только гаснущие части.
Цвет — это четвёртая координата
Проекция съедает одно измерение, и его нужно вернуть хотя бы цветом. Палитра «Глубина w» красит точку по её четвёртой координате: холодный синий — дальше в 4D, тёплый янтарный — ближе. Цвета строятся в перцептивном пространстве OKLCH, чтобы шаги были равномерными на глаз.
Вторая палитра — «Хопф». Отображение Хопфа переводит точку трёхмерной сферы в точку обычной сферы, и все точки одного «волокна» (большой окружности) получают один цвет. У 120-ячейника есть красивое свойство: если взять ось отображения у кватерниона пятого порядка из той же группы икосианов, его 120 ячеек распадаются ровно на 12 колец по 10 додекаэдров, а 12 точек-образов на сфере — вершины икосаэдра. Тест это проверяет. На витрине 120-ячейник вращается как раз вдоль этих колец: ячейки текут по двенадцати окружностям, не меняя цвета. Само расслоение Хопфа тоже есть среди экспонатов — трёхмерная сфера, сплетённая из попарно зацепленных окружностей.

Сечения: что увидит трёхмерный житель
Линза «Сечение» режет тело гиперплоскостью w = h и показывает то, что увидел бы житель нашего пространства, пока тело проходит сквозь него. Для многоячейников сечение считается на процессоре каждый кадр: каждая ячейка — трёхмерный выпуклый многогранник, её разрез гиперплоскостью — выпуклый многоугольник, и все многоугольники вместе ограничивают многогранник-сечение. Ячейки раскрашены в разные цвета (оттенки отстоят на золотой угол), а сзади слабо светится тень всего тела — и рёбра вспыхивают там, где их пересекает гиперплоскость.

Гладкие тела и кватернионный фрактал
С гладкими телами — гиперсферой, «тигром», дитором, дуоцилиндром — треугольники не нужны. Их сечения рисуются трассировкой лучей (ray marching). У каждого тела есть четырёхмерная функция расстояния. Точку луча X в нашем пространстве поднимаем в 4D как p = Rᵀ·(X, h) — это точка разрезающей гиперплоскости в системе координат самого тела. Ключевое наблюдение: расстояние в 4D до тела никогда не больше расстояния внутри среза, ведь ближайшая точка в срезе тоже где-то в 4D. Значит, 4D-расстояние — безопасный шаг для sphere tracing в 3D, и можно резать любое тело, для которого известна функция расстояния.
Отсюда же самый неожиданный экспонат — кватернионное множество Жюлиа. Это фрактал z → z² + c, где z и c — кватернионы, то есть он четырёхмерен. Мы видим его трёхмерные срезы, а параметр c медленно дрейфует по замкнутой петле, и фрактал непрерывно перетекает.

Мелочи, из которых складывается «вау»
- Кино-обработка: HDR-рендер, свечение, тональная кривая ACES, лёгкая виньетка, зерно и едва заметная хроматическая аберрация по краям.
- Пыль на заднем плане: 900 далёких точек. Когда камера плывёт, они дают параллакс, и глаз верит в объём.
- Появление: при загрузке 120-ячейник раскрывается из центра, пока камера подъезжает.
- Скорость: в 120-ячейнике около 440 тысяч треугольников (1200 трубок, 600 шаров и 720 изогнутых граней). Разрешение подстраивается под скорость кадров, а когда вкладка скрыта или сцена уехала за экран, рендер спит. В безголовом Chrome на ноутбуке — стабильные 60 кадров в секунду на всех 16 экспонатах.

Справочник с виджетами
Витрина отвечает на вопрос «как это выглядит», а справочник — на вопрос «почему». В нём одиннадцать глав: что такое измерение, Флатландия и мышление аналогиями, проекции, вращение (в 4D вращают плоскость, а не ось), сечения, шесть правильных многоячейников и почему их ровно шесть, трёхмерная сфера и расслоение Хопфа, странная геометрия больших размерностей (где почти весь объём шара — у самой поверхности), где измерения встречаются в физике и в данных, история от Шлефли до «Интерстеллара» и отдельная глава о том, как нарисована витрина. Теоремы выделены и доказаны, а каждую идею можно покрутить в виджете: вытянуть куб в тессеракт, сложить развёртку Дали, прогнать гиперсферу сквозь наше пространство.
Как шла работа
Раздел целиком, от спецификации до деплоя, собрал ИИ-агент — Claude в Claude Code. Сначала он написал спецификацию и математическое ядро с тестами, затем витрину. Каждое изменение проверялось скриншотами из безголового браузера: агент сам смотрел на кадры и исправлял то, что видел (все картинки с граблями выше — из этого цикла). Главы справочника параллельно писали пять субагентов по общему брифу: разметка, API виджетов, требования к точности и источникам. Главный агент потом вычитывал их тексты, формулы и виджеты. Моя роль — поставить задачу, смотреть и поправлять. Например, шапка витрины на телефоне сначала была в две строки, и это пришлось переделать.
Посмотреть
legost.in/utilities/4d — лучше на большом экране. Начните с «Тура»: он сам проведёт по экспонатам и линзам. Потом переходите в справочник.