Search code examples
javascriptthree.jsperspectivecamera

Three.js Orbit Controls with Two (Toggled) Cameras


I have two cameras in one scene and one viewport and one renderer. I toggle (using html buttons) between cameras.

THE PROBLEMS

Problem 1

For camera1 there is no response from moving the mouse. When I toggle to camera2 the orbit control works fine. Toggling back to camera1 there is still no response from moving the mouse.

jsfiddle v1 Original (no response from camera1) jsfiddle.net/steveow/xu0k1z75/

UPDATE: Problem 1, Fixed by Stallion - avoid setting camera position to (0,0,0).

Problem 2

For camera1 there remains a problem that Pan and Dolly are very slow, at least initially. They may speed up later (after panning & dollying with camera2) but are then very fast.

jsfiddle: v2 camera1 Pan & Dolly is very slow, at least initially, maybe excessive later. http://jsfiddle.net/steveow/uk94hxrp/

UPDATE: Pan & Dolly "slowness" is because camera is very close to the OrbitControls.target position (which defaults to (0,0,0)). So if I choose a different .target position the "slowness" can be avoided.

NOTES

I am currently creating a new THREE.OrbitControls object whenever I switch cameras. But previously I have tried creating two persistent THREE.OrbitControls objects during initialisation and then assigning a general variable called "controls" to whichever one is supposed to be active. I have tried setting the orbitControls to an html div "container" rather than the renderer.domElement. I have tried including controls.update() in the animation loop.

I did have it working with either camera some time ago but I cannot get back to that.

I have looked at the OrbitControls code but am none-the-wiser.

CODE (as for original Problem, Problem 1 but since modified slightly.).

Here is the camera initiation code:-

//... camera1

    camera1Fov = 75;
    camera1Far = 1200;
    camera1 = new THREE.PerspectiveCamera( camera1Fov, window.innerWidth / window.innerHeight, 1, camera1Far );
    //camera1.position.z = camera1Far;
    camera1.position.set(0,0,0);


    scene.add(camera1);
    camera1.name = "Camera_1";

    var sGeo        = new THREE.SphereGeometry( 40,8,8);
    var sMaterial   = new THREE.MeshPhongMaterial( { color: 0xff00ff } );
    Cam1Target      = new THREE.Mesh(sGeo, sMaterial);
    Cam1Target.position.set(0,0,-200);
    scene.add(Cam1Target);

    controls1 = new THREE.OrbitControls(camera1, renderer.domElement);
    controls = controls1;//... initially.
    camera1.lookAt( Cam1Target );
    RenderCamera = camera1;

    camera1_Helper = new THREE.CameraHelper( camera1 );
    camera1_Helper.update();
    scene.add (camera1_Helper);

// camera2
    camera2 = new THREE.PerspectiveCamera( cameraFOV, window.innerWidth / window.innerHeight, 1, 20000 );
    c2PosX =  5500;
    c2PosY =  3500;
    c2PosZ = -10000;

    camera2.position.set( c2PosX, c2PosY, c2PosZ );
    scene.add(camera2);
    camera2.name = "Camera_2";    
    //controls2 = new THREE.OrbitControls(camera2, renderer.domElement);
    //camera2.lookAt(camera1);

Here is the animation and camera switching and camera resetting code:-

//----------------------------------------------------------------
function F_frame()
{
   //... Render
   af = requestAnimationFrame(F_frame);

   controls.update(); 

   renderer.render(scene, RenderCamera);


   tick+=0.001;

}//... EOF Frame().

//-------------------------------------------------------------
function F_Switch_Camera() 
{
    var SelectedCameraString = document.getElementById('myTextField').value;
    //...toggle
    if (SelectedCameraString == "camera1") 
    {
        SelectedCameraString = "camera2";
        RenderCamera = camera2;
        controls = new THREE.OrbitControls(camera2, container);//renderer.domElement);
        //controls.object = camera2;
        //controls.update();
        //controls = controls2;
    } else {
        SelectedCameraString = "camera1";
        RenderCamera = camera1;
        controls = new THREE.OrbitControls(camera1, container);//renderer.domElement);
        //controls.object = camera1;
        //controls.update();
        //controls = controls1;     
    }
    document.getElementById('myTextField').value = SelectedCameraString;
}       
//----------------------------------------------------------------------
function F_Reset_Camera1()
{
    camera1.position.set(0,0,0);
    camera1.lookAt ( Cam1Target );
}
//----------------------------------------------------------------------
function F_Reset_Camera2()
{
    camera2.position.set( c2PosX, c2PosY, c2PosZ );
    camera2.lookAt ( camera1 );
}

UPDATE

Many thanks to user Stallion for the simple fix - don't set camera world position to (0,0,0) use (0,0,1) instead.


