项目中直接使用的是three.js直接加载gltf模型文件。
gtlf文件被誉为3d界的“jpeg”,很形象
个人接触的gltf文件不是很多,所以这里举一个项目中用的列子,图片奉上
scene.bin文件为3d模型导出的二进制文件,最最主要的文件之一(哈哈)
我在写模型加载的时候,是引用的外部配置文件,这样的话,就能使组件能够复用,写一个组件,更改不同的配置文件,加载不同的模型
后面调用组件时传递给组件的参数
{
// 传入给3d模型渲染组件的服务器路径
"model": "/web_app/three3d/models/019/scene.gltf",
"textures": "",
"envMap": "",
"texturesOn": true,
"enableKeys": false,
"autoRotate": false
}
组件编写
render函数长这样,ref的作用是获取到标签的真实dom(哈哈,个人理解)
render() {
let style = { width: '100%', height: '100%' };
return (
{ this.mount = mount; }}
/>
);
}
定义一些需要用到的全局变量
let container,
mixer,
controls;
let camera,
scene,
renderer,
light;
// 定义three中的动画时间
let clock = new THREE.Clock();
let threeConf = this.state.threeConf;
container = document.createElement('div');
this.mount.appendChild(container);
container = this.mount;
let width = this.mount.clientWidth;
let height = this.mount.clientHeight;
camera = new THREE.PerspectiveCamera(60, width / height, 1, 1000);
container.addEventListener('resize', () => {
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
}, false);
定义textures环境图片,有这个的话模型就会有反射环境,图片是从服务器拿回来的。
// 环境设置,设置之后可以反射周围环境
let envMap = new THREE.CubeTextureLoader().load([
'/images/sys/three3d/envmappic/posx.jpg',
'/images/sys/three3d/envmappic/negx.jpg',
'/images/sys/three3d/envmappic/posy.jpg',
'/images/sys/three3d/envmappic/negy.jpg',
'/images/sys/three3d/envmappic/posz.jpg',
'/images/sys/three3d/envmappic/negz.jpg'
]);
scene = new THREE.Scene();
// 添加光线
light = new THREE.HemisphereLight(0xbbbbff, 0x444422);
light.position.set(0, 1, 0);
scene.add(light);
关于gltf的模型加载器不用自己写loader加载。scene.gltf文件就是我们需要loader的文件
// 新建loader,加载gltf文件
let loader = new THREE.GLTFLoader();
loader.load(threeConf.model, (gltf) => {
const gltfScene = gltf.scene || gltf.scenes[0];
const clips = gltf.animations || [];
gltfScene.updateMatrixWorld();
const box = new THREE.Box3().setFromObject(gltfScene);
const size = box.getSize(new THREE.Vector3()).length();
const center = box.getCenter(new THREE.Vector3());
// controls.reset();
gltfScene.position.x += gltfScene.position.x - center.x;
gltfScene.position.y += gltfScene.position.y - center.y;
gltfScene.position.z += gltfScene.position.z - center.z;
// 重新设置相机参数
controls.maxDistance = size * 10;
camera.near = size / 100;
camera.far = size * 100;
camera.updateProjectionMatrix();
camera.position.copy(center);
camera.position.x += size / 2.0;
camera.position.y += size / 2.0;
camera.position.z += size / 2.0;
camera.lookAt(center);
gltf.scene.traverse((child) => {
if (child.isMesh) {
child.material.envMap = envMap;
}
});
scene.add(gltf.scene);
// 判断当前的gltf问价中是否有动画
if (clips.length !== 0) {
mixer = new THREE.AnimationMixer(gltfScene);
mixer.clipAction(gltf.animations[0]).play();
} else {
}
});
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setPixelRatio(container.devicePixelRatio);
renderer.setSize(width, height);
renderer.gammaOutput = true;
控制器和动画的加载
controls = new THREE.OrbitControls(camera, container);
//按键控制
controls.enableKeys = false;
// 自动旋转(默认flase)
controls.autoRotate = false;
container.appendChild(renderer.domElement);
container.addEventListener('resize', this.onWindowResize, false);
requestAnimationFrame(function fn() {
if (mixer) {
let delta = clock.getDelta();
mixer.update(delta);
}
requestAnimationFrame(fn);
renderer.render(scene, camera);
controls.update();
});
控制器中用到的监听方法
onWindowResize = () => {
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
}
最后附上完整代码
import React from 'react';
const THREE = window.THREE = require('three');
require('three/examples/js/loaders/GLTFLoader');
require('three/examples/js/loaders/DRACOLoader');
require('three/examples/js/loaders/DDSLoader');
require('three/examples/js/controls/OrbitControls');
require('three/examples/js/controls/TrackballControls');
require('three/examples/js/loaders/RGBELoader');
require('three/examples/js/loaders/HDRCubeTextureLoader');
require('three/examples/js/pmrem/PMREMGenerator');
require('three/examples/js/pmrem/PMREMCubeUVPacker');
THREE.DRACOLoader.setDecoderPath('lib/draco/');
class Three3D extends React.Component {
state = ({ threeConf: this.props.threeConf });
componentDidMount() {
let container,
mixer,
controls;
let camera,
scene,
renderer,
light;
// 定义three中的动画时间
let clock = new THREE.Clock();
let threeConf = this.state.threeConf;
container = document.createElement('div');
this.mount.appendChild(container);
container = this.mount;
let width = this.mount.clientWidth;
let height = this.mount.clientHeight;
camera = new THREE.PerspectiveCamera(60, width / height, 1, 1000);
container.addEventListener('resize', () => {
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
}, false);
// 环境设置,设置之后可以反射周围环境
let envMap = new THREE.CubeTextureLoader().load([
'/images/sys/three3d/envmappic/posx.jpg',
'/images/sys/three3d/envmappic/negx.jpg',
'/images/sys/three3d/envmappic/posy.jpg',
'/images/sys/three3d/envmappic/negy.jpg',
'/images/sys/three3d/envmappic/posz.jpg',
'/images/sys/three3d/envmappic/negz.jpg'
]);
scene = new THREE.Scene();
// 添加光线
light = new THREE.HemisphereLight(0xbbbbff, 0x444422);
light.position.set(0, 1, 0);
scene.add(light);
// 新建loader,加载gltf文件
let loader = new THREE.GLTFLoader();
loader.load(threeConf.model, (gltf) => {
const gltfScene = gltf.scene || gltf.scenes[0];
const clips = gltf.animations || [];
gltfScene.updateMatrixWorld();
const box = new THREE.Box3().setFromObject(gltfScene);
const size = box.getSize(new THREE.Vector3()).length();
const center = box.getCenter(new THREE.Vector3());
// controls.reset();
gltfScene.position.x += gltfScene.position.x - center.x;
gltfScene.position.y += gltfScene.position.y - center.y;
gltfScene.position.z += gltfScene.position.z - center.z;
// 重新设置相机参数
controls.maxDistance = size * 10;
camera.near = size / 100;
camera.far = size * 100;
camera.updateProjectionMatrix();
camera.position.copy(center);
camera.position.x += size / 2.0;
camera.position.y += size / 2.0;
camera.position.z += size / 2.0;
camera.lookAt(center);
gltf.scene.traverse((child) => {
if (child.isMesh) {
child.material.envMap = envMap;
}
});
scene.add(gltf.scene);
// 判断当前的gltf问价中是否有动画
if (clips.length !== 0) {
mixer = new THREE.AnimationMixer(gltfScene);
mixer.clipAction(gltf.animations[0]).play();
} else {
}
});
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setPixelRatio(container.devicePixelRatio);
renderer.setSize(width, height);
renderer.gammaOutput = true;
controls = new THREE.OrbitControls(camera, container);
//按键控制
controls.enableKeys = false;
// 自动旋转(默认flase)
controls.autoRotate = false;
container.appendChild(renderer.domElement);
container.addEventListener('resize', this.onWindowResize, false);
requestAnimationFrame(function fn() {
if (mixer) {
let delta = clock.getDelta();
mixer.update(delta);
}
requestAnimationFrame(fn);
renderer.render(scene, camera);
controls.update();
});
}
onWindowResize = () => {
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
}
render() {
let style = { width: '100%', height: '100%' };
return (
{ this.mount = mount; }}
/>
);
}
}
export default Three3D;
调用