Some Trouble With Multi Canvas

Hi~
I create a multi canvas engine to render 4 scene to 4 different canvas, but camera and scene.onPointerObservable do not work well: only the last canvas invoking event when mouse hover it. Nothing happened for other 3 canvas. When I move camera in the last canvas, four camera are move at the same time.
Question:

  1. Does only one scene/camera could be attachControl to engine at same time?
  2. If I want four scenes could be controlled, do I have to attachControl on and detachControl others?
    looking for any help :grin:

Here is my code

const Test = () => {
    const canvas = document.createElement("canvas");
    const engine = new Engine(canvas, true, {}, true);
    const views: {
        view: EngineView,
        scene: Scene,
        canvas: any,
    }[] = [];
    engine.runRenderLoop(function () {
        if (engine.activeView) {
            for (const view of views) {
                if (view.view === engine.activeView) {
                    view.scene.render();
                }
            }
        }
    });
    for (let index = 1; index <= 4; index++) {
        const scene = new Scene(engine, {});
        const meshRoot = new TransformNode("Mesh Root", scene);
        const camera = new ArcRotateCamera("Camera", 0, Math.PI / 2, 5, Vector3.Zero(), scene);
        for (let meshIndex = 0; meshIndex < 3; meshIndex++) {
            const randomInt = Math.floor(Math.random() * 5);
            let mesh: Mesh
            switch (randomInt) {
                case 1:
                    mesh = MeshBuilder.CreateBox("Box", { size: 1 }, scene);
                    break;
                case 2:
                    mesh = MeshBuilder.CreateCapsule("Capsule", { height: 2, radius: 0.5 }, scene);
                    break;
                case 3:
                    mesh = MeshBuilder.CreateCylinder("Cylinder", { height: 2, diameterTop: 1, diameterBottom: 1 }, scene);
                    break;
                default:
                    mesh = MeshBuilder.CreateSphere("Sphere", { diameter: 1 }, scene);
                    break;
            }
            mesh.setParent(meshRoot);
            mesh.position.set((meshIndex - 1) * 2, 0, 0);
        }
        const key = "unityPreview" + index;
        const sceneCanvas = document.getElementById(key) as any
        const view = engine.registerView(sceneCanvas, camera);
        views.push({ view, scene, canvas });
        scene.detachControl();
        camera.detachControl();
        engine.inputElement = sceneCanvas;
        scene.attachControl(sceneCanvas, true);
        camera.attachControl();
        scene.onPointerObservable.add(() => {
            console.log("Pointer Event", key);
        })

    }


}

Video about code working

Hello,

I think problem comes from the fact that you are using multiple canvases :

  • If your goal is just to create a split screen, then you can use 1 full screen canvas, 1 engine, and 4 scenes. Then you would use camera.viewport to render on only part of the canvas.

Example with 2 cameras :

const camera1 = new BABYLON.FreeCamera("camera1", new BABYLON.Vector3(0, 5, -10), scene1);
camera1.viewport = new BABYLON.Viewport(0, 0, 0.5, 1); // Left half

const camera2 = new BABYLON.FreeCamera("camera2", new BABYLON.Vector3(0, 5, 10), scene2);
camera2.viewport = new BABYLON.Viewport(0.5, 0, 0.5, 1); // Right half

scene1.activeCamera = camera1;
scene2.activeCamera = camera2;

engine.runRenderLoop(() => {
    scene1.render();
    scene2.render();
});
  • Now if for some reason you absolutely need 4 canvas, I think that 1 engine is strictly linked to 1 canvas. I might be wrong, but to me the fact that the engine constructor takes the canvas as 1st parameter, is because it needs to bind to a WebGL (or WebGPU) context which is provided by the canvas itself.
    So it means you would need to create 4 engines :
function createScene(engine, canvas) {
    // Scene & Camera
    const scene = new BABYLON.Scene(engine);
    const camera = new BABYLON.ArcRotateCamera("camera", 0.25 * Math.PI, 1, 8, new BABYLON.Vector3(0, 0, 0), scene);
    camera.attachControl(canvas, true);

    // Light
    const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);

    // Ground & Sphere
    var ground = BABYLON.MeshBuilder.CreateGround("ground", { width: 6, height: 6 }, scene);
    var sphere = BABYLON.MeshBuilder.CreateSphere("sphere", { diameter: 2, segments: 32 }, scene);
    sphere.position.y = 1;

    return scene;
}

// Create 4 scenes on 4 canvases
for (let i = 0; i < 4; i++) {
    // Canvas
    const canvas = document.getElementById("renderCanvas" + i);

    // Engine
    const engine = new BABYLON.Engine(canvas, true);

    // Resize
    window.addEventListener("resize", function () {
        engine.resize();
    });

    // Scene
    const scene = createScene(engine, canvas);

    // Render loop
    engine.runRenderLoop(function () {
        scene.render();
    });
}

And this is OK with attaching independently to mouse :

Screencast from 21-02-2025 12:54:51

2 Likes

Thank you! I attach/detach control when mouse hover in/off canvas, now I can control each canvas. sometimes multi engines have some confuse problem: if a canvas is out of screen, when it show in screen again, scene is all black, it likes that chrome delete texture and light memory when they are not active.