代码之家  ›  专栏  ›  技术社区  ›  Rodrigo

3.js-显示边界球的开放透视照相机

  •  2
  • Rodrigo  · 技术社区  · 6 年前

    我有一个场景 .obj 文件。

    使用 PerspectiveCamera 我不想在镜头完全可见的情况下开始拍摄。

    • FOV固定在60
    • 对象大小可变
    • TrackballControls 用来控制摄像机

    我试过 this solution 但没用。

    代码:

    FOV = 60
    
    scene = new THREE.Scene()
    camera = new THREE.PerspectiveCamera( FOV, 500 / 350, 0.1, 10000 )
    scene.add( camera )
    
    objLoader = new THREE.OBJLoader();
    objLoader.load('/airboat.obj', function (object) {
      scene.add( object )
    
      var boundingBox = new THREE.Box3();
      boundingBox.setFromObject( object );
      var sphere = boundingBox.getBoundingSphere()
    
      // don't work
      var center = boundingBox.getCenter();
      var size = boundingBox.getSize();
      var maxDim = Math.max( size.x, size.y, size.z );
      var cameraZ = maxDim / 2 / Math.tan(Math.PI * FOV / 360);
      camera.lookAt(center)
      camera.position.set( center.x, center.y, cameraZ );
      camera.updateProjectionMatrix();
    })
    

    编辑

    我创造 this fiddle (向下滚动JS代码)和@ BreBeBin解决方案工作大部分时间,但有时边界框边缘是不可见的。

    3 回复  |  直到 6 年前
        1
  •  1
  •   Brakebein    6 年前

    你可以试着打电话 camera.lookAt(center) 设置相机位置后:

    camera.position.set( center.x, center.y, cameraZ );
    camera.lookAt(center);
    

    或者您可以尝试使用boundingsphere方法,因为我在项目中使用它来将对象调整到视图中: OrbitControls ,但它应该工作相同 TrackballControls )

    var sphere = boundingBox.getBoundingSphere();
    
    // compute new distance between camera and center of object/sphere
    var h = sphere.radius / Math.tan( camera.fov / 2 * THREE.Math.DEG2RAD );
    
    // get direction of camera (when using Orbit or TrackballControls, the camera is
    // always pointing  to the control's target, so I'm using this)
    var dir = new THREE.Vector3().subVectors(camera.position, controls.target);
    
    // compute new camera position
    var newPos = new THREE.Vector3().addVectors(sphere.center, dir.setLength(h));
    
    // copy values
    camera.position.copy(newPos);
    controls.target.copy(sphere.center);
    
    camera.lookAt(controls.target); // -> you don't need to call this as this is already part of controls.update() routine
    

    提示: 确保相机和控制器不在同一位置。否则,计算方向是行不通的。

    编辑: 好吧,我从@rabbid76的图表中得到了提示:我的解决方案基本上与第一个一致。在某些情况下,对象或边界框的某些部分将位于视锥之外。所以第二张图所示的方法更好。为此,我们只需要改变 tan sin :

    var h = sphere.radius / Math.sin( camera.fov / 2 * THREE.Math.DEG2RAD );
    
        2
  •  2
  •   Rabbid76    6 年前

    新的摄像机目标是球体的中心,新的摄像机位置沿当前视线的相反方向移动计算距离:

    视线是 camera.position controls.target

    var current_los = new THREE.Vector3().subVectors(controls.target, camera.position);
    

    新目标的位置计算如下:

    var new_pos     = new THREE.Vector3().addVectors(center, current_los.setLength(-cameraZ));
    var new_target  = center; 
    

    更进一步 position 必须在 look At 调用,因为函数取决于位置和 controls 必须更新:

    var boundingBox = new THREE.Box3();
    boundingBox.setFromObject( group );
    
    var center = boundingBox.getCenter();
    var size   = boundingBox.getSize();
    
    var distance = Math.max( size.x, size.y, size.z );
    //var distance = boundingBox.getBoundingSphere().radius * 2;
    
    //var cameraZ = distance / 2 / Math.tan(Math.PI * FOV / 360);
    var cameraZ = distance / 2 / Math.sin(Math.PI * FOV / 360);
    
    var current_los = new THREE.Vector3().subVectors(controls.target, camera.position);
    var new_pos     = new THREE.Vector3().addVectors(center, current_los.setLength(-cameraZ));
    var new_target  = center; 
    
    // copy values
    camera.position.copy(new_pos);
    camera.lookAt(center);
    
    controls.target.copy(center);
    controls.update();
    

    如果你使用包围包围盒的球的直径,而不是盒子的最大角度长度,你将获得一个稍微大一些的图像部分:

    var distance = boundingBox.getBoundingSphere().radius * 2;
    

    但请注意,这可能仍然不是你想要的。请参见图片:

    FOV sphere

    或许你想这么做:

    FOV sphere 2

    所以你必须用正弦代替正切:

    sin(FOV/2) == (maxDim/2) / cameraZ;
    

    var cameraZ = maxDim / 2 / Math.sin(Math.PI * FOV / 360);
    
        3
  •  0
  •   user7193487    6 年前

    加载.obj,然后将相机设置为.obj的“中心”,您将告诉相机移动到加载的obj的确切中心,而不是加载到视图中。

    var center = boundingBox.getCenter(); //This gets the exact center of the box
    

    你要做的是得到你正在加载的对象的宽度和深度,然后从相机的中心位置减去它。

    例如

    camera.position.set( center.x - size.x, center.y - size.y, cameraZ - size.z );
    

    上面的代码片段(未经测试)会将相机位置移回加载对象的整个大小,从而使其进入视图。您还可以减去另一个“缓冲区”,这将使您能够看到更多,而不是在总边界。

    希望这有帮助