Tutorials
Articles
OpenGL Demos
Games
OpenGL Misc
MSG Board
About
Donate
Links
Home
Megabyte Softworks
C++, OpenGL, Algorithms




Current series: OpenGL 3.3
(Return to list of OpenGL 3.3 tutorials)

Download (2.12 MB)
8633 downloads. 8 comments
10.) Skybox

Welcome to the 10th OpenGL 3.3 tutorial. This time we are going to discuss a simple, but powerful effect, that adds realism to our scene - skybox. No more single color backgrounds, this time we'll have nice skies and mountains in the background! So let's get right into the topic.

Skybox

For those of you that don't know what a skybox is - it's nothing but a big box that's around our world and has some textures of skies and mountains mapped onto it. These textures are seamless, so you don't see any box edges or anything and the whole thing will look as one continuous sky. We don't need any new OpenGL knowledge, but some implementation details of how to add skybox to the world will be useful. But first, let's have a look at single skybox, so that you have an idea what it looks like:

As you can see, skybox is just a textured cube and when we unfold its faces, we will get such a shape, as if we wanted to cut out a cube from the paper and then fold it. This idea is very simle, but yet very powerful. As usual, we will create a class for skybox, to have everything we need in one place:


class CSkybox
{
public:
   void loadSkybox(string a_sDirectory, string a_sFront, string a_sBack, string a_sLeft, string a_sRight, string a_sTop, string a_sBottom);
   void renderSkybox();

   void releaseSkybox();
private:
   UINT uiVAO;
   CVertexBufferObject vboRenderData;
   CTexture tTextures[6];
   string sDirectory;
   string sFront, sBack, sLeft, sRight, sTop, sBottom;
};

We can briefly have a look at load function:


void CSkybox::loadSkybox(string a_sDirectory, string a_sFront, string a_sBack, string a_sLeft, string a_sRight, string a_sTop, string a_sBottom)
{
   tTextures[0].loadTexture2D(a_sDirectory+a_sFront);
   tTextures[1].loadTexture2D(a_sDirectory+a_sBack);
   tTextures[2].loadTexture2D(a_sDirectory+a_sLeft);
   tTextures[3].loadTexture2D(a_sDirectory+a_sRight);
   tTextures[4].loadTexture2D(a_sDirectory+a_sTop);
   tTextures[5].loadTexture2D(a_sDirectory+a_sBottom);

   sDirectory = a_sDirectory;

   sFront = a_sFront;
   sBack = a_sBack;
   sLeft = a_sLeft;
   sRight = a_sRight;
   sTop = a_sTop;
   sBottom = a_sBottom;

   FOR(i, 6)
   {
      tTextures[i].setFiltering(TEXTURE_FILTER_MAG_BILINEAR, TEXTURE_FILTER_MIN_BILINEAR);
      tTextures[i].setSamplerParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
      tTextures[i].setSamplerParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
   }

   glGenVertexArrays(1, &uiVAO);
   glBindVertexArray(uiVAO);

   vboRenderData.createVBO();
   vboRenderData.bindVBO();

   // Proceed with VBO creation...
}

What we do is load 6 textures - front, back, left, right, top and bottom texture, then we create a rendering VBO for the skybox, and we're done loading. Additionaly, we set texture wrapping parameters to GL_CLAMP_TO_EDGE. Without setting it we would be able to see edges of the box in the sky, and it doesn't help realism much . More interesting part is rendering. This will also be the only trickier thing in this tutorial (as I said, there's nothing difficult this time). Let's have a look at it:


void CSkybox::renderSkybox()
{
   glDepthMask(0);
   glBindVertexArray(uiVAO);
   FOR(i, 6)
   {
      tTextures[i].bindTexture();
      glDrawArrays(GL_TRIANGLE_STRIP, i*4, 4);
   }
   glDepthMask(1);
}

This may require a little explanation. As I said, we want the skybox to surround us, so it must be big enough. But what value is big enough? 200? 1000? Far clipping plane distance ? Maybe some of these is enough, but there is more elegant solution and it's this: Skybox will be rendered before everything else, but with depth buffer writing disabled (glDepthMask(0) does just that). What shall we achieve with this? With the first thing rendered, we can be sure, that the box will pass depth tests, because it's the first thing rendered and it's size isn't too big (it's 50.0 in our case). With depth buffer writing disabled, the cube will get rendered, but the depth values will remain in depth buffer, as if nothing was rendered, so practically we just change colors in framebuffer before whole scene, and then we start rendering scene. And actually, it doesn't matter which constant we use as size of skybox, I went for 50.0, but you can try changing it to see that really nothing happens. And the last thing we must do before rendering skybox is to translate it to position of camera, so that it moves with us and we cannot escape it now. After rendering, don't forget to restore writing to depth buffer (glDepthMask(1)). That's absolutely all you need to know for basic skybox.

Conclusion

Result is nice, it adds a real feel to the scene - no more single colored backgrounds!

Some things to be considered - when rendering skybox, you probably don't want to use directional light or any lighting on it, maybe just some simple color modulation depending on time of the day. So it would be probably better to create another shader program for skybox, or generally for rendering in 3D without lighting with color modulation. But I was lazy and I rather set ambient intensity of skybox to 1.0, so that I don't have to do it . The scene is just a pile of boxes, and one interesting object made from 3 tori . Oh, and another thing - starting from this tutorial, the application data (textures, shaders etc...) will only be in the root directory of tutorial (by now, they were copied in the root, so that you could run tutorial right away, and second copy was in source directory, so that after compilation you can run it). Anyway, after compilation you still had to copy DLLs to debug directory in order to run tutorial, so it's a waste of space to copy this data twice, especially now, when their amount is getting bigger each tutorial. So just copy DLLs and data yourself to proper place and you're good to modify code and learn something .

And that's all for today! This tutorial was probably the shortest so far, I hope you enjoyed it. Took me a little longer to write, it was because of duties in my work. I'm not sure what next tutorial will be, so I let it to be a surprise and I hope I will write it in reasonable small time, like one week .



Download (2.12 MB)
8633 downloads. 8 comments
 
Name:

E-mail:
(Optional)
Entry:

Enter the text from image:



Smileys




Jerry (xpost2000cod@gmail.com) on 11.08.2015 22:49:06
For some reason this tutorial's camera isn't working. Because of GLM.
What version of GLM was used for this
guest on 29.06.2015 14:57:12
In the tutorial, you give your frag shader only the first texture (front image) via gSampler, which is always GL_TEXTURE0. How does your .exe render all 6 images?
kam on 05.12.2014 03:04:01
What is the tTextures array? I don't see it initialized anywhere.

Great tutorial!
Question on 07.08.2014 14:15:36
Why is the skybox drawn over other items even if I have DepthMask disabled while drawing skybox?
Michal Bubnar (michalbb1@gmail.com) on 11.08.2014 23:35:35
Well, this isn't supposed to happen. Send me your whole solution so I can check through it.
Fan on 13.04.2014 13:16:13
Not sure if you'll see this any time soon, but I downloaded your Skybox tutorial to understand it, but when I ran it from the solution, nothing showed up. From the actual .exe it runs fine though. If you do read this, what could be causing it to not run from the solution?
Michal Bubnar (michalbb1@gmail.com) on 11.08.2014 23:36:53
It's easy. In some of my first tutorial I have explained, that you need to set the working directory in VS2008 to $(SolutionDir)bin in order to make it working. This kind of thing isn't retained with solution I pack in, but it's stored somewhere else, maybe user files, which are deleted before I publish the tutorial.
Sebastian on 14.02.2012 12:16:34
Good tutorials. Shorted and intresting.
Jump to page:
1