Three.js使用正交相机将2D映射到3D

问题描述 投票:1回答:2

我使用正交摄影机拍摄了Three.js场景

this.camera = new THREE.OrthographicCamera(this.width / - 2, this.width / 2, this.height / 2, this.height / - 2, 1, 1000);
this.camera.position.z = this.width / 2;
this.scene.add(this.camera);

然后我有一个立方体(红色),我想将其定位在某些2D坐标(绿色)上。我目前正在使用此功能将2D坐标转换为3D坐标。

from2Dto3D( position2d ) {
    var vector = new THREE.Vector3(),
        camera = this.camera,
        x = (position2d.x / this.width) * 2 - 1,
        y = (position2d.y / this.height) * 2 + 1;
    vector.set(x, y, 0.5);
    vector.unproject(this.camera);
    var direction = vector.sub(this.camera.position).normalize(),
        distance = - this.camera.position.z / direction.z,
        scaled = direction.multiplyScalar(distance),
        coords = this.camera.position.clone().add(scaled);
    return new THREE.Vector3(coords.x, coords.y, 0);
  }

...

let position3d = this.from2Dto3D(position2d);

...

this.cube.position.x = position3d.x; // updating only x position for the moment

如何将立方体精确定位在2D位置?

enter image description here

javascript three.js 3d
2个回答
1
投票

您需要显示更多代码。我们如何知道问题出在您的鼠标位置计算上?

这里的代码无论相机的方向如何,只要没有对画布应用css转换,就可以工作。

      canvas.addEventListener('mousemove', (e) => {
        const rect = canvas.getBoundingClientRect();
        // get a canvas content pixel position
        const {width, height} = renderer.domElement;
        const canvasX = (e.clientX - rect.left) * width / rect.width;
        const canvasY = (e.clientY - rect.top) * height / rect.height;

        const clipX = (canvasX / width)  *  2 - 1;
        const clipY = (canvasY / height) * -2 + 1;

        // get the object's clip space Z
        const clipPos = new THREE.Vector3();
        mesh.getWorldPosition(clipPos);
        clipPos.project(camera);

        const pos = new THREE.Vector3(clipX, clipY, clipPos.z);
        pos.unproject(camera);
        mesh.position.copy(pos);
        render();
      });

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

  const size = 5;
  const near = 5;
  const far = 50;
  const camera = new THREE.OrthographicCamera(-size, size, size, -size, near, far);
  camera.position.set(5, 7, 12);
  camera.lookAt(0, 0, 0);

  const scene = new THREE.Scene();
  scene.background = new THREE.Color('white');
  
  const gridHelper = new THREE.GridHelper(10, 10);
  scene.add(gridHelper);

  const cubeSize = 1;
  const cubeGeo = new THREE.BoxBufferGeometry(cubeSize, cubeSize, cubeSize);
  const cubeMat = new THREE.MeshBasicMaterial({color: 'red'});
  const mesh = new THREE.Mesh(cubeGeo, cubeMat);
  scene.add(mesh);

  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;
  }

  function render() {
    if (resizeRendererToDisplaySize(renderer)) {
      const aspect = renderer.domElement.clientWidth / renderer.domElement.clientHeight;
      camera.left  = -size * aspect;
      camera.right =  size * aspect;
      camera.updateProjectionMatrix();
    }
    renderer.render(scene, camera);
  }
  render();
  
  canvas.addEventListener('mousemove', (e) => {
    const rect = canvas.getBoundingClientRect();
    // get a canvas content pixel position
    const {width, height} = renderer.domElement;
    const canvasX = (e.clientX - rect.left) * width / rect.width;
    const canvasY = (e.clientY - rect.top) * height / rect.height;
    
    const clipX = (canvasX / width)  *  2 - 1;
    const clipY = (canvasY / height) * -2 + 1;
        
    // get the object's clip space Z
    const clipPos = new THREE.Vector3();
    mesh.getWorldPosition(clipPos);
    clipPos.project(camera);

    // note this code moves the object in the plane
    // of the camera, not the plane of the grid
    const pos = new THREE.Vector3(clipX, clipY, clipPos.z);
    pos.unproject(camera);
    mesh.position.copy(pos);
    render();
  });
  
  window.addEventListener('resize', render);
}

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

请注意,相同的代码也适用于PerspecitveCamera

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

  const fov = 60;
  const near = 0.1;
  const far = 50;
  const camera = new THREE.PerspectiveCamera(fov, 1, near, far);
  camera.position.set(2, 5, 10);
  camera.lookAt(0, 0, 0);

  const scene = new THREE.Scene();
  scene.background = new THREE.Color('white');
  
  const gridHelper = new THREE.GridHelper(10, 10);
  scene.add(gridHelper);

  const cubeSize = 1;
  const cubeGeo = new THREE.BoxBufferGeometry(cubeSize, cubeSize, cubeSize);
  const cubeMat = new THREE.MeshBasicMaterial({color: 'red'});
  const mesh = new THREE.Mesh(cubeGeo, cubeMat);
  scene.add(mesh);

  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;
  }

  function render() {
    if (resizeRendererToDisplaySize(renderer)) {
      camera.aspect = renderer.domElement.clientWidth / renderer.domElement.clientHeight;
      camera.updateProjectionMatrix();
    }
    renderer.render(scene, camera);
  }
  render();
  
  canvas.addEventListener('mousemove', (e) => {
    const rect = canvas.getBoundingClientRect();
    // get a canvas content pixel position
    const {width, height} = renderer.domElement;
    const canvasX = (e.clientX - rect.left) * width / rect.width;
    const canvasY = (e.clientY - rect.top) * height / rect.height;
    
    const clipX = (canvasX / width)  *  2 - 1;
    const clipY = (canvasY / height) * -2 + 1;
        
    // get the object's clip space Z
    const clipPos = new THREE.Vector3();
    mesh.getWorldPosition(clipPos);
    clipPos.project(camera);

    // note: this code moves the object in the plane
    // of the camera not the plane of the grid
    const pos = new THREE.Vector3(clipX, clipY, clipPos.z);
    pos.unproject(camera);
    mesh.position.copy(pos);
    render();
  });
  
  window.addEventListener('resize', render);
}

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

如果您的画布是CSS转换的,则see this

请注意,如果要在网格(或任何任意平面)上移动对象,则不可行的解决方案是对不可见的平面使用RayCaster。

此外,如果您的对象在场景中具有被平移,旋转或缩放的父对象,则只是

    const parent = object.parent;
    scene.attach(object);

    // move the object as above

    parent.attach(object);

0
投票

事实证明,正交摄影机不需要此功能。我们只需要将屏幕系统坐标转换为three.js系统坐标即可。可以使用以下功能:

convertCoords(x, y, z) {
        return {
            x: x - this.width / 2,
            y: -1*y + this.height / 2,
            z
        }
}
© www.soinside.com 2019 - 2024. All rights reserved.