初学three 漫游

269 阅读1分钟

漫游实际上相机沿着自定义轨道移动,来实现场景漫游效果

项目代码 github.com/642134542/0…

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…

我正在参加「创意开发 投稿大赛」详情请看:掘金创意开发大赛来了!