javascript - THREE.JS更新透视相机的fov值并保持相同的相机距离

标签 javascript three.js camera

我想在动画过程中修改相机的 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/

相关文章:

javascript - HTML5 Game Canvas - 使用最后的 velX 和 velY 计算子弹的速度和方向

javascript - Three.js 摸脸神器

javascript - 如何在单击时切换元素的边框属性,而不取代其他元素?

javascript - AJAX PHP,在按钮上单击将变量从javascript提示传递给php脚本以设置cookie?

javascript - 在 three.js 中使用 jquery

javascript - 将简单的自定义着色器应用于 Three.js 多维数据集

android - 切换到前置摄像头和后置摄像头 Android SurfaceView

android - Android 6.0 未经许可抓拍图片

c# - unity 3d碰撞时相机抖动

javascript - react 路由器在不在 route 时显示组件的问题