程序的开始就像之前的教程那样。这里要注意的是:如果你想根据活动的角色产生动态投影,那么createDevice()函数的‘shadows’标志位要设成true。但如果运行缓慢,则设为false。如果引擎检测到你的硬件不支持模板缓存(stencil buffer),为了防止运行过慢,会自己关闭阴影。
#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("Please select the driver you want for this example:\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; }
// create device and exit if creation failed 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();
|
我们加载了一个*.3ds格式的文件作为世界环境。这是我用Anim8or 建模并生成*.3ds格式(由于写本教程时,IrrLicht引擎不支持*.an8格式的模型)的小房间。我是一个很差的3d图形美工,因此模型的纹理贴图并不漂亮。幸运的是我作为程序员比美工好,并能够用IrrLicht引擎创建一个很cool的纹理贴图:只要用网格管理器(mesh manipulator)为网格创建平面纹理贴图就可以了。如果你想看我用Anim8or 作的贴图效果,只需把这行注释就行了。我也不打算用Anim8or 设置材质,因为有我很不喜欢的emissive光。我会在代码中禁用它。
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);
|
现在要实现的第一个特效是:流动的水。它应该这样工作:将一个网格作为输入制作成水表面场景节点(WaterSurfaceSceneNode),并使它好像水的表面产生波纹。而且如果把场景节点的材质设成MT_REFLECTION_2_LAYER ,那么效果真的很cool。我们会在下几行代码实现。作为输入网格,我们创建一个丘陵平面网格。其实任何网格都可以的,甚至可以使用room。3ds(虽然看起来很奇怪)作为网格。
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);
|
第二个特效是很基础的,你应该在IrrLicht引擎的其它演示中看过:一个结合了动态光的透明公告板。我们简单地创建一个飞来飞去的灯光场景节点,并绑定一个公告板(能够知道灯光的位置),使它看起来更cool。
// 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"));
|
第三个特效是更有趣的:粒子系统(particle system)。IrrLicht引擎中的粒子系统是一个可扩展并且很容易使用的模块。要放一个粒子发射器到粒子系统里,发射器就会产生粒子了。这些发射器是什么灵活的,而且通常在创建时要设置很多参数(例如方向,数量和粒子的颜色)。 有各种不同的发射器,例如一个点发射器能使粒子从一个固定点射出。如果引擎中的粒子发射器不能满足你的要求,你可以自己创建一个,只要简单地创建一个继承自IParticleEmitter 接口的类并用setEmitter()函数把它绑定到粒子系统, 在这个例子中我们创建一个盒形粒子发射器,用于在盒子中随机创建粒子。createBoxEmitter()函数要定义盒子的大小,粒子的方向,每秒最多最少粒子生成量,粒子颜色和粒子的最长最短生存时间。
只使用了发射器的粒子系统是有点枯燥的,因此还使用粒子效果器来修改漂浮粒子的效果。效果器添加到粒子系统里,能附加模拟重力和风的效果。在这个盒子中,我们使用粒子效果器来修改粒子的颜色:使粒子淡出(渐渐消失)。就如粒子发射器那样,你可以自己实现一个继承自IParticleAffector的效果器并用addAffector()添加到粒子系统中。当设置好粒子系统的材质后,我们就有一个看起来很cool的营火。通过设置材质,纹理,粒子发射器和粒子效果器,我们还能制作烟雾,下雨,爆炸和雪等效果。
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);
|
作为最后的特效,我们想根据活动人物创建一个动态阴影。为此我们加载格式为*.x的DirectX模型并放置在世界里。要创建阴影,我们只需简单地调用addShadowVolumeSceneNode()函数. 全局的阴影颜色都是通过调用ISceneManager::setShadowColor()函数设置的。瞧,这就是我们的动态阴影。 因为对于这个场景来说人物确实小了点,所以调用setScale()放大了模型。并因为要使人物根据动态光来发光,需要规格化法线来使人物正确地发光。如果动态光的比例不是(1,1,1),那么是必需这样设的。否则,会根据法线的比例而太光或太暗。
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);
|
最后,我们只需简单地绘制全部,就如下面那样。
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;
}
|
|