CRYO
// Copyright © 2026
Все права защищены.
////// Манифест
Мы строим тишину, в которой слышно, как растёт лёд.
Крутите вниз.
Наведите курсор на снег.

01 / кладка

Сфера собрана кодом

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

count = round(2 · rings · sin θ)

θ здесь широта середины кольца. У полюса синус мал и кирпичей выходит девять, на экваторе он равен единице и их двадцать четыре. Ширина кирпича при этом почти не меняется: ради этого всё и затевалось, иначе у полюсов получилась бы мозаика из осколков.

Каждый второй ряд сдвинут на полкирпича, то есть на π / count. Выключите перевязку на схеме: швы тут же выстроятся в сплошные меридианы, и кладка станет похожа на дольки арбуза. В рядах с окнами сдвиг, наоборот, отключён принудительно, иначе верхняя пара кирпичей окна съезжает относительно нижней и проём выходит лесенкой.

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

Ни одной готовой модели: форма здесь функция, а не файл.

перевязка
кирпичей
122
по кольцам
9 · 14 · 18 · 20 · 20 · 18 · 14 · 9

02 / снег

Снег помнит курсор

Обычный двумерный холст, тот же, на котором в браузере рисуют графики. Ни одного шейдера здесь нет.

проведите курсором

разрез по линии

  1. 1 / холст

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

  2. 2 / карта высоты

    Готовая картинка отдаётся материалу земли как карта смещения. Материал читает яркость пикселя и двигает вершину вдоль нормали ровно на эту величину.

  3. 3 / геометрия

    Двигаются настоящие вершины: у земли их сто сорок восемь тысяч. Ямка под курсором — честная вмятина в поверхности, а не подрисованная тень.

512 × 512
карта следов, пикселей
1.2 м
радиус кисти под курсором
0,5 %
затягивается за кадр
1 из 2
кадров уходит в видеопамять

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

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

Самый эффектный элемент сцены сделан инструментом, который знает любой фронтендер.

03 / свет

Сведите фаску к нулю: блик схлопнется в линию перепада, и объект станет читаться пластиковым. В сцене это число стоит на 0.36 от толщины кирпича.

Дорого выглядит свет, а не материал

Прозрачного стекла в сцене нет вообще: сквозь лёд ничего не видно, преломление не считается. Всё впечатление собрано из пяти настроек, и ни одна из них не про материал как таковой.

Контурное свечение
sheen 1
Края, повёрнутые от зрителя, светлее середины. Это встроенное свойство физического материала, придуманное для ткани: у бархата так ведёт себя ворс. Ни одной строки шейдера, одно число в настройках.
Источник внутри
185 · 6 м
Точечный свет стоит в центре сферы и гаснет к восемнадцати метрам. Наружу он выходит через зазоры между кирпичами, поэтому нижние грани оказываются ярче верхних. Снаружи так не бывает, и глаз сразу считывает, что внутри что-то есть.
Ядро ярче белого
× 5.5
Шарик радиусом 0.95 м светится цветом, умноженным на 5.5. Значение выше единицы обязательно: в ореол собирается только то, что ярче порога, а обычный белый до него не дотягивает.
Порог свечения
0.92
Светиться начинает только то, что ярче девяноста двух процентов яркости. Порог держится высоким намеренно: снег и сам почти белый, и стоит его опустить, как в ореол уходит весь кадр и картинка превращается в молоко.
Приглушённый снег
#a3a5aa
Земля отражает заметно меньше настоящего снега. Иначе свет изнутри сферы выбивает её в чистый белый, и кратер под объектом пропадает вместе со всем остальным рельефом.

что не сработало

Свет изнутри пересвечивал снег, и напрашивалось очевидное: развести источник и землю по слоям, чтобы свет доставал кирпичи, но не поверхность. В three.js так нельзя. Слой источника сверяется со слоями камеры, а не со слоями объектов, которые он освещает. Свет, положенный на слой, невидимый камере, выключается целиком, и сфера остаётся тёмной.

Пересвет лечится не слоями, а отражаемостью самой земли.

04 / переход

Всё живёт от одного числа

1020304050вид сбоку, метры
до сферы
29.4 м
высота камеры
9.1 м
фаза
глитч

Высота блока со сценой — четыре экрана, а холст внутри прилипает к верху окна и никуда не едет. Прокрутка делится на эту высоту и превращается в число от нуля до единицы. Больше в сцене нет ничего, что зависело бы от страницы.

Камера идёт не по прямой между точками, а по кривой Катмулла-Рома: она проходит ровно через заданные позиции, но скругляет углы между ними. Точек всего четыре, и отдельная такая же кривая ведёт цель, на которую камера смотрит. Поэтому взгляд не дёргается в момент, когда объект уходит из центра кадра.

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

Это единственный самописный шейдер во всём проекте. Всё остальное — готовые эффекты из библиотеки, собранные в цепочку.

Переход, который вы только что прошли, это одна переменная.

05 / цена

Кадр стоит 16,7 миллисекунды

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

  • Разъезд цветовых каналов и зерно

    8,1 мс

    Оба эффекта идут одним проходом. Дорога не сила разъезда, а сам проход по всем пикселям кадра

  • Свечение

    7,2 мс

    Считается в половинном разрешении. Снижение до четверти дало 0,7 мс: дорого не размытие, а сборка результата поверх кадра

  • Шейдер перехода

    4,9 мс

    Висит в цепочке всю прокрутку, хотя работает только в конце. Убрать его на время нельзя: библиотека не переживает пересборку цепочки на ходу

  • Сама сцена целиком

    16,7 мс

    Снег, сфера, три тысячи снежинок, тени, туман. Упирается в частоту экрана, то есть на деле быстрее

Складывать эти числа бессмысленно: эффекты идут по одному кадру, и отключение одного не освобождает время другого целиком. Важна не сумма, а порядок: сама сцена бесплатна, а весь перерасход делают полноэкранные эффекты, которым безразлично, что в кадре.

Отсюда единственный рычаг, который работает всегда, — количество пикселей. На экране с удвоенной плотностью картинка рисуется крупнее, чем показывается, и каждый эффект платит за это дважды.

плотность пикселей
×1.54,5 Мп33 кадров
×1.253,1 Мп45 кадров
×1.02,0 Мп60 кадров

Ещё одна ловушка: сервер разработки вдвое медленнее собранной версии. Сорок два миллисекунды против двадцати трёх на одном и том же кадре. Судить о плавности по нему нельзя.