Фраза про сайт в метавселенной звучит как фантастика, пока её не разобрать на файлы и стандарты. Технически за ней стоят три слоя: трёхмерные ассеты в формате glTF, браузерный API WebXR, который выводит сцену прямо в шлем, и идентичность через кошелёк, заменяющая отдельную регистрацию. Всё это работает уже сейчас в обычных браузерах, без установки приложений, и подготовка сайта сводится к честной инженерной работе: собрать модели, уложиться в бюджет кадра, проверить поддержку устройств и связать уровни доступа с токенами. Ниже разбор каждого слоя с кодом, командами и цифрами, без обещаний и магии.
Трёхмерные ассеты по стандарту glTF вместо плоских картинок
Двумерный веб вырос на форматах JPEG и PNG, трёхмерному нужен собственный язык. Стандарт де-факто - glTF 2.0 от консорциума Khronos: сцена описывается JSON-ом, геометрия и текстуры лежат в бинарных буферах, а весь набор упаковывается в один файл .glb. Формат читают все движки: браузерные three.js и Babylon, игровые Unity и Unreal, встроенные просмотрщики операционных систем. Blender экспортирует glTF нативно, без промежуточных конвертеров: модель с материалами, анимациями и иерархией узлов выгружается в один файл командой из меню.
Размер - первое, за что платят. Сырая сетка с 4K текстурами весит сотни мегабайт и убивает мобильную загрузку, поэтому стандарт растёт расширениями. Геометрию сжимают Draco или meshopt (расширения KHR_draco_mesh_compression и EXT_meshopt_compression), текстуры переводят в GPU-сжатый KTX2 (расширение KHR_texture_basisu), и модель худеет в разы: сетка теряет девять десятых объёма, а текстуры перестают разворачиваться в памяти целиком. Правило практики простое: главная сцена не тяжелее 5-10 МБ, остальное догружается потоково по мере движения посетителя.
glTF переносит не только статичную сетку. Скелетная анимация, морф-целевые формы и анимация материалов записываются в тот же файл, поэтому идущие фигуры, машущие крылья экспонатов и меняющаяся подсветка живут рядом с геометрией. Отдельная дисциплина - запекание: сложное освещение и мелкие детали выдавливают в текстуры заранее, чтобы видеокарта не считала их во время показа. Трёхмерный контент собирают как монтаж видеоролика: сырые исходники в Blender, сборка и запекание отдельно, на сайт уезжает только готовый компактный файл.
Быстрая проверка результата без программирования - веб-компонент model-viewer: одна HTML-строчка даёт вращение мышью, зум и режим AR на телефоне. Атрибуты camera-controls и ar включают интерактив и запуск в реальности, auto-rotate оживляет витрину:
<model-viewer
src="/assets/product.glb"
camera-controls
ar
auto-rotate
shadow-intensity="1">
</model-viewer>
Для мира Apple существует параллельный формат USDZ: модель в нём открывается в режиме быстрого просмотра дополненной реальности прямо из поисковой выдачи. Готовят оба варианта одновременно: glTF для веба и сцен WebXR, USDZ для мобильного AR, а исходник в Blender хранят один.
Сцены WebXR в браузере и проверка шлема до загрузки модели
WebXR - браузерный интерфейс к шлему и очкам дополненной реальности. Первый шаг по спецификации W3C - не запуск сцены, а тихий вопрос браузеру:
const supported = await navigator.xr.isSessionSupported("immersive-vr");
if (supported) {
// можно предлагать кнопку входа в шлем
}
Такой порядок экономит и нервы, и трафик: страница не грузит сотню мегабайт модели человеку, у которого шлема нет. Сама сессия запрашивается методом requestSession с режимом immersive-vr или immersive-ar, а система координат выбирается из референсных пространств: local-floor ставит пол на уровень глаз вниз, что для интерьеров важнее абстрактного центра мира. Работает WebXR только в защищённом контексте браузера, то есть на страницах с шифрованным соединением, и только после явного согласия человека, поэтому кнопку входа в шлем рисуют свою, а не устраивают автозапуск.
В three.js вход в шлем занимает две строки: флаг xr.enabled у рендерера и цикл setAnimationLoop вместо старого requestAnimationFrame, потому что кадры теперь отсчитывает шлем, а не монитор:
import * as THREE from "three";
import { VRButton } from "three/addons/webxr/VRButton.js";
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
import { DRACOLoader } from "three/addons/loaders/DRACOLoader.js";
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
renderer.xr.enabled = true;
document.body.appendChild(renderer.domElement);
document.body.appendChild(VRButton.createButton(renderer));
const draco = new DRACOLoader();
draco.setDecoderPath("assets/draco/"); // файлы декодера лежат рядом с сайтом
const loader = new GLTFLoader();
loader.setDRACOLoader(draco);
loader.load("assets/scene.glb", (gltf) => scene.add(gltf.scene));
renderer.setAnimationLoop(() => {
renderer.render(scene, camera); // частоту задаёт шлем: 72, 90 или 120 Гц
});
Ввод в шлеме - это два контроллера с осями, кнопками и триггерами, события которых приходят в сессию как устройства ввода. В three.js контроллеры получают через renderer.xr.getController(0) и getController(1), модель геймпада подгружается фабрикой XRControllerModelFactory, а луч указателя рисуется обычной линией сцены. Пространственный звук - второй якорь присутствия: аудиопоток привязывается к точке сцены, и собеседник слышится слева именно потому, что стоит слева. Для AR-режимов арсенал шире: hit-test ищет точку на реальной поверхности, куда ставится модель, анкеры закрепляют объекты в пространстве, а DOM overlay выводит обычные HTML-кнопки поверх камеры. Набор возможностей сессии запрашивается списками requiredFeatures и optionalFeatures, и страница обязана честно деградировать: нет hit-test - нет и раскладки мебели, но витрина продолжает работать.
Бюджет кадра мобильного шлема и арифметика 14 миллисекунд
Виртуальная реальность прощает меньше, чем плоский веб. Кадр должен уложиться в долю секунды с точностью до миллисекунды, иначе изображение дёргается, а у человека за шлемом начинается укачивание. Арифметика жёсткая: 72 кадра в секунду означают бюджет 13.9 миллисекунды на всё - опрос контроллеров, анимацию, рендер, 90 кадров оставляют 11.1, а 120 оставляют 8.3. Мобильные шлемы работают на частотах 72, 90 или 120 в зависимости от модели, и ориентироваться нужно на худший целевой режим.
Дальше умножение: стереорендер рисует картинку дважды, по разу на каждый глаз, так что нагрузка почти удваивается. Отсюда практические бюджеты, к которым приходят команды: сцена порядка 100-150 тысяч треугольников, материалы считаются по-простому, тени и постобработка первыми выключаются, когда кадр поехал. Именно вызовы отрисовки, а не полигоны, душат мобильные сцены: двести объектов по 500 треугольников убьют кадр быстрее, чем один объект из 100 тысяч, ведь каждый вызов - отдельный разговор процессора с видеокартой. Лечение классическое: атласы текстур, объединение сеток, инстансинг повторяющихся объектов и уровни детализации, подменяющие дальние модели грубыми копиями.
Спасает и периферия: фиксированная фовеация рендерит центр поля зрения в полном разрешении, а края грубее, глаз всё равно не замечает. В three.js это настраивается одной строкой:
renderer.xr.setFoveation(1); // максимальное упрощение периферии
Замеры делают на реальных устройствах, а не в эмуляторе: статистика рендерера, счётчик кадров и подсказки браузера покажут, где именно сцена выходит за бюджет.
Токен-гейт открывает закрытые разделы держателям NFT токенов
Идентичность в трёхмерном мире повторяет клубную карту: показал токен - вошёл. Роль карты играет NFT в кошельке, а проверка - вызов balanceOf у контракта коллекции, механика знакома по блокчейн-витринам. Уровни доступа задаются порогами: один токен открывает обычный раздел, три - закрытую комнату, пять - ранний показ новинок. Код проверки умещается в пару строк:
const amount = await gate.methods.balanceOf(account).call();
const level =
amount >= 5n ? "founder" :
amount >= 3n ? "insider" :
amount > 0n ? "guest" : null;
if (level) {
// открыть разделы, соответствующие уровню
}
Деградацию строят мирно: человеку без токена показывают публичную часть мира и объяснение, как попасть внутрь, а не пустой экран с ошибкой. Пороги проверяются и на сервере при выдаче контента: клиентская проверка открывает интерфейс, но тяжёлые ассеты закрытого раздела отдаются только после серверной сверки баланса, иначе токен-гейт превращается в декорацию. Тот же кошелёк обслуживает и вход: одноразовый код с сервера подписывается в кошельке, сервер восстанавливает адрес из подписи и выдаёт сессию без паролей. Пороги сначала проверяют на тестовой сети Sepolia с номером 11155111: там токены раздаются из крана бесплатно, и можно спокойно проверить все уровни доступа до выхода в основную сеть.
Аватары по стандарту VRM и имена из кошелька вместо профилей
Житель метавселенной - это аватар, и его переносимость между мирами закреплена открытым стандартом VRM, который строится поверх glTF. Внутри файла скелет humanoid с единой картой костей, блендшейпы для эмоций, пружинные кости для физики волос и одежды, шейдер MToon для стилизованной графики. Один и тот же файл аватара загружается в разные миры, поддерживающие стандарт, и человек сохраняет внешность без переконвертации, как один пароль в менеджере открывает разные сайты. Речь и мимика достаются аватару из коробки: блендшейпы открывают улыбку и удивление, виземы подстраивают губы под фонемы, и во встрече внутри мира участники узнают друг друга по привычной внешности, а не по безликим кружкам с инициалами.
Имя решает та же связка, что и доступы: кошелёк и открытые сервисы имён. Запись вида name.eth резолвится в адрес, работает в любых приложениях сразу и не требует регистрации на каждой площадке. Для сайта это значит, что профиль посетителя собирается из открытых записей: имя, аватар, список токенов - всё читается из сети, а подпись сообщения кошельком заменяет пароль. Человек заходит в мир с той же карточкой, которой открывал закрытые разделы, и площадка не требует заполнять лишние формы.
Распределённое хранение тяжёлых ассетов и адресация по хешу
Трёхмерный контент тяжёлый, и хранение встаёт ребром. Распределённые файловые сети адресуют содержимое хешем: идентификатор вычисляется из файла, изменился байт - изменился адрес, и подменить модель под старым адресом невозможно в принципе. Это удобно для метавселенных: сцена однажды опубликована, и указатель на неё вечно ведёт ровно к той версии. Побочный эффект - неизменность: обновление модели означает новый адрес, и реестр версий ведёт либо смарт-контракт, либо сервер метаданных. Отдельный плюс для долгих проектов: старые версии не пропадают, и демо трёхлетней давности открывается в точности таким, каким его опубликовали.
Хостинг решается пиннингом: узлы сети держат копию за договорённую плату, а сеть Arweave хранит файл после однократного взноса, без ежемесячных платежей. Человеческие имена вида site.eth ссылаются на актуальный хеш через запись в реестре имён, и адрес остаётся прежним при смене версий. Для больших сцен держат два слоя: холодное хранение ассетов в сети и быстрые шлюзы с кэшированием у ребра, иначе загрузка сотни мегабайт превратится в минуты ожидания у первого же посетителя.
План подготовки сайта к трёхмерному присутствию по шагам
Сборка всех слоёв в один план выглядит так:
- Инвентаризировать контент и выбрать, что станет трёхмерным: продукция, пространства, интерфейсы;
- Собрать ассеты в glTF со сжатием Draco и текстурами KTX2, проверить в model-viewer на телефонах;
- Поднять WebXR сцену с проверкой поддержки шлема до загрузки и честной деградацией до плоской страницы;
- Связать уровни доступа с токенами и проверить пороги на тестовой сети до основного запуска;
- Замерить бюджет кадра на целевых шлемах и добиться стабильных 72 кадров в секунду на средней модели.
Метавселенная в этой картине - не жанр фантастики, а слой протоколов поверх обычного сайта: те же страницы, дополненные glTF-моделями, сессиями WebXR и доступом по токенам. Ни один кусок не требует пересборки бизнеса с нуля, каждый добавляется отдельно и тестируется отдельно. Владелец магазина, у которого уже есть сайт, находится ближе к трёхмерному присутствию, чем ему кажется: инвентарь ассетов, один WebGL-рендерер и честный бюджет кадра - вот и вся волшебная дверь.