Three.js: проецирование объектов на плоскость, перпендикулярную камере - PullRequest
0 голосов
/ 06 ноября 2018

У меня есть простая сцена в Трех с некоторыми самолетами. Прямо сейчас при нажатии самолеты перемещаются в случайные положения.

После щелчка я хотел бы вместо этого переместить плоскости в новую сетку, перпендикулярную камере, так, чтобы оси 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 голосов
/ 06 ноября 2018

Это небольшой путь, но он поможет вам избежать многих математических вычислений: просто сгруппируйте ваши самолеты в 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.up и controls.target, чтобы это работало. Если вы измените эти 2 атрибута, вам нужно скопировать их в planeGroup, чтобы этот подход работал.

Вот документация по методу .lookAt ()

...