Skip to main content

History: Basic Tutorial 2

Source of version: 136

Copy to clipboard
            {TRANSCLUDE(page="tutbox")}This tutorial will expand on the use of Lights in a scene and using them to cast shadows. It will also cover the basics of using the Camera in Ogre.

The full source for this tutorial is ((BasicTutorial2SourceCurrent|here.))
{TRANSCLUDE}
%tutorialhelp%
!Prerequsites
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.
{maketoc}
!Setting Up the Scene
This time we are going to add some new methods to our TutorialApplication class. Add the following to the protected section of your header:
{CODE(caption="TutorialApplication.h",wrap="1",colors="c++")}
virtual void createCamera();
virtual void createViewport();
{CODE}
These two methods are already defined as virtual functions in the BaseApplication class. In this tutorial, we are going to provide overrides. This is how we will slowly take over some functionality that was hidden in BaseApplication.

Remember to add definitions to your cpp file as well:
{CODE(caption="TutorialApplication.cpp",wrap="1",colors="c++")}
void TutorialApplication::createCamera()
{
}

void TutorialApplication::createViewport()
{
}
{CODE}

!The Ogre Camera Class
A Camera is the object we use to view our scene. A [http://www.ogre3d.org/docs/api/1.9/class_ogre_1_1_camera.html|Camera] is a special object that works similar to a SceneNode. It has methods like {MONO()}setPosition{MONO} and {MONO()}yaw{MONO}. You can also attach it to a SceneNode. For instance, you might want to temporarily attach your Camera to a SceneNode that follows a path through the sky to create an aerial cutscene. Just like a SceneNode the Camera's position will be relative to its parent SceneNode. The Camera is not a SceneNode (it actually inherits from the Frustum class), but for movement and rotation, you can treat it like a SceneNode.

!Creating a Camera
We will now override the BaseApplication {MONO()}createCamera{MONO} method. The first step will be asking the SceneManager to create a new Camera. Add the following to {MONO()}createScene{MONO}:
{CODE(wrap="1", colors="c++")}
mCamera = mSceneMgr->createCamera("PlayerCam");
{CODE}
You can retrieve the Camera by name using the SceneManager's  {MONO()}getCamera{MONO} method.

Next, we will position the Camera and use a method called {MONO()}lookAt{MONO} to set its direction.
{CODE(wrap="1", colors="c++")}
mCamera->setPosition(Ogre::Vector3(0, 300, 500));
mCamera->lookAt(Ogre::Vector3(0, 0, 0));
{CODE}
The [http://www.ogre3d.org/docs/api/1.9/class_ogre_1_1_camera.html#a54542bfe56c8a09949d35d6c75a5045c|lookAt] method is very useful. It does exactly what it says. It rotates the Camera so that its line of sight focuses on the vector you give it. It makes the Camera "look at" the point.

The last thing we'll do is set the near clipping distance to 5 units. This is the distance at which the Camera will no longer render any mesh. If you get very close to a mesh, this will sometimes cut the mesh and allow you to see inside of it. The alternative is filling the entire screen with a tiny, highly magnified piece of the mesh's texture. It's up to you what you want in your scene. For demonstration, we'll set it here.
{CODE(wrap="1", colors="c++")}  
mCamera->setNearClipDistance(5);
{CODE}
You can also set the far clip distance for the Camera. This will chop off meshes in the distance. Although, you should not set the far clip distance when using stencil shadows, which we will be using in this tutorial.

The last thing we'll do is create a new ((SdkCameraMan)). This is the Camera controller provided by OgreBites.
{CODE(wrap="1", colors="c++")}
OgreBites::SdkCameraMan(mCamera);
{CODE}
Since we've just requested some dynamic memory, we always have to make sure it is cleaned up appropriately. In our case, the {MONO()}mCameraMan{MONO} variable will be taken care of by the destructor for BaseApplication, because we are simply recreating the Camera code that class was doing for us. If you look at {MONO()}BaseApplication::~BaseApplication{MONO}, then you'll see this line:
{CODE(wrap="1", colors="c++")}
if (mCameraMan) delete mCameraMan;
{CODE}
This sends the camera man home at the end of the day.
!Viewports
When dealing with multiple Cameras in a scene, the concept of a Viewport becomes very useful. We will touch on it now, because it will help you understand more about how Ogre decides which Camera to use when rendering a scene. Ogre makes it possible to have multiple SceneManagers running at the same time. It also allows you to break up the screen and use separate Cameras to render different views of a scene. This would allow the creation of things like splitscreens and minimaps. These kinds of things will be covered in later tutorials.

There are three constructs that are crucial to understanding how Ogre renders a scene: the Camera, the SceneManager, and the RenderWindow. We have not yet covered the RenderWindow. It basically represents the whole window we are rendering to. The SceneManager will create Cameras to view the scene, and then we tell the RenderWindow where to display each Camera's view. The way we tell the RenderWindow which area of the screen to use is by giving it a [http://www.ogre3d.org/docs/api/1.9/class_ogre_1_1_viewport.html|Viewport]. For many circumstances, we will simply create one Camera and create a Viewport which represents the whole screen. In fact, that is exactly what was already being done for us in BaseApplication.
!Creating a Viewport
Let's create a Viewport for our scene. To do this, we will use the {MONO()}addViewport{MONO} method of the RenderWindow. Add the following to {MONO()}TutorialApplication::createViewports{MONO}:
{CODE(wrap="1", colors="c++")}
Ogre::Viewport* vp = mWindow->addViewport(mCamera);
{CODE}
{MONO()}mWindow{MONO} is another variable defined for us in BaseApplication. Let's set the background color of the Viewport.
{CODE(wrap="1", colors="c++")}
vp->setBackgroundColour(Ogre::ColourValue(0, 0, 0);
{CODE}
We've set it to black because we are going to add colored lighting later, and we don't want the background color affecting how we see the lighting.

The last thing we are going to do is set the aspect ratio of our Camera. If you are using something other than a standard full-window viewport, then failing to set this can result in a distorted scene. We will set it here for demonstration even though we are using the default aspect ratio.
{CODE(wrap="1", colors="c++")}
mCamera->setAspectRatio(
  Ogre::Real(vp->getActualWidth()) /
  Ogre::Real(vp->getActualHeight()));    
{CODE}
We have retrieved the width and height from the Viewport to set the aspect ratio. As we mentioned, the default is already set to use the full screen's dimensions. 

Compile and run your application. You should still only see a black screen with the overlays, just make sure it runs.
!Building the Scene
Before we get to shadows and lighting, let's add some elements to our scene. Let's put a ninja right in the middle of things. Add the following to {MONO()}createScene{MONO} right after we set the ambient light:
{CODE(wrap="1", colors="c++")}
Ogre::Entity* ninjaEntity = mSceneMgr->createEntity("ninja.mesh");
ninjaEntity->setCastShadows(true);

mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(ninjaEntity);
{CODE}
This should look familiar, except we are asking the mesh to cast shadows this time. And notice that we have created a child scene node and attached the {MONO()}ninjaEntity{MONO} all in one call this time.

We will also create something for the ninja to be standing on. We can use the [http://www.ogre3d.org/docs/api/1.9/class_ogre_1_1_mesh_manager.html|MeshManager] to create meshes from scratch. We will use it to generate a textured plane to use as the ground.

The first thing we'll do is create an abstract Plane object. This is not the mesh, it is more of a blueprint.
{CODE(wrap="1", colors="c++")}
Ogre::Plane plane(Ogre::Vector3::UNIT_Y, 0);
{CODE}
We create a plane by supplying a vector that is normal to our plane and its distance from the origin. So we have created a plane that is perpendicular to the y-axis and zero units from the origin. Here's a picture:
{img fileId="2276" rel="box[g]"}
There are other overloads of the Plane constructor that let us pass a second vector instead of a distance from the origin. This allows us to build any plane in 3D space we want.

Now we'll ask the MeshManager to create us a mesh using our Plane blueprint. The MeshManager is already keeping track of the resources we loaded when initializing our application. On top of this, it can create new meshes for us.
{CODE(wrap="1", colors="c++")}
Ogre::MeshManager::getSingleton().createPlane(
  "ground",
  Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
  plane, 
  1500, 1500, 20, 20, 
  true, 
  1, 5, 5, 
  Ogre::Vector3::UNIT_Z);
{CODE}
This is a complicated method, and we're not entirely equipped to understand all of it yet. You can read through the [http://www.ogre3d.org/docs/api/1.9/class_ogre_1_1_mesh_manager.html|MeshManager] class specification if you want to learn more now. Basically, we've created a new mesh called "ground" with a size of 1500x1500.

Now we will create a new Entity using this mesh.
{CODE(wrap="1", colors="c++")}
Ogre::Entity* groundEntity = mSceneMgr->createEntity();
mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(groundEntity);
{CODE}
We want to tell our SceneManager not to cast shadows from our ground Entity. It would just be a waste. Don't get confused, this means the ground won't cast a shadow, it doesn't mean we can't cast shadows ''on to the ground''.
{CODE(wrap="1", colors="c++")}
groundEntity->setCastShadows(false);
{CODE}
And finally we need to give our ground a material. For now, it will be easiest to use a material from the script that Ogre includes with its samples. You should have these resources in your SDK or the source directory you downloaded to build Ogre.
{CODE(wrap="1", colors="c++")}
groundEntity->setMaterialName("Examples/Rockwall");
{CODE}
Make sure you add the texture for the material and the Examples.material script to your resource loading path. In our case, the texture is called 'rockwall.tga'. You can find the name yourself by reading the entry in the material script.
!Using Shadows in Ogre
Enabling shadows in Ogre is easy. The SceneManager class has a [http://www.ogre3d.org/docs/api/1.9/class_ogre_1_1_scene_manager.html#a0f8f32d176059a385527cf9970ddc892|setShadowTechnique] method we can use. Then whenever we create an Entity, we call {MONO()}setCastShadows{MONO} to choose which Entities will cast shadows.

Let's turn off the ambient light so we can see the full effect of our lights. Find the {MONO()}setAmbientLight{MONO} call in {MONO()}createScene{MONO}, and make the following changes: 
{CODE(wrap="1", colors="c++")}
mSceneMgr->setAmbientLight(Ogre::ColourValue(0, 0, 0));
mSceneMgr->setShadowTechnique(Ogre::SHADOWTYPE_STENCIL_ADDITIVE);
{CODE}
Now the SceneManager will use additive stencil shadows. Let's add some lights to see this in action.
!Lights
Ogre provides three types of lighting.
*Ogre::Light::LT_POINT - This Light speads out equally in all directions from a point.
*Ogre::Light::LT_SPOTLIGHT - This Light works like a flashlight. It produces a solid cylinder of light that is brighter at the center and fades off.
*Ogre::Light::LT_DIRECTIONAL - This Light simulates a huge source that is very far away - like daylight. Light hits the entire scene at the same angle everywhere.
The [http://www.ogre3d.org/docs/api/1.9/class_ogre_1_1_light.html|Light] class has a wide range of properties. Two of the most important are the ((-Diffuse (Light)|diffuse)) and ((-Specular (Light)|specular)) color. Each material script defines how much specular and diffuse lighting a material reflects. These properties will be covered in some of the later tutorials.

!Creating a Light
Let's add a Light to our scene. We do this by calling the SceneManager's {MONO()}createLight{MONO} method. Add the following to {MONO()}createScene{MONO} right after we finish creating the {MONO()}groundEntity{MONO}:
{CODE(wrap="1", colors="c++")}
Ogre::Light* spotLight = mSceneMgr->createLight("SpotLight");
{CODE}
We'll set the diffuse and specular colors to pure blue.
{CODE(wrap="1", colors="c++")}
spotLight->setDiffuseColour(0, 0, 1.0);
spotLight->setSpecularColour(0, 0, 1.0);
{CODE}
Next we will set the type of the light to spotlight.
{CODE(wrap="1", colors="c++")}
spotLight->setType(Ogre::Light::LT_SPOTLIGHT);
{CODE}
The spotlight requires both a position and a direction - remember it acts like a flashlight. We'll place the spotlight above the right shoulder of the ninja.
{CODE(wrap="1", colors="c++")}
spotLight->setDirection(-1, -1, 0);
spotLight->setPosition(Ogre::Vector3(200, 200, 0));
{CODE}
{img fileId="2281" rel="box[g]"}
Finally, we set what is called the spotlight range. These are the angles that determine where the light fades from bright in the middle to dimmer on the outside edges.
{CODE(wrap="1", colors="c++")}
spotLight->setSpotlightRange(Ogre::Degree(35), Ogre::Degree(50));
{CODE}
Compile and run the application. You should see the shadowy blue figure of a ninja.

{img fileId="2279" rel="box[g]"}
!Creating More Lights
Next we'll add a directional light to our scene. This type of light essentially simulates daylight or moonlight. The light is cast at the same angle across the entire scene equally. As before, we'll start by creating the Light and setting its type.
{CODE(wrap="1" colors="c++")}
Ogre::Light* directionalLight = mSceneMgr->createLight("DirectionalLight");
directionalLight->setType(Ogre::Light::LT_DIRECTIONAL);
{CODE}
Now we'll set the diffuse and specular colors to a dark red.
{CODE(wrap="1" colors="c++")}
directionalLight->setDiffuseColour(Ogre::ColourValue(.4, 0, 0));
directionalLight->setSpecularColour(Ogre::ColourValue(.4, 0, 0));
{CODE}
Finally, we need to set the Light's direction. A directional light does not have a position, because it is modeled as a point light that is infinitely far away. We will place this light up and in front of the ninja.
{img fileId="2282" rel="box[g]"}
{CODE(wrap="1" colors="c++")}
directionalLight->setDirection(Ogre::Vector3(0, -1, 1));
{CODE}








!Shadow Types
Ogre currently supports three types of Shadows:
# Modulative Texture Shadows (Ogre::SHADOWTYPE_TEXTURE_MODULATIVE) - The least computationally expensive of the three. This creates a black and white render-to-texture of shadow casters, which is then applied to the scene.
# Modulative Stencil Shadows (Ogre::SHADOWTYPE_STENCIL_MODULATIVE) - This technique renders all shadow volumes as a modulation after all non-transparent objects have been rendered to the scene. This is not as intensive as Additive Stencil Shadows, but it is also not as accurate.
# Additive Stencil Shadows (Ogre::SHADOWTYPE_STENCIL_ADDITIVE) - This technique renders each light as a separate additive pass on the scene. This is very hard on the graphics card because each additional light requires an additional pass at rendering the scene.

Ogre does not support soft shadows as part of the engine. If you want soft shadows you will need to write your own vertex and fragment programs. Note that this is just a quick introduction here - the Ogre manual [http://www.ogre3d.org/docs/manual/manual_70.html|fully describes shadows] in Ogre and the implications of using them.
!Things to Try
!!Different Shadow Types
In this demo we only set the shadow type to be SHADOWTYPE_STENCIL_ADDITIVE. Try setting it to the other two types of shadows and see what happens. There are also many other shadow-related functions in the [http://www.ogre3d.org/docs/api/1.9/class_ogre_1_1_scene_manager.html|SceneManager] class. Try playing with some of them and seeing what you come up with.

!!Light Attenuation
Lights define a [http://www.ogre3d.org/docs/api/1.9/class_ogre_1_1_light.html#a2880f26669477a55cf01919f906bb65d|setAttenuation] function which allows you to control how the light dissipates as you get farther away from it. Add a function call to the Point light that sets the attenuation to different values. How does this affect the light?

!!~np~SceneManager::setAmbientLight~/np~
Experiment with the [http://www.ogre3d.org/docs/api/1.9/class_ogre_1_1_scene_manager.html#a7c26cdbb5703cf10a99add1f6a930ca2|setAmbientLight] function of mSceneMgr.

!!Viewport Background Colour
Change the default ColourValue in the createViewports function. While it is not really appropriate to change it to something other than black in this situation, it is a good thing to know how to change.

!!~np~Camera::setFarClipDistance~/np~
In createCamera we set the near clip distance. Add a function call to [http://www.ogre3d.org/docs/api/1.9/class_ogre_1_1_frustum.html#a9429acc6a3e8cfd4fbd0b2472c82f565|setFarClipDistance] and set it to be 500, watch what happens when you move from seeing the Ninja and not seeing the Ninja with stencil shadows turned on. Notice the slowup?

Note: You'll need to set mSceneMgr->setShadowUseInfiniteFarPlane(false), for this to work, and you might get some strange shadows. (See this [http://www.ogre3d.org/phpBB2/viewtopic.php?t=13081|thread])

!!Planes
We did not cover much about Planes in this tutorial (it was not the focus of this article). We will go back and revisit this topic in a later tutorial, but for now you should look up the [http://www.ogre3d.org/docs/api/1.9/class_ogre_1_1_mesh_manager.html#a0b9b36dd24c9288b1b0211035d951d20|createPlane] function and try playing with some of the inputs to the function.

!Conclusion
Now you should have an understanding of the basis on Camera, Lights and Shadows. We will be using them more in the next tutorials.

!!Full Source
If you are having difficulty building this tutorial, take a look at the ((BasicTutorial2Source|source code)) for it and compare it to your project.

!!Next
Proceed to ((Basic Tutorial 3)) ''Terrain, Sky, and Fog''

---
Alias: (alias(Basic_Tutorial_2))
        

History

Information Version
Sun 27 of May, 2018 22:41 GMT-0000 paroj 164
Sun 29 of Oct, 2017 22:52 GMT-0000 paroj 163
Mon 23 of Oct, 2017 14:37 GMT-0000 paroj 162
Sat 07 of Oct, 2017 12:41 GMT-0000 paroj 161
Fri 10 of Jul, 2015 22:13 GMT-0000 Duke missing right parenthesis 160
Wed 22 of Apr, 2015 20:37 GMT-0000 dsobotta createViewport -> createViewports to reflect virtual functions that are being overridden from BaseApplication 159
Tue 21 of Apr, 2015 22:44 GMT-0000 a0903638 wrong method 158
Sun 05 of Apr, 2015 07:50 GMT-0000 kabbotta 157
Fri 03 of Apr, 2015 03:00 GMT-0000 kabbotta 156
Fri 03 of Apr, 2015 02:57 GMT-0000 kabbotta 155
Fri 03 of Apr, 2015 02:26 GMT-0000 kabbotta 154
Fri 03 of Apr, 2015 02:13 GMT-0000 kabbotta 153
Fri 03 of Apr, 2015 02:02 GMT-0000 kabbotta 152
Thu 02 of Apr, 2015 23:23 GMT-0000 kabbotta 151
Thu 02 of Apr, 2015 23:18 GMT-0000 kabbotta 150
Thu 02 of Apr, 2015 23:16 GMT-0000 kabbotta 149
Thu 02 of Apr, 2015 22:38 GMT-0000 kabbotta 148
Thu 02 of Apr, 2015 22:37 GMT-0000 kabbotta 147
Thu 02 of Apr, 2015 22:34 GMT-0000 kabbotta 146
Thu 02 of Apr, 2015 22:32 GMT-0000 kabbotta 145
Thu 02 of Apr, 2015 22:31 GMT-0000 kabbotta 144
Thu 02 of Apr, 2015 22:30 GMT-0000 kabbotta 143
Thu 02 of Apr, 2015 22:12 GMT-0000 kabbotta 142
Thu 02 of Apr, 2015 22:11 GMT-0000 kabbotta 141
Thu 02 of Apr, 2015 22:11 GMT-0000 kabbotta 140