Basic Tutorial 3         Terrain, Sky, and Fog
Print

Tutorial Introduction
Image

This tutorial will focus on rendering terrain in a scene. We will cover the basic set up that needs to be done, and we will introduce the use of lighting with terrains. We will also give a brief introduction to simulating a sky using Skyboxes, Skydomes, and Skyplanes. Finally, we will explain how to add a fog effect to the scene.

The full source for this tutorial is here.

Any problems you encounter during working with this tutorial should be posted in the Help Forum(external link).

Prerequisites

This tutorial assumes that you already know how to set up an Ogre project and compile it successfully. If you need help with this, then read Setting Up An Application. This tutorial is also part of the Basic Tutorials series and knowledge from the previous tutorials will be assumed.

Note: Ignore the FPS stats in the screenshots. They were rendered on an ancient computer.

Image

Setting Up the Scene

The first thing we want to do is add some methods and variables to our class. Set up your TutorialApplication class like this:

TutorialApplication.h
#include <Terrain/OgreTerrain.h>
#include <Terrain/OgreTerrainGroup.h>
 
#include "BaseApplication.h"
 
class TutorialApplication : public BaseApplication
{
public:
  TutorialApplication();
  virtual ~TutorialApplication();
 
protected:
  virtual void createScene();
  virtual void createFrameListener();
  virtual void destroyScene();
  virtual bool frameRenderingQueued(const Ogre::FrameEvent& fe);
 
private:
  void defineTerrain(long x, long y);
  void initBlendMaps(Ogre::Terrain* terrain);
  void configureTerrainDefaults(Ogre::Light* light);
 
  bool mTerrainsImported;
  Ogre::TerrainGroup* mTerrainGroup;
  Ogre::TerrainGlobalOptions* mTerrainGlobals;
  OgreBites::Label* mInfoLabel;
 
};
TutorialApplication.cpp
#include "TutorialApplication.h"
 
TutorialApplication::TutorialApplication()
  : mTerrainGroup(0),
    mTerrainGlobals(0),
    mInfoLabel(0)
{
}
 
TutorialApplication::~TutorialApplication()
{
}
 
void TutorialApplication::createScene()
{
}
 
void TutorialApplication::createFrameListener()
{
  BaseApplication::createFrameListener();
}
 
void TutorialApplication::destroyScene()
{
}
 
bool TutorialApplication::frameRenderingQueued(const Ogre::FrameEvent& fe)
{
  bool ret = BaseApplication::frameRenderingQueued(fe);
 
  return ret;
}
 
void getTerrainImage(bool flipX, bool flipY, Ogre::Image& img)
{
}
 
void TutorialApplication::defineTerrain(long x, long y)
{
}
 
void TutorialApplication::initBlendMaps(Ogre::Terrain* terrain)
{
}
 
void TutorialApplication::configureTerrainDefaults(Ogre::Light* light)
{
}
 
// MAIN FUNCTION OMITTED FOR SPACE

 

Project Settings

Visual Studio

Click for Instructions

 

Code::Blocks

Click for Instructions

 

CMake

Click for Instructions

 

AutoTools

Click for Instructions



An Introduction to Terrain

With older versions of Ogre, we had to use the Terrain Scene Manager to render terrain in a scene. This is a separate SceneManager that runs alongside your other managers. The new Ogre Terrain System has shifted to a component system that doesn't require using a separate manager. Since Ogre 1.7 (Cthugha), there are three terrain components: Terrain, Paging, and Property. The Paging component is used together with the Terrain component to help optimize large terrains. It will be covered in later tutorials. This tutorial will focus largerly on the Terrain component.

To set up the terrain we will focus on two main classes: Terrain(external link) and TerrainGroup(external link). The Terrain class represents one chunk of terrain and the TerrainGroup holds a series of Terrain pieces. It is used for LOD (Level of Detail) rendering. LOD rendering reduces the resolution for terrain that is farther away from the camera. An individual Terrain object consists of tiles with a material mapped on to them. We will use a single TerrainGroup without paging. Paging will be covered in later tutorials.