Solution

  • There is quick fix:

    set camera1 position to

    camera1.position.set(0,0,1);

    check the fiddle http://jsfiddle.net/Stallion33/sgcfu4tt/

    // Variables
    var cameraDistance = 500;
    var cameraFOV = 45;
    var lightD = 20;
    var sphereRadius = 20;
    
    //var SelectedCameraString = "camera" ;
    var camera1, camera2, RenderCamera;
    var controls, controls1, controls2;
    
    // Scene
    var scene = new THREE.Scene();
    
    // Renderer
    var renderer = new THREE.WebGLRenderer();
    renderer.setClearColor(0x008888); //scene.fog.color ); 
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.sortObjects = false;
    
    document.getElementById('container').appendChild(renderer.domElement);
    
    
    scene.add(new THREE.AmbientLight(0xffffff));
    
    ///var light1 = new THREE.PointLight(0xffffff);
    //light1.position.set(-5400, 1400, -1400);
    
    //... camera1
    
    camera1Fov = 75;
    camera1Far = 1200;
    camera1 = new THREE.PerspectiveCamera(camera1Fov, window.innerWidth / window.innerHeight, 1, camera1Far);
    //camera1.position.z = camera1Far;
    camera1.position.set(0, 0, 0);
    
    /*
        // Near & Far Plane dimensions
        c1_hNear = 2 * Math.tan(camera1.fov * Math.PI / 180 / 2) * camera1.near; // CHANGED - deg to radians
        c1_wNear = c1_hNear * camera1.aspect; // width
    
        c1_hFar = 2 * Math.tan(camera1.fov * Math.PI / 180 / 2) * camera1.far; // CHANGED - deg to radians
        c1_wFar = c1_hFar * camera1.aspect; // width
    
        //... Frustrum Vertex Points
        //... far
        var camera1_farTopLeft      = new THREE.Vector3(-c1_wFar / 2,  c1_hFar / 2, -camera1.far); // CHANGED - sign flips
        var camera1_farBottomRight  = new THREE.Vector3( c1_wFar / 2, -c1_hFar / 2, -camera1.far);
        var camera1_farTopRight     = new THREE.Vector3( c1_wFar / 2,  c1_hFar / 2, -camera1.far);
        var camera1_farBottomLeft   = new THREE.Vector3(-c1_wFar / 2, -c1_hFar / 2, -camera1.far);
        //... near
        var camera1_nearTopLeft     = new THREE.Vector3(-c1_wNear / 2,  c1_hNear / 2, -camera1.near); // CHANGED - sign flips
        var camera1_nearBottomRight = new THREE.Vector3( c1_wNear / 2, -c1_hNear / 2, -camera1.near);
        var camera1_nearTopRight    = new THREE.Vector3( c1_wNear / 2,  c1_hNear / 2, -camera1.near);
        var camera1_nearBottomLeft  = new THREE.Vector3(-c1_wNear / 2, -c1_hNear / 2, -camera1.near);
        
        //... SW store vertices in array 
        
        Frustrum_Vertices = [];
        
        Frustrum_Vertices.push (camera1_farTopLeft);
        Frustrum_Vertices.push (camera1_farBottomRight);
        Frustrum_Vertices.push (camera1_farTopRight);
        Frustrum_Vertices.push (camera1_farBottomLeft);
        
        Frustrum_Vertices.push (camera1_nearTopLeft);
        Frustrum_Vertices.push (camera1_nearBottomRight);
        Frustrum_Vertices.push (camera1_nearTopRight);
        Frustrum_Vertices.push (camera1_nearBottomLeft);
        
        //... Frustrum_Marker_Spheres
        
        Frustrum_Marker_Spheres = [];
        
        var FV = Frustrum_Vertices;
        for (iii=0;iii<Frustrum_Vertices.length; iii++)
        {
    var sphereRadius = 60;
            var FVsphereMaterial = new THREE.MeshPhongMaterial( { color: 0xffff00, side: THREE.DoubleSide  } );
            
            if(iii>3)
            {
                sphereRadius = 0.05;//90;
                var FVsphereMaterial = new THREE.MeshPhongMaterial( { color: 0x0000ff, side: THREE.DoubleSide } );              
            }
            var sphereGeo  = new THREE.SphereGeometry( sphereRadius, 8, 8); 
            var sphereMesh = new THREE.Mesh( sphereGeo, FVsphereMaterial);
            Frustrum_Marker_Spheres.push ( sphereMesh );            
            sphereMesh.position.set( FV[iii].x, FV[iii].y, FV[iii].z ); 
    
        camera1.add( sphereMesh );              
        }
        */
    
    scene.add(camera1);
    camera1.position.set(0, 0, 1);
    camera1.name = "Camera_1";
    RenderCamera = camera1;
    
    var sGeo = new THREE.SphereGeometry(40, 8, 8);
    var sMaterial = new THREE.MeshPhongMaterial({
      color: 0xff00ff
    });
    Cam1Target = new THREE.Mesh(sGeo, sMaterial);
    Cam1Target.position.set(0, 0, -200);
    scene.add(Cam1Target);
    
    controls1 = new THREE.OrbitControls(camera1, renderer.domElement);
    controls = controls1; //... initially.
    camera1.lookAt(Cam1Target);
    
    camera1_Helper = new THREE.CameraHelper(camera1);
    camera1_Helper.update();
    scene.add(camera1_Helper);
    
    // camera2
    camera2 = new THREE.PerspectiveCamera(cameraFOV, window.innerWidth / window.innerHeight, 1, 20000);
    var c2PosX = 5500;
    var c2PosY = 3500;
    var c2PosZ = -10000;
    
    camera2.position.set(c2PosX, c2PosY, c2PosZ);
    scene.add(camera2);
    camera2.name = "Camera_2";
    
    
    //controls2 = new THREE.OrbitControls(camera2, renderer.domElement);
    //camera2.lookAt(camera1);
    
    /*
    // Material
      var material = new THREE.MeshPhongMaterial({ 
           color: 0xff0000, 
           opacity: 1,
           visible: true,
           side: THREE.DoubleSide
      });
    
    // Smaterial
      var Smaterial = new THREE.MeshLambertMaterial({ 
           color: 0xffff00, 
           opacity: 0.5,
           visible: true,
           transparent: true,
           side: THREE.DoubleSide
      });
    
        //... Fbox  
        //approximation to frustrum geometry
          var FboxGeo      = new THREE.BoxGeometry( 200,100,500);
          var FboxMesh     = new THREE.Mesh(FboxGeo, Smaterial);
          FboxMesh.position.set(0, 0, -125 );
          //camera1.add(FboxMesh);
     
    
    //====================================================
    
    function srnd(rng){return (Math.random()*rng*2)-rng;}
    
    //====================================================
    
    //... make balls
      var sphereRadius = 20;
      for(var i=0;i<100;i++)
      {
          // Ball
          var ballGeo      = new THREE.SphereGeometry( sphereRadius, sphereRadius, sphereRadius);
          var ballMaterial = new THREE.MeshLambertMaterial( { color: 0x00ff00 } );
          var ballMesh     = new THREE.Mesh(ballGeo, ballMaterial);
          ballMesh.position.set(srnd(500),srnd(500), -camera1Far + srnd(500));
          scene.add(ballMesh);
      }
    */
    //var tick=0;
    var raf;
    
    //var frustum = new THREE.Frustum();
    
    F_frame();
    
    //... END OF MAIN
    //====================================================
    
    
    //----------------------------------------------------------------
    function F_frame() {
      //... Render
      raf = requestAnimationFrame(F_frame);
    
      controls.update(); //...BAD in jsfiddle
    
      renderer.render(scene, RenderCamera);
    
      //tick+=0.001;
    
    } //... EOF Frame().
    
    //-------------------------------------------------------------
    function F_Switch_Camera() {
      var SelectedCameraString = document.getElementById('myTextField').value;
      //...toggle
      //alert("Hello");
      if (SelectedCameraString == "camera1") {
        SelectedCameraString = "camera2";
        RenderCamera = camera2;
        //controls = new THREE.OrbitControls(camera2, container);//renderer.domElement);
        controls = new THREE.OrbitControls(camera2, renderer.domElement);
        //controls.object = camera2;
        //controls.update();
        //controls = controls2;
      } else {
        SelectedCameraString = "camera1";
        RenderCamera = camera1;
        //controls = new THREE.OrbitControls(camera1, container);//renderer.domElement);
        controls = new THREE.OrbitControls(camera1, renderer.domElement);
        //controls.object = camera1;
        //controls = controls1;     
        //controls.update();
      }
      document.getElementById('myTextField').value = SelectedCameraString;
    }
    
    //----------------------------------------------------------------------
    function F_Reset_Camera1() {
      camera1.position.set(0, 0, 1);
      camera1.lookAt(Cam1Target);
    }
    //----------------------------------------------------------------------
    function F_Reset_Camera2() {
      camera2.position.set(c2PosX, c2PosY, c2PosZ);
      camera2.lookAt(camera1);
    }
    #container {
      height: 100%;
      width: 100%;
      min-height: 100%;
      min-width: 100%;
    }
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/controls/OrbitControls.min.js"></script>
    
    <h1 id="title_A">Camera Frustrum Box</h1>
    <input type="submit" id="byBtn1" value="Switch Camera" onclick="F_Switch_Camera()" />
    <input type="text" id="myTextField" value="camera1" />
    <input type="submit" id="byBtn2" value="Reset Camera1" onclick="F_Reset_Camera1()" />
    <input type="submit" id="byBtn3" value="Reset Camera2" onclick="F_Reset_Camera2()" />
    <div id="container"></div>