Welcome to VR Me Up developer log number 13.
I have been wanting to add “sky boxes” to VR Me Up for a while and I have finally done it. They really make the worlds look a lot more interesting and they do improve performance a little as the previous sky was made up of 3D models which all had to be individually rendered.
I really wanted to be able to create my own sky boxes, so I worked out how to create them using Blender. In this video I’m going to share with you how I created them and I’m also sharing some example Blender files on my GitHub repository for you to download and use to create your own or explorer the demo.
There are 3 basic types of sky box, Equirectangular (or “panoramic”), cube map, and 6-sided cube textures.
Equirectangular sky boxes
Equirectangular images contain a 360 degree view in a single file. There are plenty of sites on the internet to find great looking HDRI or JPEG images. They are the best option for environment lighting and reflections, however, they can appear a bit “blocky” if you don’t use very high resolution HDRI images which can be hundreds of megabytes in size.
Equirectangular sky boxes are easy to create in Blender by changing the camera type in the Cycles renderer. In the camera properties panel select a “Type” of “Panoramic” and a “Panoramic Type” of “Equirectangular”. Render the scene with “F12” and you should end up with an Equirectangular image.
With a little Three.js Javascript code you can load the image into both the background and the environment scene variables. If you want to use HDRI images, then you will have to use the “RGBELoader”.
const pmremGenerator = new THREE.PMREMGenerator( renderer );
const loader = new THREE.TextureLoader() ;
// const loader = new RGBELoader() ;
loader.load( 'equirectangular.jpg', function ( texture ) {
const envMap = pmremGenerator.fromEquirectangular( texture ).texture;
texture.dispose();
scene.background = envMap
scene.environment = envMap
} );
Cube Map sky boxes
“Cube Map” sky boxes are also very common and contain views from 6 angles (up, down, left, right, forward and backward) all in a single image. These can also appear a little blocky unless you use high resolution files. Three.js does not appear to support them out of the box and your’ll need to write some code to load them, map the textures onto a cube and then place the cube in the world. The down side of these is also that you don’t get environment lighting or reflections from them.
Creating both “cube maps” and “cube textures” in Blender is tricky and I originally thought about using 6 different cameras. However, I came up with a much easier method by “baking” the 3D scene onto a single mirrored cube set to 100% metallic with each face mapped to a different part of the image. The corner normals all have to be pointing at diagonals, which was easy to do by just smoothing the normals.
I have provided a [cubemap.blend](https://github.com/vrmeup/blender-threejs-skyboxes/blob/main/cubemap.blend Blender file that you can append to the scene. Move the sky box cube to the desired projection location in the scene and don’t forget to turn off shadows from the cube on the object properties settings. Once your ready, select the cube, then go to “Render” and “Bake” and press the “Bake” button. This will project the scene onto the cube and the corresponding image.
The code to map the image onto the cube is a little difficult, however, I have provided my implementation in the cube map example that maps the locations in the image on to the cube. Position the cube in the centre of the scene. It will have to be large to give the correct effect for a sky box and don’t forget to set the side to “BackSide” so you can see the cube from within.
const loader = new THREE.TextureLoader();
loader.load(
'cubemap.jpg',
function ( texture ) {
texture.colorSpace = THREE.SRGBColorSpace;
const cube = new THREE.Mesh(
new THREE.BoxGeometry(1000,1000,1000),
new THREE.MeshBasicMaterial({ map : texture, side:THREE.BackSide })
)
// update cube UV so they map the skybox onto the cube
var uv = cube.geometry.attributes.uv;
const sx = 1 / 4.0 ; // 4 images across
const sy = 1 / 3.0 ; // 3 images down
uv.setXY(0, sx*3, sy*2); uv.setXY(1, sx*2,sy*2); uv.setXY(2, sx*3,sy*1); uv.setXY(3, sx*2,sy*1); // Right
uv.setXY(4, sx*1, sy*2); uv.setXY(5, sx*0,sy*2); uv.setXY(6, sx*1,sy*1); uv.setXY(7, sx*0,sy*1); // Left
uv.setXY(8, sx*1, sy*2); uv.setXY(9, sx*2,sy*2); uv.setXY(10, sx*1,sy*3); uv.setXY(11, sx*2,sy*3); // Top
uv.setXY(12, sx*1, sy*0); uv.setXY(13, sx*2,sy*0); uv.setXY(14, sx*1,sy*1); uv.setXY(15, sx*2,sy*1); // Bottom
uv.setXY(16, sx*4, sy*2); uv.setXY(17, sx*3,sy*2); uv.setXY(18, sx*4,sy*1); uv.setXY(19, sx*3,sy*1); // Back
uv.setXY(20, sx*2, sy*2); uv.setXY(21, sx*1,sy*2); uv.setXY(22, sx*2,sy*1); uv.setXY(23, sx*1,sy*1); // Front
scene.add(cube);
}
)
If you try to bake the scene and you get a error message “Uninitialized image” then blender has disassociated its internal images from the external disk files. Select the image in the “UV Editor” and replace it with the corresponding file on disk.
6 Sided Cube Textures
6 Sided “Cube Texture” sky boxes are similar to “Cube Map” sky boxes, except all 6 views map to separate images, one for each direction. I decided to use “cube textures” in my project as they are really easy to work with in Three.js and also support environment mapping, reflections and lighting.
I used the same method as with the “cube map” sky box by baking the scene on a cube. The difference is that each face of the cube is mapped to a separate image. The hard part was correctly mapping the orientation and rotation of the cubes faces “UV” coordinates onto the 6 separate images.
Once the scene is ready, append the cubetexture.blend blender file and move or scale the sky box cube to the desired location. Select the cube, then go to “Render” and “Bake” and press the “Bake” button. This will project the scene onto the cube and the corresponding images. Once complete, save all the images.
Loading a cube texture is very straight forward in Three.js as it supports them out of the box, so this a little Java Script you can load both a sky box and an environment texture for lighting and reflections.
const loader = new THREE.CubeTextureLoader() ;
scene.background = loader
.setPath( '/cubetexture/' )
.load( [
'xp.jpg',
'xn.jpg',
'yp.jpg',
'yn.jpg',
'zp.jpg',
'zn.jpg'
],
(data) => {
scene.environment = data ;
});
Depending on your application you may require higher or lower resolution sky box images. For all of the methods, resize the target image (or images) to adjust the resolution of the final result.
I hope you have found this developer log interesting and useful and enjoy the new sky boxes in VR Me Up.
See you in the metaverse!
VR Me Up!