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

Mic*_*aël 4 javascript camera three.js

我想在动画过程中修改相机的 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
            });
            
        
Run Code Online (Sandbox Code Playgroud)
body{margin:0;padding:0;overflow:hidden;background: #666;}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

Rab*_*d76 10

透视投影描述了从针孔相机看到的世界上的 3D 点到视口的 2D 点的映射。这意味着投影在视口上的对象因其深度而变小。
视图空间中的投影面积与视图空间的 Z 坐标之间的关系是线性的。这取决于视角和纵横比。
另请参见THREE.js PerspectiveCamera focusLength 关闭两倍,与 FOV 不一致

透视投影时, Z 距离与视野大小之间的关系fov_y为:

depht_s = Math.tan(fov_y/2.0 * Math.PI/180.0) * 2.0;
Run Code Online (Sandbox Code Playgroud)

对象在视口上的投影尺寸取决于视野和深度。这导致当视野改变时,3维物体永远不会“看起来”相同。您可以在一定距离(深度)内定义一个平面并找到一个新距离,以便在该深度上,对象的投影大小不会发生变化。当然,该特定距离“之前”和“之后”的物体的投影尺寸将会(至少轻微地)改变。另请参阅如何在透视相机和正交相机之间切换,分别保持所需对象的大小,在 Three.js 中将 z 位置从透视相机转置为正交相机

请参阅插图。当物体靠近相机时,从相机看不到立方体的顶点,但当物体远离相机时,可以看到它:

初始视野为4.0。所以 Z 距离和尺寸之间的比率是:

let init_depht_s    = Math.tan(4.0/2.0 * Math.PI/180.0) * 2.0;
Run Code Online (Sandbox Code Playgroud)

当视野动画化时,Z 距离和大小之间的当前比率为:

let current_depht_s = Math.tan(progress.fov/2.0 * Math.PI/180.0) * 2.0;
Run Code Online (Sandbox Code Playgroud)

现在您必须定义一个距离,该距离必须“保持尺寸”。到立方体中心的初始距离是 14.0,所以我将选择它作为参考距离。该距离必须按 ration 进行缩放init_depht_s / current_depht_s,然后立方体的投影(恰好在该距离处)保持其大小:

camera.position.set(0, 0, 14 * init_depht_s / current_depht_s);
Run Code Online (Sandbox Code Playgroud)

请参阅示例,该示例基于您的原始代码(我已将近平面更改为 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
});
Run Code Online (Sandbox Code Playgroud)
body{margin:0;padding:0;overflow:hidden;background: #666;}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)