three.js 相机平滑过渡移动到目标物体

前言

在网上找的相机过渡代码,不是代码有问题,就是建议写死轨迹坐标来进行移动,而我想要的是移动到目标坐标后,能正面朝向目标模型,并且能调整相机朝向目标模型的角度。最后靠着以前做 Canvas 积累的三角函数的知识终于实现了这个功能。

准备工作

  1. 下载 tween.js 用于实现过渡的效果
  2. 想要复习三角函数的可以看看这个网站 www.shuxuele.com/sine-cosine…

问题描述

经过测试相机移动到目标坐标是没有问题的,问题是出在相机看向的目标角度不对,相机不能正面朝向目标模型。效果如下图所示

2022-4-5.gif

功能实现

  • 公式:
    • Math.PI / 2 = 弧度,弧度换算成角度 = 90度角。
    • X = Math.cos(Math.PI / 2) * 间隔距离 + 目标坐标
    • Y = Math.sin(Math.PI / 2) * 间隔距离 + 目标坐标

QQ截图20220405181945.png

其实不使用 Math.PI / 2 也能实现看向90度角的效果,这样也就意味着不能自己设置想看向的其他角度。

完整代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        body {
            margin: 0;
        }
    </style>
</head>
<body>
<div id="WebGl-output"></div>
<script src="libs/three.js"></script>
<script src="libs/OrbitControls.js"></script>
<script src="libs/tween.js"></script>
<script>
    let scene = new THREE.Scene()
    let camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000)

    let renderer = new THREE.WebGLRenderer()
    renderer.setClearColor(0x000000);
    renderer.setSize(window.innerWidth, window.innerHeight)
    renderer.shadowMap.enabled = true

    let axes = new THREE.AxesHelper(20)
    scene.add(axes)

    let planeGeometry = new THREE.PlaneGeometry(60, 20, 1, 1)
    let planeMatrices = new THREE.MeshLambertMaterial({color: 0xAAAAAA})
    let plane = new THREE.Mesh(planeGeometry, planeMatrices)
    plane.position.set(15, 0, 0)
    plane.rotation.set(-0.5 * Math.PI, 0, 0)
    plane.receiveShadow = true
    scene.add(plane)

    let cubeGeometry = new THREE.BoxGeometry(4, 4, 4)
    let cubeMatrices = new THREE.MeshLambertMaterial({
        color: 0xff0000,
    })
    let cube = new THREE.Mesh(cubeGeometry, cubeMatrices)
    cube.castShadow = true
    cube.position.set(-4, 3, 0)
    scene.add(cube)

    let sphereGeometry = new THREE.SphereGeometry(4, 20, 20)
    let sphereMatrices = new THREE.MeshLambertMaterial({
        color: 0x7777ff,
    })
    let sphere = new THREE.Mesh(sphereGeometry, sphereMatrices)
    sphere.position.set(20, 4, 2)
    sphere.castShadow = true
    scene.add(sphere)

    let spotLight = new THREE.SpotLight(0xffffff)
    spotLight.position.set(-40, 40, -15)
    spotLight.castShadow = true
    spotLight.shadow.mapSize = new THREE.Vector2(1024, 1024);
    spotLight.shadow.camera.far = 130;
    spotLight.shadow.camera.near = 40;
    scene.add(spotLight)

    camera.position.set(-30, 40, 30)
    camera.lookAt(scene.position)

    let ambient = new THREE.AmbientLight(0x353535);
    scene.add(ambient);

    document.getElementById('WebGl-output').append(renderer.domElement)

    let controls = new THREE.OrbitControls(camera, renderer.domElement)

    controls.target = new THREE.Vector3(0, 0, 0)

    const raycaster = new THREE.Raycaster()
    let mouse = new THREE.Vector2()
    let intersects = null
    renderer.domElement.addEventListener('click', function (event) {
        mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
        mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
        raycaster.setFromCamera(mouse, camera);
        intersects = raycaster.intersectObject(scene, true);
        if (intersects.length > 0) {
            let boxMaxY = new THREE.Box3().setFromObject(intersects[0].object).max.y

            let distance = boxMaxY + 10
            let angel = Math.PI / 5

            let position = {
                x: intersects[0].object.position.x + Math.cos(angel) * distance,
                y: intersects[0].object.position.y,
                z: intersects[0].object.position.z + Math.sin(angel) * distance
            }

            let tween = new TWEEN.Tween(camera.position).to(position, 3000)
            let tween1 = new TWEEN.Tween(controls.target).to(intersects[0].object.position, 3000)

            controls.enabled = false;
            tween.onComplete(function () {
                controls.enabled = true;
            })

            tween.start()
            tween1.start()
        }
    });

    function render() {
        requestAnimationFrame(render)
        TWEEN.update()
        controls.update()
        renderer.render(scene, camera)
    }

    render()
</script>
</body>
</html>
复制代码

猜你喜欢

转载自juejin.im/post/7083068912627089438
今日推荐