Setting Up the Camera

Let's first set up our Camera. Add the following to the beginning of createScene:

mCamera->setPosition(Ogre::Vector3(1683, 50, 2116));
mCamera->lookAt(Ogre::Vector3(1963, 50, 1660));
mCamera->setNearClipDistance(0.1);

This should look familiar from the previous tutorial.

bool infiniteClip =
  mRoot->getRenderSystem()->getCapabilities()->hasCapability(
    Ogre::RSC_INFINITE_FAR_PLANE);
 
if (infiniteClip)
  mCamera->setFarClipDistance(0);
else
  mCamera->setFarClipDistance(50000);

The last thing we do is check to see if our current render system has the capability to handle an infinite far clip distance. If it does, then we set the far clip distance to zero (which means no far clipping). If it does not, then we simply set the distance really high so we can see distant terrain.

Setting Up a Light for Our Terrain

The Terrain component can use a directional light to compute a lightmap. Let's add a Light for this purpose and add some ambient light to the scene while we're at it.

mSceneMgr->setAmbientLight(Ogre::ColourValue(0.2, 0.2, 0.2));
 
Ogre::Vector3 lightdir(0.55, -0.3, 0.75);
lightdir.normalise();
 
Ogre::Light* light = mSceneMgr->createLight("TestLight");
light->setType(Ogre::Light::LT_DIRECTIONAL);
light->setDirection(lightdir);
light->setDiffuseColour(Ogre::ColourValue::White);
light->setSpecularColour(Ogre::ColourValue(0.4, 0.4, 0.4));

This was also covered in the previous tutorial if you're confused by any of it. The normalise method will make the vector's length equal to one while maintaining its direction. This is something that you will see a lot of when working with vectors. It is done to avoid extra factors showing up in calculations.

Configuring the Terrain

Now we'll get into the actual terrain setup. First, we create a new TerrainGlobalOptions using the OGRE_NEW macro.

mTerrainGlobals = OGRE_NEW Ogre::TerrainGlobalOptions();

This is a class that holds information for all of the terrains we might create - that is why they are called global options. It also provides a few getters and setters. There are also local options for each TerrainGroup that we will see later in this tutorial.

Next we construct our TerrainGroup object. This will manage a grid of Terrains.

mTerrainGroup = OGRE_NEW Ogre::TerrainGroup(
  mSceneMgr, 
  Ogre::Terrain::ALIGN_X_Z, 
  513, 12000.0);
mTerrainGroup->setFilenameConvention(Ogre::String("terrain"), Ogre::String("dat"));
mTerrainGroup->setOrigin(Ogre::Vector3::ZERO);

The TerrainGroup constructor takes the SceneManager as its first parameter. It then takes an alignment option, terrain size, and terrain world size. You can read the class reference(external link) for more information. The setFilenameConvention allows us to choose how our terrain will be saved. Finally, we set the origin to be used for our terrain.

The next thing we will do is call our terrain configuration method, which we will fill in soon. Make sure to pass the Light we created as a parameter.

configureTerrainDefaults(light);

The next thing we do is define our terrains and ask the TerrainGroup to load them all.

for (long x = 0; x <= 0; ++x)
  for (long y = 0; y <= 0; ++y)
    defineTerrain(x, y);
 
mTerrainGroup->loadAllTerrains(true);

We are only using a single terrain, so the method will only be called once. The for loops are just for demonstration in our case. Again, we will fill in the defineTerrain method soon.

We will now initialize the blend maps for our terrain.

if (mTerrainsImported)
{
  Ogre::TerrainGroup::TerrainIterator ti = mTerrainGroup->getTerrainIterator();
 
  while (ti.hasMoreElements())
  {
    Ogre::Terrain* t = ti.getNext()->instance;
    initBlendMaps(t);
  }
}

We get a TerrainIterator from our TerrainGroup and then loop through any Terrain elements and initialize their blend maps - initBlendMaps will also be written soon. The mTerrainsImported variable will be set during the configureTerrainDefaults function when we complete it.

