Multiple Scenes with independent cameras

Hi All,

Ive been chasing my tail for most of the day trying to come up with something that would work. I feel like I am over complicating it at this point.

I want to be able to display multiple 3d objects on a page… that you can move around independently of each other. I started by using the multiple views demo and hacking away at it for a bit while looking at the documentation for multiple scenes.

As such this is what ive come up with so far… I tried to create two functions to do the work and then eventually tried it with one function to achieve it… I have left the commented out code to show my original attempt… so far the result is just 2 objects/canvases It typically only allows one object to be moved and moves both of them when moving that single object. This is day 1 for me with babylonjs and I really love it… just a little confused here on how to achieve this.

        // Create a working document



var createScene = function () {

    // This creates a basic Babylon Scene object (non-mesh)
    var scene = new BABYLON.Scene(engine);

    // This creates and positions a free camera (non-mesh)
    var camera = new BABYLON.ArcRotateCamera("Camera0", 0, 0.8, 5, new BABYLON.Vector3.Zero(), scene);
    camera.setTarget(BABYLON.Vector3.Zero());

    camera.lowerRadiusLimit = 4;
    camera.upperRadiusLimit = 20;

    // This attaches the camera to the canvas
    camera.attachControl(document.getElementById("renderCanvas0"), true);

    // This creates a light, aiming 0,1,0 - to the sky (non-mesh)
    var light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);

    // Default intensity is 1. Let's dim the light a small amount
    light.intensity = 0.7;

    // Our built-in 'sphere' shape.
    var box = BABYLON.MeshBuilder.CreateBox("Box", {size: 2}, scene);

    box.position.y = 0.5;

    var mat = new BABYLON.PBRMetallicRoughnessMaterial("mat", scene);

    mat.metallic = 1;
    mat.roughness = 0.5;

    box.material = mat;

    scene.createDefaultEnvironment();
    
    engine.registerView(document.getElementById("renderCanvas0"));

    // This creates a basic Babylon Scene object (non-mesh)
    var scene1 = new BABYLON.Scene(engine);

    // This creates and positions a free camera (non-mesh)
    var camera1 = new BABYLON.ArcRotateCamera("Camera1", 0, 0.8, 5, new BABYLON.Vector3.Zero(), scene);
    camera1.setTarget(BABYLON.Vector3.Zero());

    camera1.lowerRadiusLimit = 4;
    camera1.upperRadiusLimit = 20;

    // This attaches the camera to the canvas
    camera1.attachControl(document.getElementById("renderCanvas1"), true);

    // This creates a light, aiming 0,1,0 - to the sky (non-mesh)
    var light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);

    // Default intensity is 1. Let's dim the light a small amount
    light.intensity = 0.7;

    // Our built-in 'sphere' shape.
    var box1 = BABYLON.MeshBuilder.CreateBox("Box", {size: 2}, scene);

    box1.position.y = 0.5;

    var mat1 = new BABYLON.PBRMetallicRoughnessMaterial("mat", scene);

    mat1.metallic = 1;
    mat1.roughness = 0.5;

    box1.material = mat1;

    scene1.createDefaultEnvironment();
    
    engine.registerView(document.getElementById("renderCanvas1"));


    return scene;

};

// var createScene1 = function () {

//     // This creates a basic Babylon Scene object (non-mesh)
//     var scene1 = new BABYLON.Scene(engine);

//     // This creates and positions a free camera (non-mesh)
//     var camera1 = new BABYLON.ArcRotateCamera("Camera1", 0, 0.8, 5, new BABYLON.Vector3.Zero(), scene);
//     camera1.setTarget(BABYLON.Vector3.Zero());

//     camera1.lowerRadiusLimit = 4;
//     camera1.upperRadiusLimit = 20;

//     // This attaches the camera to the canvas
//     camera1.attachControl(document.getElementById("renderCanvas1"), true);

//     // This creates a light, aiming 0,1,0 - to the sky (non-mesh)
//     var light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);

//     // Default intensity is 1. Let's dim the light a small amount
//     light.intensity = 0.7;

//     // Our built-in 'sphere' shape.
//     var box1 = BABYLON.MeshBuilder.CreateBox("Box", {size: 2}, scene);

//     box1.position.y = 0.5;

//     var mat1 = new BABYLON.PBRMetallicRoughnessMaterial("mat", scene);

//     mat1.metallic = 1;
//     mat1.roughness = 0.5;

//     box1.material = mat1;

//     scene1.createDefaultEnvironment();
    
//     engine.registerView(document.getElementById("renderCanvas1"));



//     return scene;

// };

