three.js - Import Models

  • Formats - many 3D models formats, each one responding a problem
  • Popular 3D Models Formats
    • OBJ 簡(jiǎn)單课舍、高效
    • FBX
    • STL
    • PLY
    • COLLADA
    • 3DS
    • GLTF 目前使用最多的
  • GLTF
    • a format is becoming a standard and should cover most of our needs
    • it's for GL transmission format
    • it supports different sets of data like geometries, materials, cameras, lights, scene graph, animations, etc.
    • you can export various formats like json, binary, embed texture, etc.
    • most 3D softwares, game engines and libraries support it
    • the gltf team provides various models glTF-Sample-Models
  • GLTF Formats - a GLTF file can have different formats
    • 我們可以參考這個(gè) Duck Model鹅颊,對(duì)應(yīng)了解一下以下幾種主要格式
    1. GLTF
    • the default format
    • .gltf - is a JSON that contains cameras, lights, scenes, materials, objects transformations, but no geometries nor textures
    • .bin - is a binary that usually contains the data like geometries(vertices position, UV coordinates, colors, etc.)
    • .png - is the texture
    • we load the .gltf file and the other files will be loaded automatically
    1. GLTF-BINARY
    • only one file
    • contains the all the data we talk about
    • binary
    • usually lighter
    • easier to load because only one file
    • hard to alter its data
    1. GLTF-DRACO
    • like the gltf default format, but the buffer data is compressed using the Draco algorithm
    • much lighter
    1. GLTF-EMBEDDED
    • one file can read
    • JSON
    • heavier
  • Set up 場(chǎng)景配置(plane + lights),axesHelper非必選
    • 當(dāng)我們導(dǎo)入一個(gè)gltf文件時(shí)就漾,gltf會(huì)有真實(shí)的model郭毕,這些model通常會(huì)是 PBR material,PBR material 將會(huì)在three.js中創(chuàng)建 MeshStandardMaterial,所以我們需要 lights
  import * as THREE from 'three'
  import {OrbitControls} from 'three/addons/controls/OrbitControls.js'

  /**
   * scene
  */
  const scene = new THREE.Scene()

  /**
   * floor
  */
  const floor = new THREE.Mesh(
    new THREE.PlaneGeometry(10, 10),
    new THREE.MeshStandardMaterial({
      color: '#444444',
      metalness: 0.4,
      roughness: 0.5
    })
  )
  floor.receiveShadow = true
  floor.rotation.x = - Math.PI * 0.5
  scene.add(floor)

  /**
   * light
  */
  const ambientLight = new THREE.AmbientLight(0xffffff, 0.8)
  scene.add(ambientLight)

  const directionalLight = new THREE.DirectionalLight(0xffffff, 0.6)
  directionalLight.castShadow = true
  directionalLight.shadow.mapSize.set(1024, 1024)
  directionalLight.shadow.camera.far = 15
  directionalLight.shadow.camera.left = - 7
  directionalLight.shadow.camera.top = 7
  directionalLight.shadow.camera.right = 7
  directionalLight.shadow.camera.bottom = - 7
  directionalLight.position.set(- 5, 5, 0)
  scene.add(directionalLight)

  /**
   * camera
  */
  const camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    100
  )
  camera.position.set(2, 2, 2)

  /**
   * renderer
  */
  const renderer = new THREE.WebGLRenderer()
  renderer.shadowMap.enabled = true
  renderer.shadowMap.type = THREE.PCFSoftShadowMap
  renderer.setSize(window.innerWidth, window.innerHeight)
  document.body.appendChild(renderer.domElement)

  window.addEventListener('resize', () => {
    camera.aspect = window.innerWidth / window.innerHeight
    camera.updateProjectionMatrix()

    renderer.setSize(window.innerWidth, window.innerHeight) 
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))   
  })

  /**
   * axesHelper
  */
  const axesHelper = new THREE.AxesHelper(5)
  scene.add(axesHelper)

  /**
   * control
  */
  const controls = new OrbitControls(camera, renderer.domElement)
  controls.enableDamping = true

  /**
   * render
  */
  const clock = new THREE.Clock()
  const tick = () => {
    let elapsedTime = clock.getElapsedTime()

    controls.update()
    requestAnimationFrame(tick)
    renderer.render(scene, camera)
  }
  tick()
set up.png
  • Load the model in three.js, use the GLTFLoader
    • 這里用的是一個(gè) Duck Model 作為例子展示淹仑,內(nèi)置參數(shù)的結(jié)構(gòu)可參考下圖
    • the Mesh should be our duck
    • we don't need the PerspectiveCamera
    • the camera and the duck are in an Object3D
    • the Object3D has a scale set to small value
  import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

  /**
   * scene
  */
  ...

  /**
   * model
  */
  const gltfLoder = new GLTFLoader()
  gltfLoder.load(
    '../public/models/Duck/glTF/Duck.gltf',
    (gltf) => {
      // success
      console.log(gltf);
    }
  )