The last thing we will do is make sure to cleanup any temporary resources that were created while configuring our terrain.

mTerrainGroup->freeTemporaryResources();

That completes our createScene method. Now we just have to complete all of the methods we jumped over.

Writing configureTerrainDefaults

The Ogre Terrain component has a large number of options that can be set to change how the terrain is rendered. To start out, add the following to configureTerrainDefaults:

mTerrainGlobals->setMaxPixelError(8);
mTerrainGlobals->setCompositeMapDistance(3000);

We are setting two global options here. The first call sets the largest error in pixels allowed between our ideal terrain and the mesh that is created to render it. A smaller number will mean a more accurate terrain, because it will require more vertices to reduce the error. The second call determines the distance at which Ogre will still apply our lightmap. If you increase this, then you will see Ogre apply lighting effects out to a farther distance.

The next thing we'll do is pass our lighting information to our terrain.

mTerrainGlobals->setLightMapDirection(light->getDerivedDirection());
mTerrainGlobals->setCompositeMapAmbient(mSceneMgr->getAmbientLight());
mTerrainGlobals->setCompositeMapDiffuse(light->getDiffuseColour());

In the first call, we are sure to call getDerivedDirection, because this will apply any transforms that are applied to our Light's direction by any SceneNode it may be attached to. Since our Light is attached to the root Node, this will be the same as calling getDirection, but the difference is important to know about. The next two calls should be pretty self-explanatory. We simply set the ambient light and diffuse color for our terrain to match our scene lighting.

The next thing we do is get a reference to the import settings of our TerrainGroup and set some basic values.

Ogre::Terrain::ImportData& importData = mTerrainGroup->getDefaultImportSettings();
importData.terrainSize = 513;
importData.worldSize = 12000.0;
importData.inputScale = 600;
importData.minBatchSize = 33;
importData.maxBatchSize = 65;

We are not going to cover the exact meaning of these options in this tutorial, but you may have noticed that terrainSize and worldSize are set to match the global options we set in createScene. The inputScale determines how the heightmap image will be scaled up for the scene. We are using a somewhat large scale because our heightmap image has limited precision. You can use floating point raw heightmaps to avoid applying any input scaling, but these images usually require some data compression.

The last step is adding the textures our terrain will use. First, we resize the list to hold three textures.

importData.layerList.resize(3);

After that, we set each texture's worldSize and add them to the list.

importData.layerList[0].worldSize = 100;
importData.layerList[0].textureNames.push_back(
  "dirt_grayrocky_diffusespecular.dds");
importData.layerList[0].textureNames.push_back(
  "dirt_grayrocky_normalheight.dds");
importData.layerList[1].worldSize = 30;
importData.layerList[1].textureNames.push_back(
  "grass_green-01_diffusespecular.dds");
importData.layerList[1].textureNames.push_back(
  "grass_green-01_normalheight.dds");
importData.layerList[2].worldSize = 200;
importData.layerList[2].textureNames.push_back(
  "growth_weirdfungus-03_diffusespecular.dds");
importData.layerList[2].textureNames.push_back(
  "growth_weirdfungus-03_normalheight.dds");

The texture's worldSize determines how big each splat of texture is going to be when applied to the terrain. A smaller value will increase the resolution of the rendered texture layer because each piece will be stretched less to fill in the terrain.

The default material generator requires two textures per layer: a diffuse specular texture and a heightmap texture. You can read Ogre Terrain Textures if you want to learn more about these textures and how they're made. The textures used in the tutorial reside in the Samples directory of your SDK or source distribution. As of this writing, they are included in this directory: '/Samples/Media/materials/textures/nvidia/'. Remember that Ogre will not automatically search subdirectories when loading resources, so you will have to add a line to your 'resources.cfg' file telling it to include the nvidia directory, and, of course, you'll have to copy the actual textures into your project's media folder.

Writing defineTerrain

Now we will tackle our defineTerrain method. The first thing we do is ask the TerrainGroup to define a unique filename for this Terrain. Add the following to defineTerrain:

