Well lets get started, first grab a copy of XEffects from here. Please get the latest version because I made some changes and bug fixes that are necessary for this tutorial code to function properly. From here on, I'm going to go through every little bit about the example, even the obvious stuff, so that you can just copy paste every code snippet and you will have the entire CPP file. Please bear with me on that.
Open up a new project, and type down the basic boilerplate code we need at the start of every Irrlicht tutorial. Here I am also including "effectWrapper.h" from XEffects, and a custom Diablo/Dungeon Siege style character controller I made to use along with this project:
Code: Select all
#include <irrlicht.h>
#include "effectWrapper.h"
#include "IOrthoControllerAnimator.h"
Code: Select all
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
Code: Select all
int main()
{
IrrlichtDevice* device = createDevice(EDT_OPENGL, dimension2di(800,600), 32);
ISceneManager* smgr = device->getSceneManager();
IVideoDriver* driver = device->getVideoDriver();
Code: Select all
IMeshSceneNode* ground = smgr->addCubeSceneNode();
ground->setScale(vector3df(200,1,200));
smgr->getMeshManipulator()->makePlanarTextureMapping(ground->getMesh(),2);
ground->getMaterial(0).setTexture(0,driver->getTexture("media/rockwall.bmp"));
ground->getMaterial(0).Lighting = true;
Code: Select all
ICameraSceneNode* orthoCam = smgr->addCameraSceneNode();
orthoCam->setPosition(vector3df(300,400,0));
orthoCam->setTarget(vector3df(0,0,0));
matrix4 projMat;
projMat.buildProjectionMatrixOrthoLH(650,500,10,1000);
orthoCam->setProjectionMatrix(projMat);
orthoCam->setIsOrthogonal(true);
Code: Select all
IAnimatedMeshSceneNode* dwarf = smgr->addAnimatedMeshSceneNode(smgr->getMesh("media/dwarf.ms3d"));
dwarf->setPosition(vector3df(0,8,0));
dwarf->getMaterial(0).Lighting = false;
IOrthoControllerAnimator* orthoAnimator = new IOrthoControllerAnimator(smgr);
dwarf->addAnimator(orthoAnimator);
device->setEventReceiver(orthoAnimator);
Code: Select all
effectHandler* effect = new effectHandler(device,dimension2di(1024,1024));
effect->setShadowDarkness(1.0f);
Code: Select all
effect->addShadowToNode(dwarf, EFT_8PCF, ESM_BOTH);
effect->addShadowToNode(ground, EFT_8PCF, ESM_RECEIVE);
Here we will generate another orthographic projection matrix and apply it to light's camera. When a light is set up in an orthographic manner, it behaves as a directional light source, similar to that of the sun. This makes it great for outdoor scenes. Note that we are setting the width and height here a little bigger than last time, to make sure that we are capturing everything within the viewport. Depending on how tall the objects in your levels are, you may have to increase these values. We also set the maximum shadow distance from the light to 1. This is because when using orthographic projection, the depth values behave slightly differently. Make sure that the near and far value of your projection matrix enclose the visible area as tightly as possible, to get the best depth resolution:
Code: Select all
effect->setMaxShadowDistanceFromLight(1.0f);
projMat.buildProjectionMatrixOrthoLH(800,850,10,600);
effect->getLightCamera()->setProjectionMatrix(projMat);
Code: Select all
for(u32 i = 0;i < 3;++i)
{
IMeshSceneNode* house = smgr->addMeshSceneNode(smgr->getMesh("media/house.3ds"));
house->setScale(vector3df(0.2f,0.2f,0.2f));
house->setPosition(vector3df(200 * i,0,-200));
house->setRotation(vector3df(0,90,0));
house->getMaterial(0).setTexture(0,driver->getTexture("media/fw12b.jpg"));
house->setAutomaticCulling(EAC_FRUSTUM_BOX);
house->getMaterial(0).Lighting = false;
effect->addShadowToNode(house,EFT_8PCF,ESM_BOTH);
}
for(u32 i = 0;i < 3;++i)
{
IMeshSceneNode* house = smgr->addMeshSceneNode(smgr->getMesh("media/house.3ds"));
house->setScale(vector3df(0.2f,0.2f,0.2f));
house->setPosition(vector3df(200,0,200 * i));
house->getMaterial(0).setTexture(0,driver->getTexture("media/fw12b.jpg"));
house->setAutomaticCulling(EAC_FRUSTUM_BOX);
house->getMaterial(0).Lighting = false;
effect->addShadowToNode(house,EFT_8PCF,ESM_BOTH);
}
Code: Select all
int lastFPS = 0;
while(device->run())
{
driver->beginScene(true,true,SColor(0,0,0,0));
if(driver->getFPS() != lastFPS)
{
stringw title = "Orthographic shadow test FPS: ";
title += driver->getFPS();
device->setWindowCaption(title.c_str());
}
Code: Select all
orthoCam->setPosition(dwarf->getPosition() + vector3df(200,400,0));
orthoCam->setTarget(dwarf->getPosition());
effect->setLightPosition(dwarf->getPosition() + vector3df(-200,400,0));
effect->setLightTarget(dwarf->getPosition());
effect->update();
driver->endScene();
}
Code: Select all
device->drop();
device = NULL;
return 0;
}
A few nice things to note about this screenshot:
- The quality is consistant throughout, the shadow camera follows the view where ever it goes, and the shadowmap is uniformally distributed because of the orthographic projection.
- The chimneys cast soft shadows on the roofs of the houses.
- The dwarf is partly shadowed by the house in front of him.
- The dwarf and house shadows blend seamlessly together to form a single shadow, the dwarf isn't unrealistically casting a shadow of its own when it's already inside a house shadow.
About the character controller:
When the user clicks on a position on the screen, the intersection is found between the screen ray and the ground plane. The dwarf then orients itself towards the direction of the point where the user clicked, and runs there until he is a certain distance away. The animation frames specified are specific to the dwarf, so be sure to set these to something else if you plan to use the character controller in your own projects with a different model. Regarding that, the IOrthoControllerAnimator is free to use, no restrictions.
Here is an archive containing the demo binary/media and full sources:
http://irrlichtirc.g0dsoft.com/BlindSid ... Shadow.zip
(Remember: To move the dwarf just click somewhere and he will run there.)
That about settles it for today, have fun.
Cheers