漫游实际上相机沿着自定义轨道移动,来实现场景漫游效果
1、模型环境
在前面一篇文章中 初学three 基于tween.js完成聚焦等任务, 已经搭建好一个场景模型,我们实现漫游,就需要先实现自定义轨道
2、自定义轨道
这里使用three的CatmullRomCurve3方法,创建曲线
在代码示例中,可以看到,创建曲线,必须知道坐标,但是模型的地板坐标如何获取了,
2.1 获取坐标
上篇文章中,有说到模型拾取的功能,我们可以通过点击场景,点击地板,来获取坐标,
围绕着四周点击,就可以获取到相应的坐标
const pointList = [
-1.6721884850946012, -1.368973127897772e-13, 360.53068686806284,
298.37007281642354, -8.379092438255298e-14, 377.360775826295,
399.80896905293105, -1.6407097263069702e-14, 73.8909971201922,
418.7797065307649, 1.94614550535104e-13, -364.466017270771,
-14.04454024443163, 8.129869563788722e-14, -366.13677538049626,
-376.16307011275296, 8.369871230981553e-14, -376.9454895698756,
-381.77316036296304, 1.2270056905470107e-13, -40.594237072899546,
-380.1627330533074, 3.923847449397506e-14, 335.2856208903472,
];
根据坐标数组转为点数组
let points = [];
for (let i = 0; i < pointList.length; i += 3) {
points.push(new THREE.Vector3(
pointList[i],
pointList[i + 1],
pointList[i + 2]
));
}
创建曲线
const curve = new THREE.CatmullRomCurve3( points, true,'catmullrom',0.1 );
const geometry = new THREE.BufferGeometry().setFromPoints(curve.getPoints(3000));
const material = new THREE.LineBasicMaterial({
color: 'red',
});
const line = new THREE.Line(geometry, material);
scene.add(line) // 线条对象添加到场景中
2、漫游
定义一个方法,用来移动相机
将曲线分段后,取位置点坐标,
let time = 0;
function moveCamera () {
// 把曲线分割成2999段, 可以得到3000个点
let points = curve.getPoints(3000);
// 更新取点索引
time += 1.5;
// 相机所在点索引
const index1 = time % 3000;
// 前方机器人所在位置点的索引
const index2 = (time + 50) % 3000;
// 根据索引取点
let point = points[index1];
let point1 = points[index2];
// 修改相机和模型位置
if(point&&point.x){
camera.position.set(point.x, 5, point.z);
camera.lookAt(point1.x, 5, point1.z);
}
}
3、加载模型
我们选用一个小车模型 threejs.org/examples/?q…
相应的文件模型和阴影在three demo中可以找到,这里就不做展示了
// Car
// materials
const wheels = [];
let carModel;
const bodyMaterial = new THREE.MeshPhysicalMaterial( {
color: 0xff0000, metalness: 1.0, roughness: 0.5, clearcoat: 1.0, clearcoatRoughness: 0.03, sheen: 0.5
} );
const detailsMaterial = new THREE.MeshStandardMaterial( {
color: 0xffffff, metalness: 1.0, roughness: 0.5
} );
const glassMaterial = new THREE.MeshPhysicalMaterial( {
color: 0xffffff, metalness: 0.25, roughness: 0, transmission: 1.0
} );
const shadow = new THREE.TextureLoader().load( './textures/ferrari_ao.png' );
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath( './gltf/' );
const loader = new GLTFLoader();
loader.setDRACOLoader( dracoLoader );
loader.load( './models/ferrari.glb', function ( gltf ) {
carModel = gltf.scene.children[ 0 ];
gltf.scene.children[ 0 ].position.set( -1.6721884850946012, -1.368973127897772e-13, 360.53068686806284);
gltf.scene.children[ 0 ].scale.set(10, 10, 10);
carModel.getObjectByName( 'body' ).material = bodyMaterial;
carModel.getObjectByName( 'rim_fl' ).material = detailsMaterial;
carModel.getObjectByName( 'rim_fr' ).material = detailsMaterial;
carModel.getObjectByName( 'rim_rr' ).material = detailsMaterial;
carModel.getObjectByName( 'rim_rl' ).material = detailsMaterial;
carModel.getObjectByName( 'trim' ).material = detailsMaterial;
carModel.getObjectByName( 'glass' ).material = glassMaterial;
wheels.push(
carModel.getObjectByName( 'wheel_fl' ),
carModel.getObjectByName( 'wheel_fr' ),
carModel.getObjectByName( 'wheel_rl' ),
carModel.getObjectByName( 'wheel_rr' )
);
// shadow
const mesh = new THREE.Mesh(
new THREE.PlaneGeometry( 0.655 * 4, 1.3 * 4 ),
new THREE.MeshBasicMaterial( {
map: shadow, blending: THREE.MultiplyBlending, toneMapped: false, transparent: true
} )
);
mesh.rotation.x = - Math.PI / 2;
mesh.renderOrder = 2;
carModel.add( mesh );
carModel.rotation.y = - Math.PI / 2;
scene.add( carModel );
} );
因为小车在移动的时候,轮胎也是相应的滚动,自转,所以我们需要在render中,
function render() {
moveCamera();
// 小车的轮胎自转
const time2 = - performance.now() / 1000;
for ( let i = 0; i < wheels.length; i ++ ) {
wheels[ i ].rotation.x = time2 * Math.PI * 2;
}
requestAnimationFrame(render);
renderer.render(scene, camera);
}
在示例图中我们可以看到小车和相机的位置不是匀速的,moveCamera有问题,需要优化
let progress = 0;
function moveCamera() {
if (progress <= 1 - 0.0004 * 20){
const point = curve.getPointAt(progress) //获取样条曲线指定点坐标,作为相机的位置
const pointBox = curve.getPointAt(progress + 0.0004 * 20) //获取样条曲线指定点坐标
if (carModel) {
carModel.position.set(pointBox.x, pointBox.y, pointBox.z);
carModel.lookAt(point.x, point.y, point.z);
}
// camera.position.set(point.x, point.y + 15, point.z)
// camera.lookAt(pointBox.x, pointBox.y + 5, pointBox.z)
// controls.position0.set(point.x, point.y + 5, point.z) //非必要,场景有控件时才加上
// controls.target.set(pointBox.x, pointBox.y + 5, pointBox.z) //非必要,场景有控件时才加上
progress += 0.0004
} else {
progress = 0
}
}
优化后的moveCamera方法的,小车按照曲线轨迹运动
刚开始没有把camera打开因为同时打开漫游的时候,只能看到车尾,后来发现是自己把车的放大系数写太高了,
gltf.scene.children[ 0 ].position.set( -1.6721884850946012, -1.368973127897772e-13, 360.53068686806284);
gltf.scene.children[ 0 ].scale.set(10, 10, 10);
于是,把scale系数调回了4
gltf.scene.children[ 0 ].scale.set(4, 4, 4);
但是这里会抖动,很奇怪,留着待后续排查吧
4、参考链接:
1、Three.js的物体点击选中拾取原理剖析blog.csdn.net/xyphf/artic…
2、threejs 实现场景漫游效果(相机沿着自定义轨道移动) blog.csdn.net/weixin_4085…
我正在参加「创意开发 投稿大赛」详情请看:掘金创意开发大赛来了!