/** *逐像素光照,雾,移动光,粒子 */ #include <iostream> #include <irrlicht.h> #include "driverChoice.h" using namespace irr; #ifdef _IRR_WINDOWS_ #pragma comment(lib, "irrlicht.lib") //#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup") #endif /** *1.写一个事件接收器 *作用:用户选择当前材质种类,创建小的GUI窗口显示当前被选用的材质 */ class MyEventReceiver :public IEventReceiver { private: /** *1.1四个变量 */ gui::IGUIStaticText* ProblemText;//显示错误内容 gui::IGUIListBox* ListBox;//列表盒,用于展示所有可选内容 scene::ISceneNode* Room;//保存场景节点 video::IVideoDriver* Driver;//保存driver /** *1.2方法 *二个共有方法:初始化,事件处理函数 *一个私有方法:设置材质 */ public: //1.2.1构造函数,初始化 MyEventReceiver(scene::ISceneNode* room, gui::IGUIEnvironment* env, video::IVideoDriver* driver) { //1.2.1.1这两个变量用于改变绘制模式 Room = room; Driver = driver; //1.2.1.2添加listBox //调整字体 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>(0, 375, 160, 470), false, L"Use 'E'+'R' to change"); ListBox = env->addListBox( core::rect<s32>(2, 32, 165, 88), window); //添加内容 ListBox->addItem(L"Diffuse");//0 ListBox->addItem(L"Dump mapping");//1 ListBox->addItem(L"Parallax mapping");//2 //1.2.1.3创建错误文本 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, 570, 80)); ProblemText->setOverrideColor(video::SColor(100, 255, 255, 255)); //1.2.1.4设置初始材质 //IMatterialRenderer是材质渲染接口,可以通过它进行扩展 video::IMaterialRenderer* renderer = Driver->getMaterialRenderer( video::EMT_PARALLAX_MAP_SOLID); if (renderer&&renderer->getRenderCapability() == 0) ListBox->setSelected(2);//selected =2 } //1.2.2事件处理函数 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;//error!如果不加这行,将不能处理其他消息 } private: //1.2.3设置材质 void setMaterial() { //使用当前列表盒选用的材质设置房间 video::E_MATERIAL_TYPE type = video::EMT_SOLID; //改变材质设置 switch (ListBox->getSelected()) { case 0: type = video::EMT_SOLID; break; case 1: /** *实体法线图渲染器 *第一个图为纹理图,第二个图为法线图 *只有当顶点是S3DVertexTangents(EVT_TANGENTS)格式才可以使用这种材质 *可以使用IMeshManipulator::createMeshWithTangent()将任何网格转换成所需格式 */ type = video::EMT_NORMAL_MAP_SOLID; break; case 2: /** *和EMT_NORMAL_MAP_SOLID差不多,只是只有的是parallax图 *更加真实 *需要硬件支持:顶点着色器1.1,片元着色器1.4 *第一个图为纹理图,第二个图为法线图(32bits)(高度图) *法线图纹理需要在其透明度通道中包含高度信息 *高度信息可以通过函数IVideoDriver::makeNormalMapTexture自动设置, *函数参数:第一个:纹理图(其alpha通道可改变),第二个:amplitude,缩放比例 */ type = video::EMT_PARALLAX_MAP_SOLID; break; } Room->setMaterialType(type); /* *如果材质不能正常显示,需要发出警告信息, *检查硬件是否支持: *IMaterialRenderer::getRenderCapability(),返回0就支持 */ video::IMaterialRenderer* renderer = Driver->getMaterialRenderer(type); if (!renderer || renderer->getRenderCapability() != 0) ProblemText->setVisible(true); else ProblemText->setVisible(false); } }; int main(int argc, char** argv) { video::E_DRIVER_TYPE driverType = driverChoiceConsole(); IrrlichtDevice *device = createDevice(driverType, core::dimension2d<u32>(720, 455), 32, false, true, false, 0); if (!device) return 1; device->setWindowCaption(L"11PerPixelLighting"); video::IVideoDriver *driver = device->getVideoDriver(); scene::ISceneManager *smgr = device->getSceneManager(); gui::IGUIEnvironment *guiev = device->getGUIEnvironment(); /* *2.准备工作: *加载logo,设置FPS摄像机,让引擎始终创建32位纹理, *最后一项是parallax图所必须的 */ driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true); guiev->addImage( driver->getTexture("../media/irrlichtlogo2.png"), core::position2di(10, 10)); scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS(); camera->setPosition(core::vector3df(-200, 200, -200)); device->getCursorControl()->setVisible(false); /* *3.添加雾. *有多种雾,这里是使用pixel fog,配合将使用的材质 *对每一个场景节点,只有将其EMF_FOG_ENABLE置true,才会被雾影响 */ driver->setFog( video::SColor(0, 138, 125, 81),//color video::EFT_FOG_LINEAR,//fogType 250,//start 1000,//end 0.003,//density true,//pixelFog false//rangeFog ); /* *4.加载房间 *修复纹理:ImeshManipulator::makePlanarTextureMapping() */ scene::IAnimatedMesh* roomMesh = smgr->getMesh("../media/room.3ds"); scene::ISceneNode* room = 0; if (roomMesh) { smgr->getMeshManipulator()->makePlanarTextureMapping(roomMesh->getMesh(0), 0.003f); /* *4.1 *贴纹理图, *法线图作为第二层图, *法线图由加载进来的灰度纹理形式的高度图生成 *通常法线图可以用自己创建的法线图, *这里用VideoDriver::makeNormalMapTexture产生 */ video::ITexture* normalMap = driver->getTexture("../media/rockwall_height.bmp"); if (normalMap) driver->makeNormalMapTexture(normalMap, 9.0f/*越大越凹凸*/); /* *4.2 *所要使用的材质需要其他信息,比如: *tangents,binormals *所以需要计算这些信息. *使用:IMeshManipulator::createMeshWithTangents()创建 *它从其他网格拷贝创建带有切线tangents和副法线binormals的网格 *然后需要用这个网格创建一个标注的网格 *然后就可以设置其他参宿了 */ 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->getMaterial(0).MaterialTypeParam = 0.035f;//自由参数 tangentMesh->drop(); } /* *5.添加一个球 */ /* *6.添加移动光源 *逐像素光照在移动光照下才能凸显 *这里添加两个移动光源,都绑定一个绕圈的动画和一个布告板 *其中一个绑定一个粒子系统 */ /* *6.1.红色灯光 */ 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, -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);//不添加到z缓存 bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR); bill->setMaterialTexture(0, driver->getTexture("../media/particlered.bmp")); /* *6.2.light2 *额外:添加粒子系统, *由于这个版本(irr1.7.3)的这个材质shader使用的是ps1.1,vs1.1所以只能添加两个灯光 */ scene::ILightSceneNode* light2 = smgr->addLightSceneNode(0, core::vector3df(0, 0, 0), video::SColorf(1.0f, 0.2f, 0.2f, 0.0f), 800.0f); //动画 anim = smgr->createFlyCircleAnimator( core::vector3df(0, 150, 0), 200, 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);//不添加到z缓存 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_ADD_COLOR); MyEventReceiver receiver(room, guiev, driver); device->setEventReceiver(&receiver); int lastFPS = -1; while (device->run()) { driver->beginScene(true, true, video::SColor(255, 100, 101, 140)); smgr->drawAll(); guiev->drawAll(); driver->endScene(); int fps = driver->getFPS(); if (lastFPS != fps) { core::stringw str = L"11PerPixelLighting ["; str += driver->getName(); str += "]FPS.", str += fps; device->setWindowCaption(str.c_str()); lastFPS = fps; } } device->drop(); return 0; }