ThreeJS导入外部obj和mtl
本文将介绍如何使用ThreeJS导入obj和带贴图mtl的外部obj模型;使用AmbientLight为场景所有物体添加基础光源;使用SpotLight为场景添加聚光灯效果,并为obj添加阴影效果;使用SpotLightHelper,为聚光灯添加光源调试辅助线;使用GUI,添加一些简单的属性操作obj模型;本文代码结构将在之前的文章VUE整合ThreeJS并创建一个带动画的简单场景代码的基础上进行
本文将介绍
- 如何使用ThreeJS导入obj和带贴图mtl的外部obj模型;
- 使用AmbientLight为场景所有物体添加基础光源;
- 使用SpotLight为场景添加聚光灯效果,并为obj添加阴影效果;
- 使用SpotLightHelper,为聚光灯添加光源调试辅助线;
- 使用GUI,添加一些简单的属性操作obj模型;
本文代码结构将在之前的文章 VUE整合ThreeJS并创建一个带动画的简单场景 代码的基础上进行修改。
效果预览
一、导入外部模型OBJ
首先引入所需的包
import { MTLLoader, OBJLoader } from 'three-obj-mtl-loader'
准备好模型文件
obj是模型文件,mtl是贴图文理描述文件,png是mtl中引用的贴图文件;
这里需要注意的是,mtl中引用贴图文件的路径。如果导入后发现,模型是全白色或者全黑色的,那么很有可能是这个地方不对。mtl是可以修改的,因为我们是VUE,为了方便测试,就指定了一个vue能加载到的路径。
主要代码
// 实例化obj loader
this.objLoader = new OBJLoader()
// 实例化mtl loader
this.mtlLoader = new MTLLoader()
const that = this
// 加载mtl
this.mtlLoader.load('objs/jeep/jeepCar.mtl', function(materials) {
materials.preload()
that.objLoader.setMaterials(materials)
// 加载obj
that.objLoader.load('objs/jeep/jeepCar.obj', function(obj) {
// 模型文件太大,缩小一下比例,方便显示
obj.scale.set(0.1, 0.1, 0.1)
// 设置可以投影
obj.children.forEach(item => {
item.castShadow = true
item.receiveShadow = true
})
that.jeepCar = obj
// 添加到场景
that.scene.add(that.jeepCar)
})
})
// 加载不带mtl的obj
const otherObjLoad = new OBJLoader()
otherObjLoad.load('objs/jeep/jeepCar.obj', function(obj) {
obj.scale.set(0.1, 0.1, 0.1)
for (const k in obj.children) {
obj.children[k].castShadow = true
obj.children[k].receiveShadow = true
}
obj.scale.set(0.1, 0.1, 0.1)
obj.position.x = 50
obj.position.z = 30
that.scene.add(obj)
})
// 创建一个几何平面,作为地板,400,400
const planeGeometry = new THREE.PlaneGeometry(400, 400)
// 创建带颜色材质,更换为MeshLambertMaterial材质,去掉网格结构
const planeMaterial = new THREE.MeshLambertMaterial({ color: 'rgb(110,110,110)' })
this.plane = new THREE.Mesh(planeGeometry, planeMaterial)
// 平面开启接收阴影效果
this.plane.receiveShadow = true
// 设置平面角度和位置
this.plane.rotation.x = -0.5 * Math.PI
this.plane.position.x = 0
this.plane.position.y = 0
this.plane.position.z = 0
// 添加平面
this.scene.add(this.plane)
坑
运行代码时,浏览器会报错:THREE.Loader: Handlers.get() has been removed. Use LoadingManager.getHandler() instead;
解决办法需要修改node_module > three-obj-mtl-loader > index.js代码,在第543行;如果你用的是webstorm,可以直接CTRL+G,定位543行;
注释掉
var loader = THREE.Loader.Handlers.get( url );
在第545行添加
var loader = manager.getHandler(url);
本文中使用的three-obj-mtl-loader的版本是1.0.3,不知道以后会不会更新版本来解决这个问题。如果有自动化部署的需求,可以将这个文件本地化临时解决一下。
浏览器警告addAttribute:
原因是BufferGeometry的.addAttribute()方法已经更名为.setAttribute();
要么修改源码、要么等新版本、要么关了浏览器警告。
二、添加光源
AmbientLight:基本光源,该光源的颜色会叠加到场景现有物体的颜色上,一般应用到全局,没有特别的来源方向,不会产生投影,不能作为场景唯一的光源。
SpotLight:聚光灯光源,类似台灯、吊灯、手电筒之类的光源,可以投影。使用SpotLight需要注意设置光的距离,否则即使发光了,因为距离不够而没到物体上,物体也不会产生阴影。
主要代码
// 为场景所有物体添加基础光源
const ambientLight = new THREE.AmbientLight(0xffffff, 2)
this.scene.add(ambientLight)
// 添加聚光灯光源
const spotLight = new THREE.SpotLight(0xffffff, 2, 1000)
spotLight.shadow.mapSize.set(2048, 2048)
spotLight.position.set(-400, 100, -100)
// 开启投影
spotLight.castShadow = true
this.scene.add(spotLight)
// 添加聚光灯光源辅助线便于调试
const spotLightHelper = new THREE.SpotLightHelper(spotLight)
this.scene.add(spotLightHelper)
三、添加GUI菜单
先引入
import * as Dat from 'dat-gui'
我们可以吧吉普车的xy坐标,xyz角度放到ui上,这样就可以通过UI调整吉普车的坐标和角度
初始化代码
// 为带贴图MTL的OBJ模型添加UI控制(xy坐标,xyz角度)
this.controls = {
positionX: -50,
positionZ: -30,
rotationX: 0,
rotationY: 0,
rotationZ: 0
}
const gui = new Dat.GUI()
// 设置允许操作范围
gui.add(this.controls, 'positionX', -200, 200)
gui.add(this.controls, 'positionZ', -200, 200)
gui.add(this.controls, 'rotationX', -360, 360)
gui.add(this.controls, 'rotationY', -360, 360)
gui.add(this.controls, 'rotationZ', -360, 360)
监听代码,需要放在render中
// UI事件
if (this.jeepCar) {
this.jeepCar.position.x = this.controls['positionX']
this.jeepCar.position.z = this.controls['positionZ']
// 这里设置的角度,需要转换为弧度
this.jeepCar.rotation.x = this.angle2Radian(this.controls['rotationX'])
this.jeepCar.rotation.y = this.angle2Radian(this.controls['rotationY'])
this.jeepCar.rotation.z = this.angle2Radian(this.controls['rotationZ'])
}
四、完整代码
import * as THREE from 'three'
import Stats from 'stats-js'
import * as Dat from 'dat-gui'
import TrackballControls from 'three-trackballcontrols'
import { MTLLoader, OBJLoader } from 'three-obj-mtl-loader'
export default {
name: 'LoadObjMtl',
data() {
return {
step: 0
}
},
mounted() {
this.renderer = ''
this.camera = ''
this.scene = ''
this.light = ''
this.gui = ''
this.axesHelper = ''
this.stats = ''
this.trackballControls = ''
this.clock = ''
this.jeepCar = ''
this.controls = ''
this.objLoader = ''
this.mtlLoader = ''
this.plane = ''
// 执行
this.execute()
// 窗口大小变化
window.onresize = this.onWindowResize
},
methods: {
initScene() {
this.scene = new THREE.Scene()
},
initCamera() {
this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 5000)
// 设置相机位置
this.camera.position.x = -400
this.camera.position.y = 400
this.camera.position.z = 400
// 设置相机指向的位置 默认0,0,0
this.camera.lookAt(this.scene.position)
},
initHelper() {
this.axesHelper = new THREE.AxesHelper(100)
this.scene.add(this.axesHelper)
},
initRender() {
this.renderer = new THREE.WebGLRenderer({ antialias: true })
this.renderer.setSize(window.innerWidth, window.innerHeight)
// 告诉渲染器需要阴影效果
this.renderer.shadowMap.enabled = true
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap
// 设置背景色
this.renderer.setClearColor(new THREE.Color('rgb(61,61,61)'))
document.querySelector('#container').appendChild(this.renderer.domElement)
},
initStats() {
this.stats = new Stats()
document.body.appendChild(this.stats['dom'])
},
initModel() {
// 实例化obj loader
this.objLoader = new OBJLoader()
// 实例化mtl loader
this.mtlLoader = new MTLLoader()
const that = this
// 加载mtl
this.mtlLoader.load('objs/jeep/jeepCar.mtl', function(materials) {
materials.preload()
that.objLoader.setMaterials(materials)
// 加载obj
that.objLoader.load('objs/jeep/jeepCar.obj', function(obj) {
// 模型文件太大,缩小一下比例,方便显示
obj.scale.set(0.1, 0.1, 0.1)
// 设置可以投影
obj.children.forEach(item => {
item.castShadow = true
item.receiveShadow = true
})
that.jeepCar = obj
// 添加到场景
that.scene.add(that.jeepCar)
})
})
// 加载不带mtl的obj
const otherObjLoad = new OBJLoader()
otherObjLoad.load('objs/jeep/jeepCar.obj', function(obj) {
obj.scale.set(0.1, 0.1, 0.1)
for (const k in obj.children) {
obj.children[k].castShadow = true
obj.children[k].receiveShadow = true
}
obj.scale.set(0.1, 0.1, 0.1)
obj.position.x = 50
obj.position.z = 30
that.scene.add(obj)
})
// 创建一个几何平面,作为地板,400,400
const planeGeometry = new THREE.PlaneGeometry(400, 400)
// 创建带颜色材质,更换为MeshLambertMaterial材质,去掉网格结构
const planeMaterial = new THREE.MeshLambertMaterial({ color: 'rgb(110,110,110)' })
this.plane = new THREE.Mesh(planeGeometry, planeMaterial)
// 平面开启接收阴影效果
this.plane.receiveShadow = true
// 设置平面角度和位置
this.plane.rotation.x = -0.5 * Math.PI
this.plane.position.x = 0
this.plane.position.y = 0
this.plane.position.z = 0
// 添加平面
this.scene.add(this.plane)
},
initLight() {
// 为场景所有物体添加基础光源
const ambientLight = new THREE.AmbientLight(0xffffff, 2)
this.scene.add(ambientLight)
// 添加聚光灯光源
const spotLight = new THREE.SpotLight(0xffffff, 2, 1000)
spotLight.shadow.mapSize.set(2048, 2048)
spotLight.position.set(-400, 100, -100)
// 开启投影
spotLight.castShadow = true
this.scene.add(spotLight)
// 添加聚光灯光源辅助线便于调试
const spotLightHelper = new THREE.SpotLightHelper(spotLight)
this.scene.add(spotLightHelper)
},
initGui() {
// 为带贴图MTL的OBJ模型添加UI控制(xy坐标,xyz角度)
this.controls = {
positionX: -50,
positionZ: -30,
rotationX: 0,
rotationY: 0,
rotationZ: 0
}
const gui = new Dat.GUI()
// 设置允许操作范围
gui.add(this.controls, 'positionX', -200, 200)
gui.add(this.controls, 'positionZ', -200, 200)
gui.add(this.controls, 'rotationX', -360, 360)
gui.add(this.controls, 'rotationY', -360, 360)
gui.add(this.controls, 'rotationZ', -360, 360)
},
initControls() {
this.trackballControls = new TrackballControls(this.camera, this.renderer.domElement)
this.trackballControls.rotateSpeed = 1.0
this.trackballControls.zoomSpeed = 1
this.trackballControls.panSpeed = 1
this.trackballControls.noZoom = false
this.trackballControls.noPan = false
this.trackballControls.staticMoving = true
this.trackballControls.dynamicDampingFactor = 0.3
this.trackballControls.keys = [65, 83, 68]
},
initClock() {
this.clock = new THREE.Clock()
},
render() {
this.trackballControls.update(this.clock.getDelta())
this.stats.update()
// UI事件
if (this.jeepCar) {
this.jeepCar.position.x = this.controls['positionX']
this.jeepCar.position.z = this.controls['positionZ']
// 这里设置的角度,需要转换为弧度
this.jeepCar.rotation.x = this.angle2Radian(this.controls['rotationX'])
this.jeepCar.rotation.y = this.angle2Radian(this.controls['rotationY'])
this.jeepCar.rotation.z = this.angle2Radian(this.controls['rotationZ'])
}
// render using requestAnimationFrame
requestAnimationFrame(this.render)
this.renderer.render(this.scene, this.camera)
},
onWindowResize() {
this.camera.aspect = window.innerWidth / window.innerHeight
this.camera.updateProjectionMatrix()
this.renderer.setSize(window.innerWidth, window.innerHeight)
},
// 角度转弧度(弧度 = π / 180 * 角度)
angle2Radian(angle) {
return Math.PI / 180 * angle
},
execute() {
// 初始化场景
this.initScene()
// 初始化摄像头
this.initCamera()
// 初始化三维坐标系
this.initHelper()
// 初始化辅助UI
this.initGui()
// 初始化帧数显示工具
this.initStats()
// 初始化时钟工具
this.initClock()
// 初始化模型
this.initModel()
// 初始化渲染器
this.initRender()
// 初始化光源
this.initLight()
// 初始化控制器
this.initControls()
// 执行渲染
this.render()
}
}
}
更多推荐
所有评论(0)