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.
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.
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.
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:
- Rename your panoramic to environment.jpg
- Place the panoramic image alongside the Blender file
- Load the Blender file
- Click on "Animation" on the right hand side
- 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.) The mappings are:
- 0001.png → pos-z.png
- 0002.png → neg-x.png
- 0003.png → neg-z.png
- 0004.png → pos-x.png
- 0005.png → neg-y.png
- 0006.png → 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 = [
// wrap it up into the
// object that we need
cubemap = THREE.ImageUtils.
// 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
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 email or Twitter.