MerGC_Team | Дата: Четверг, 03.04.2014, 16:35 | Сообщение # 1 |
Веселый админ
Группа: Администраторы
Сообщений: 32
Статус: Оффлайн
| Этот урок посвящен спецэффектам. В нем вы узнаете достаточно много полезных вещей: как реализовывать тени, работать с системой частиц, выводить билборды, помещать на сцену динамические источники света и имитировать водные поверхности.
Начнем как и обычно... Подключим заголовочный файл IrrLicht, объявим пространстов имен 'irr' включенным. Но мы хотим использовать в уроке тени, поэтому при вызове createDevice() параметр устанавливающий использования трафаретного буфера установим в true. Если ваша видеокарта не поддерживает трафаретный буфер, то Irrlicht сам отключит его использование, но в некоторых случаях пример выполняется медленно, поэтому если теней вы не увидели, а пример выполняется медленно, то при запуске примера лучше отказаться от них и тогда все будет нормально.
Код #include < irrlicht.h > #include < iostream > #include "driverChoice.h"
using namespace irr;
int main() { // спрашиваем пользователя хочет ли он использовать тени
char i; printf("Please press 'y' if you want to use realtime shadows.n");
std::cin >> i;
const bool shadows = (i == 'y');
// предлагаем пользователю выбрать драйвер (DirectX, OpenGL и т.д.) video::E_DRIVER_TYPE driverType=driverChoiceConsole(); if (driverType==video::EDT_COUNT) return 1;
Создаем корневой объект(движок) IrrLicht, последний параметр отвечает за тени, его значение мы запростили чуть выше
Код IrrlichtDevice *device = createDevice(driverType, core::dimension2d< u32 >(640, 480), 16, false, shadows);
if (device == 0) return 1; // произошла ошибка, движок не создан - выходим.
video::IVideoDriver* driver = device->getVideoDriver(); scene::ISceneManager* smgr = device->getSceneManager();
Для создания обстановки загрузим .3ds файл с маленькой комнаткой, которая смоделирована в Anim8or и экспортирована в 3ds формат, потому что Irrlicht не поддерживает его родной .an8 формат. Я не крут ни разу в 3d моделировании, поэтому текстуры там натянуты на троечку. К счастью я хороший программист и в движке есть возможность маппить текстуры: и я замапил текстуры с помощью метода planar texture mapping "натравив" на модель метод манипулятора makePlanarTextureMapping. Если вам интересно оценить мой скил(навык) 3d моделирования, то закоментируйте ту строку и увидите после запуска что я намапил в редакторе.
Код scene::IAnimatedMesh* mesh = smgr->getMesh("../../media/room.3ds");
smgr->getMeshManipulator()->makePlanarTextureMapping(mesh->getMesh(0), 0.004f);
scene::ISceneNode* node = 0;
node = smgr->addAnimatedMeshSceneNode(mesh); node->setMaterialTexture(0, driver->getTexture("../../media/wall.jpg")); node->getMaterial(0).SpecularColor.set(0,0,0,0);
Добавляем на сцену анимированную воду. В движке реализован класс, который загружает указанный ему набор вершин(модель) и соответственно заданным параметрам колеблет образованную им поверхность. А если мы еще создадим для воды материал типа EMT_REFLECTION_2_LAYER то это вообще будет выглядеть круто. И так в качестве модели мы сгенерируем холмы без холмов, т.е. вызовем генератор холмистых поверхностей с нулевым разбросом высот, на основе полученного меша(набора вершин) создадим на сцене узел(ноду) имитирующую воду addWaterSurfaceSceneNode, спозиционируем ее и привяжем материал с двумя текструрами и отражающим слоем.
Код mesh = smgr->addHillPlaneMesh( "myHill", core::dimension2d< f32 >(20,20), core::dimension2d< u32 >(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);
Вторым спецэффектом будет динамический источник света, в центр которого мы поместим билборд и заставим эту комбинацию летать по комнате. Вернее заставим летать только источник света, т.к. при создании билборда мы укажем ему наше светило как родителя и он автоматически будет наследовать координаты родителя, т.е. будет летать вместе с ним.
Код // создаем источник света
node = smgr->addLightSceneNode(0, core::vector3df(0,0,0), video::SColorf(1.0f, 0.6f, 0.7f, 1.0f), 800.0f); scene::ISceneNodeAnimator* anim = 0; anim = smgr->createFlyCircleAnimator (core::vector3df(0,150,0),250.0f); node->addAnimator(anim); anim->drop();
// цепляем билборд к источнику света
node = smgr->addBillboardSceneNode(node, core::dimension2d(50, 50)); node->setMaterialFlag(video::EMF_LIGHTING, false); node->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR); node->setMaterialTexture(0, driver->getTexture("../../media/particlewhite.bmp"));
Следующий спецэффект поинтереснее, он основан на системе частиц. В движке существует специальный тип узла(ноды) IParticleSystemSceneNode, который является эмиттером(испускателем) частиц, создание которого достаточно гибко настраивается за счет большого количества параметров (направление, кол-во, цвет и т.п.).
Существует несколько эмиттеров, для примера точечный эмиттер, который извергает частицы из одной определенной точки. Если нужного вам эмиттера нет в движке, вы без особого труда добавите необходимый, надо создать класс унаследованный от IParticleEmitter и присоединить его к системе частиц методом setEmitter(). В этом примере мы создадим кубический эмиттер, который создает частицы случайным образом в пределах описанного нами кубического объема. Параметры нашего эмиттера: направление частиц, минимальное и максимальное число частиц в секунду, цвет и минимальное и максимальное время жизни частиц.
Вдобавок в системе частиц движка присутствует возможность прицеплять к эмиттерам - аффекторы. Аффекторы добавляются для симуляции гравитации, ветра и т.п. В данном примере мы будем использовать аффектор, который меняет цвет частиц, создавая эффект затухания. Равно как и эмиттеры, вы можете добавлять в систему и свои собственные аффекторы, достаточно унаследовать свой клас от IParticleAffector добавить в систему методом addAffector().
После настройки эмиттеров/аффекторов, мы добавим матерал для симулирования костра. Комбинируюя материалы, текстуры, эмиттеры и аффекторы можно симулировать дым, дождь, снег, взрывы и т.п.
Код // добавляем систему частиц
scene::IParticleSystemSceneNode* ps = smgr->addParticleSystemSceneNode(false);
scene::IParticleEmitter* em = ps->createBoxEmitter( core::aabbox3d< f32 >(-7,0,-7,7,1,7), // размер эмиттера(куба) core::vector3df(0.0f,0.06f,0.0f), // начальное направление 80,100, // частота испускания (мин,макс) video::SColor(0,255,255,255), // самый темный цвет video::SColor(0,255,255,255), // самый яркий цвет 800,2000,0, // време жизни (мин,макс), угол core::dimension2df(10.f,10.f), // минимальный размер частиц core::dimension2df(20.f,20.f)); // максимальный размер частиц
ps->setEmitter(em); // отдаем эмиттер системе em->drop(); // а лично нам он не нужен
scene::IParticleAffector* paf = ps->createFadeOutParticleAffector();
ps->addAffector(paf); // отдаем аффектор системе и дропаем, т.к. нам он не нужен paf->drop();
ps->setPosition(core::vector3df(-70,60,40)); ps->setScale(core::vector3df(2,2,2)); ps->setMaterialFlag(video::EMF_LIGHTING, false); ps->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false); ps->setMaterialTexture(0, driver->getTexture("../../media/fire.bmp")); ps->setMaterialType(video::EMT_TRANSPARENT_VERTEX_ALPHA);
Добавляем на сцену узел(ноду) объемного источника света, который будет освещать сцену мерцающим светом. Чтобы свет мерцал мы привяжем к источнику текстурку и аниматор, который ее анимирует.
Код scene::IVolumeLightSceneNode * n = smgr->addVolumeLightSceneNode(0, -1, 32, // Subdivisions on U axis 32, // Subdivisions on V axis video::SColor(0, 255, 255, 255), // foot color video::SColor(0, 0, 0, 0)); // tail color
if (n) { n->setScale(core::vector3df(56.0f, 56.0f, 56.0f)); n->setPosition(core::vector3df(-120,50,40));
// грузим текстуру для анимации core::array< video::ITexture* > textures; for (s32 g=7; g > 0; --g) { core::stringc tmp; tmp = "../../media/portal"; tmp += g; tmp += ".bmp"; video::ITexture* t = driver->getTexture( tmp.c_str() ); textures.push_back(t); }
// создаем аниматор для текстуры scene::ISceneNodeAnimator* glow = smgr->createTextureAnimator(textures, 150);
// добавляем аниматор и источнику света n->addAnimator(glow);
// дробаем аниматор за ненадобностью нам, а у системы он уже есть - отдали строчкой выше. glow->drop(); }
Ну и последним эффектом будет тень отбрасываемая персонажем. Для этого загрузим .x и поставим в центр комнаты. Для создания тени надо всего лишь добавить узел(ноду) методом addShadowVolumeSceneNode(). Цвет теней устанавливается только глобально вызовом ISceneManager::setShadowColor(). Вуаля, персонаж отбрасывает тень.
Т.к. изначально персонаж великоват для комнаты, мы его отмаштабируем методом setScale(). А т.к. персонаж освещается динамически, мы должны нормализовать все его нормали, чтобы реакция на свет была корректной. Но это только если модель масштабируется на сцене, иначе в этом нет необходимости.
Код // добавляем анимированный персонаж
mesh = smgr->getMesh("../../media/dwarf.x"); scene::IAnimatedMeshSceneNode* anode = 0;
anode = smgr->addAnimatedMeshSceneNode(mesh); anode->setPosition(core::vector3df(-50,20,-60)); anode->setAnimationSpeed(15);
// добавляем тени anode->addShadowVolumeSceneNode(); smgr->setShadowColor(video::SColor(150,0,0,0));
// маштабируем модель // нормализуем нормали 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));
// прячем указатель мыши device->getCursorControl()->setVisible(false);
s32 lastFPS = -1;
while(device->run()) if (device->isWindowActive()) { driver->beginScene(true, true, 0);
smgr->drawAll();
driver->endScene();
const s32 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; }
Вот и все, компилируем, запускаем, наслаждаемся.
|
|
| |