HTML:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
    <title>Babylon.js - Multi views demo</title>
    <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
    <script src="https://preview.babylonjs.com/babylon.js"></script>
    <script src="index.js"></script>
    <link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">

    <style>
        html,
        body {
            width: 100%;
            height: 100%;
            padding: 0;
            margin: 0;
            overflow: hidden;
            font-family: 'Open Sans';
            color: white;
            background: #2A2342;
        }

        a {
            color: white;
        }

        canvas {
            width: 100%;
            height: 100%;
            align-self: center;
            justify-self: center;
        }

        #root {            
            width: 100%;
            height: 100%;
            padding: 0px;
            margin: 0;
            display: grid;
            grid-template-columns: 100%;
            grid-template-rows: auto auto 1fr;
        }

        #title {
            grid-row: 1;
            grid-column: 1;
            margin: 20px;
            align-self: center;
            justify-self: center;
        }

        #description {
            grid-row: 2;
            grid-column: 1;
            margin: 20px;
            align-self: center;
            justify-self: center;
        }    

        .container {
            grid-row: 3;
            grid-column: 1;
            width: 100%;
            height: 100%;
            padding: 0px;
            margin: 0;
            display: grid;
            grid-template-columns: 45% 10% 45%;
            grid-template-rows: 45% 10% 45%;            
        }

        .renderCanvas {            
            width: 100%;
            height: 100%;
        }

        #renderCanvas0 {
            grid-row: 1;
            grid-column: 1;
        }

        #renderCanvas1 {
            grid-row: 1;
            grid-column: 3;
        }

        #renderCanvas2 {
            grid-row: 3;
            grid-column: 1;
        }

        #renderCanvas3 {
            grid-row: 3;
            grid-column: 3;
        }
       </style>
</head>

<body>
    <div id="root">
        <h1 id="title">
            Babylon.js Multi Views (multi canvases) demo
        </h1>
        <div id="description">
            This test page is intended to demonstrate the use of Babylon.js with multiple canvases but only one Engine (and thus only one WebGL context)
            <br/>
            There are <b>4 independent canvases</b> on this page all rendered using the same scene but with different cameras. The first canvas (Top left) can be controlled with an interactive camera
            <br/>
            <br/>
            Click <a href="https://doc.babylonjs.com/how_to/multi_canvases">here</a> for more documentation.
        </div>
        <div class="container">
            <canvas class="renderCanvas" id="renderCanvas0" touch-action="none"></canvas>
            <canvas class="renderCanvas" id="renderCanvas1" touch-action="none"></canvas>

        </div>
    </div>
    
    <script>
        // Create a working document
        var canvas = document.createElement("canvas");
      //  var canvas1 = document.createElement("canvas");

        var engine = new BABYLON.Engine(canvas, true);
       // var engine1 = new BABYLON.Engine(canvas1, true);

        // Set the default canvas to use for events
        engine.inputElement = document.getElementById("renderCanvas0");
       // engine1.inputElement = document.getElementById("renderCanvas1");

        var scene = createScene();
       // var scene1 = createScene1();

        engine.runRenderLoop(function() {
       
                scene.render();
        //        scene1.render();
        });
    </script>

</body>
</html>

a couple things first, you need to register the camera you want to use for each views.

Then you need to add some logic to switch which canvas is currently responsible for the inputs.

could look like this:

// Create a working document



var createScene = function () {

    // This creates a basic Babylon Scene object (non-mesh)
    var scene = new BABYLON.Scene(engine);

    // This creates and positions a free camera (non-mesh)
    var camera = new BABYLON.ArcRotateCamera("Camera0", 0, 0.8, 5, new BABYLON.Vector3.Zero(), scene);
    camera.setTarget(BABYLON.Vector3.Zero());

    camera.lowerRadiusLimit = 4;
    camera.upperRadiusLimit = 20;

    // This attaches the camera to the canvas
    camera.attachControl(document.getElementById("renderCanvas0"), true);

    // This creates a light, aiming 0,1,0 - to the sky (non-mesh)
    var light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);

    // Default intensity is 1. Let's dim the light a small amount
    light.intensity = 0.7;

    // Our built-in 'sphere' shape.
    var box = BABYLON.MeshBuilder.CreateBox("Box", {size: 2}, scene);

    box.position.y = 0.5;

    var mat = new BABYLON.PBRMetallicRoughnessMaterial("mat", scene);

    mat.metallic = 1;
    mat.roughness = 0.5;

    box.material = mat;

    scene.createDefaultEnvironment();
    
    engine.registerView(document.getElementById("renderCanvas0"), camera);

    // This creates a basic Babylon Scene object (non-mesh)
    var scene1 = new BABYLON.Scene(engine);

    // This creates and positions a free camera (non-mesh)
    var camera1 = new BABYLON.ArcRotateCamera("Camera1", 0, 0.8, 5, new BABYLON.Vector3.Zero(), scene1);
    camera1.setTarget(BABYLON.Vector3.Zero());

    camera1.lowerRadiusLimit = 4;
    camera1.upperRadiusLimit = 20;

    // This attaches the camera to the canvas
    camera1.attachControl(document.getElementById("renderCanvas1"), true);

    // This creates a light, aiming 0,1,0 - to the sky (non-mesh)
    var light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene1);

    // Default intensity is 1. Let's dim the light a small amount
    light.intensity = 0.7;

    // Our built-in 'sphere' shape.
    var box1 = BABYLON.MeshBuilder.CreateBox("Box", {size: 2}, scene1);

    box1.position.y = 0.5;

    var mat1 = new BABYLON.PBRMetallicRoughnessMaterial("mat", scene1);

    mat1.metallic = 1;
    mat1.roughness = 0.5;

    box1.material = mat1;

    scene1.createDefaultEnvironment();
    
    engine.registerView(document.getElementById("renderCanvas1"), camera1);

    return { scene, scene1 };
};

and

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
    <title>Babylon.js - Multi views demo</title>
    <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
    <script src="https://preview.babylonjs.com/babylon.js"></script>
    <script src="index.js"></script>
    <link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">

    <style>
        html,
        body {
            width: 100%;
            height: 100%;
            padding: 0;
            margin: 0;
            overflow: hidden;
            font-family: 'Open Sans';
            color: white;
            background: #2A2342;
        }

        a {
            color: white;
        }

        canvas {
            width: 100%;
            height: 100%;
            align-self: center;
            justify-self: center;
        }

        #root {            
            width: 100%;
            height: 100%;
            padding: 0px;
            margin: 0;
            display: grid;
            grid-template-columns: 100%;
            grid-template-rows: auto auto 1fr;
        }

        #title {
            grid-row: 1;
            grid-column: 1;
            margin: 20px;
            align-self: center;
            justify-self: center;
        }

        #description {
            grid-row: 2;
            grid-column: 1;
            margin: 20px;
            align-self: center;
            justify-self: center;
        }    

        .container {
            grid-row: 3;
            grid-column: 1;
            width: 100%;
            height: 100%;
            padding: 0px;
            margin: 0;
            display: grid;
            grid-template-columns: 45% 10% 45%;
            grid-template-rows: 45% 10% 45%;            
        }

        .renderCanvas {            
            width: 100%;
            height: 100%;
        }

        #renderCanvas0 {
            grid-row: 1;
            grid-column: 1;
        }

        #renderCanvas1 {
            grid-row: 1;
            grid-column: 3;
        }

        #renderCanvas2 {
            grid-row: 3;
            grid-column: 1;
        }

        #renderCanvas3 {
            grid-row: 3;
            grid-column: 3;
        }
       </style>
</head>

<body>
    <div id="root">
        <h1 id="title">
            Babylon.js Multi Views (multi canvases) demo
        </h1>
        <div id="description">
            This test page is intended to demonstrate the use of Babylon.js with multiple canvases but only one Engine (and thus only one WebGL context)
            <br/>
            There are <b>4 independent canvases</b> on this page all rendered using the same scene but with different cameras. The first canvas (Top left) can be controlled with an interactive camera
            <br/>
            <br/>
            Click <a href="https://doc.babylonjs.com/how_to/multi_canvases">here</a> for more documentation.
        </div>
        <div class="container">
            <canvas class="renderCanvas" id="renderCanvas0" touch-action="none"></canvas>
            <canvas class="renderCanvas" id="renderCanvas1" touch-action="none"></canvas>

        </div>
    </div>
    
    <script>
        // Create a working document
        var canvas = document.createElement("canvas");
      //  var canvas1 = document.createElement("canvas");

        var engine = new BABYLON.Engine(canvas, true);
       // var engine1 = new BABYLON.Engine(canvas1, true);

        // Set the default canvas to use for events
        var canvas0 = document.getElementById("renderCanvas0");
        var canvas1 = document.getElementById("renderCanvas1");

        var {scene, scene1} = createScene();

        scene.detachControl();
        scene1.detachControl();

        let attachedScene = scene1;
        engine.inputElement = canvas1;
        scene1.attachControl();

        canvas0.addEventListener("click", () => {
            if(attachedScene !== scene) {
                attachedScene.detachControl();
                engine.inputElement = canvas0;
                scene.attachControl();
                attachedScene = scene;
            }
        });

        canvas1.addEventListener("click", () => {
            if(attachedScene !== scene1) {
                attachedScene.detachControl();
                engine.inputElement = canvas1;
                scene1.attachControl();
                attachedScene = scene1;
            }
        });
      
        engine.runRenderLoop(function() {
            scene1.render();
            if (engine.activeView.camera === scene.activeCamera) {
                scene.render();
            } else if (engine.activeView.camera === scene1.activeCamera) {
                scene1.render();
            }
        });
    </script>

</body>
</html>
2 Likes