בניית מציגי תלת-ממד אינטראקטיביים עם Three.js ו-Babylon.js

מבקרים עוזבים אם מודל תלת־ממדי נטען לאט או נתקע במכשירים חלשים. אנחנו משלבים צופי תלת־ממד מהירים וחלקים על בסיס Three.js או Babylon.js באתר שלכם, תוך אופטימיזציה של המודלים והתאמה למובייל. הצוות שלנו מספק פרויקטים סוהריים—מבחירת הספרייה ועד תמיכה שוטפת—ומבטיח ביצועים אמינים ללא פשרות על לוחות זמנים.

פיתוח ותחזוקה של כל סוגי האתרים:

אתרי מידע או יישומי אינטרנט
אתרי תדמית, דפי נחיתה, אתרי חברה, קטלוגים מקוונים, חידונים, אתרי קידום, בלוגים, מקורות חדשות, פורטלי מידע, פורומים, אגרגטורים
אתרי מסחר אלקטרוני או יישומי אינטרנט
חנויות מקוונות, פורטלי B2B, שווקים, בורסות מקוונות, אתרי קאשבק, בורסות, פלטפורמות דרופשיפינג, מנתחי מוצרים
יישומי אינטרנט לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, פורטלים ארגוניים, מערכות ניהול ייצור, מנתחי מידע
אתרי שירות אלקטרוני או יישומי אינטרנט
פלטפורמות מודעות, בתי ספר מקוונים, בתי קולנוע מקוונים, בוני אתרים, פורטלים לשירותים אלקטרוניים, פלטפורמות אירוח וידאו, פורטלים נושאיים

אלה רק חלק מהסוגים הטכניים של אתרים שאנו עובדים איתם, ולכל אחד מהם יכולים להיות מאפיינים ופונקציונליות ספציפיים משלו, וכן ניתן להתאים אותם לצרכים ולמטרות הספציפיים של הלקוח.

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
בניית מציגי תלת-ממד אינטראקטיביים עם Three.js ו-Babylon.js
מורכב
~1-2 שבועות

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אתר חברה B2B ADVANCE
    פיתוח אתר חברה B2B ADVANCE
    1502
  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1344
  • פיתוח אתר עבור BELFINGROUP
    פיתוח אתר עבור BELFINGROUP
    1052
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1307
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1050
  • פיתוח אתר לחברת FIXPER
    פיתוח אתר לחברת FIXPER
    1033

תרחיש אופייני ובעיות שנפתרות

לקוח מספק מודל GLB של ספה במשקל 50 מגה-בייט, והאתר באירוח ישן מתקשה לטעון אותו — מבקרים עוזבים אחרי 5 שניות. הפתרון: שילוב מציג תלת-ממד באמצעות Three.js או Babylon.js עם אופטימיזציה: דחיסת Draco, LOD, תאורה נכונה. זהו פתרון מלא לאתר עם מציג תלת-ממד. יישמנו פרויקטים כאלה לעשרות לקוחות: מקונפיגורטורים לפנים ועד ויטרינות תכשיטים. עם ניסיון של 5+ שנים בפיתוח WebGL ו-30+ פרויקטים מוצלחים, אנו מספקים מציגי תלת-ממד אמינים. הניסיון שלנו עזר ללקוחות להפחית את זמני הטעינה ל-2 שניות ולהגדיל את ההמרה ב-15–20%. לדוגמה, לקוח אחד חסך $1,200 בשנה על ידי הפחתת רוחב פס עם דחיסת Draco. Three.js נטען פי 3 מהר יותר מ-Babylon.js לסצנות פשוטות אך חסר פיזיקה מתקדמת ו-XR. חיסכון בעלויות אירוח מדחיסת Draco יכול להיות משמעותי לפרויקטים גדולים. עלות יישום: מ-$1,500 למציג בסיסי; עלות פרויקט ממוצעת היא $2,500.

אתגרים טכניים נפוצים כוללים:

  • קבצי מודל גדולים: מודלי GLTF של רהיטים או מכוניות יכולים לשקול עשרות מגה-בייט. ללא דחיסת Draco ו-LOD, משתמשים ממתינים עד 30 שניות לטעינה, מה שפוגע בהמרה. בפרויקט אחד, LOD הגדיל את ה-FPS מ-20 ל-55 במכשירים ניידים.
  • תאימות דפדפנים: WebGL יציב בכרום, פיירפוקס, אדג', אך גרסאות ישנות יותר של ספארי עשויות להציג תקלות. יש צורך בזיהוי נכון של WebGLRenderingContext והודעות חלופיות.
  • ביצועים במכשירים ניידים: עיבוד סצנות עם Pixel Ratio גבוה מרוקן את הסוללה במהירות. הגבלת devicePixelRatio ל-2× והשבתת צללים על GPUs חלשים הם צעדים חיוניים.

Three.js או Babylon.js: באיזה מהם לבחור?

