Dieses Turotial sollzeigen wie man in Irrlicht Spezialeffekte erzeugt.
Unter anderrem wird gezeigt wie man stencil buffer shadows, das partikel system, billboards, dynamisches licht und das water surface scene node benutzt
Und so soll das ganze am ende dann aussehen:
Los geht's!Wir Starten hier wie bei den letzten Tutorials, mit dem unterschied das wir das 'shadows' flag bei createDevice() auf true setzen.
Code:
#include <irrlicht.h>
#include <iostream>
using namespace irr;
#pragma comment(lib, "Irrlicht.lib")
int main()
{
// ask user for driver
video::E_DRIVER_TYPE driverType;
printf("Bitte waehlen sie den Treiber aus den sie fuer dieses Programm benutzen wollen:\n"\
" (a) Direct3D 9.0c\n (b) Direct3D 8.1\n (c) OpenGL 1.5\n"\
" (d) Software Renderer\n (e) Apfelbaum Software Renderer\n"\
" (f) NullDevice\n (otherKey) exit\n\n");
char i;
std::cin >> i;
switch(i)
{
case 'a': driverType = video::EDT_DIRECT3D9;break;
case 'b': driverType = video::EDT_DIRECT3D8;break;
case 'c': driverType = video::EDT_OPENGL; break;
case 'd': driverType = video::EDT_SOFTWARE; break;
case 'e': driverType = video::EDT_SOFTWARE2;break;
case 'f': driverType = video::EDT_NULL; break;
default: return 1;
}
// erstelle device und beende fals nicht erfolgreich
IrrlichtDevice *device = createDevice(driverType, core::dimension2d<s32>(640, 480), 16, false, true);
if (device == 0)
return 1;
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
Als umgebung laden wir eine 3ds datei. Dieser wurde mal mit Anim8or Modeliert und ins 3ds format exportiert, da Irrlicht zu dem Zeitpunkt wo dieses Tutorial entstand noch keine .an8 dateien lesen konnte. Ausserdem is das Texture mapping nicht so schön. Wir gehen hier einfach mal davon aus das man ein besserer Programmierer als ein artist ist daher hilft uns Irrlicht beim erstellen eines Texture mapings. Einfach den mesh manipulator benutzen und eine planares texture mapping für das mesh erstellen.
Man wusste auch nicht wie man das Material in anim8or korrekt setzt. Ebenfals gibt es eine helle farbe(emessive color) die nicht so gemocht ist, daraufhin schalten wir dies auch gleich hier im code ab:
Code:
scene::IAnimatedMesh* mesh = smgr->getMesh(
"../../media/room.3ds");
smgr->getMeshManipulator()->makePlanarTextureMapping(
mesh->getMesh(0), 0.008f);
scene::ISceneNode* node = 0;
node = smgr->addAnimatedMeshSceneNode(mesh);
node->setMaterialTexture(0, driver->getTexture("../../media/wall.jpg"));
node->getMaterial(0).EmissiveColor.set(0,0,0,0);
Und nun der erste spezial effekt: Animiertes Wasser. Das ganze funktioniert wie folgt: Das WaterSurfaceSceneNode nutzt ein mesh als Vorlage und lässt es Wellen schlagen wie bei einer realen Wasser-Oberfläche. Und wenn wir bei diesem SceneNode MT_REFLECTION_2_LAYER benutzen, sieht das richtig gut aus. Und das ganze machen wir in den nächsten Code-Zeilen:
Als Grundmesh für das WaterSurfaceSceneNode erstellen wir ein hill plane mesh ohne die hügel und berge, aber man kann jedes beliebige Mesh dafür benutzen.
Code:
mesh = smgr->addHillPlaneMesh("myHill",
core::dimension2d<f32>(20,20),
core::dimension2d<s32>(40,40), 0, 0,
core::dimension2d<f32>(0,0),
core::dimension2d<f32>(10,10));
node = smgr->addWaterSurfaceSceneNode(mesh->getMesh(0), 3.0f, 300.0f, 30.0f);
node->setPosition(core::vector3df(0,7,0));
node->setMaterialTexture(0, driver->getTexture("../../media/stones.jpg"));
node->setMaterialTexture(1, driver->getTexture("../../media/water.jpg"));
node->setMaterialType(video::EMT_REFLECTION_2_LAYER);
Der zweite Spezialeffekt gehört wohl zu den Standard sachen. Diesen wirst du bestimmt schon in einigen Irrlicht-Engine Demos gesehen haben, ein Transparentes BillboardSceneNode kombiniert mit einem dynamischem licht. Wir erstellen ein simples lightSceneNode und lassen es umher fliegen und damit es noch ein wenig cooler aussieht hängen wir noch das billboardSceneNode dran.
Code:
// create light
node = smgr->addLightSceneNode(0, core::vector3df(0,0,0),
video::SColorf(1.0f, 0.6f, 0.7f, 1.0f), 600.0f);
scene::ISceneNodeAnimator* anim = 0;
anim = smgr->createFlyCircleAnimator (core::vector3df(0,150,0),250.0f);
node->addAnimator(anim);
anim->drop();
// attach billboard to light
node = smgr->addBillboardSceneNode(node, core::dimension2d<f32>(50, 50));
node->setMaterialFlag(video::EMF_LIGHTING, false);
node->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
node->setMaterialTexture(0, driver->getTexture("../../media/particlewhite.bmp"));
Der nächste Spezialeffekt ist etwas interressanter, denn es ist ein Partikelsystem.
Das Partikelsystem der IrrlichtEngine ist frei modular und modifizierbar, dennoch ist es einfach zu benutzen.
Es gibt ein partikelSystemSceneNode welchem man so genannte emitter übergeben kann, diese bewirken dann zB. das Partikel aus dem Nichts auftauchen.
Diese emitter sind sehr flexibel und haben viele Parameter, wie zB Richtung, Menge und Farbe der zu erzeugenden Partikel.
Es gibt unterschiedliche emitter, so zB. gibt es den point-emitter der die Partikel von einem festem punkt aus erscheinen lässt.
Wenn einem die von Irrlicht vorgegebennen Partikel-emitter nicht genug sind, kann man sich eine von IParticleEmitter abgeletette klasse erstellen und diese dann mit setEmitter an das Partikel System übergeben.
In diesem Beispiel erstellen wir einen BoxPartikelEmitter der zufällig Partikel innerhalb einer Box erstellt.
Die Parameter definieren die Box, Richtung der Partikel, Mindeste und Maximale Anzahl neuer Partikel pro Sekunde, Farbe und Minimale und Maximale Lebenszeit der Partikel.
Aber nur mit einem emitter ist unser Partikelsystem ein wenig langweilig, es gibt benutzen wir auch noch particle affectors, diese modifizieren nämlich die Partikel während sie umherfliegen. Sie können zum Partikelsystem hinzugefügt werden und Simulieren zB. Gravitation und Wind. Den affector den wir in diesem Tutorial benutzen werden ist ein affector der die Farbe der Partikel verändert um genauer zu sein er erzeugt ein FadeOut. Wie auch bei den emittern kann man sich auch ein eigennen affector erstellen indem man eine neue klasse erstellt die von IParticleAffector abgeleitet ist und fügt diese mit addAffector zum Partikelsystem hinzu. Nachdem wir noch ein paar materials gesetzt haben, besitzen wir ein richtig toll aussehendes Lagerfeuer. Wenn man das material, die Textur, den partikel emitter und die affector parameter noch ein wenig modifiziert, ist es sogar möglich Rauch, Regen, Schnee, Explosionen usw. zu erstellen
Code:
scene::IParticleSystemSceneNode* ps = 0;
ps = smgr->addParticleSystemSceneNode(false);
ps->setPosition(core::vector3df(-70,60,40));
ps->setScale(core::vector3df(2,2,2));
ps->setParticleSize(core::dimension2d<f32>(20.0f, 10.0f));
scene::IParticleEmitter* em = ps->createBoxEmitter(
core::aabbox3d<f32>(-7,0,-7,7,1,7),
core::vector3df(0.0f,0.03f,0.0f),
80,100,
video::SColor(0,255,255,255), video::SColor(0,255,255,255),
800,2000);
ps->setEmitter(em);
em->drop();
scene::IParticleAffector* paf =
ps->createFadeOutParticleAffector();
ps->addAffector(paf);
paf->drop();
ps->setMaterialFlag(video::EMF_LIGHTING, false);
ps->setMaterialTexture(0, driver->getTexture("../../media/particle.bmp"));
ps->setMaterialType(video::EMT_TRANSPARENT_VERTEX_ALPHA);
Als letzten Spezialeffekt wollen wir einen dynamischen Schatten von einem Animiertem Charakter erstellen. Dafür laden wir extra ein DirectX .x Model. Um einen Schatten zu erstellen müssen wir einfach nur addShadowVolumeSceneNode aufrufen. Die Farbe des Schattens kann man nur global, also für alle Schatten, verändern indem man ISceneManager::setShadowColor() aufruft. Und Voila, unser Schatten ist erzeugt.
Weil unser Charakter ein wenig klein für diese Szene ist, lassen wir ihn ein wenig grösser werden und rufen dafür setScale() auf. Und weil der Charakter von einem dynamischem Licht angeleuchtet wird, müssen die Normalen normalisiert werden damit das licht den Charakter korrekt beleuchtet. Das ist immer notwendig wenn man das model in seiner skalierung nicht (1,1,1) gesetzt ist. Andernfals wird es zu Dunkel oder zu Hell weil die Normalen auch Skaliert wurden.
Code:
mesh = smgr->getMesh("../../media/dwarf.x");
scene::IAnimatedMeshSceneNode* anode = 0;
anode = smgr->addAnimatedMeshSceneNode(mesh);
anode->setPosition(core::vector3df(-50,20,-60));
anode->setAnimationSpeed(15);
// add shadow
anode->addShadowVolumeSceneNode();
smgr->setShadowColor(video::SColor(220,0,0,0));
// make the model a little bit bigger and normalize its normals
// because of this for correct lighting
anode->setScale(core::vector3df(2,2,2));
anode->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true);
Und nun müssen wir nurnoch so wie immer alles Anzeigen.
Code:
scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();
camera->setPosition(core::vector3df(-50,50,-150));
int lastFPS = -1;
while(device->run())
{
driver->beginScene(true, true, 0);
smgr->drawAll();
driver->endScene();
int fps = driver->getFPS();
if (lastFPS != fps)
{
core::stringw str = L"Irrlicht Engine - SpecialFX example [";
str += driver->getName();
str += "] FPS:";
str += fps;
device->setWindowCaption(str.c_str());
lastFPS = fps;
}
}
device->drop();
return 0;
}