Ogre::String filename = mTerrainGroup->generateFilename(x, y);

We want to check to see if a filename for this grid location has already been generated.

bool exists =
  Ogre::ResourceGroupManager::getSingleton().resourceExists(
    mTerrainGroup->getResourceGroup(),
    filename);

If it has already been generated, then we can call TerrainGroup::defineTerrain method to set up this grid location with the previously generated filename automatically. If it has not been generated, then we generate an image with getTerrainImage and then call a different overload of TerrainGroup::defineTerrain that takes a reference to our generated image. Finally, we set the mTerrainsImported flag to true.

if (exists)
  mTerrainGroup->defineTerrain(x, y);
else
{
  Ogre::Image img;
  getTerrainImage(x % 2 != 0, y % 2 != 0, img);
  mTerrainGroup->defineTerrain(x, y, &img);
 
  mTerrainsImported = true;
}

You might have to look at this method for a little while to fully understand it. Make sure you notice that there are three different defineTerrain methods in use. One of them from TutorialApplication and two of them from TerrainGroup.

Writing getTerrainImage

We need to write the helper function that was used by defineTerrain in the last step. This function is a static local function. If you've moved things around, then make sure this function is defined before defineTerrain. Since it is not a member function, it needs to be defined before being used. Add the following to getTerrainImage:

