Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/three.js/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 如何使相机适合对象_Javascript_Three.js - Fatal编程技术网

Javascript 如何使相机适合对象

Javascript 如何使相机适合对象,javascript,three.js,Javascript,Three.js,使用three.js,我有以下内容 包含多个Object3D实例的场景 几个预定义的摄像机矢量3位置 如果屏幕调整大小,画布的动态宽度/高度 用户可以选择一个对象(从上面) 用户可以选择摄像机位置(从上方) 给定正在查看的对象和他们选择的相机位置,我如何计算最终相机位置以“最适合”屏幕上的对象 如果在某些屏幕上“按原样”使用摄影机位置,则对象会在我的视口边缘上流血,而其他对象则会显得更小。我相信可以将对象安装到相机的平截头台上,但没有找到合适的对象。我假设您使用的是透视相机 您可以设置相机的

使用three.js,我有以下内容

  • 包含多个Object3D实例的场景
  • 几个预定义的摄像机矢量3位置
  • 如果屏幕调整大小,画布的动态宽度/高度
  • 用户可以选择一个对象(从上面)
  • 用户可以选择摄像机位置(从上方)
给定正在查看的对象和他们选择的相机位置,我如何计算最终相机位置以“最适合”屏幕上的对象


如果在某些屏幕上“按原样”使用摄影机位置,则对象会在我的视口边缘上流血,而其他对象则会显得更小。我相信可以将对象安装到相机的平截头台上,但没有找到合适的对象。

我假设您使用的是透视相机

您可以设置相机的位置、视野或两者

下面的计算对于立方体对象是精确的,因此请考虑对象的边界框,该边界框与摄影机对齐


如果相机居中并正面查看立方体,请定义

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

如果按如下方式设置摄影机的视野

fov = 2 * Math.atan( height / ( 2 * dist ) ) * ( 180 / Math.PI ); // in degrees
然后立方体高度将与可见高度匹配

在这一点上,您可以将相机向后退一点,或者稍微增大视野

如果视场是固定的,则使用上述方程来求解距离


编辑:如果希望立方体
宽度
与可见宽度相匹配,则将
纵横比
设为画布的纵横比(画布宽度除以画布高度),并按如下方式设置相机视野

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

three.js r.69

根据Westlangley的回答,这里是使用固定摄像机视场计算距离的方法:

dist = height / 2 / Math.tan(Math.PI * fov / 360);

要计算相机放置到屏幕上的距离,可以使用以下公式(在Javascript中):

其中
objectSize
是对象的高度或宽度。对于立方体/球体对象,可以使用高度或宽度。对于长度或宽度较大的非立方体/非球体对象,请使用
var objectSize=Math.max(width,height)
获取较大的值

请注意,如果对象位置不在
0,0,0
,则需要调整相机位置以包括偏移

在行动中展示这一点。相关线路:

var fov = cameraFov * ( Math.PI / 180 );
var objectSize = 0.6 + ( 0.5 * Math.sin( Date.now() * 0.001 ) );

var cameraPosition = new THREE.Vector3(
    0,
    sphereMesh.position.y + Math.abs( objectSize / Math.sin( fov / 2 ) ),
    0
);

您可以看到,如果抓住窗口控制柄并调整其大小,球体仍然占据屏幕高度的100%。此外,对象以正弦波方式上下缩放(
0.6+(0.5*Math.sin(Date.now()*0.001))
),以显示相机位置考虑了对象的缩放。

根据用户151496关于使用纵横比的建议,这似乎有效,虽然我只测试了几个不同的参数集

var maxDim = Math.max(w, h);
var aspectRatio = w / h;        
var distance = maxDim/ 2 /  aspectRatio / Math.tan(Math.PI * fov / 360);

请尝试使用此控件

    let padding = 48;
    let w = Math.max(objectLength, objectWidth) + padding;
    let h = objectHeight + padding;

    let fovX = camera.fov * (aspectX / aspectY);
    let fovY = camera.fov;

    let distanceX = (w / 2) / Math.tan(Math.PI * fovX / 360) + (w / 2);
    let distanceY = (h / 2) / Math.tan(Math.PI * fovY / 360) + (w / 2);

    let distance = Math.max(distanceX, distanceY);

我也有同样的问题,但我希望如果整个屏幕比我的屏幕宽,那么我的手机上的对象(由
Box3
整体表示)可以旋转,这样我就可以通过尽可能近的放大来查看它

const objectsize=bboxMap.getSize();
log('centerPoint',centerPoint,bboxMap,objectsize,tileMap);
//设置等轴测正交摄影机(bboxMap);
//https://gamedev.stackexchange.com/questions/43588/how-to-rotate-camera-centered-around-the-cameras-position
//https://threejs.org/docs/#api/en/cameras/PerspectiveCamera
//https://stackoverflow.com/questions/14614252/how-to-fit-camera-to-object
//顶
//      +--------+
//左|摄像机|右
//      +--------+
//底部
//canvas.height/2/disance=tan(视野);canvas.width/2/disance=tan(fovLR);
//=>canvas.width/canvas.height=tan(视野)/tan(视野);
//=>tan(视场)=tan(视场)*aspectRatio;
//如果将相机在局部空间中绕z轴旋转90度。
//左
//        +---+
//底部| |顶部
//        |   |
//        +---+
//对
//=>tan(视野)=tan(视野)/aspectRatio;
常数填充=0,视场=50;
设aspectRatio=canvas.width/canvas.height;
设tanFOV=Math.tan(Math.PI*fov/360);
让viewWidth=padding+objectSizes.x,viewHeight=padding+objectSizes.y;
//距离与视图的宽度或高度成比例
let distanceH=视图宽度/2/(tanFOV*aspectRatio);
let distanceV=视图高度/2/坦福夫;
const-camera=this.camera=new-THREE.perspective-camera(视野,视角,0.11000)//视角、方向、近距离、远距离
如果(aspectRatio>1!=视图宽度>视图高度){
log(“屏幕比要查看的对象更窄”);
//viewWidth/canvas.width=>viewHeight/canvas.width
//viewHeight/canvas.height=>viewWidth/canvas.height;
距离*=视图高度/视图宽度;
距离V*=视图宽度/视图高度;
camera.rotateZ(Math.PI/2);
}
camera.position.z=Math.max(distanceH,distanceV)+bboxMap.max.z;
//摄像机注视(tileMap位置);
我用手机在两个不同方向(横向和纵向)上测试了Box3的两个不同方面,效果很好

参考资料

  • target
    -结果将复制到此
    Vector3
    中。 返回此框的宽度、高度和深度

  • (透视照相机)
    rad
    -以弧度为单位旋转的角度。 在局部空间中绕z轴旋转对象

  • 其他答案

假设对象适合屏幕,如果其边界球体适合,我们将任务简化为将球体适合摄影机视图

在给定的示例中,我们在更改摄影机旋转以获得对象的最佳视点时保持不变。变焦效果是通过移动相机来实现的。观察方向向量

在图片上,您可以看到问题定义: 给定边界球体和camera.fov,找到L,使边界球体接触相机的平截头体平面。

这是怎么做的
var maxDim = Math.max(w, h);
var aspectRatio = w / h;        
var distance = maxDim/ 2 /  aspectRatio / Math.tan(Math.PI * fov / 360);
    let padding = 48;
    let w = Math.max(objectLength, objectWidth) + padding;
    let h = objectHeight + padding;

    let fovX = camera.fov * (aspectX / aspectY);
    let fovY = camera.fov;

    let distanceX = (w / 2) / Math.tan(Math.PI * fovX / 360) + (w / 2);
    let distanceY = (h / 2) / Math.tan(Math.PI * fovY / 360) + (w / 2);

    let distance = Math.max(distanceX, distanceY);
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();