说明:
本例子介绍:
逐像素光照
雾
移动粒子.
使用引擎内置的一个更复杂的材质shader:
使用法线图和视差(parallax)图实现逐像素光照表面
详细步骤,请看源码注释
源码:
/**
*逐像素光照,雾,移动光,粒子
*/
#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;
}