Create your own environment maps

Introduction

Environment maps are pretty cool. Whenever you do reflections or refractions in WebGL or OpenGL, chances are you’ll use one. To make one can take a lot of time and specific equipment, but what if you want to make your own? Actually it turns out it’s not too taxing to go ghetto.

To make this work you’re going to need an application that lets you take panoramic pictures. In my case I’m using Microsoft’s Photosynth app on iPhone since it does exactly what we need.

Environment Maps

To begin with let’s talk about what Environment Maps are. In the case of WebGL and OpenGL they’re actually cube maps typically, which is a specific kind of environment map. A cube map is simply a panoramic view of a scene (could be a real-world one or a virtual one) that has been mapped to the inside of a cube. Imagine being on the top of a hill and looking straight ahead, then 90 degrees to your left and then 90 degrees your right, straight up and down and finally right behind you. Handily you would see each side of this “cube”. If the cube we’re talking about is big enough it becomes difficult to perceive the joins in the cube edges and corners and it gives us the effect of being in a large environment. If all that confuses you the Wikipedia article on cube maps is pretty helpful!

Cool, but how does this help? Well it means we can do things like reflection and refraction, and in fact these two functions are built into GLSL, the shading language of WebGL. The theory is that you give the shader 6 images to represent the sides of this cube, and you tell WebGL that this is a cube map and from there you can start using it to do the aforementioned visual effects.

Half Axes

A quick note on the terminology employed for cube maps. Since there are three axes we typically use: x, y and z, it follows that the images used for cube maps are labelled for those axes. However, as I said, there are 6 images, two per axis. These are called half axes, e.g. positive x and negative x.

Get a Panoramic

So the first step is to go outside and use the app to take a panoramic picture. In this example I took a walk to London’s financial district and snapped a panoramic of the Gherkin.

I then get an image that looks rather like this:

It’s worth pointing out that these panoramic apps often map back to a sphere, which is fine, but it does mean that we now need to convert it over to a cube so that it’s compatible with our WebGL-supported cube maps.

Convert to cube

This step I’ve actually shortcutted for you, because I’m nice like that. The theory is that we take the picture and load it into some 3D application like Maya or Blender and map it onto a sphere. From there we create 6 90-degree cameras, each pointing at one of the half axes. Then we export the images seen by these cameras and we have the six images to load into our WebGL scene. Since that’s a huge pain for me to explain and for you to do I’ve created a Blender file with everything set up for you!

To use the file:

  1. Rename your panoramic to environment.jpg
  2. Place the panoramic image alongside the Blender file
  3. Load the Blender file
  4. Click on “Animation” on the right hand side
  5. Sit back in wonder as 6 images are created in the folder

Pretty cool, right? In my case I just created a single camera and then an “animation” with 6 frames, on each frame I updated the camera’s angle to one of the half axes.

Now you can rename the files so they match the half axis they represent. (This is optional but it’s probably worth doing.) Here’s what you need to do:

  • Rename 0001.png to pos-z.png
  • Rename 0002.png to neg-x.png
  • Rename 0003.png to neg-z.png
  • Rename 0004.png to pos-x.png
  • Rename 0005.png to neg-y.png
  • Rename 0006.png to pos-y.png

Add to the scene

Now we’ve got our very own environment map the last thing we need to do is to load it into the scene and use it. I’ll show you how to do that with Three.js since that, as usual, makes everything nice and easy for us:

// urls of the images, one per half axis
var urls = [
  'path/to/pos-x.png',
  'path/to/neg-x.png',
  'path/to/pos-y.png',
  'path/to/neg-y.png',
  'path/to/pos-z.png',
  'path/to/neg-z.png'
],

// wrap it up into the object that we need
cubemap = THREE.ImageUtils.loadTextureCube(urls);

// set the format, likely RGB unless you've gone crazy
cubemap.format = THREE.RGBFormat;

Now all you need to do is assign the cube map to a material and off you go!

var material = new THREE.MeshLambertMaterial({
  color: 0xffffff,
  envMap: cubemap
});

See it running

Conclusion

So there you go. It may be tricky to get a good environment map image, depending on where you can take your snaps, but it’s a really nice way to personalise your WebGL scenes. As usual there’s a zip for you to download if you want a copy of the code. If you’ve enjoyed this or have any questions let me know via Twitter.