我想在动画过程中修改相机的 FOV 值。 不幸的是,一旦我实现了 FOV 值,我就可以观察到我的场景变小了。
所以我一直在想,FOV 值和透视相机的距离位置之间的数学联系是什么?
想法是在 FOV 值发生变化时拥有相同的场景(通过修改相机位置获得相同的大小)。
非常感谢。
编辑 1:
这是一个说明我的问题的片段:当我实现相机的 FOV 值(从 4 到 45)时,我的正方形和相机之间的距离发生变化。我该如何预防?
let W = window.innerWidth, H = window.innerHeight;
let renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( W, H );
document.body.appendChild( renderer.domElement );
let camera = new THREE.PerspectiveCamera( 4, W/H, 1, 100 );
let scene = new THREE.Scene();
camera.position.set(0,0,14);
camera.lookAt(0,0,0);
let geo = new THREE.BoxGeometry(0.5, 0.5, 0.5);
let mat = new THREE.MeshNormalMaterial();
let mesh = new THREE.Mesh(geo, mat);
mesh.rotation.set(0.2,0.4,-0.1);
scene.add(mesh);
renderer.render(scene, camera);
let progress = {};
progress.fov = 4;
TweenMax.to(progress, 2,{
fov:45,
onUpdate:function(){
camera.lookAt(0,0,0);
camera.updateProjectionMatrix();
camera.fov = progress.fov;
renderer.render(scene, camera);
},
repeat:-1,
ease:Power3.easeInOut
});
body{margin:0;padding:0;overflow:hidden;background: #666;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.3/TweenMax.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/108/three.js"></script>
最佳答案
透视投影描述了从针孔相机看到的世界中的 3D 点到视口(viewport)的 2D 点的映射。
这意味着投影到视口(viewport)上的对象会随着深度变小。
视空间中的投影面积与视空间的 Z 坐标之间的关系是线性的。这取决于视角和纵横比。
另见 THREE.js PerspectiveCamera focalLength off by a factor of two, inconsistent with FOV .
在透视投影中,Z 距离和视野 fov_y
大小之间的关系是:
depht_s = Math.tan(fov_y/2.0 * Math.PI/180.0) * 2.0;
对象在视口(viewport)上的投影大小取决于视野和深度。这导致当视野发生变化时,3 维对象永远不会“看起来”相同。您可以在某个距离(深度)定义一个平面并找到一个新的距离,这样物体的投影就不会改变大小,正好是那个深度。当然,这个特定距离“之前”和“之后”的物体的投影大小会(至少是轻微的)变化。另见 How to switch between Perspective and Orthographic cameras keeping size of desired object分别Transpose z-position from perspective to orthographic camera in three.js .
请参见插图。虽然当物体靠近相机时无法从相机看到立方体的顶点,但当它远离相机时可以看到:
初始视野为 4.0。所以 Z 距离和大小之间的比率是:
let init_depht_s = Math.tan(4.0/2.0 * Math.PI/180.0) * 2.0;
当视野动画时,Z 距离和大小之间的当前比例为:
let current_depht_s = Math.tan(progress.fov/2.0 * Math.PI/180.0) * 2.0;
现在您必须定义一个距离,该距离必须“保持大小”。到立方体中心的初始距离是 14.0,所以我会选择这个作为引用距离。这个距离必须按比例 init_depht_s/current_depht_s
进行缩放,然后立方体的投影(恰好在这个距离)保持其大小:
camera.position.set(0, 0, 14 * init_depht_s / current_depht_s);
查看基于您原始代码的示例(我已将近平面更改为 0.1,否则立方体将被裁剪,因为最终距离低于 1.0):
let W = window.innerWidth, H = window.innerHeight;
let renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( W, H );
document.body.appendChild( renderer.domElement );
let camera = new THREE.PerspectiveCamera( 4, W/H, 0.1, 100 );
let scene = new THREE.Scene();
camera.position.set(0,0,14);
camera.lookAt(0,0,0);
let geo = new THREE.BoxGeometry(0.5, 0.5, 0.5);
let mat = new THREE.MeshNormalMaterial();
let mesh = new THREE.Mesh(geo, mat);
mesh.rotation.set(0.2,0.4,-0.1);
scene.add(mesh);
renderer.render(scene, camera);
let progress = {};
progress.fov = 4;
TweenMax.to(progress, 2,{
fov:45,
onUpdate:function(){
let init_depht_s = Math.tan(4.0/2.0 * Math.PI/180.0) * 2.0;
let current_depht_s = Math.tan(progress.fov/2.0 * Math.PI/180.0) * 2.0;
camera.position.set(0, 0, 14 * init_depht_s / current_depht_s);
camera.lookAt(0,0,0);
camera.updateProjectionMatrix();
camera.fov = progress.fov;
renderer.render(scene, camera);
},
repeat:-1,
ease:Power3.easeInOut
});
body{margin:0;padding:0;overflow:hidden;background: #666;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.3/TweenMax.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/108/three.js"></script>
关于javascript - THREE.JS更新透视相机的fov值并保持相同的相机距离,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57959190/