Реализация 3D-просмотрщика (Three.js/Babylon.js) на сайте

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

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Реализация 3D-просмотрщика (Three.js/Babylon.js) на сайте
Сложная
~1-2 недели
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Реализация 3D-просмотрщика (Three.js/Babylon.js) на сайте

3D-просмотрщик в браузере нужен для конфигураторов продуктов (мебель, автомобили, ювелирные украшения), архитектурных порталов, образовательных платформ с 3D-моделями, игровых витрин. WebGL рендеринг через Three.js или Babylon.js — стандартный подход, работающий без плагинов во всех современных браузерах.

Three.js vs Babylon.js

Three.js — минималистичная библиотека рендеринга (~600 КБ), огромное сообщество, тысячи примеров. Требует больше ручной работы для сложных сцен (физика, collision detection).

Babylon.js — полноценный игровой движок в браузере (~2 МБ). Встроенная физика, PBR-материалы, Inspector, GUI, XR-поддержка. Хорош для сложных интерактивных сцен.

Для просмотра 3D-моделей — Three.js. Для интерактивных конфигураторов и сцен — Babylon.js.

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' }} />
}

Оптимизация производительности

  • Draco-компрессия GLTF снижает размер геометрии на 60–90%
  • renderer.setPixelRatio(Math.min(devicePixelRatio, 2)) — не рендерим выше 2x
  • LOD (Level of Detail) — более детальная модель вблизи, упрощённая вдали
  • Instanced Mesh для множества одинаковых объектов (деревья, кресла)
  • Отключаем autoRotate во время взаимодействия пользователя

Срок: просмотрщик с загрузкой GLTF и orbit controls — 2–3 дня. Конфигуратор с выбором цвета/материала и несколькими моделями — 5–7 дней.