img.load("terrain.png", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
 
if (flipX)
  img.flipAroundY();
if (flipY)
  img.flipAroundX();

This will load our 'terrain.png' resource. Make sure it has been added to one of your resource loading paths. It is also included in the Ogre Samples directory.

Flipping is used to create seamless terrain so that unlimited terrain can be created using a single heightmap. If your terrain's heightmap is already seamless, then you don't need to use this trick. In our case, the flipping code is also useless, because we are using a 1x1 TerrainGroup. Flipping a 1x1 tile doesn't change anything. It is just for demonstration.

Writing initBlendMaps

Finally, we will finish up our configuration methods by completing the initBlendMaps method. This method will blend together the different layers we defined in configureTerrainDefaults. For now, you should pretty much view this method as a magic. The details will not be covered in this tutorial. Basically, the method blends the textures based on the height of the terrain at that point. This is not the only way of doing blending. It's a complicated topic and sits right at the verge between Ogre and the things it tries to abstract away. Add the following to initBlendMaps:

Ogre::Real minHeight0 = 70;
Ogre::Real fadeDist0 = 40;
Ogre::Real minHeight1 = 70;
Ogre::Real fadeDist1 = 15;
 
Ogre::TerrainLayerBlendMap* blendMap0 = terrain->getLayerBlendMap(1);
Ogre::TerrainLayerBlendMap* blendMap1 = terrain->getLayerBlendMap(2);
 
float* pBlend0 = blendMap0->getBlendPointer();
float* pBlend1 = blendMap1->getBlendPointer();
 
for (Ogre::uint16 y = 0; y < terrain->getLayerBlendMapSize(); ++y)
{
  for (Ogre::uint16 x = 0; x < terrain->getLayerBlendMapSize(); ++x)
  {
    Ogre::Real tx, ty;
 
    blendMap0->convertImageToTerrainSpace(x, y, &tx, &ty);
    Ogre::Real height = terrain->getHeightAtTerrainPosition(tx, ty);
    Ogre::Real val = (height - minHeight0) / fadeDist0;
    val = Ogre::Math::Clamp(val, (Ogre::Real)0, (Ogre::Real)1);
    *pBlend0++ = val;
 
    val = (height - minHeight1) / fadeDist1;
    val = Ogre::Math::Clamp(val, (Ogre::Real)0, (Ogre::Real)1);
    *pBlend1++ = val;
  }
}
 
blendMap0->dirty();
blendMap1->dirty();
blendMap0->update();
blendMap1->update();

 

The Scene So Far

Compile and run your application. You should get a nicely rendered terrain. Victory!

There are a number of things we will improve. We will add a label to the overlay that allows us to see when the terrain generation has finished. We will also make sure to save our terrain so that it can be reloaded instead of rebuilding it every time. Finally, we will make sure to clean up after ourselves. Just like with the normal 'new' and 'delete' in c++, every call to 'OGRE_NEW' requires a call to 'OGRE_DELETE'.

Image

Terrain Loading Label

First, we need to add a data member to private section of our TutorialApplication header.

TutorialApplication.h
OgreBites::Label* mInfoLabel;

And remember to initialize the pointer in the constructor.

TutorialApplication.cpp
mInfoLabel(0)

Let's construct this label in the createFrameListener method. Add the following to the end of createFrameListener:

mInfoLabel = mTrayMgr->createLabel(OgreBites::TL_TOP, "TerrainInfo", "", 350);

We use the TrayManager pointer that was defined in BaseApplication to request the creation of a new label. This method takes a TrayLocation, a name for the label, a caption to display, and a width.

Next we will add logic to frameRenderingQueued that tracks whether the terrain is still loading or not. We will also take care of saving our terrain after it has been loaded. Add the following to frameRenderingQueued right after the call to the parent method:

if (mTerrainGroup->isDerivedDataUpdateInProgress())
{
  mTrayMgr->moveWidgetToTray(mInfoLabel, OgreBites::TL_TOP, 0);
  mInfoLabel->show();
 
  if (mTerrainsImported)
    mInfoLabel->setCaption("Building terrain...");
  else   
    mInfoLabel->setCaption("Updating terrain...");
}
else
{
  mTrayMgr->removeWidgetFromTray(mInfoLabel);
  mInfoLabel->hide();
 
  if (mTerrainsImported)
  {
    mTerrainGroup->saveAllTerrains(true);
    mTerrainsImported = false;
  }
}

The first thing we do is determine if our terrain is still being built. If it is, then we add our Label to the tray and ask for it to be shown. Then we check to see if any new terrains have been imported. If they have, then we display text saying that the terrain is still being built. Otherwise we assume the textures are being updated.

If the terrain is no longer being updated, then we ask the SdkTrayManager to remove the our Label widget and hide the Label. We also check to see if new terrains have been imported and save them for future use. In our case, the file will be named 'terrain_00000000.dat' and it will reside in your 'bin' directory alongside your application's executable. After saving any new terrains, we reset the mTerrainsImported flag.

Compile and run your application again. You should now see a Label at the top of the screen while the terrain is being built. While the terrain is loading, you will not be able to press escape to exit and your movement controls will be choppy. This is what loading screens are for in games. But if you exit and run the application a second time, then it should load the terrain file that was saved the first time. This should be a much faster process.

Image

Cleaning Up

We must make sure to call OGRE_DELETE for every time we called OGRE_NEW. Add the following to destroyScene:

OGRE_DELETE mTerrainGroup;
OGRE_DELETE mTerrainGlobals;

These macros will ensure that any memory that was allocated by Ogre is released in the correct manner.

SkyBoxes

A SkyBox is basically a huge textured cube that surrounds all of the objects in your scene. It is one of the methods for simulating a sky. We will need six textures to cover all of the interior faces of the SkyBox. The Samples directory that comes with Ogre used to include a space-themed SkyBox. The files are attached to this tutorial, because they don't seem to be included anymore.

stevecube_up.jpg
stevecube_dn.jpg
stevecube_lf.jpg
stevecube_rt.jpg
stevecube_fr.jpg
stevecube_bk.jpg

Add these files to your resource loading path. It is very easy to include a SkyBox in your scene. Add the following to the end of createScene:

mSceneMgr->setSkyBox(true, "Examples/SpaceSkyBox");

Compile and run your application. That's all there is to it. The SkyBox will look really grainy because we are using a rather low resolution collection of textures.

The first parameter of this method determines whether or not to immediately enable the SkyBox. If you want to later disable the SkyBox you can call mSceneMgr->setSkyBox(false, ""). This disables the SkyBox.

The third and fourth parameters to setSkyBox are important to understand. We have allowed them to take their default values in our call. The third parameter is the distance between the Camera and the SkyBox. Make this change to your call:

mSceneMgr->setSkyBox(true, "Examples/SpaceSkyBox", 300);

Compile and run your application. Nothing has changed. This is because the fourth parameter sets whether or not to render the SkyBox before the rest of the scene. If the SkyBox is rendered first, then no matter how close it is the rest of your scene objects will be rendered on top of it. Now try this call:

mSceneMgr->setSkyBox(true, "Examples/SpaceSkyBox", 300, false);

Compile and run your application again. This time you should definitely see something different. Only a small patch of terrain should exist below the camera. Move around and notice what is happening. The SkyBox is being rendered only 300 pixels away from the Camera and it is no longer being rendered before everything else. This means the SkyBox is drawn over terrain that is farther than 300 units away from the Camera.

You can get a modest performance boost by not rendering the SkyBox first, but as you can see, you'll need to make sure not to cause strange problems like this when doing it. For the most part, leaving these additional parameters at their defaults is good enough. Although you may want to purposely use this strange culling behavior in your application. Try not to get to locked into how things are "supposed to work". If something catches your eye, then play around with it.

SkyDomes

Another method of simulating a sky is the SkyDome. The sky texture is still applied to a huge cube that surrounds the scene, but the textures is projected in such a way that it appears to create a dome over the scene. The best way to understand this is to see it in practice. Comment out our call to setSkyBox and add the following line:

mSceneMgr->setSkyDome(true, "Examples/CloudySky", 5, 8);

Compile and run your application. Make sure to move the Camera to the edge of the terrain so you can better idea of what is being done. The main drawback to this method is that the texture won't cover the bottom face of the cube. You would need to make sure a user could not accidentally see behind the curtain.

The first two parameters of the setSkyDome method are the same as setSkyBox. You can disable the SkyDome in the same way as well. The third parameter is the curvature for the dome projection. It is suggested to use values between 2 and 65. Lower values will produce a better effect at far distances, but higher values will cause less distortion of the texture. The fourth parameter is the number of times the texture will be tiled. This parameter is a Ogre::Real value. You can tile your texture 3.14 times if you want. The last two parameters are the distance and whether or not to draw the SkyDome first. These are the same last two parameters we had with setSkyBox.

SkyPlanes

The third method for simulating sky is rather different from the first two. This method will use a single plane. The first thing we need to do is create a Plane object. Comment out our call to setSkyDome and add the following:

Ogre::Plane plane;
plane.d = 1000;
plane.normal = Ogre::Vector3::NEGATIVE_UNIT_Y;

We've defined a plane by providing a distance from the origin (d) and a vector that is normal to our plane (normal). By choosing the the negative unit vector along the y-axis we have a plane that is parallel to the ground and facing downwards.

Now we can create the SkyPlane.

mSceneMgr->setSkyPlane(true, plane, "Examples/SpaceSkyPlane", 1500, 75);

The fourth parameter is the size of the SkyPlane (1500x1500 units), and the fifth parameter is number of times to tile the texture.

Compile and run your application. Again, the texture we are using is rather low-resolution. A high definition texture would look much better. It also doesn't tile very well. These issuse can both be fixed by using higher quality resources. The real problem is that it is very likely a user will be able to see the end of the SkyPlane as soon as they move anywhere near the edge of the terrain. For this reason, a SkyPlane is often used most in scenes that have high walls. In these cases, a SkyPlane offers a decent increase in performance over the other techniques.

The SkyPlane has some other attributes that can be used to produce a better effect. The sixth parameter of the setSkyPlane method is the "renderFirst" parameter we covered for the past two methods. The seventh parameter allows us to specify a curvature for the SkyPlane. This will pull down the corners of the SkyPlane turning it into a curved surface instead of a flat plane. If we set the curvature to something other flat, we also need to set the number of segments Ogre should use to render the SkyPlane. When the SkyPlane was a flat plane, everything was one large square, but if we add curvature, then it will require a more complicated geometry. The eighth and ninth parameters to the function are the number of segments for each dimension of the plane.

Let's test this out. Make these changes to our call:

mSceneMgr->setSkyPlane(
  true, plane, "Examples/SpaceSkyPlane", 1500, 50, true, 1.5, 150, 150);

Compile and run the application. This should help the look of our SkyPlane a bit. Go to the edge of the terrain to get a better look at what was done with the curvature.

Fog

Like practically everything in graphics programming, the fog effect in Ogre is an illusion. Ogre does not render a fog object into our scene. Instead, it simply applies a filter to our scene. This filter allows the Viewport's background color to show through our scenery to varying degrees based on the object's distance from the Camera. What this means is that your fog will have the same color has your Viewport's background color.

There are two basic types of fog in Ogre: linear and exponential. The difference is the rate at which the fog gets thicker as you move away from the Camera.
Image

Adding Fog to Our Scene

We will first add linear fog to our scene. We need to make sure to set our Viewport's background color to our desired fog color. Add the following to createScene right before our code for setting up the terrain:

Ogre::ColourValue fadeColour(0.9, 0.9, 0.9);
mWindow->getViewport(0)->setBackgroundColour(fadeColour);

Make sure you add this before the terrain code otherwise it won't work. If you were using more than one Viewport, then you may have to iterate through them all by using getNumViewports(external link).

Now we can create the fog.

mSceneMgr->setFog(Ogre::FOG_LINEAR, fadeColour, 0, 600, 900);

The first parameter is the fog type. The second parameter is the color we used to set our Viewport's background color. The third parameter is not used for linear fog. The fourth and fifth parameters specify the beginning and the end of the range for the fog. In our example, the fog will begin at 600 units out from the Camera, and it will end at 900 units. The reason this is called linear fog is because the thickness increases between these two values in a linear fashion. Compile and run your application.

The next type of fog is exponential fog. Like the picture suggests, exponential fog grows slowly at first and then gets dense very quickly. We do not set a range for this fog, instead we simply supply a desired density.

mSceneMgr->setFog(Ogre::FOG_EXP, fadeColour, 0.002);

Compile and run the application. You can see this creates a different kind of fog effect. It is more like a haze that fills the area surrounding the Camera. There is a variation of the exponential fog that increases at a faster rate.

mSceneMgr->setFog(Ogre::FOG_EXP2, fadeColour, 0.002);

Compile and run your application to see the difference this makes.

Conclusion

This tutorial covered the basics of using the Ogre Terrain System. We gave a brief overview of the setup that needs to be done to allow the importing of a terrain heightmap into our scene. We mentioned the notion of a TerrainGroup although we only used one Terrain object in our "group" for this tutorial. We also made sure to initialize our terrain with a directional light so that we would get specular reflections and shadows cast across our terrain.

We also covered the different methods Ogre offers to simulate a sky in your scene. These included: SkyBoxes, SkyDomes, and SkyPlanes. Finally, we introduced Ogre's fog effects. Fog is rendered by applying a filter to our scene that allows the Viewport's background color to bleed through our scene based on distance from our Camera.

This is a tutorial that you should spend a lot of time experimenting with. All of these features can be configured a great deal, and you can create some very convincing scenes with just what we've covered so far.

Full Source

The full source for this tutorial is here.

Next

Basic Tutorial 4


Alias: Basic_Tutorial_3


Contributors to this page: Nightmask31 points  , tothmarcell97166 points  , SRombauts2857 points  , progman118 points  , peters181 points  , Oxymoron290464 points  , noirenex982 points  , montaropdf61 points  , matti_kariluoma7 points  , Latin1603 points  , kabbotta41 points  , jstormgraphics31 points  , JacobM1 points  , jacmoe180265 points  , holocronweaver3177 points  , Harkathmaker245 points  and Beauty14565 points  .
Page last modified on Tuesday 16 of June, 2015 12:10:06 UTC by Nightmask31 points .


The content on this page is licensed under the terms of the Creative Commons Attribution-ShareAlike License.
As an exception, any source code contributed within the content is released into the Public Domain.