Banner

Monday, 6 January 2014

Photo spheres with three.js

Ever wanted to create those fancy-looking 360° panoramas? If you mix a mobile phone with three.js and some knowledge about (map) projections, it's quite easy. 

I love travelling, especially to mountainous areas of our planet. Taking photos of vast landscapes can be challenging. This drew my interest in spherical panoramas, photographs that captures everything around you - up, down and all around - 360 x 180 degrees. 

Google calls it a photo sphere, and if you own a mobile phone with Android 4.2 or higher, you can easily create one yourself. I captured this winter scene with my phone a few days ago. But what if you want to get rid of the Google wrapping and show your panorama in your own viewer? Let's try to do it with three.js.

If you download the image from your phone it looks like this:


This is called an equirectangular photograph, which will be entirely seamless when wrapped around a sphere. It has the same projection as most of the world maps you download on the web. You'll find several funny-looking equirectangular photographs on Flickr.

Wrapping such images around a sphere is easy with three.js (read the details here):

var sphere = new THREE.Mesh(
  new THREE.SphereGeometry(100, 32, 32),
  new THREE.MeshBasicMaterial({
    map: THREE.ImageUtils.loadTexture('bergsjostolen.jpg')
  })
);

You simply create a mesh of a geometric sphere and a material containing your image as a texture.


This will create a christmas ball effect your photo. Next we need to place the camera inside the sphere. You won't see much unless you invert the mesh "inside-out" by setting:

sphere.scale.x = -1;

I'm using the orbit controls to rotate the camera and allow the user to look around inside the sphere.


var controls = new THREE.OrbitControls(camera);
controls.noPan = true;
controls.noZoom = true; 
controls.autoRotate = true;
controls.autoRotateSpeed = 0.5;

Pan and zoom are disabled, as they're not fit for purpose. Instead I'm allowing the user to change the field of view (code from this three.js example):

function onMouseWheel(event) {
  if (event.wheelDeltaY) { // WebKit
    camera.fov -= event.wheelDeltaY * 0.05;
  } else if (event.wheelDelta) { // Opera / IE9
    camera.fov -= event.wheelDelta * 0.05;
  } else if (event.detail) { // Firefox
    camera.fov += event.detail * 1.0;
  }

  camera.fov = Math.max(40, Math.min(100, camera.fov));
  camera.updateProjectionMatrix();
}

document.addEventListener('mousewheel', onMouseWheel, false);
document.addEventListener('DOMMouseScroll', onMouseWheel, false);

I'm also using the detector script to use the Canvas renderer when WebGL is not supported:

var renderer = Detector.webgl ? new THREE.WebGLRenderer() : new THREE.CanvasRenderer();

Now we have our own photo sphere viewer:

[ Fullscreen ]

Feel free to improve it on GitHub.


Bergsjøstølen at dawn.

Towards Reineskarvet.

Hesthovdstølen shielding at 1155 m. 

9 comments:

Mano Marks said...

Cool! How does it handle tiling?

Bjørn Sandvik said...

No tiling support yet! :-)

Sean de Basti said...

really awesome, thank you! now, I dont need the several plattforms to visualize my 360 panoramas

Jake Parker said...

Hi! I can't seem to get the github download to run. I'm pretty new to HTML5, but I come from a C# background and I am just beginning to understand how js hooks into HTML5 stuff. My steps were as follows:
1. Downloaded the github zip.
2. Opened index.html in Chrome (just a black screen)
3. Opened gaustatoppen.html in Chome with same black screen result.

There's probably some super simple step I'm missing just because of my own ignorance with these systems. Any help you can offer is very much appreciated, and I'm a great researcher so if you just want to point me in the right direction that's perfectly fine too!

Thanks!

Jake Parker said...

As and experiment, I just saved your fullscreen photosphere page and found it also gave me a black screen. Maybe these things cannot be run locally? I'm going to throw this up on a server and find out.

Mátyás Albert Nagy said...

It cannot be run locally because images cannot be loaded into the webGL scene (blocked by X-Origin policy / tainted canvas).

Locally different files act like they are from completely different domains, even if in same folder.

Stefan said...

Hi.

Nice tutorial.

It is a way to get the orientation of what I see? Something like north arrow.

Thank you,

Stefan

NN said...

Hi Bjørn, nice tool for panoramas,
i work years ago with pano 360 takes from iphone and js reel library (http://jquery.vostrel.cz/reel)
to leafletjs maps.

examples in http://www.mesenken.com/demos/demo06.html

Regards!

George Grissom said...

How do you reverse the controls?