OrbitControls on a shared camera cannot handle panning

Hi Community,

I am trying to create two renderers on two different div containers.
Each renderer wish have its own OrbitControls for updating the associated camera.
The camera is shared between the two renderer.

The goal is to sync the camera for OrbitControl (so that two containers can have the same camera view) and render between the two render loops.

I noticed that the general rotation can be shared automatically, however, when I do panning, it behaves a bit strange. Here is a video:

This is the minimum reproducible code:

<!DOCTYPE html>
<html lang="en">
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>

    <div id="rend1" style="height: 400px; width: 400px;"></div>
    <div id="rend2" style="height: 400px; width: 400px;"></div>

<script type="module">
import { OrbitControls } from 'https://cdn.jsdelivr.net/npm/three@0.121.1/examples/jsm/controls/OrbitControls.js';

const rend1 = document.getElementById("rend1")
const rend2 = document.getElementById("rend2")

const camera = new THREE.PerspectiveCamera(40, 1, 0.01, 1000);
camera.position.copy(new THREE.Vector3(0, 1, 2));
camera.lookAt(new THREE.Vector3(0, 0, 0));

const r1 = new THREE.WebGLRenderer({
    antialias: true,
    preserveDrawingBuffer: true,
    precision: "highp",
    alpha: true,
const s1 = new THREE.Scene();

const c1 =  new OrbitControls(camera, r1.domElement)
s1.add(new THREE.AxesHelper(1));
const g1 = new THREE.BoxGeometry( 0.5, 0.5, 0.5 );
const m1 = new THREE.MeshBasicMaterial( {color: 0x0080ff} );
const cube1 = new THREE.Mesh(g1, m1);

const r2 = new THREE.WebGLRenderer({
    antialias: true,
    preserveDrawingBuffer: true,
    precision: "highp",
    alpha: true,
const s2 = new THREE.Scene();
const c2 =  new OrbitControls(camera, r2.domElement)
s2.add(new THREE.AxesHelper(1));
const g2 = new THREE.BoxGeometry( 0.5, 0.5, 0.5 );
const m2 = new THREE.MeshBasicMaterial( {color: 0x0080ff} );
const cube2 = new THREE.Mesh(g2, m2);

const render1 = () => {
    r1.setSize(400, 400);
    r1.render(s1, camera);
    window.requestAnimationFrame(() => render1());
const render2 = () => {
    r2.setSize(400, 400);
    r2.render(s2, camera);
    window.requestAnimationFrame(() => render2());

What is the reason and how could we achieve desired behavior?

Thank you!

A simple fix would be to change c2 to instance of c1.

const c2 = c1;

Thanks! But then we can’t control the scene from r2’s domElement anymore.

Sorry about that. I’ll come up with a demo that copies transformation camera_origin to camera_target when changes detect. That should work 100%.

1 Like

OK, 2 Scenes controlled with OribitControl:

2S_!OC.html (90.6 KB)

EDIT: changes were made to attachment.

1 Like

Thanks a lot. The solution looks fantastic!

I just want to make sure that I understand your code correct:
In the code, instead of using a shared camera, we create different cameras for different renderers. But sync the camera position and rotation vectors if an orbit control is changed.

Do I get that right?

1 Like

Yes, you have two different independent scenes. _1 and _2 suffixes are used to distinguish the components.

One function to animate both graphs and OBControl.addEventListener() to sync them.

The original attachment was missing OBControl.target adjustment, so you may want to redownload it.

1 Like