Three.js היא ספריית עיבוד מינימליסטית (~600 ק"ב), קהילה ענקית, אלפי דוגמאות. דורשת יותר עבודה ידנית לסצנות מורכבות (פיזיקה, זיהוי התנגשויות).

Babylon.js היא מנוע משחק מלא בדפדפן (~2 מגה-בייט). כולל פיזיקה מובנית, חומרי PBR, Inspector, GUI, תמיכת XR. מצוינת לסצנות אינטראקטיביות מורכבות.

לצפייה במודלי תלת-ממד, השתמשו ב-Three.js. לקונפיגורטורים וסצנות אינטראקטיביים, השתמשו ב-Babylon.js. ב-80% מהפרויקטים שלנו, אנו משתמשים ב-Three.js בשל קלות האינטגרציה והגודל הקטן יותר. שניהם מנועי תלת-ממד חזקים לדפדפן ליישומי אינטרנט.

קריטריון Three.js Babylon.js
גודל ספרייה ~600 ק"ב ~2 מגה-בייט
פיזיקה אין מובנית Cannon.js מובנית
חומרי PBR דורש הגדרה פתרונות מוכנים
תמיכת XR דרך WebXR Inspector מובנה

Three.js: מציג מודל GLTF עם Draco

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

כיצד לייעל טעינת מודל תלת-ממד עם דחיסה ו-LOD?

משתמשים לא יחכו יותר מ-3 שניות לטעינה. שיטות מפתח:

  • דחיסת Draco מפחיתה את גודל הגיאומטריה ב-60–90%. זה מקצר את זמן הטעינה פי 3–5 לעומת קבצים לא דחוסים. לדוגמה, מודל של 50 מגה-בייט יכול להפוך ל-15 מגה-בייט, מה שמקצר את זמן הטעינה מ-20 שניות ל-4 שניות.
  • npm install three @types/three — לעולם אל תעבדו מעל 2x, חוסך בסוללה.
  • LOD (רמת פירוט) — מודל מפורט מקרוב, מפושט מרחוק. במכשירים ניידים, LOD משפר את ה-FPS בעד 50%.
  • Instanced Mesh עבור אובייקטים זהים רבים (עצים, כיסאות).
  • השבתת 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" /> ) } במהלך אינטראקציה של משתמש.
שיטה השפעה מורכבות יישום
דחיסת Draco הפחתת גודל ב-60–90% בינונית (נדרש מפענח)
LOD האצת עיבוד עד 50% גבוהה (יצירת רמות)
הגבלת Pixel Ratio הפחתת עומס GPU נמוכה (שורה אחת)

Draco היא ספריית גוגל לדחיסת גיאומטריה תלת-ממדית. היא מקודדת מיקומי קודקודים, נורמלים, קואורדינטות טקסטורה לפורמט קומפקטי. בצד הלקוח, המפענח משחזר את הנתונים המקוריים. עבור GLTF, פשוט חברו DRACOLoader וציין את נתיב המפענח. דחיסה ללא אובדן; לא בשימוש לאנימציות.

מה כלול בעבודה (תוצרים)

  1. ניתוח מודל — בדיקת גיאומטריה, טקסטורות, אנימציות. המרה ל-GLTF עם Draco.
  2. הגדרת סצנה — תאורה (סביבה, אור כיווני), צללים, מצלמה.
  3. אינטגרציה — רכיב React עם טוען, בקרות, רספונסיביות.
  4. קונפיגורטור (אופציונלי) — בחירת צבע/חומר, החלפת מודלים.
  5. אופטימיזציה — בדיקת מכשירים ניידים, הגדרת LOD.
  6. תיעוד — תיאור API של הרכיב, הוראות להחלפת מודלים.
  7. הדרכה — מפגש אחד לצוות שלך על איך להשתמש ולתחזק את המציג.
  8. גישה לקוד מקור — מאגר מלא עם הערות והוראות בנייה.
  9. תמיכה — אחריות ל-30 יום וחוזה תחזוקה אופציונלי.

לוחות זמנים ועלות משוערים

  • מציג פשוט עם טעינת GLTF ובקרות סיבוב: 2–3 ימים, עלות מ-$1,500.
  • קונפיגורטור עם בחירת צבע/חומר ומודלים מרובים: 5–7 ימים, עלות מ-$4,000.
  • העלות נקבעת לאחר ניתוח דרישות ספציפיות; פרויקט טיפוסי נע בין $1,500–$5,000. לדוגמה, מציג בסיסי עולה $1,500 וניתן לספק אותו ביומיים.

אבטחת איכות

אנו בודקים את המציג על שלוש הגרסאות העדכניות ביותר של כרום, פיירפוקס, ספארי ואדג'. למכשירים ניידים, אנו מוודאים באייפון (ספארי) ואנדרואיד (כרום). אנו מספקים אחריות ל-30 יום לאחר המסירה. הניסיון שלנו: 5+ שנים בפיתוח WebGL, 30+ פרויקטים מוצלחים הכוללים מעל 50 מודלי תלת-ממד שונים. הגישה שלנו להטמעת מודלי תלת-ממד מבטיחה טעינה מהירה ואינטראקציה חלקה.

צרו קשר כדי להעריך את הפרויקט שלכם. קבעו פגישת ייעוץ — נדון בדרישות ונכין הצעה מסחרית מותאמת.