共享相机上的 OrbitControls 无法处理平移

OrbitControls on a shared camera cannot handle panning

我正在尝试在两个不同的 div 容器上创建两个渲染器。 每个渲染器都希望有自己的 OrbitControls 来更新关联的相机。 相机在两个渲染器之间共享。

目标是同步 OrbitControl 的相机(以便两个容器可以具有相同的相机视图)并在两个渲染循环之间进行渲染。

我注意到可以自动共享常规旋转。但是,当我平移时,它的行为有点奇怪。这是一个视频:

https://user-images.githubusercontent.com/5498964/170499367-44c2581d-8089-492e-bc90-a6675976bb44.mov

这是最小可重现代码:

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

    <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,
  });
r1.setPixelRatio(window.devicePixelRatio);
const s1 = new THREE.Scene();
rend1.append(r1.domElement);

const c1 =  new OrbitControls(camera, r1.domElement)
s1.add(new THREE.AxesHelper(1));
s1.add(camera);
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);
s1.add(cube1)

const r2 = new THREE.WebGLRenderer({
    antialias: true,
    preserveDrawingBuffer: true,
    precision: "highp",
    alpha: true,
  });
r2.setPixelRatio(window.devicePixelRatio);
const s2 = new THREE.Scene();
rend2.append(r2.domElement);
const c2 =  new OrbitControls(camera, r2.domElement)
s2.add(new THREE.AxesHelper(1));
s2.add(camera);
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);
s2.add(cube2)



const render1 = () => {
    r1.setSize(400, 400);
    c1.update();
    r1.render(s1, camera);
    window.requestAnimationFrame(() => render1());
};
const render2 = () => {
    r2.setSize(400, 400);
    c2.update();
    r2.render(s2, camera);
    window.requestAnimationFrame(() => render2());
};
render1 ();
render2 ();
</script>
</body>
</html>

原因是什么?我们如何才能达到预期的效果?

谢谢!

您不需要将相机添加到任何场景。相机可以是独立的。另外,一个Object3D只能有一个 parent,所以当你做

s1.add(camera);
s2.add(camera);

...然后相机只会添加到 s2,因为当您分配新的 parent.

时它会自动从 s1 中删除

尝试从您的代码中删除所有 .add(camera)

编辑:

此外,您要将两个 CameraControls 添加到同一个相机,然后调用 c1.update()c2.update()。这两个向相机发出相互冲突的命令,因为一个控件告诉它移动,而另一个控件告诉它保持静止。如果你想操纵一个相机,你应该只使用一个控件。

此问题的实际解决方案是在相机和轨道控制目标发生变化时明确同步它们。参见 c1.addEventListener("change", () => { ... }}c2.addEventListener("change", () => { ... }}

这是解决问题的完整代码:

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

    <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 camera1 = new THREE.PerspectiveCamera(40, 1, 0.01, 1000);
camera1.position.copy(new THREE.Vector3(0, 1, 2));
camera1.lookAt(new THREE.Vector3(0, 0, 0));
const camera2 = new THREE.PerspectiveCamera(40, 1, 0.01, 1000);
camera2.position.copy(new THREE.Vector3(0, 1, 2));
camera2.lookAt(new THREE.Vector3(0, 0, 0));
const r1 = new THREE.WebGLRenderer({
    antialias: true,
    preserveDrawingBuffer: true,
    precision: "highp",
    alpha: true,
  });
r1.setPixelRatio(window.devicePixelRatio);
const s1 = new THREE.Scene();
rend1.append(r1.domElement);

const c1 =  new OrbitControls(camera1, r1.domElement)
s1.add(new THREE.AxesHelper(1));
s1.add(camera1);
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);
s1.add(cube1)
c1.addEventListener("change", () => {
    camera2.position.copy(camera1.position)
    camera2.rotation.copy(camera1.rotation)
    c2.target.copy(c1.target)
})

const r2 = new THREE.WebGLRenderer({
    antialias: true,
    preserveDrawingBuffer: true,
    precision: "highp",
    alpha: true,
  });
r2.setPixelRatio(window.devicePixelRatio);
const s2 = new THREE.Scene();
rend2.append(r2.domElement);
const c2 =  new OrbitControls(camera2, r2.domElement)
s2.add(new THREE.AxesHelper(1));
s2.add(camera2);
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);
s2.add(cube2)
c2.addEventListener("change", () => {
    camera1.position.copy(camera2.position)
    camera1.rotation.copy(camera2.rotation)
    c1.target.copy(c2.target)
})




const render1 = () => {
    r1.setSize(400, 400);
    c1.update();
    r1.render(s1, camera1);
    window.requestAnimationFrame(() => render1());
};
const render2 = () => {
    r2.setSize(400, 400);
    c2.update();
    r2.render(s2, camera2);
    window.requestAnimationFrame(() => render2());
};
render1 ();
render2 ();
</script>
</body>
</html>