[ Новые сообщения · Участники · Правила форума · Поиск · RSS ]
  • Страница 1 из 1
  • 1
Форум » Игровые движки » IrrLicht Engine » Туториал №11: Попиксельное освещение
Туториал №11: Попиксельное освещение
MerGC_TeamДата: Четверг, 03.04.2014, 17:15 | Сообщение # 1
Веселый админ
Группа: Администраторы
Сообщений: 32
Статус: Оффлайн
В данном туториале будет рассмотрено, как использовать встроенные в движок комплексные материалы: попиксельное освещение поверхностей использующих карты нормалей иparallax mapping. Так узнаете как включать туман и создавать движущиеся системы частиц.

По началу, как обычно, подключаем заголовочный файл, включаем пространство именн irr.
Код
#include < irrlicht.h >
#include "driverChoice.h"

using namespace irr;


Для этого примера нам понадобится обработчик событий, а имеено для переключений типов материалов, а так же для рализации GUI окошка. В общем, комментировать особо нечего.
Код
class MyEventReceiver : public IEventReceiver
{
public:

         MyEventReceiver(scene::ISceneNode* room,
                 gui::IGUIEnvironment* env, video::IVideoDriver* driver)
         {
                 // парочка указателей для обратной связи с движком
                 Room = room;
                 Driver = driver;

                 // ставим шрифт по красивши
                 gui::IGUISkin* skin = env->getSkin();
                 gui::IGUIFont* font = env->getFont("../../media/fonthaettenschweiler.bmp");
                 if (font)
                         skin->setFont(font);

                 // добавляем окошко и список на него
                 gui::IGUIWindow* window = env->addWindow(
                         core::rect< s32 >(460,375,630,470), false, L"Use 'E' + 'R' to change");

                 ListBox = env->addListBox(core::rect< s32 >(2,22,165,88), window);

                 ListBox->addItem(L"Diffuse");
                 ListBox->addItem(L"Bump mapping");
                 ListBox->addItem(L"Parallax mapping");
                 ListBox->setSelected(1);

                 // текст для сообщений об проблемах
                 ProblemText = env->addStaticText(
                         L"Your hardware or this renderer is not able to use the "
                         L"needed shaders for this material. Using fall back materials.",
                         core::rect< s32 >(150,20,470,80));

                 ProblemText->setOverrideColor(video::SColor(100,255,255,255));

                 // материал по умолчанию (пытаемся с parallax mapping если возможно)
                 video::IMaterialRenderer* renderer =
                         Driver->getMaterialRenderer(video::EMT_PARALLAX_MAP_SOLID);
                 if (renderer && renderer->getRenderCapability() == 0)
                         ListBox->setSelected(2);

                 // применяем материал выбранный в списке
                 setMaterial();
         }

         bool OnEvent(const SEvent& event)
         {
                 // если пользователь нажал 'E' или 'R'
                 if (event.EventType == irr::EET_KEY_INPUT_EVENT &&
                         !event.KeyInput.PressedDown && Room && ListBox)
                 {
                         // изменяем выбранный в списке элемент

                         int sel = ListBox->getSelected();
                         if (event.KeyInput.Key == irr::KEY_KEY_R)
                    ++sel;
                         else
                         if (event.KeyInput.Key == irr::KEY_KEY_E)
                    --sel;
                         else
                    return false;

                         if (sel > 2) sel = 0;
                         if (sel < 0) sel = 2;
                         ListBox->setSelected(sel);

                         // устанавливаем материал выбранный в списке
                         setMaterial();
                 }

                 return false;
         }

private:

         // применяем материал к модели комнаты
         void setMaterial()
         {
                 video::E_MATERIAL_TYPE type = video::EMT_SOLID;

                 // меняем настройки материала
                 switch(ListBox->getSelected())
                 {
                 case 0: type = video::EMT_SOLID;
                         break;
                 case 1: type = video::EMT_NORMAL_MAP_SOLID;
                         break;
                 case 2: type = video::EMT_PARALLAX_MAP_SOLID;
                         break;
                 }

                 Room->setMaterialType(type);


Мы должны выдать предупреждение, если материал не может быть отображен корректно. Проблем не будет, т.к. движок подменит проблемный материал на другой, но пользователь должен знать, что была подмена и результирующая картинка могла бы быть лучше на другой видеокарте. Мы будем просто проверять на совместимость выводимый материал с возможностями видюхи с помощью функции IMaterialRenderer::getRenderCapability().
Код
video::IMaterialRenderer* renderer = Driver->getMaterialRenderer(type);

                 // выводим соответствующий текст при несовместимости
                 if (!renderer || renderer->getRenderCapability() != 0)
                         ProblemText->setVisible(true);
                 else
                         ProblemText->setVisible(false);
         }

private:

         gui::IGUIStaticText* ProblemText;
         gui::IGUIListBox* ListBox;

         scene::ISceneNode* Room;
         video::IVideoDriver* Driver;
};


