-
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)了解一下以下幾種主要格式
- 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
- 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
- GLTF-DRACO
- like the gltf default format, but the buffer data is compressed using the Draco algorithm
- much lighter
- 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()
-
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);
}
)
-
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
-
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])
}
)
-
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
-
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])
}
)
-
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
/**
* 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()