gltf 結(jié)構(gòu).png
  • There are many ways can add the duck to our scene
    • add the whole scene in our scene
    • add the children of the scene to our scene and ignore the PerspectiveCamera
    • filter the children before adding to the scene
    • add only the Mesh and end up with a duck with a wrong scale, position and rotation
    • open the file at a 3D software clean it and export again
  • Add the Object3D to our scene and ignore the unused PerspectiveCamera
    1. mesh 結(jié)構(gòu)較為單一丙挽,例如上面的 Duck Model
    // duck
    gltfLoader.load(
      '../public/models/Duck/glTF/Duck.gltf',
      (gltf) => {
        // console.log(gltf.scene);
    
        scene.add(gltf.scene.children[0])
      }
    )
    
    Duck model.png
    1. mesh 結(jié)構(gòu)復(fù)雜的,也就是有多個(gè)mesh組成的 FlightHelmet Model
    // flightHelmet
    gltfLoader.load(
      '../public/models/FlightHelmet/glTF/FlightHelmet.gltf',
      (gltf) => {
        // console.log(gltf.scene);
    
        // 不可用匀借,取值過(guò)程會(huì)移動(dòng)原數(shù)組數(shù)據(jù)颜阐,導(dǎo)致循環(huán)混亂
        // for (const child of gltf.scene.children) {  
        //   scene.add(child)
        // }
    
        // 可用
        // while(gltf.scene.children.length) {
        //   scene.add(gltf.scene.children[0])
        // }
    
        const children = [...gltf.scene.children]
        for (const child of children) {
          scene.add(child)
        }
      }
    )
    
    FlightHelmet結(jié)構(gòu).png

    FlightHelmet model.png
  • Draco Compression
    • the draco verion can be lighter than the default version(gltf)
    • compression is applied to the buffer data(typically the geometry)
    • draco is not exclusive to gltf but they got the popular at the same time and implementation went faster with gltf exports
    • Take the /draco/ folder and copy it to your statistic floder (node_modules/three/examples/jsm/libs/draco)
    • when we use the DRACOLoader, the geometries are lighter but has to load the DRACOLoader and the decoder(上一步,我們需要移動(dòng)至public文件夾的部分)
  import {DRACOLoader} from 'three/addons/loaders/DRACOLoader.js'

  /**
   * model
   * **/
  const dracoLoader = new DRACOLoader()  // 必須寫(xiě)在實(shí)例化gltfLoader之前
  dracoLoader.setDecoderPath('/public/draco/')  // 根據(jù)自己項(xiàng)目的結(jié)構(gòu) provide the path to our dracoLoader

  const gltfLoader = new GLTFLoader()
  gltfLoader.setDRACOLoader(dracoLoader)  // provide the dracoLoader instance to gltfLoader

  // duck
  gltfLoader.load(
    '../public/models/Duck/glTF-Draco/Duck.gltf',
    (gltf) => {  
      scene.add(gltf.scene.children[0])
    }
  )
Draco loader.png
  • Animations - gltf can load animation Fox Model
    • to load a gltf object contains a animations property composed of multiple AnimationClip - 可以理解為關(guān)鍵幀的合集
    • we need to create an AnimationMixer, it's like a player 播放器
    • add on of the AnimationClips to the mixer and then call the play()
    • update the mixer on each frame
      animation.png
  /**
   * model
   */
  ...
  let mixer = null
  // fox
  gltfLoader.load(
    '../public/models/Fox/glTF/Fox.gltf',
    (gltf) => {
      // console.log(gltf);

      gltf.scene.scale.set(0.025, 0.025, 0.025)
      scene.add(gltf.scene)

      // animation
      mixer = new THREE.AnimationMixer(gltf.scene)
      const action = mixer.clipAction(gltf.animations[0])
      // console.log(action);
      action.play()
    }
  )
/**
 * render
 */
const clock = new THREE.Clock()
let previousTime = 0
const tick = () => {
  const elapsedTime = clock.getElapsedTime()
  const deltaTime = elapsedTime - previousTime
  previousTime = elapsedTime

  // update mixer
  mixer && mixer.update(deltaTime)

  ...
}
tick()
animation fox model.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末吓肋,一起剝皮案震驚了整個(gè)濱河市凳怨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌是鬼,老刑警劉巖肤舞,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異均蜜,居然都是意外死亡李剖,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門囤耳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)篙顺,“玉大人,你說(shuō)我怎么就攤上這事充择〉旅担” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵椎麦,是天一觀的道長(zhǎng)宰僧。 經(jīng)常有香客問(wèn)我,道長(zhǎng)铃剔,這世上最難降的妖魔是什么撒桨? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮键兜,結(jié)果婚禮上凤类,老公的妹妹穿的比我還像新娘。我一直安慰自己普气,他們只是感情好谜疤,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著现诀,像睡著了一般夷磕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上仔沿,一...
    開(kāi)封第一講書(shū)人閱讀 49,046評(píng)論 1 285
  • 那天坐桩,我揣著相機(jī)與錄音,去河邊找鬼封锉。 笑死绵跷,一個(gè)胖子當(dāng)著我的面吹牛膘螟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播碾局,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼荆残,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了净当?” 一聲冷哼從身側(cè)響起内斯,我...
    開(kāi)封第一講書(shū)人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎像啼,沒(méi)想到半個(gè)月后俘闯,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡忽冻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年备徐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片甚颂。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖秀菱,靈堂內(nèi)的尸體忽然破棺而出振诬,到底是詐尸還是另有隱情,我是刑警寧澤衍菱,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布赶么,位于F島的核電站,受9級(jí)特大地震影響脊串,放射性物質(zhì)發(fā)生泄漏辫呻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一琼锋、第九天 我趴在偏房一處隱蔽的房頂上張望放闺。 院中可真熱鬧,春花似錦缕坎、人聲如沸怖侦。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)匾寝。三九已至,卻和暖如春荷腊,著一層夾襖步出監(jiān)牢的瞬間艳悔,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工女仰, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留猜年,地道東北人抡锈。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像码倦,于是被迫代替她去往敵國(guó)和親企孩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容