Типичная ситуация: клиент предоставил GLB-модель дивана весом 50 МБ, а сайт на старом хостинге не тянет загрузку — посетители уходят через 5 секунд ожидания. Решение — встроить 3D-просмотрщик на Three.js или Babylon.js с оптимизацией: Draco-компрессия, LOD, правильное освещение. Мы реализовали такие проекты для десятков клиентов: от интерьерных конфигураторов до витрин ювелирных украшений. Наш опыт — более 5 лет веб-разработки и свыше 30 успешных интеграций 3D-просмотрщиков, что позволило клиентам сократить время загрузки до 2 секунд и увеличить конверсию на 15-20%. При этом Three.js загружается в 3 раза быстрее Babylon.js для простых сцен, но уступает в физике и XR. Экономия на хостинге при Draco-сжатии может достигать $500 в месяц для крупных проектов.
Проблемы, которые решаем
Типичные технические сложности при внедрении 3D-просмотрщиков:
- Большие файлы моделей: GLTF-модели мебели или автомобилей могут весить десятки мегабайт. Без Draco-компрессии и LOD пользователи ждут загрузку по 30 секунд, что критично для конверсии.
- Кросс-браузерность: WebGL стабилен в Chrome, Firefox, Edge, но на старых версиях Safari возможны артефакты. Нужна корректная проверка
WebGLRenderingContextи fallback-сообщение. - Производительность на мобильных: рендеринг сцены с высоким Pixel Ratio быстро сажает батарею. Ограничение
devicePixelRatioдо 2× и отключение теней на слабых GPU — обязательные меры.
Three.js vs Babylon.js: как выбрать?
Three.js — минималистичная библиотека рендеринга (~600 КБ), огромное сообщество, тысячи примеров. Требует больше ручной работы для сложных сцен (физика, collision detection).
Babylon.js — полноценный игровой движок в браузере (~2 МБ). Встроенная физика, PBR-материалы, Inspector, GUI, XR-поддержка. Хорош для сложных интерактивных сцен.
Для просмотра 3D-моделей — Three.js. Для интерактивных конфигураторов и сцен — Babylon.js. В 80% наших проектов используем Three.js из-за простоты интеграции и меньшего размера.
| Критерий | Three.js | Babylon.js |
|---|---|---|
| Размер библиотеки | ~600 КБ | ~2 МБ |
| Физика | Нет встроенной | Встроенный Cannon.js |
| PBR-материалы | Требует настройки | Готовые решения |
| XR-поддержка | Через WebXR | Встроенный Inspector |
Three.js: просмотрщик GLTF-модели
npm install three @types/three import * as THREE from 'three' import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader' import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader' import { useEffect, useRef } from 'react' interface ModelViewerProps { modelUrl: string envMapUrl?: string } export function ModelViewer({ modelUrl, envMapUrl }: ModelViewerProps) { const mountRef = useRef<HTMLDivElement>(null) useEffect(() => { const container = mountRef.current! const width = container.clientWidth const height = container.clientHeight // Сцена const scene = new THREE.Scene() scene.background = new THREE.Color(0xf8fafc) // Камера const camera = new THREE.PerspectiveCamera(50, width / height, 0.01, 1000) camera.position.set(2, 1.5, 3) // Рендерер const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, }) renderer.setSize(width, height) renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)) renderer.toneMapping = THREE.ACESFilmicToneMapping renderer.toneMappingExposure = 1.2 renderer.outputColorSpace = THREE.SRGBColorSpace container.appendChild(renderer.domElement) // Освещение const ambientLight = new THREE.AmbientLight(0xffffff, 0.5) scene.add(ambientLight) const dirLight = new THREE.DirectionalLight(0xffffff, 2) dirLight.position.set(5, 10, 5) dirLight.castShadow = true dirLight.shadow.mapSize.set(2048, 2048) scene.add(dirLight) const fillLight = new THREE.DirectionalLight(0x8bb8e8, 0.5) fillLight.position.set(-5, 2, -5) scene.add(fillLight) // Orbit Controls const controls = new OrbitControls(camera, renderer.domElement) controls.enableDamping = true controls.dampingFactor = 0.08 controls.minDistance = 0.5 controls.maxDistance = 20 controls.autoRotate = true controls.autoRotateSpeed = 1.5 // GLTF загрузчик с Draco сжатием const dracoLoader = new DRACOLoader() dracoLoader.setDecoderPath('/draco/') // Копируем в public/draco/ const loader = new GLTFLoader() loader.setDRACOLoader(dracoLoader) let mixer: THREE.AnimationMixer | null = null loader.load( modelUrl, (gltf) => { const model = gltf.scene // Центрируем модель const box = new THREE.Box3().setFromObject(model) const center = box.getCenter(new THREE.Vector3()) const size = box.getSize(new THREE.Vector3()) const maxDim = Math.max(size.x, size.y, size.z) model.position.sub(center) camera.position.multiplyScalar(maxDim * 0.8) controls.update() scene.add(model) // Анимации if (gltf.animations.length > 0) { mixer = new THREE.AnimationMixer(model) gltf.animations.forEach((clip) => { mixer!.clipAction(clip).play() }) } }, (xhr) => { const progress = Math.round((xhr.loaded / xhr.total) * 100) console.log(`Loading: ${progress}%`) }, (error) => console.error('Error loading model:', error) ) // Анимационный цикл const clock = new THREE.Clock() let animFrameId: number function animate() { animFrameId = requestAnimationFrame(animate) const delta = clock.getDelta() mixer?.update(delta) controls.update() renderer.render(scene, camera) } animate() // Resize function handleResize() { const w = container.clientWidth const h = container.clientHeight camera.aspect = w / h camera.updateProjectionMatrix() renderer.setSize(w, h) } window.addEventListener('resize', handleResize) return () => { window.removeEventListener('resize', handleResize) cancelAnimationFrame(animFrameId) controls.dispose() renderer.dispose() container.removeChild(renderer.domElement) } }, [modelUrl]) return ( <div ref={mountRef} style={{ width: '100%', height: '500px' }} className="rounded-xl overflow-hidden cursor-grab active:cursor-grabbing" /> ) } Конфигуратор цвета материала
function changeModelColor(scene: THREE.Scene, meshName: string, color: string) { scene.traverse((object) => { if (object instanceof THREE.Mesh && object.name === meshName) { const material = object.material as THREE.MeshStandardMaterial material.color.set(color) } }) } // Использование в UI <div className="flex gap-2"> {['#ef4444', '#3b82f6', '#22c55e', '#f59e0b', '#8b5cf6'].map((color) => ( <button key={color} onClick={() => changeModelColor(sceneRef.current!, 'Body', color)} style={{ background: color }} className="w-8 h-8 rounded-full border-2 border-white shadow" /> ))} </div> Babylon.js: альтернатива для сложных сцен
npm install @babylonjs/core @babylonjs/loaders import { Engine, Scene, ArcRotateCamera, HemisphericLight, Vector3 } from '@babylonjs/core' import { SceneLoader } from '@babylonjs/core/Loading/sceneLoader' import '@babylonjs/loaders/glTF' function BabylonViewer({ modelUrl }: { modelUrl: string }) { const canvasRef = useRef<HTMLCanvasElement>(null) useEffect(() => { const engine = new Engine(canvasRef.current!, true, { preserveDrawingBuffer: true, stencil: true, }) const scene = new Scene(engine) const camera = new ArcRotateCamera('camera', -Math.PI / 2, Math.PI / 4, 5, Vector3.Zero(), scene) camera.attachControl(canvasRef.current!, true) camera.lowerRadiusLimit = 1 camera.upperRadiusLimit = 20 new HemisphericLight('light', new Vector3(0, 1, 0), scene) SceneLoader.ImportMeshAsync('', '', modelUrl, scene).then(({ meshes }) => { // Автоцентрирование camera.setTarget(meshes[0]) }) engine.runRenderLoop(() => scene.render()) const handleResize = () => engine.resize() window.addEventListener('resize', handleResize) return () => { window.removeEventListener('resize', handleResize) engine.dispose() } }, [modelUrl]) return <canvas ref={canvasRef} style={{ width: '100%', height: '500px' }} /> } Почему важна оптимизация производительности?
Пользователи не готовы ждать загрузку дольше 3 секунд. Вот ключевые методы:
- Draco-компрессия GLTF снижает размер геометрии на 60–90%. Это уменьшает время загрузки в 3-5 раз по сравнению с несжатым файлом.
-
renderer.setPixelRatio(Math.min(devicePixelRatio, 2))— не рендерим выше 2x, что экономит энергопотребление. - LOD (Level of Detail) — более детальная модель вблизи, упрощённая вдали. На мобильных LOD даёт прирост FPS до 50%.
- Instanced Mesh для множества одинаковых объектов (деревья, кресла).
- Отключаем
autoRotateво время взаимодействия пользователя.
| Метод | Эффект | Сложность реализации |
|---|---|---|
| Draco-компрессия | Снижение размера на 60–90% | Средняя (нужен декодер) |
| LOD | Ускорение рендеринга до 50% | Высокая (создание уровней) |
| Ограничение Pixel Ratio | Снижение нагрузки на GPU | Низкая (одна строка) |
Что такое Draco-компрессия?
Draco — библиотека от Google для сжатия 3D-геометрии. Она кодирует позиции вершин, нормали, текстурные координаты в компактный формат. На клиенте декодер восстанавливает оригинальные данные. Для GLTF достаточно подключить DRACOLoader и указать путь к декодеру. Сжатие без потерь, но для анимаций не применяется.
Что входит в работу?
- Анализ модели — проверка геометрии, текстур, анимаций. Конвертация в GLTF с Draco.
- Настройка сцены — освещение (окружение, направленный свет), тени, камера.
- Интеграция — React-компонент с загрузчиком, контролами, адаптивностью.
- Конфигуратор (опционально) — выбор цвета/материала, переключение моделей.
- Оптимизация — тесты на мобильных устройствах, настройка LOD.
- Документация — описание API компонента, инструкция по замене модели.
Сроки ориентировочно
- Простой просмотрщик с загрузкой GLTF и orbit controls — 2–3 дня.
- Конфигуратор с выбором цвета/материала и несколькими моделями — 5–7 дней.
Стоимость рассчитывается индивидуально в зависимости от сложности сцены и количества интерактивных элементов. Стоимость базового просмотрщика начинается от $1000, сложные конфигураторы — от $3000. Экономия на трафике может составлять до 30%. При больших объёмах возможна экономия бюджета — обсудим на консультации.
Как мы гарантируем качество?
Мы тестируем просмотрщик на трёх последних версиях Chrome, Firefox, Safari и Edge. Для мобильных устройств проверяем iPhone (Safari) и Android (Chrome). Предоставляем гарантию на работоспособность в течение 30 дней после сдачи. Наш опыт — более 5 лет в WebGL-разработке, более 30 успешных проектов.
Свяжитесь с нами для оценки вашего проекта. Закажите консультацию — обсудим требования и подготовим коммерческое предложение под ключ.







