Реалізація 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 КБ), величезна спільнота, тисячі прикладів. Вимагає більше ручної роботи для складних сцен (фізика, виявлення колізій).

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(`Завантаження: ${progress}%`)
      },
      (error) => console.error('Помилка завантаження моделі:', 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()

    // Зміна розміру
    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 днів.