44

three.js を使用すると、次のようになります。

  • 複数の Object3D インスタンスを含むシーン
  • 複数の定義済みカメラ Vector3 位置
  • 画面のサイズが変更された場合のキャンバスの動的な幅/高さ
  • ユーザーはオブジェクトを選択できます(上から)
  • ユーザーはカメラの位置を選択できます(上から)

表示されているオブジェクトと、彼らが選択したカメラ位置が与えられた場合、画面上のオブジェクトに「最適」になる最終的なカメラ位置を計算するにはどうすればよいですか?

一部の画面でカメラの位置を「そのまま」使用すると、オブジェクトがビューポートの端からはみ出し、他の画面では小さく表示されます。オブジェクトをカメラ錐台に合わせることは可能だと思いますが、適切なものを見つけることができませんでした。

4

7 に答える 7

66

パースペクティブ カメラを使用していると仮定します。

カメラの位置、視野、またはその両方を設定できます。

次の計算は立方体であるオブジェクトに対して正確であるため、カメラに面するように配置されたオブジェクトのバウンディング ボックスについて考えてください。

カメラが中央にあり、立方体を正面から見ている場合は、次のように定義します。

dist = distance from the camera to the _closest face_ of the cube

height = height of the cube.

カメラの視野を次のように設定した場合

fov = 2 * Math.atan( height / ( 2 * dist ) ) * ( 180 / Math.PI ); // in degrees

立方体の高さは、表示される高さと一致します。

この時点で、カメラを少し上に戻すか、視野を少し広げることができます。

視野が固定されている場合は、上記の式を使用して距離を解きます。


編集:立方体を表示幅widthと一致させたい場合は、キャンバスの縦横比(キャンバス幅をキャンバス高さで割ったもの)にし、カメラの視野を次のように設定しますaspect

fov = 2 * Math.atan( ( width / aspect ) / ( 2 * dist ) ) * ( 180 / Math.PI ); // in degrees

three.js r.69

于 2013-01-30T22:06:48.777 に答える
6

オブジェクトが境界球体に収まる場合、オブジェクトが画面に収まると仮定すると、球体をカメラ ビューに合わせる作業が軽減されます。

与えられた例では、オブジェクトの最適な視点を実現するためにカメラの回転を変更しながら、 PerspectiveCamera.fovを一定に保ちます。ズーム効果は、.lookAt 方向ベクトルに沿ってカメラを移動することによって実現されます。

three.js ズーム範囲のデモ

画像では、問題の定義を見ることができます。 境界球と camera.fov が与えられた場合、L を見つけて、境界球がカメラの錐台面に接触するようにします。

three.js すべてのカメラをズーム

球体からカメラまでの必要な距離を計算する方法は次のとおりです。

three.js すべてのカメラをズーム

完全なソリューション: https://jsfiddle.net/mmalex/h7wzvbkt/

var renderer;
var camera;
var scene;
var orbit;
var object1;

function zoomExtents() {
  let vFoV = camera.getEffectiveFOV();
  let hFoV = camera.fov * camera.aspect;

  let FoV = Math.min(vFoV, hFoV);
  let FoV2 = FoV / 2;

  let dir = new THREE.Vector3();
  camera.getWorldDirection(dir);

  let bb = object1.geometry.boundingBox;
  let bs = object1.geometry.boundingSphere;
  let bsWorld = bs.center.clone();
  object1.localToWorld(bsWorld);

  let th = FoV2 * Math.PI / 180.0;
  let sina = Math.sin(th);
  let R = bs.radius;
  let FL = R / sina;

  let cameraDir = new THREE.Vector3();
  camera.getWorldDirection(cameraDir);

  let cameraOffs = cameraDir.clone();
  cameraOffs.multiplyScalar(-FL);
  let newCameraPos = bsWorld.clone().add(cameraOffs);

  camera.position.copy(newCameraPos);
  camera.lookAt(bsWorld);
  orbit.target.copy(bsWorld);

  orbit.update();
}

scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(54, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.x = 15;
camera.position.y = 15;
camera.position.z = 15;
camera.lookAt(0, 0, 0);

renderer = new THREE.WebGLRenderer({
  antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(new THREE.Color(0xfefefe));
document.body.appendChild(renderer.domElement);

orbit = new THREE.OrbitControls(camera, renderer.domElement);

// create light
{
  var spotLight = new THREE.SpotLight(0xffffff);
  spotLight.position.set(0, 100, 50);
  spotLight.castShadow = true;
  spotLight.shadow.mapSize.width = 1024;
  spotLight.shadow.mapSize.height = 1024;
  spotLight.shadow.camera.near = 500;
  spotLight.shadow.camera.far = 4000;
  spotLight.shadow.camera.fov = 30;
  scene.add(spotLight);
}

var root = new THREE.Object3D();
scene.add(root);

function CustomSinCurve(scale) {
  THREE.Curve.call(this);
  this.scale = (scale === undefined) ? 1 : scale;
}

CustomSinCurve.prototype = Object.create(THREE.Curve.prototype);
CustomSinCurve.prototype.constructor = CustomSinCurve;

CustomSinCurve.prototype.getPoint = function(t) {
  var tx = t * 3 - 1.5;
  var ty = Math.sin(2 * Math.PI * t);
  var tz = 0;

  return new THREE.Vector3(tx, ty, tz).multiplyScalar(this.scale);
};

var path = new CustomSinCurve(10);
var geometry = new THREE.TubeGeometry(path, 20, 2, 8, false);

var material = new THREE.MeshPhongMaterial({
  color: 0x20f910,
  transparent: true,
  opacity: 0.75
});

object1 = new THREE.Mesh(geometry, material);
object1.geometry.computeBoundingBox();
object1.position.x = 22.3;
object1.position.y = 0.2;
object1.position.z = -1.1;
object1.rotation.x = Math.PI / 3;
object1.rotation.z = Math.PI / 4;

root.add(object1);

object1.geometry.computeBoundingSphere();

var geometry = new THREE.SphereGeometry(object1.geometry.boundingSphere.radius, 32, 32);
var material = new THREE.MeshBasicMaterial({
  color: 0xffff00
});
material.transparent = true;
material.opacity = 0.35;
var sphere = new THREE.Mesh(geometry, material);
object1.add(sphere);

var size = 10;
var divisions = 10;
var gridHelper = new THREE.GridHelper(size, divisions);
scene.add(gridHelper);

var animate = function() {
  requestAnimationFrame(animate);
  renderer.render(scene, camera);
};

animate();
于 2020-10-28T11:23:41.463 に答える