Irrlicht学习之光照的研究
最近研究一下Irrlicht的光照。发现Irrlicht的光照还是比较简单的,相比低于它的OpenGL和Direct3D,设置光源以及设置光照的参数更加人性化(可能是因为OpenGL是基于C的,Direct3D是基于COM的,都没有面向对象那么简洁易懂?)但是光照的实现还是根据底层的渲染器来决定的,也就是说可能是OpenGL的高洛德光照模型,也可能是Direct3D中的光照模型。
演示程序和源代码下载地址:这里
为了准确地展现Irrlicht设置和显示光照的特性,需要载入一个场景模型。这里我从游艺网中下载了一个max模型,然后通过3DS Max转为obj格式,这样能被Irrlicht识别并显示。模型在载入后需要设置其材质以便能够接受到光照。下面是设置的代码:
pDoorNode->setMaterialFlag(EMF_LIGHTING, true );// 接受光照 pDoorNode->setMaterialFlag(EMF_ANISOTROPIC_FILTER, true );// 使用各向异性过滤 pDoorNode->setMaterialType(EMT_TRANSPARENT_ALPHA_CHANNEL_REF );// 材质开启Alpha通道
这里第一句是让Irrlicht的光照能够作用于该节点的材质上;第二句是使用各项异性过滤,通常在投影的时候能够在远处获得清晰的过滤效果;第三句则是开启材质的Alpha通道,能够解决一些Alpha以及深度的问题,具体功能还需探究(可以注释掉来看看效果)。
在进行完准备工作后,开始设置光照了。就我的学习实践来说会分为这几步:
1、设置全局光颜色;
2、向场景添加光源节点(俗称打灯);
3、设置各个光源的属性。
光源的属性有漫反射颜色、镜面反射颜色、自发光颜色等,OpenGL和Direct3D所有的Irrlicht应该也不会缺少(有关光源的属性参见我以前写的一篇日志)。所不同的是,Irrlicht的光源作为节点属性成为场景管理的一部分,这和Blender和其它成熟3D建模软件的场景管理属性差不多。
下面是演示程序的截图,截图中开了四盏灯,分别位于四个火把的位置。原作者似乎只想制作一个静态的模型,所以火把什么的没有做成粒子系统,而是一张面片而已。程序还创建了一个FPS摄像机,我们可以很方便地对场景进行漫游。
下面是程序的部分代码:
#include "IrrInclude.h" void CreateMap( ISceneManager* pSMgr ) { IMesh* pDoorMesh = pSMgr->getMesh( "Door/Door.obj"); if ( pDoorMesh == nullptr ) return; ISceneNode* pDoorNode = pSMgr->addMeshSceneNode( pDoorMesh ); if ( pDoorNode == nullptr ) return; pDoorNode->setMaterialFlag( EMF_LIGHTING, true );// 接受光照 pDoorNode->setMaterialFlag( EMF_ANISOTROPIC_FILTER, true );// 使用各向异性过滤 pDoorNode->setMaterialType( EMT_TRANSPARENT_ALPHA_CHANNEL_REF);// 材质开启Alpha通道 // 设置全局光照 pSMgr->setAmbientLight( SColor( 255, 80, 0, 0 ) ); // 添加点光源 ILightSceneNode* pBlueFireLight_1 = pSMgr->addLightSceneNode(pDoorNode ); ILightSceneNode* pBlueFireLight_2 = pSMgr->addLightSceneNode(pDoorNode ); ILightSceneNode* pRedFireLight_1 = pSMgr->addLightSceneNode(pDoorNode ); ILightSceneNode* pRedFireLight_2 = pSMgr->addLightSceneNode(pDoorNode ); SLight blueFireLight; blueFireLight.DiffuseColor = SColor( 255, 80, 80, 225 ); blueFireLight.SpecularColor = SColor( 255, 230, 230, 210 ); SLight redFireLight; blueFireLight.DiffuseColor = SColor( 255, 225, 80, 80 ); blueFireLight.SpecularColor = SColor( 255, 230, 230, 210 ); // 设置光源属性 pBlueFireLight_1->setLightData( blueFireLight ); pBlueFireLight_1->setPosition( vector3df( 7.0f, 12.0f, 9.0f )); pBlueFireLight_2->setLightData( blueFireLight ); pBlueFireLight_2->setPosition( vector3df( -7.0f, 12.0f, 9.0f )); pRedFireLight_1->setLightData( redFireLight ); pRedFireLight_1->setPosition( vector3df( 20.0f, 2.0f, 6.0f )); pRedFireLight_2->setLightData( redFireLight ); pRedFireLight_2->setPosition( vector3df( -20.0f, 2.0f, 6.0f )); } void CreateGUI( IGUIEnvironment* pGUIEnv ) { // 创建中文字体 IGUIFont* pChineseFont = CGUITTFont::createTTFont( pGUIEnv, // GUI环境 FONTPATH, // 字体名称 20, // 字体大小(多少磅) true, // 抗锯齿吗? true ); // 半透明吗? // 设置GUI风格 pGUIEnv->getSkin( )->setFont( pChineseFont ); pGUIEnv->getSkin( )->setColor( EGDC_BUTTON_TEXT, SColor(255, 255, 255, 255 ) ); } int main( int argc, char** argv ) { IrrlichtDevice* pDevice = createDevice( EDT_OPENGL, // 渲染设备 dimension2d<u32>( 640, 480 ), // 宽和高 16, // 颜色位 false, // 是否全屏 false, // 模版缓存 false, // 垂直同步 nullptr ); // 事件接收器 if ( pDevice == nullptr ) return -1; pDevice->setWindowCaption( L"鬼火引擎演示- 光照的研究" ); IVideoDriver* pDriver = pDevice->getVideoDriver( ); ISceneManager* pSMgr = pDevice->getSceneManager( ); IGUIEnvironment* pGUIEnv = pDevice->getGUIEnvironment( ); CreateMap( pSMgr );// 创建地图 ICameraSceneNode* pCamera = pSMgr->addCameraSceneNodeFPS( nullptr, 100.0f, 0.2f ); pCamera->setPosition( vector3df( -4.0f, -4.0f, 12.0f ) ); pCamera->setRotation( vector3df( 0.0f, 90.0f, 90.0f ) ); CreateGUI( pGUIEnv );// 创建GUI IGUIStaticText* pInfoText = pGUIEnv->addStaticText( L"", rect<s32>( 10, 10, 400, 100 )); pDevice->getCursorControl( )->setVisible( false );// 隐藏鼠标光标 while ( pDevice->run( ) ) { vector3df pos = pCamera->getPosition( ); wchar_t info[100] = { 0 }; swprintf( info, L"当前摄像机坐标(%.2f,%.2f, %.2f)\nMade By Jiangcaiyang", pos.X, pos.Y, pos.Z ); pInfoText->setText( info ); pDriver->beginScene( true, //后台缓存 true, //Z缓存 SColor( 0, 0, 0, 0 ) ); // 填充颜色 pSMgr->drawAll( ); pGUIEnv->drawAll( ); pDriver->endScene( ); } return 0; }