首页 > 解决方案 > Three.js 将对象移动到相机前面

问题描述

您好我正在尝试将一个物体移动到相机前面,当它到达目标位置时,我希望它停止。但它不起作用。

function objectToCamera(mX, mY, object)
{
                var vector = new THREE.Vector3(mX, mY, 1);
                vector.unproject(camera);
                vector.sub(object.position);        

                var dx = object.position.x - camera.position.x;
                var dy = object.position.y - camera.position.y;
                var dz = object.position.z - camera.position.z;

                var distance = Math.sqrt(dx*dx + dy*dy + dz*dz);

                if(lastDistance < distance && lastDistance != -1)
                    keepOut = -1;

                lastDistance = distance;

                setTimeout(function(){
                    if( distance > 200 && keepOut == 1)
                    {
                        var amount = (1)*(indexForZoom/3);

                        amount = (amount>15) ? 15 : (1)*(indexForZoom/3);

                        if(distance - amount < 200)
                            amount = (distance-200)+1;

                        indexForZoom++;
                        object.translateZ(amount);
                        controls.target.addVectors(controls.target,vector.setLength(amount));
                        objectToCamera(mX, mY, object)
                    }
                    else
                    {
                    //  stopForZoom = 1;
                        keepOut = -1;
                        objectClickHandler(object.name, object);
                    }
                }, 10);
}

我正在检查相机和物体之间的距离,如果达到目标距离,我会让它停下来,但它不起作用。在坐标中,如果我在正 X 坐标中,则距离正在减小,否则,距离正在增加。

我认为,在我的代码中,距离应该一直在减少,但事实并非如此。

请帮忙。谢谢。

图片来解释我的情况

标签: three.js

解决方案


您可以使用object.position.lerp(target, amount)将对象移向目标。金额是一个从 0 到 1 的值,其中 1 = 100% 一直到目标,0.5 = 50% 到目标。

如果您想以固定速度移动,那么您可以获得到目标的距离

distance = object.position.distanceTo(target);

假设您希望每次交互最多 0.1 个单位。然后

moveSpeed = 0.1;
distance = object.position.distanceTo(target);
amount = Math.min(moveSpeed, distance) / distance;
object.position.lerp(target, amount)

剩下的就是让你选择一个目标。

摄像机前面的位置是

const distanceFromCamera = 3;  // 3 units
const target = new THREE.Vector3(0, 0, -distanceToCamera);
target.applyMatrix4(camera.matrixWorld);

因此,例如,如果您移动相机(用鼠标拖动,使用滚轮)。注意:在代码中,速度被调整为与帧速率无关。

function main() {
  const canvas = document.querySelector('#c');
  const renderer = new THREE.WebGLRenderer({canvas});

  const fov = 45;
  const aspect = 2;  // the canvas default
  const near = 0.1;
  const far = 1000;
  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  camera.position.set(0, 10, 20);

  const controls = new THREE.OrbitControls(camera, canvas);
  controls.target.set(0, 0, 0);
  controls.update();

  const scene = new THREE.Scene();
  scene.background = new THREE.Color('lightblue');

  {
    const color = 0xFFFFFF;
    const intensity = 1;
    const light = new THREE.DirectionalLight(color, intensity);
    light.position.set(0, 10, 0);
    light.target.position.set(-5, 0, 0);
    scene.add(light);
    scene.add(light.target);
  }

  const gridHelper = new THREE.GridHelper(100, 10);
  scene.add(gridHelper);
  gridHelper.position.set(0, -5, 0);

  const cube = new THREE.Mesh(
     new THREE.BoxBufferGeometry(1, 1, 1),
     new THREE.MeshPhongMaterial({color: 'red'}),
  );
  scene.add(cube);

  function resizeRendererToDisplaySize(renderer) {
    const canvas = renderer.domElement;
    const width = canvas.clientWidth;
    const height = canvas.clientHeight;
    const needResize = canvas.width !== width || canvas.height !== height;
    if (needResize) {
      renderer.setSize(width, height, false);
    }
    return needResize;
  }

  let then = 0;
  function render(now) {
    now *= 0.001; // convert to seconds
    const deltaTime = now - then;
    then = now;

    if (resizeRendererToDisplaySize(renderer)) {
      const canvas = renderer.domElement;
      camera.aspect = canvas.clientWidth / canvas.clientHeight;
      camera.updateProjectionMatrix();
    }
    
    cube.rotation.x = now;
    cube.rotation.y = now * 1.1;
    
    // move cube in front of camera
    {
      const distanceFromCamera = 3;  // 3 units
      const target = new THREE.Vector3(0, 0, -distanceFromCamera);
      target.applyMatrix4(camera.matrixWorld);    
    
      const moveSpeed = 15;  // units per second
      const distance = cube.position.distanceTo(target);
      if (distance > 0) {
        const amount = Math.min(moveSpeed * deltaTime, distance) / distance;
        cube.position.lerp(target, amount);
        cube.material.color.set('green');
      } else {
        cube.material.color.set('red');
      }
    }

    renderer.render(scene, camera);

    requestAnimationFrame(render);
  }

  requestAnimationFrame(render);
}

main();
body { margin: 0; }
#c { width: 100vw; height: 100vh; display: block; }
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r112/build/three.min.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r112/examples/js/controls/OrbitControls.js"></script>
<canvas id="c"></canvas>

请注意,您可能想camera.updateMatrixWorld()在所有数学运算之前调用以确保目标不会迟到一帧。

如果对象在层次结构中,那么还有更多工作要做。您可以进行数学运算,也可以使用将对象附加到场景,然后将其附加回其在层次结构中的位置

const parent = object.parent;

// move object to scene without changing it's world orientation
scene.attach(object);

// do stuff above

// move object to parent without changing it's world orientation
parent.attach(object);

推荐阅读