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