[ Новые сообщения · Участники · Правила форума · Поиск · RSS ]
  • Страница 1 из 1
  • 1
Форум » Игровые движки » IrrLicht Engine » Туториал №20: Менеджер освещения
Туториал №20: Менеджер освещения
MerGC_TeamДата: Четверг, 03.04.2014, 18:24 | Сообщение # 1
Веселый админ
Группа: Администраторы
Сообщений: 32
Статус: Оффлайн
Пример написан Colin MacDonald. Этот туториал показывает как использовать Менеджер Источников Света для IrrLicht Engine. Он позволяет использовать больше динамических источников, чем фактически поддерживается аппаратно. Усложнённые способы применения менеджера источников света, такое как при обратных вызовах нод сцены, не входят в рассматриваемую область для простоты примера.
Код
#include < irrlicht.h >
#include "driverChoice.h"

using namespace irr;
using namespace core;

#if defined(_MSC_VER)
#pragma comment(lib, "Irrlicht.lib")
#endif // MSC_VER


Как правило, вы ограничены 8-ю динамическими источниками света на сцене: это аппаратный предел. Если вы желаете использовать больше динамических источников в вашей сцене, то вы можете зарегистрировать дополнительный(необязательный) менеджер освещения, что позволит вам в свою очередь включать и выключать освещение в определённый момент во время рендеринга. Вы по прежнему ограничены 8-ю светилками, но лимит распостраняется лишь на видимые источники света.

Создавать менеджер не обязательно: Если вы не зарегитстрируете менеджер источников света, то по умолчанию будет использована схема распределения источников света на основе расстояния. Аппаратные источникики освещения будут получать приоритет(вкл./выкл.) в зависимости от расстояния между источником и активной камерой..

Флаг NO_MANAGEMENT отключает менеджер светилок и задаёт светилкам Иррлихта поведение "по умолчанию". 8 источников света ближайших к камере будут включены, а остальные выключены. В этом примере это приводит не только к тому, что свет выглядит несимпатично с заметным мерцанием и артефактными бликами.

Флаг LIGHTS_NEAREST_NODE указывает, что реализация включает ограничение кол-ва светилок для меша ноды сцены. Если во время рендеринга находит 3 светилки оказавшиеся ближайшими к ноде, включает их и выключает все остальные источники света. Это работает, но так как операция производится с каждым источником света для каждой ноды, то это не очень хорошо взаимодействует с большим количеством источников света. Вы можете увидеть мерцание в этом демо, при смене позиций светилок относительно кубов (это преднамеренная демонстрация ограничений подобной техники).

Флаг LIGHTS_IN_ZONE режим включающий источники света зонально. В данном режиме используется наследование. За "зону" отвечает пустой узел (EmptySceneNode), который служит родителем(parent-ом) для узла(ноды) 3д модели, который в свою очередь является родителем для своих источников света. В процессе рендеринга менеджер обегает по очереди пустые узлы (зоны) и для каждого выцепляет 3д модели и далее их источники освещения, т.е. при рендеринге каждой "зоны" используются только принадлежащие ей источники света. Это приводит к верной 'локальной' освещённости для каждого куба в этом примере. Вы можете использовать подобную технику локального освещения всего меша напр. комнаты, без утечки света во внешние/другие комнаты.

Менеджер источников света в примере так же является производным от обработчика событий, это сделано просто для упрощения примера, это не является ни необходимым, ни рекомендуемым для реального применения.
Код
class CMyLightManager : public scene::ILightManager, public IEventReceiver
{
         typedef enum
         {
                 NO_MANAGEMENT,
                 LIGHTS_NEAREST_NODE,
                 LIGHTS_IN_ZONE
         }
         LightManagementMode;

         LightManagementMode Mode;
         LightManagementMode RequestedMode;

         // Эти данные представляют собой информацию о состояниях, используемых менеджером освещения.
         scene::ISceneManager * SceneManager;
         core::array< scene::ILightSceneNode* > * SceneLightList;
         scene::E_SCENE_NODE_RENDER_PASS CurrentRenderPass;
         scene::ISceneNode * CurrentSceneNode;

public:
         CMyLightManager(scene::ISceneManager* sceneManager)
                 : Mode(NO_MANAGEMENT), RequestedMode(NO_MANAGEMENT),
                 SceneManager(sceneManager),  SceneLightList(0),
                 CurrentRenderPass(scene::ESNRP_NONE), CurrentSceneNode(0)
         { }