Начнем главную функцию.
Код
int main()
{
         // запрашиваем у пользователя драйвер (DirectX, OpenGL и т.п.)

         video::E_DRIVER_TYPE driverType = driverChoiceConsole();  

         // создаем движок

         IrrlichtDevice* device = createDevice(driverType, core::dimension2d< u32 >(640, 480));

         if (device == 0) return 1; // движок не создан, выходим.


Прежде чем мы начнем творить интересное, надо чутка подготовится: создадим несколько важных указателель (на видеодрайвер, среду GUI, менеджер сцены) что не "просить" их все время у движка, прилепим логотип irrlicht и создадим FPS камеру, а так же сообщим движку по возможности хранить все текструры, как 32х битные (это необходимо для parallax mapping.
Код
video::IVideoDriver* driver = device->getVideoDriver();
         scene::ISceneManager* smgr = device->getSceneManager();
         gui::IGUIEnvironment* env = device->getGUIEnvironment();

         driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true);

         // логотип irrlicht
         env->addImage(driver->getTexture("../../media/irrlichtlogo2.png"),
                 core::position2d< s32 >(10,10));

         // добавляем камеру
         scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();
         camera->setPosition(core::vector3df(-200,200,-200));

         // отключаем показ указателя мыши
         device->getCursorControl()->setVisible(false);


Затуманим сцену с помощью IVideoDriver::setFog(). У этого метода несколько настроек, в этом примере мы будем использовать пиксельный туман, т.к. он хорошо совмещается с материалами, которые мы используем в этом уроке. Кстати, движок помещает узел сцены в туман, если для него установлен флаг EMF_FOG_ENABLE в 'true'.
Код
driver->setFog(video::SColor(0,138,125,81), video::EFT_FOG_LINEAR, 250, 1000, .003f, true, false);


***
 
MerGC_TeamДата: Четверг, 03.04.2014, 17:15 | Сообщение # 2
Веселый админ
Группа: Администраторы
Сообщений: 32
Статус: Оффлайн
Грузим модель комнаты из .3ds файла, созданного в anim8or. Это все таже комната из туториала№8. Если вы помните, в том туториале я сказал, что не моделлер ни разу и поэтому текстуры замаплены кое-как, но это легко правится программно с помощью метода
Код
IMeshManipulator::makePlanarTextureMapping().
   scene::IAnimatedMesh* roomMesh = smgr->getMesh(
  "../../media/room.3ds");
   scene::ISceneNode* room = 0;

   if (roomMesh)
   {
  smgr->getMeshManipulator()->makePlanarTextureMapping(
   roomMesh->getMesh(0), 0.003f);


И так комната загружена, теперь чтобы ее текстуры выглядели эффектно, надо настроить материал комнаты. Вместо обычной загрузки цветовой карты (color map) как обычно, мы еще загрузим карту вершин (height map) - текстуру в оттенках серого. Из карты вершин мы создадим нормальную карту (normal map) и положим ее на второй слой материала комнаты. Если у вас уже есть нормальная карта, то можете установить ее на прямую, но у меня таковой не было, поэтому сгенерил с помощью метода makeNormalMapTexture(), второй параметр которого отвечает за "гористость" поверхности.
Код
video::ITexture* normalMap =
driver->getTexture("../../media/rockwall_height.bmp");

  if (normalMap)
driver->makeNormalMapTexture(normalMap, 9.0f);


Но это еще не все. Для материала надо еще объявить по вершинную информацию о тангенсах(tangents) и бинормалях(binormals). Эти расчеты мы поручим методу IMeshManipulator::createMeshWithTangents(), он создает копию модели со сгенеренной информации о тангенсах и бинормалях. Ну и последнее - это включим туман для комнаты, установив EMF_FOG_ENABLE = true.
Код
scene::IMesh* tangentMesh = smgr->getMeshManipulator()->
   createMeshWithTangents(roomMesh->getMesh(0));

  room = smgr->addMeshSceneNode(tangentMesh);
  room->setMaterialTexture(0,
   driver->getTexture("../../media/rockwall.jpg"));
  room->setMaterialTexture(1, normalMap);

  room->getMaterial(0).SpecularColor.set(0,0,0,0);

  room->setMaterialFlag(video::EMF_FOG_ENABLE, true);
  room->setMaterialType(video::EMT_PARALLAX_MAP_SOLID);
  // выравняем высоту для эффекта parallax
  room->getMaterial(0).MaterialTypeParam = 0.035f;

  // дропаем меш модели за ненадобностью лично нам.
  tangentMesh->drop();
   }


После создания комнаты освещенной попиксельным освещением, мы добавим внутрь сферу с тем же материалом, но немножко прозрачным и заставим сферу вращаться как планету. Модель ее загрузим из .x файла, который уже содержит цветовую карту и слегка отмаштабируем по размер комнаты.
Код
// добавляем сферу

   scene::IAnimatedMesh* earthMesh = smgr->getMesh("../../media/earth.x");
   if (earthMesh)
   {
  //получаем манипулятор вершинами
  scene::IMeshManipulator *manipulator = smgr->getMeshManipulator();

  // создаем копию модели earth.x с тангенсами и бинормалями
  scene::IMesh* tangentSphereMesh =
manipulator->createMeshWithTangents(earthMesh->getMesh(0));

  // устанавливаем прозрачность 200
  manipulator->setVertexColorAlpha(tangentSphereMesh, 200);

  // маштабируем на 50 ед.
  core::matrix4 m;
  m.setScale ( core::vector3df(50,50,50) );
  manipulator->transformMesh( tangentSphereMesh, m );

  scene::ISceneNode *sphere = smgr->addMeshSceneNode(tangentSphereMesh);

  sphere->setPosition(core::vector3df(-70,130,45));

  // грузим карту вершин и создаем карту нормалей
  video::ITexture* earthNormalMap = driver->getTexture("../../media/earthbump.jpg");
  if (earthNormalMap)
  {
driver->makeNormalMapTexture(earthNormalMap, 20.0f);
sphere->setMaterialTexture(1, earthNormalMap);
sphere->setMaterialType(video::EMT_NORMAL_MAP_TRANSPARENT_VERTEX_ALPHA);
  }

  // подгоняем натройки материала
  sphere->setMaterialFlag(video::EMF_FOG_ENABLE, true);

  // добавляем вращающий аниматор
  scene::ISceneNodeAnimator* anim =
smgr->createRotationAnimator(core::vector3df(0,0.1f,0));
  sphere->addAnimator(anim);
  anim->drop();

  // дропаем меш.
  tangentSphereMesh->drop();
   }


Материалы с попиксельным освещением проявляют всю свою красоту при наличии движущихся источников света и потому добавим таковые. Это будет источник света + билборд + система частиц.
Код
// первый источник света (красный)
   scene::ILightSceneNode* light1 =
  smgr->addLightSceneNode(0, core::vector3df(0,0,0),
  video::SColorf(0.5f, 1.0f, 0.5f, 0.0f), 800.0f);

   light1->setDebugDataVisible ( scene::EDS_BBOX );

   // добавляем аниматор движения по кругу
   scene::ISceneNodeAnimator* anim =
  smgr->createFlyCircleAnimator (core::vector3df(50,300,0),190.0f, -0.003f);
   light1->addAnimator(anim);
   anim->drop();

   // прицепляем билборд
   scene::ISceneNode* bill =
  smgr->addBillboardSceneNode(light1, core::dimension2d< f32 >(60, 60));

   bill->setMaterialFlag(video::EMF_LIGHTING, false);
   bill->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false);
   bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
   bill->setMaterialTexture(0, driver->getTexture("../../media/particlered.bmp"));


