Saturday, January 19, 2013

Can't Render a Three.js Scene in Two Places

‹prev | My Chain | next›

I am done with the Moon simulator chapter in 3D Game Programming for Kids and am ready to move onto other things (like more actual games). It occurs to me, however, that I do not know how to allow two cameras to be active at the same time in Three.js. I was able to do this king of thing in CubicVR.js / Gladius, but am not even sure it is possible in Three.js.

In the chapter, I include some side-by-side comparisons of the Moon's position and its phase:


Those screenshots are labor intensive—I need to screen capture each (and resize the print scale of each). This would not be something that I would have the kids do, but it would be great to have the Moon's phase superimposed on the overhead shot.

Unfortunately, as best I can tell, there is no way to accomplish this in Three.js. The render that spits the scene out onto canvas, takes only one camera:
  function animate() {
    requestAnimationFrame(animate);
    renderer.render(scene, camera);
  }
Three.js does include a CombinedCamera. Sadly this does not do what I hope—it just makes it easy to switch back and forth between a perspective camera and an orthographic camera.

I might not be entirely out of luck though. Just because Three.js renders are restricted to a single camera does not necessarily mean that I am restricted to a single renderer.

So I add a "picture-in-picture" element to the screen:
  var pip = document.createElement('div');
  pip.style.width = 250;
  pip.style.height = 250;
  pip.style.position = 'absolute';
  pip.style.backgroundColor = 'black';
  pip.style.borderRadius = "5px";
  pip.style.border = '2px solid white';
  pip.style.padding = "0px 20px";
  pip.style.left = "50px";
  pip.style.top = "25px";
  document.body.appendChild(pip);
Then I create a second renderer and add its domElement to the picture-in-picture:
  var renderer2 = new THREE.WebGLRenderer();
  renderer2.setSize(250, 250);
  pip.appendChild(renderer2.domElement);
This is just like the regular renderer, except that I append the renderer's DOM element to a specific element instead of appending it to the document body. I also note that the aspect ratio that I will have is 1:1, so I redefine the "Earth Cam" to have a 1:1 aspect ratio:
  //var earth_cam = new THREE.PerspectiveCamera(45, aspect_ratio, 1, 1e6);
  var earth_cam = new THREE.PerspectiveCamera(45, 1, 1, 1e6);
With that, I am ready to try double rendering in Three.js. I add the second renderer to my animate() method:
  function animate() {
    requestAnimationFrame(animate);
    renderer2.render(scene, earth_cam);
    renderer.render(scene, camera);
    // ...
  }
Unfortunately, that does not work. I only get the new camera, with the rest of the scene completely blank:


In addition to the visual evidence, Three.js spits out a lot of warnings telling me that I am doing something very wrong:
THREE.WebGLRenderer 52 Three.js:15545
THREE.WebGLRenderer 52 Three.js:15545
WebGL: INVALID_OPERATION: useProgram: object not from this context Three.js:20282
WebGL: INVALID_OPERATION: uniformMatrix4fv: location is not from current program Three.js:20298
WebGL: INVALID_OPERATION: uniform3f: location not for current program Three.js:20743
WebGL: INVALID_OPERATION: uniform1f: location not for current program
...
I try moving the second renderer out into its own animate2() function:
  function animate2() {
    requestAnimationFrame(animate2);
    renderer2.render(scene, earth_cam);
  }
  animate2();
But this has no effect. I still only see a single camera's output and get lots of warnings in the JavaScript console. I try rendering the second camera just a single time. I also try switching the renderer to a CanvasRendered. All have no effect.

It seems that I simply cannot render the same scene to two different renderers. If anyone has any thoughts, the non-worky code is up. In the meantime, I will sleep on it and perhaps revisit tomorrow.


Day #635

2 comments:

  1. Is this what you were looking for? http://mrdoob.github.io/three.js/examples/webgl_multiple_views.html

    ReplyDelete
    Replies
    1. Yup, that would have really helped at the time :)

      I did manage to hack together a more or less workable solution: http://japhr.blogspot.com/2013/01/multiple-renderers-in-threejs.html

      But that example is better.

      Delete