         virtual ~CMyLightManager(void) { }

         // обработка событий: переключение стратегии менеджера освещения
         bool OnEvent(const SEvent & event)
         {
                 bool handled = false;

                 if (event.EventType == irr::EET_KEY_INPUT_EVENT && event.KeyInput.PressedDown)
                 {
                         handled = true;
                         switch(event.KeyInput.Key)
                         {
                         case irr::KEY_KEY_1:
                    RequestedMode = NO_MANAGEMENT;
                    break;
                         case irr::KEY_KEY_2:
                    RequestedMode = LIGHTS_NEAREST_NODE;
                    break;
                         case irr::KEY_KEY_3:
                    RequestedMode = LIGHTS_IN_ZONE;
                    break;
                         default:
                    handled = false;
                    break;
                         }

                         if(NO_MANAGEMENT == RequestedMode)
                    // Указать, что менеджер освещения не работает
                    SceneManager->setLightManager(0);
                         else
                    SceneManager->setLightManager(this);
                 }

                 return handled;
         }

         // Это вызывается перед рендерингом первой ноды сцены.
         virtual void OnPreRender(core::array< scene::ILightSceneNode* > & lightList)
         {
                 // ринудительно устанавливаем режим, на всякий случай
                 Mode = RequestedMode;

                 // Сохраняем список источников света, которым будем оперировать до OnPostRender().
                 SceneLightList = &lightList;
         }

         // Вызывается после рендеринга последней ноды сцены.
         virtual void OnPostRender()
         {
                 // Так как менеджер освещения может быть выключен в обработчике событий, мы включим
                 // источники света, чтобы гарантировать, что они находятся в согласованном состоянии.  
                 // Обычно вам не нужно делать этого при использовании менеджера освещения, так как вы
                 //управляете источниками света самостоятельно.
                 for(u32 i = 0; i < SceneLightList->size(); i++)
                         (*SceneLightList)[i]->setVisible(true);
         }

         virtual void OnRenderPassPreRender(scene::E_SCENE_NODE_RENDER_PASS renderPass)
         {
                 // Я ничего не делаю здесь кроме того, что сохраняю текущий проходе рендера
                 CurrentRenderPass = renderPass;
         }

         virtual void OnRenderPassPostRender(scene::E_SCENE_NODE_RENDER_PASS renderPass)
         {
                 // Я хочу осветить лишь твёрдотелые ноды, поэтому после
                 // прохождения твёрдых, выключить все источники света.
                 if(scene::ESNRP_SOLID == renderPass)
                 {
                         for(u32 i = 0; i < SceneLightList->size(); ++i)
                    (*SceneLightList)[i]->setVisible(false);
                 }
         }