Аналогично со вторым источником света. Отличие в том, что мы добавим еще и систему частиц. А т.к. этот источник будет двигаться, то система частиц должна двигаться за ним, поэтому при создании мы передадим ей источник света как родителя. Мы будем использовать два источника света лишь потому, что низкоуровневые материалы, написанные на ps1.1 и vs1.1, не отражают больше 2х. Можно добавить и больше, но на стенах будет все равно 2 тени. Возможно в будущем это упущение будет исправленно в движке.
Код
// добавляем второй источник света (серый)
   scene::ISceneNode* light2 =
  smgr->addLightSceneNode(0, core::vector3df(0,0,0),
  video::SColorf(1.0f, 0.2f, 0.2f, 0.0f), 800.0f);

   // добавляем аниматор движения по кругу к источнику освещения 2
   anim = smgr->createFlyCircleAnimator(core::vector3df(0,150,0), 200.0f,
0.001f, core::vector3df(0.2f, 0.9f, 0.f));
   light2->addAnimator(anim);
   anim->drop();

   // прицепляем билборд
   bill = smgr->addBillboardSceneNode(light2, core::dimension2d< f32 >(120, 120));
   bill->setMaterialFlag(video::EMF_LIGHTING, false);
   bill->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false);
   bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
   bill->setMaterialTexture(0, driver->getTexture("../../media/particlewhite.bmp"));

   // добавляем систему частиц
   scene::IParticleSystemSceneNode* ps =
  smgr->addParticleSystemSceneNode(false, light2);

   // создаем эмиттер
   scene::IParticleEmitter* em = ps->createBoxEmitter(
  core::aabbox3d< f32 >(-3,0,-3,3,1,3),
  core::vector3df(0.0f,0.03f,0.0f),
  80,100,
  video::SColor(0,255,255,255), video::SColor(0,255,255,255),
  400,1100);
   em->setMinStartSize(core::dimension2d< f32 >(30.0f, 40.0f));
   em->setMaxStartSize(core::dimension2d< f32 >(30.0f, 40.0f));

   ps->setEmitter(em);
   em->drop();

   // создаем аффектор
   scene::IParticleAffector* paf = ps->createFadeOutParticleAffector();
   ps->addAffector(paf);
   paf->drop();

   // подгоняем настройки материала
   ps->setMaterialFlag(video::EMF_LIGHTING, false);
   ps->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false);
   ps->setMaterialTexture(0, driver->getTexture("../../media/fireball.bmp"));
   ps->setMaterialType(video::EMT_TRANSPARENT_VERTEX_ALPHA);

   MyEventReceiver receiver(room, env, driver);
   device->setEventReceiver(&receiver);


Все, начинаем рисовать!
Код
int lastFPS = -1;

   while(device->run())
   if (device->isWindowActive())
   {
  driver->beginScene(true, true, 0);

  smgr->drawAll();
  env->drawAll();

  driver->endScene();

  int fps = driver->getFPS();

  if (lastFPS != fps)
  {
core::stringw str = L"Per pixel lighting example - Irrlicht Engine [";
str += driver->getName();
str += "] FPS:";
str += fps;

device->setWindowCaption(str.c_str());
lastFPS = fps;
  }
   }

   device->drop();

   return 0;
}

Компилируем, запускаем, любуемся!
 
Форум » Игровые движки » IrrLicht Engine » Туториал №11: Попиксельное освещение
  • Страница 1 из 1
  • 1
Поиск: