Irrlicht学习笔记(10)--PerPixelLighting

说明:

本例子介绍:
逐像素光照
移动粒子.
使用引擎内置的一个更复杂的材质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;
}




你可能感兴趣的:(Irrlicht)