Three.js:将对象投影到与相机垂直的平面上

时间:2018-11-06 16:31:46

标签: javascript math three.js geometry linear

我在《三人行》中有一个简单的场景,有一些飞机。现在,单击将飞机移至随机位置。

单击后,我想将平面移到垂直于相机的新网格中,以使投影网格的x和y轴与屏幕的x和y轴平行。

这是场景:

// generate a scene object
var scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);

// generate a camera
var aspectRatio = window.innerWidth / window.innerHeight;
var camera = new THREE.PerspectiveCamera(75, aspectRatio, 0.01, 10000);

// position the camera
camera.position.set(51.389, -451.056, 839.455);
var rotObjectMatrix = new THREE.Matrix4();
var q = {_x: 0.0184, _y: -0.2122, _z: 0.9770, _w: -0.0081};
rotObjectMatrix.makeRotationFromQuaternion(q);
camera.quaternion.setFromRotationMatrix(rotObjectMatrix);
camera.up.set(0.00806, -0.91008, -0.41432);

// generate a renderer
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio); // <3 retina
renderer.setSize(window.innerWidth, window.innerHeight); // canvas size
document.body.appendChild(renderer.domElement);

// generate controls
var controls = new THREE.TrackballControls(camera, renderer.domElement);
controls.target.set(19.053, -111.316, 93.996);

// generate some lights
var ambientLight = new THREE.AmbientLight(0xeeeeee);
scene.add(ambientLight);

// render loop
function render() {
  requestAnimationFrame(render);
  renderer.render(scene, camera);
  controls.update();
};

// draw a grid
var grid = new THREE.GridHelper(2000, 20, 0x000000, 0x000000);
grid.material.opacity = 0.2;
grid.material.transparent = true;
grid.rotation.x = -Math.PI;
scene.add(grid);

// draw some
planes = [];
for (var i=0; i<2**10; i++) {
  var geometry = new THREE.PlaneGeometry( 20, 20, 32 );
  var material = new THREE.MeshBasicMaterial(
    {color: 0xff0000, side: THREE.DoubleSide} );
  var plane = new THREE.Mesh( geometry, material );
  var x = ((i % 2**5) * 40) - (2**5 * 40)/2;
  var z = (Math.floor(i/2**5) * 40) - (2**5 * 40)/2;
  plane.position.set(x, 0, z);
  scene.add(plane);
  planes.push(plane);
}

// transition the planes on body click
document.querySelector('body').addEventListener('click', function() {
  planes.forEach(function(plane) {
    // placeholder
    plane.position.set(
      Math.random() * 500 - (Math.random() * 500)/2,
      0,
      Math.random() * 500 - (Math.random() * 500)/2,
    )
  })
})

render();
html, body { width: 100%; height: 100%; background: #000; }
body { margin: 0; overflow: hidden; }
canvas { width: 100%; height: 100%; }
<script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/97/three.min.js'></script>
<script src='https://threejs.org/examples/js/controls/TrackballControls.js'></script>

几乎可以使用以下内容,但不会倾斜平面以使其垂直于相机:

planes.forEach(function(plane) {
  // close to projection discussed above...
  plane.position.set(
    plane.position.x,
    plane.position.z,
    0,
  )
})

有人知道如何实现上述预测吗?其他人可以提供的任何建议将不胜感激!

1 个答案:

答案 0 :(得分:2)

这有点捷径,但是它可以帮助您避免进行大量的数学计算:只需将飞机分组为Group,然后使用Group.lookAt(camera.position)将所有飞机统一指向相机。

// generate a scene object
var scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);

// generate a camera
var aspectRatio = window.innerWidth / window.innerHeight;
var camera = new THREE.PerspectiveCamera(75, aspectRatio, 0.01, 10000);

// position the camera
camera.position.set(51.389, -451.056, 839.455);
var rotObjectMatrix = new THREE.Matrix4();
var q = {_x: 0.0184, _y: -0.2122, _z: 0.9770, _w: -0.0081};
rotObjectMatrix.makeRotationFromQuaternion(q);
camera.quaternion.setFromRotationMatrix(rotObjectMatrix);

// Not sure why you're changing camera's up axis, but this will
// need to be duplicated on planeGroup;
camera.up.set(0.00806, -0.91008, -0.41432);

// generate a renderer
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio); // <3 retina
renderer.setSize(window.innerWidth, window.innerHeight); // canvas size
document.body.appendChild(renderer.domElement);

// generate controls
var controls = new THREE.TrackballControls(camera, renderer.domElement);

// If you change the pos where the camera is looking at,
// you'll need to place planeGroup at this position.
controls.target.set(19.053, -111.316, 93.996);

// generate some lights
var ambientLight = new THREE.AmbientLight(0xeeeeee);
scene.add(ambientLight);

// render loop
function render() {
  requestAnimationFrame(render);
  renderer.render(scene, camera);
  controls.update();
};

// draw a grid
var grid = new THREE.GridHelper(2000, 20, 0x000000, 0x000000);
grid.material.opacity = 0.2;
grid.material.transparent = true;
grid.rotation.x = -Math.PI;
scene.add(grid);

// draw some
planes = [];
var planeGroup = new THREE.Group();

for (var i=0; i<2**10; i++) {
  var geometry = new THREE.PlaneGeometry( 20, 20, 32 );
  var material = new THREE.MeshBasicMaterial(
    {color: 0xff0000, side: THREE.DoubleSide} );
  var plane = new THREE.Mesh( geometry, material );
  var x = ((i % 2**5) * 40) - (2**5 * 40)/2;
  var y = (Math.floor(i/2**5) * 40) - (2**5 * 40)/2;

  // Setting x,y instead of x,z
  plane.position.set(x, y, 0);
  planeGroup.add(plane);
  planes.push(plane);
}
scene.add(planeGroup);

// Copying camera.up and controls.target from above
planeGroup.up.copy(camera.up);
planeGroup.position.copy(controls.target);
planeGroup.lookAt(camera.position);

// transition the planes on body click
document.querySelector('body').addEventListener('click', function() {
  planes.forEach(function(plane) {
    // placeholder
    plane.position.set(
      Math.random() * 500 - (Math.random() * 500)/2,
      0,
      Math.random() * 500 - (Math.random() * 500)/2,
    )
  })
})

render();
html, body { width: 100%; height: 100%; background: #000; }
body { margin: 0; overflow: hidden; }
canvas { width: 100%; height: 100%; }
<script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/97/three.min.js'></script>
<script src='https://threejs.org/examples/js/controls/TrackballControls.js'></script>

然后,如果您想随机化飞机的位置,您仍然可以这样做,但是此后您只需担心的是将它们返回到其原始的x,y,0位置,它们会自动对齐“看着”相机。

编辑:我必须复制您对camera.upcontrols.target所做的更改,才能正常工作。如果您更改了这两个属性,则需要将它们复制到planeGroup才能使用此方法。

Here's the doc on the .lookAt() method