Search code examples
three.jsreact-three-fiberorbitcontrols

OrbitControls - how to make the camera look the opposite way


I'm using react-three-fiber with OrbitControls to display a textured sphere with a backside material. I want my camera to orbit inside that sphere and around it's center. OrbitControls does that just fine, but it always points at the target which is in the center. I want it to point in exact opposite direction (to the outside) while keeping all the features of OrbitControls

<Canvas camera={{ fov: 45, position: [1, 0, 0] }}>
  <Suspense fallback={null}>
    <mesh>
      <sphereGeometry args={[110, 32, 32]} attach="geometry" />
      <meshBasicMaterial side={BackSide}>
        <GradientTexture
          stops={[0, 0.45, 0.5, 0.55, 1]}
          colors={['red', 'green', 'black', 'blue', 'pink']}
          size={1024}
        />
      </meshBasicMaterial>          
      <OrbitControls target={[0, 0, 0]} enableZoom={false} reverseOrbit={true}/>
    </mesh>
  </Suspense>
</Canvas>

Right now I'm using something like this and it looks ok, but I don't have the ability to zoom in a proper way.


Solution

  • Ok, so for anyone who might stumble upon similar issue - don't do it like this. I actually managed to achieve this by wrapping OrbitControls in a component and using useFrame:

    function Controls() {
      const controlsRef = useRef<OrbitControlsImpl>(null);
    
      useFrame(({ gl, scene, camera }) => {
        if (!controlsRef.current) {
          return
        }
        const { x, y, z } = controlsRef.current.object.position
        camera.lookAt(x * 2, y * 2, z * 2)
        gl.render(scene, camera)
      }, 1);
    
      return <OrbitControls
        target={[0, 0, 0]}
        ref={controlsRef}
        minDistance={10}
        maxDistance={80}
      />
    }
    

    But then I got my wheel and horizontal panning reversed and couldn't find a way to work around it. Eventually, the optimal solution was to switch to a PerspectiveCamera, disable zoom for OrbitControls and writing my own wheel handler that used camera.zoom property:

    const { camera, gl: { domElement } } = useThree();
    
    function handleWheel(this, ev) {
        ev.stopPropagation()
        ev.preventDefault()
        let newZoom = camera.zoom + -1*(ev.deltaY/1000)
        if(newZoom < 1) newZoom = 1
        if(newZoom > 10) newZoom = 10
        camera.zoom = newZoom
        camera.updateProjectionMatrix()
    }
    domElement.addEventListener("wheel", handleWheel);