         // Это вызывается перед рендерингом текущей ноды
         virtual void OnNodePreRender(scene::ISceneNode* node)
         {
                 CurrentSceneNode = node;

                 // Этот менеджер освещения работает только с "твёрдыми" объектами, но вы можете  
                 // свободно манипулировать светом на любом этапе, в зависимости от ваших намерений.
                 if (scene::ESNRP_SOLID != CurrentRenderPass)
                         return;

                 // Практически в этом примере, Я только хочу рассмотреть освещение узлов(нод) с 3д моделью куба  
                 if (node->getType() != scene::ESNT_CUBE)
                         return;

                 if (LIGHTS_NEAREST_NODE == Mode)
                 {
                         // Это демонстрационная реализация, которая расставляет приоритеты для каждого  
                         // источника света на сцене согласно его близости к узлу(ноде) во время рендеринга.  
                         // Это приводит к некоторому мерцанию, когда орбита чужого источника света ближе к кубу,  
                         // чем источник света из собственный 'зоны'.
                         const vector3df nodePosition = node->getAbsolutePosition();

                         // Список для сортировки источников света по приоритетам основанным на их удалении от
                         // ноды для последующего рендеринга.
                         array< LightDistanceElement > sortingArray;
                         sortingArray.reallocate(SceneLightList->size());

                         u32 i;
                         for(i = 0; i < SceneLightList->size(); ++i)
                         {
                    scene::ILightSceneNode* lightNode = (*SceneLightList)[i];
                    f64 distance = lightNode->getAbsolutePosition().getDistanceFromSQ(nodePosition);
                    sortingArray.push_back(LightDistanceElement(lightNode, distance));
                         }

                         sortingArray.sort();

                         // Список теперь сортируется по близости к ноде.
                         // Включить 3 ближайших источника света и отключить остальные.
                         for(i = 0; i < sortingArray.size(); ++i)
                    sortingArray[i].node->setVisible(i < 3);

                 }
                 else if(LIGHTS_IN_ZONE == Mode)
                 {
                         // Режим когда Ноды-пустышки используются для представления 'зоны'. При рендеринге
                         // каждой 3д модели которая визуализируется, сначала отключаем все освещение и включаем  
                         // только те источники, которые принадлежат текущей зоне. Этот алгоритм имеет главную  
                         // цель: не использовать никаких специальных знаний о том как организована графика данной сцены.
                         for (u32 i = 0; i < SceneLightList->size(); ++i)
                         {
                    scene::ILightSceneNode* lightNode = (*SceneLightList)[i];
                    video::SLight & lightData = lightNode->getLightData();

                    if (video::ELT_DIRECTIONAL != lightData.Type)
                    lightNode->setVisible(false);
                         }

                         scene::ISceneNode * parentZone = findZone(node);
                         if (parentZone)
                    turnOnZoneLights(parentZone);
                 }
         }

         // ызывается после рендеринга узла(ноды) на сцене
         virtual void OnNodePostRender(scene::ISceneNode* node)
         {
                 // Мне не нужно управлять источниками света после рендеринга индивидуальной ноды.
         }

private:

         // Найти ноду-пустышку(зону) являющуюся родителем запрошенного узла
         scene::ISceneNode * findZone(scene::ISceneNode * node)
         {
                 if(!node)
                         return 0;

                 if(node->getType() == scene::ESNT_EMPTY)
                         return node;

                 return findZone(node->getParent());
         }

         // Включить все освещение, т.е. источники света которые являются потомками запрошенной ноды.
         void turnOnZoneLights(scene::ISceneNode * node)
         {
                 core::list< scene::ISceneNode* > const & children = node->getChildren();
                 for (core::list< scene::ISceneNode* >::ConstIterator child = children.begin();
                         child != children.end();
                         ++child)
                 {
                         if((*child)->getType() == scene::ESNT_LIGHT)
                    static_cast< scene::ILightSceneNode* >(*child)->setVisible(true);
                         else  
                    // Предположим, что нода не имеет источников света, но имеет  
                    // потомков, которые в свою очередь могут иметь свои источники освещения
                    turnOnZoneLights(*child);
                 }
         }

         // Вспомогательный класс для оказания помощи в сортировке нод сцены в зависимости от расстояния
         class LightDistanceElement
         {
         public:
                 LightDistanceElement() {};

                 LightDistanceElement(scene::ILightSceneNode* n, f64 d)
                         : node(n), distance(d) { }

                 scene::ILightSceneNode* node;
                 f64 distance;

                 // Элементы на меньшей дистанции перемещаются в начало массива
                 bool operator < (const LightDistanceElement& other) const
                 {
                         return (distance < other.distance);
                 }
         };
};
***
 
MerGC_TeamДата: Четверг, 03.04.2014, 18:25 | Сообщение # 2
Веселый админ
Группа: Администраторы
Сообщений: 32
Статус: Оффлайн
Код
int main(int argumentCount, char * argumentValues[])
{
   // запросить у пользователя видеодрайвер
   video::E_DRIVER_TYPE driverType=driverChoiceConsole();
   if (driverType==video::EDT_COUNT)
  return 1;

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

   if(!device)
  return -1;

   f32 const lightRadius = 60.f; // Достаточно, чтобы достичь дальней стороны каждой 'зоны'

   video::IVideoDriver* driver = device->getVideoDriver();
   scene::ISceneManager* smgr = device->getSceneManager();
   gui::IGUIEnvironment* guienv = device->getGUIEnvironment();

   gui::IGUISkin* skin = guienv->getSkin();
   if (skin)
   {
  skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255, 255, 255, 255));
  gui::IGUIFont* font = guienv->getFont("../../media/fontlucida.png");
  if(font)
