
This was originally published oncodinhood.com
Skyboxes are typically used in video games to create the illusion of far off three-dimensional backgrounds. A skybox is essentially a cube with textures on each side of the cube. The player or camera is then placed within the cube so that all six textures surround them giving them the illusion that they're within a much larger environment.reactnativeinfinity.com makes use of this technique to create the illusion of spinning in space. The Codepen below has a few different skyboxes you can try out. Click and drag the screen to move around.
Three.js setup
First, set up ascene
,camera
, andrenderer
within aninit
function we will call to initialize Three.js. We'll use aPerspectiveCamera with a position that is zoomed out pretty far so we can see the box before jumping in. We'll also use theTHREE.WEbGLRenderer
and append it to the body of the page. Finally, theanimate
function will handle rerendering the scene with any updates we add.
letscene,camera,renderer,skyboxGeo,skybox;functioninit(){scene=newTHREE.Scene();camera=newTHREE.PerspectiveCamera(55,window.innerWidth/window.innerHeight,45,30000);camera.position.set(1200,-250,20000);renderer=newTHREE.WebGLRenderer({antialias:true});renderer.setSize(window.innerWidth,window.innerHeight);renderer.domElement.id="canvas";document.body.appendChild(renderer.domElement);animate();}functionanimate(){renderer.render(scene,camera);requestAnimationFrame(animate);}init();
Import the core Three.js library.
<scriptsrc="https://threejs.org/build/three.min.js"></script>
Set the body height to the viewport height and add a grey background to the body so we can see the cube.
body{margin:0;height:100vh;background:#bdc3c7;}
Since we haven't added any objects we'll only see a grey background right now.
Add Three.js box
We can add a box withTHREE.BoxGeometry
withwidth
,height
, anddepth
set to10000
. Then useTHREE.Mesh
to apply a texture to it, in this case, it will default to a purewhite
texture. Finally, add the object to the scene before calling theanimate
function within theinit
function.
functioninit(){...skyboxGeo=newTHREE.BoxGeometry(10000,10000,10000);skybox=newTHREE.Mesh(skyboxGeo);scene.add(skybox);animate();
Even though it's a cube, it looks like a square because we're viewing it straight on. To verify it is a cube we can add a rotation animation within theanimate
function:
functionanimate(){skybox.rotation.x+=0.005;skybox.rotation.y+=0.005;renderer.render(scene,camera);requestAnimationFrame(animate);}
Skybox Mesh Material
You can find free skybox images onopengameart.org or you can search Google for "free skybox images". There should be six images that correspond to each side of the cube that seamlessly mesh together. For example, for React Native Infinity, the six space images correspond to different sides as shown below.
Each image should be named according to the side which they correspond to, for example,purplenebula_ft.png
is the front image,purplenebula_rt.png
is the right image, andpurplenebula_dn.png
is the bottom image. There are three steps we need to follow to add these images to our cube:
- Load each image as a Texture
- Map each Texture to a Material array
- Add Material array to the Skybox cube
1. Load Images as Textures
Textures can be loaded in Three.js using theTextureLoader().load()
function. Theload()
method takes the path of the image as a parameter. We could load each image by creating sixTextureLoader()
functions, like this:
constft=newTHREE.TextureLoader().load("purplenebula_ft.jpg");constbk=newTHREE.TextureLoader().load("purplenebula_bk.jpg");constup=newTHREE.TextureLoader().load("purplenebula_up.jpg");constdn=newTHREE.TextureLoader().load("purplenebula_dn.jpg");constrt=newTHREE.TextureLoader().load("purplenebula_rt.jpg");constlf=newTHREE.TextureLoader().load("purplenebula_lf.jpg");
But it would be better to create a reusable function that loops through all of our images for us. Create a functioncreatePathStrings()
that will create an array of path strings from a file image name,filename
.
functioncreatePathStrings(filename){constbasePath="./static/skybox/";constbaseFilename=basePath+filename;constfileType=".png";constsides=["ft","bk","up","dn","rt","lf"];constpathStings=sides.map(side=>{returnbaseFilename+"_"+side+fileType;});returnpathStings;}
This should create an array of strings that represent the path to each image:
['./static/skybox/purplenebula_ft.jpg','./static/skybox/purplenebula_bk.jpg',...]
Next, load each texture usingTextureLoader().load()
by mapping over the array above. Let's create another function,createMaterialArray()
, to generate a new array of loaded textures. We will also pass in thefilename
parameter into thecreatePathStrings
function.
letskyboxImage="purplenebula";functioncreateMaterialArray(filename){constskyboxImagepaths=createPathStrings(filename);constmaterialArray=skyboxImagepaths.map(image=>{lettexture=newTHREE.TextureLoader().load(image);returntexture;});returnmaterialArray;}
2. Map each Texture to a Mesh array
TheMeshBasicMaterial()
Three.js method will allow us to map our textures above to a Three.js material. Instead of creating another function to do this, we can simply modify thecreateMaterialArray()
function to return a Three.js material instead of the loaded texture.
functioncreateMaterialArray(filename){constskyboxImagepaths=createPathStrings(filename);constmaterialArray=skyboxImagepaths.map(image=>{lettexture=newTHREE.TextureLoader().load(image);returnnewTHREE.MeshBasicMaterial({map:texture,side:THREE.BackSide});// <---});returnmaterialArray;}
3. Add Mesh array to the Skybox cube
We're finally ready to add our mesh array to the cube we created above. First, create a variable,skyboxImage
, with the base file name. Pass that variable into thecreateMaterialArray
to generate our mesh array. Finally, pass that array into the second parameter of thenew Three.Mesh()
function.
constskyboxImage='purplenebula';functioninit(){...constmaterialArray=createMaterialArray(skyboxImage);skyboxGeo=newTHREE.BoxGeometry(10000,10000,10000);skybox=newTHREE.Mesh(skyboxGeo,materialArray);scene.add(skybox);animate();}
Our cube should now have the mesh array, click the "Outside Box" button to see how this should look.
Place camera inside the cube
We can change thecamera
'sz
position from20000
to2000
to put the camera inside the cube.
functioninit()...camera.position.set(1200,-250,2000);...}
Orbit Controls
While the above works to put us inside the cube, it would be better if could control the camera with the mouse and look around the environment. The Orbit Controls package for Three.js allows us to do that add a<script>
import:
<scriptsrc="https://threejs.org/build/three.min.js"></script><scriptsrc="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
First, add another variable namedcontrols
to the initialization at the top. Then assign that variable to theOrbitControls
method while passing in thecamera
anddomElement
. Enable the controls by settingcontrols.enabled
totrue
. Finally, set aminDistance
andmaxDistance
so that the user cannot zoom outside of the cube.
letscene,camera,renderer,skyboxGeo,skybox,controls;functioninit(){...controls=newTHREE.OrbitControls(camera,renderer.domElement);controls.enabled=true;controls.minDistance=700;controls.maxDistance=1500;...animate();}
Next, remove the rotation in theanimate()
function and addcontrols.update()
;
functionanimate(){controls.update();renderer.render(scene,camera);requestAnimationFrame(animate);}
You should now be able to click and drag the environment around to see whatever part you want. If you want the environment to rotate again, like you're in space rotating, you can use theautoRotate
property:
functioninit(){...controls=newTHREE.OrbitControls(camera,renderer.domElement);controls.enabled=true;controls.minDistance=700;controls.maxDistance=1500;controls.autoRotate=true;controls.autoRotateSpeed=1.0;...animate();}
Resize window
If you resize your browser window after initialization the canvas will not resize to fit the new window size. To fix this create a function to redefine thecamera.aspect
andrenderer
size to the height and width of thewindow
:
functiononWindowResize(){camera.aspect=window.innerWidth/window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth,window.innerHeight);}
Then add an event listener on thewindow
resize
event and pass this new function. Add this event listener to theinit()
function right above theanimate()
call.
functioninit(){...window.addEventListener('resize',onWindowResize,false);animate();}
The canvas will now resize with the window.
Conclusion
Skyboxes are a neat way to create the illusion of a 3d environment quickly. Although generally used for video games, there might be some creative ways you could implement them in a web project.
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse