第一次搞three.js,网上找的加载obj的方法都试了一下,发现总是报错,不报错就是模型看不到,后来老老实实去官网读文档,原来是大多文章没有提及模型太大或者偏移的情况,建模的师傅给的模型中心位置可能和原点偏移过大,就像相对定位和绝对定位,相机看着相对的位置,模型其实“远在天边”,因此便有了这篇文章,以供参考。
<template>
<div class="app-container">
<el-row :gutter="20" align="middle">
<div style="margin-bottom:10px">
<el-button @click="cameraResert()">复位</el-button>
</div>
<div id="objContaier" ref="objContaier" class="objContaier"></div>
</el-row>
</div>
</template>
<style scoped>
.objContaier{width: 100%;height: calc(100vh - 200px);background-color: black;}
</style>
<script>
import * as THREE from 'three';
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js';
import {OBJLoader} from 'three/examples/jsm/loaders/OBJLoader';
import {MTLLoader} from 'three/examples/jsm/loaders/MTLLoader'
export default {
data() {
return {
scene:undefined,
light:undefined,
camera:undefined,
controls:undefined,
renderer:undefined,
dragControls:undefined,
object:[],
conWidth:undefined,
conHeight:undefined,
}
},
mounted() {
this.conWidth = this.$refs.objContaier.offsetWidth
this.conHeight = this.$refs.objContaier.offsetHeight
this.init()
this.animate()
},
methods:{
init() {
// 创建场景
this.scene = new THREE.Scene()
this.scene.background = new THREE.Color('#202124')
// 渲染
const container = document.getElementById('objContaier')
this.renderer = new THREE.WebGLRenderer({antialias: true, alpha: true})
this.renderer.outputEncoding = THREE.sRGBEncoding;
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(this.conWidth, this.conHeight);
container.appendChild(this.renderer.domElement)
// 相机
this.camera = new THREE.PerspectiveCamera(45, this.conWidth / this.conHeight, 0.1, 1000)
this.camera.position.set(0, 10, 20)
this.camera.lookAt(new THREE.Vector3(0, 0, 0))
// 轨道控制
this.controls = new OrbitControls(this.camera, this.renderer.domElement)
this.controls.target.set(0, 5, 0);
this.controls.update();
// 光源
const skyColor = 0xFFFFFF;
const groundColor = 0xB97A20;
const intensity = 1;
const light = new THREE.HemisphereLight(skyColor, groundColor, intensity);
this.scene.add(light);
// 直射光
const dcolor = 0xFFFFFF;
const dintensity = 1;
const dlight = new THREE.DirectionalLight(dcolor, dintensity);
dlight.position.set(0, 10, 0);
dlight.target.position.set(-5, 0, 0);
this.scene.add(dlight);
this.scene.add(dlight.target);
// 环境光
const ambient = new THREE.AmbientLight(0x404040, 3)
this.scene.add(ambient)
// 材质模型加载器,模型在服务器上
const mtlLoader = new MTLLoader()
const objLoader = new OBJLoader();
mtlLoader.load('/模型链接.mtl', (mtl) => {
mtl.preload()
for (const material of Object.values(mtl.materials)) {
material.side = THREE.DoubleSide;
}
objLoader.setMaterials(mtl)
objLoader.load('/模型链接.obj', (root) => {
this.root = root
const box = new THREE.Box3().setFromObject(root);
const boxSize = box.getSize(new THREE.Vector3()).length();
const boxCenter = box.getCenter(new THREE.Vector3());
this.objBoxSize = boxSize
this.objBoxCenter = boxCenter
this.cameraResert()
// 辅助坐标线
// var axesHelper = new THREE.AxesHelper(15);
// this.scene.add(axesHelper);
});
})
},
// 刷新渲染
animate() {
this.renderer.render(this.scene, this.camera);
this.requestId = requestAnimationFrame(this.animate);
},
// 相机位置调整
frameArea(sizeToFitOnScreen, boxSize, boxCenter) {
const halfSizeToFitOnScreen = sizeToFitOnScreen * 0.5;
const halfFovY = THREE.MathUtils.degToRad(this.camera.fov * .5);
const distance = halfSizeToFitOnScreen / Math.tan(halfFovY);
const direction = (new THREE.Vector3())
.subVectors(this.camera.position, boxCenter)
.multiply(new THREE.Vector3(1, 0, 1))
.normalize();
this.camera.position.copy(direction.multiplyScalar(distance).add(boxCenter));
this.camera.near = boxSize / 100;
this.camera.far = boxSize * 100;
this.camera.updateProjectionMatrix();
this.camera.lookAt(boxCenter.x, boxCenter.y, boxCenter.z);
},
// 相机位置重置
cameraResert(){
if(this.objBoxCenter){
this.frameArea(this.objBoxSize * 1.2, this.objBoxSize, this.objBoxCenter)
this.controls.maxDistance = this.objBoxSize * 10;
this.controls.target.copy(this.objBoxCenter);
this.controls.update();
this.scene.add(this.root);
this.renderer.render(this.scene, this.camera);
}
}
}
}
</script>