skin->setFont(font);
   }

   guienv->addStaticText(L"1 - No light management", core::rect< s32 >(10,10,200,30));
   guienv->addStaticText(L"2 - Closest 3 lights", core::rect< s32 >(10,30,200,50));
   guienv->addStaticText(L"3 - Lights in zone", core::rect< s32 >(10,50,200,70));


Добавить несколько "зон". Вы можете использовать эту технику освещения, чтобы осветить отдельные комнаты, для примера.
Код
for(f32 zoneX = -100.f; zoneX <= 100.f; zoneX += 50.f)
  for(f32 zoneY = -60.f; zoneY <= 60.f; zoneY += 60.f)
  {
// Начинаем с создания ноды-пустышки, которая у нас будет представлять зону освещения.
scene::ISceneNode * zoneRoot = smgr->addEmptySceneNode();
zoneRoot->setPosition(vector3df(zoneX, zoneY, 0));

// Каждая зона содержит вращающийся куб
scene::IMeshSceneNode * node = smgr->addCubeSceneNode(15, zoneRoot);
scene::ISceneNodeAnimator * rotation = smgr->createRotationAnimator(vector3df(0.25f, 0.5f, 0.75f));
node->addAnimator(rotation);
rotation->drop();

//  каждый куб имеет 3 источника освещения (является для них родителем). К источникам света цепляем
// биллборды, чтобы видеть где они находятся. Биллборды тоже делаем потомками кубов.
scene::IBillboardSceneNode * billboard = smgr->addBillboardSceneNode(node);
billboard->setPosition(vector3df(0, -14, 30));
billboard->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
billboard->setMaterialTexture(0, driver->getTexture("../../media/particle.bmp"));
billboard->setMaterialFlag(video::EMF_LIGHTING, false);
scene::ILightSceneNode * light = smgr->addLightSceneNode(billboard, vector3df(0, 0, 0), video::SColorf(1, 0, 0), lightRadius);

billboard = smgr->addBillboardSceneNode(node);
billboard->setPosition(vector3df(-21, -14, -21));
billboard->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
billboard->setMaterialTexture(0, driver->getTexture("../../media/particle.bmp"));
billboard->setMaterialFlag(video::EMF_LIGHTING, false);
light = smgr->addLightSceneNode(billboard, vector3df(0, 0, 0), video::SColorf(0, 1, 0), lightRadius);

billboard = smgr->addBillboardSceneNode(node);
billboard->setPosition(vector3df(21, -14, -21));
billboard->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
billboard->setMaterialTexture(0, driver->getTexture("../../media/particle.bmp"));
billboard->setMaterialFlag(video::EMF_LIGHTING, false);
light = smgr->addLightSceneNode(billboard, vector3df(0, 0, 0), video::SColorf(0, 0, 1), lightRadius);

// Каждый куб так же имеет маленький куб вращающийся вокруг него, чтобы показать, что кубы
// освещаются освещением зоны, а не только источниками света, являющимися их прямыми потомками.
node = smgr->addCubeSceneNode(5, node);
node->setPosition(vector3df(0, 21, 0));
  }

   smgr->addCameraSceneNode(0, vector3df(0,0,-130), vector3df(0,0,0));

   CMyLightManager * myLightManager = new CMyLightManager(smgr);
   smgr->setLightManager(0); // По умолчанию менеджер освещения отключен
   device->setEventReceiver(myLightManager);

   int lastFps = -1;

   while(device->run())
   {
  driver->beginScene(true, true, video::SColor(255,100,101,140));
  smgr->drawAll();
  guienv->drawAll();
  driver->endScene();

  int fps = driver->getFPS();
  if(fps != lastFps)
  {
lastFps = fps;
core::stringw str = L"Managed Lights [";
str += driver->getName();
str += "] FPS:";
str += fps;
device->setWindowCaption(str.c_str());
  }
   }

   myLightManager->drop(); // освобождаем менеджер освещения
   device->drop();
   return 0;
}


Урок окончен.
 
Форум » Игровые движки » IrrLicht Engine » Туториал №20: Менеджер освещения
  • Страница 1 из 1
  • 1
Поиск: