版权声明:本文为博主原创文章,欢迎转载,转载请保留源链接. https://blog.csdn.net/gqkly/article/details/51824047
说明:
这个例程介绍如何使用D3D8\D3D9\OpenGL的shaders,
并用他们创建新的材质类型.
关闭纹理加载产生mipmaps,
使用text场景节点
本文使用一个顶点shader,会根据相机的位置计算顶点的颜色
所以此shader需要以下数据:
世界矩阵的逆(标准化变换),裁剪矩阵(用于进行坐标变换),像机坐标和
物体的世界坐标(用于计算灯光的角度).
创建一个类继承自IShaderConstantSetCallBack接口,重写它唯一的方法:OnSetConstants().
这个方法在每帧
被调用.需要在创建一个shader的时候传递一个指针给IGPUProgrammingServices
IMaterialRendererServices接口的方法setVertexShaderConstant()用于设置shader需要的数据.
如果不用本例采用的汇编语言写的shader,而采样高级shader语言(比如HLSL,GLSL,CG)就不应该设置寄存器索引,而应该为元素设置变量名.
本文的详细流程请看源码的注释.
程序源码:
#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
IrrlichtDevice* device = 0;
bool UseHighLevelShaders = false;
//为了传递数据,继承video::IShaderConstantSetCallBack创建一个类
class MyShaderCallBack :public video::IShaderConstantSetCallBack
{
public:
//需要重写唯一的方法
virtual void OnSetConstants(
video::IMaterialRendererServices* services,
s32 userData)
{
video::IVideoDriver* driver = services->getVideoDriver();
/**
*mInvWorld矩阵,0
*mWorldViewProj矩阵,4
*mLightPos,8
*mLightColor,9
*mTransWorld,10
*都根据状态写两种方法
*/
//mInvWorld
core::matrix4 invWorld = driver->getTransform(video::ETS_WORLD);
invWorld.makeInverse();//求逆?
if (UseHighLevelShaders)
services->setVertexShaderConstant("mInvWorld", invWorld.pointer(), 16);
else
services->setVertexShaderConstant(invWorld.pointer(), 0/*寄存器*/, 4/*被设置的寄存器数量,每个寄存器可以存储4个浮点数*/);
//clipmatrix
core::matrix4 worldViewProj;
worldViewProj = driver->getTransform(video::ETS_PROJECTION);
worldViewProj *= driver->getTransform(video::ETS_VIEW);
worldViewProj *= driver->getTransform(video::ETS_WORLD);
if (UseHighLevelShaders)
services->setVertexShaderConstant("mWorldViewProj", worldViewProj.pointer(), 16);
else
services->setVertexShaderConstant(worldViewProj.pointer(), 4, 4);
//mlightpos,使用摄像机的坐标
core::vector3df pos = device->getSceneManager()->getActiveCamera()->getAbsolutePosition();
if (UseHighLevelShaders)
services->setVertexShaderConstant("mLightPos", reinterpret_cast<f32*>(&pos), 3);
else
services->setVertexShaderConstant( reinterpret_cast<f32*>(&pos),8, 1);
//mlightcol
video::SColorf col(0.0f, 1.0f, 1.0f, 0.0f);
if (UseHighLevelShaders)
services->setVertexShaderConstant("mLightColor", reinterpret_cast<f32*>(&col), 4);//这里之前错了,写错1
else
services->setVertexShaderConstant(reinterpret_cast<f32*>(&col),9, 1);
//mTransWorld
core::matrix4 world = driver->getTransform(video::ETS_WORLD);
world = world.getTransposed();//转置,列主?
if (UseHighLevelShaders)
services->setVertexShaderConstant("mTransWorld", world.pointer(), 16);
else
services->setVertexShaderConstant(world.pointer(), 10, 4);
}
};
int main(int argc, char** argv)
{
video::E_DRIVER_TYPE drivertype = driverChoiceConsole();
if (drivertype == video::EDT_COUNT)
return 1;
if (drivertype == video::EDT_OPENGL || drivertype == video::EDT_DIRECT3D9)
{
char i;
printf("Please press 'y' if you want to use high level shaders.\n");
std::cin >> i;
if (i == 'y')
UseHighLevelShaders = true;
}
device =createDevice(drivertype, core::dimension2d<u32>(720, 455), 32,
false, true, false, 0);
if (!device)
return 1;
device->setWindowCaption(L"10shapders");
video::IVideoDriver *driver = device->getVideoDriver();
scene::ISceneManager *smgr = device->getSceneManager();
gui::IGUIEnvironment *guiev = device->getGUIEnvironment();
/**
*1.加载
*如果使用Direct3D,加载顶点和像素shader程序,
*如果使用OpenGL,加载RGB fragment 和顶点程序
*/
io::path vsFileName;//vertex shader
io::path psFileName;//pixel shader
switch (drivertype)
{
case video::EDT_DIRECT3D8:
{
vsFileName = "../media/d3d8.vsh";
psFileName = "../media/d3d8.psh";
}
break;
case video::EDT_DIRECT3D9:
{
if (UseHighLevelShaders)
{
psFileName = "../media/d3d9.hlsl";
vsFileName = psFileName;//放在同一个文件中
}
else
{
vsFileName = "../media/d3d9.vsh";
psFileName = "../media/d3d9.psh";
}
}
break;
case video::EDT_OPENGL:
{
if (UseHighLevelShaders)
{
vsFileName = "../media/opengl.vert";
psFileName = "../media/opengl.frag";
}
else
{
vsFileName = "../media/opengl.vsh";
psFileName = "../media/opengl.psh";
}
}
break;
}
/**
*2.检查硬件是否支持,如果硬件不支持,则设置相应的文件名为0
*用处:当硬件只支持某一种着色器,则仍可以发挥支持的部分的功能
*当建立材质,若不检查,硬件若不能完全支持shader,则不能创建
*反之,仍可以用支持的部分建立
*/
if (!driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) &&//Direct3d像素着色器
!driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1))//opengl片元着色器
{
device->getLogger()->log("WARNING:Pixel shaders disabled "\
"because of missing driver/hardware support");
psFileName = "";
}
if (!driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1) &&//Direct3d顶点着色器
!driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1))//opengl顶点着色器
{
device->getLogger()->log("WARNING:Vertex shaders disabled "\
"because of missing driver/hardware support");
vsFileName = "";
}
/**
*3.创建新的材质
*在Irrlicht引擎中材质种类是32位的值,比如:video::EMT_SOLID
*获取值:通过IGPUProgrammingServices调用addShaderMaterialFromFiles()获得
*参数介绍:
*name 包含shader的文件名
*IShaderConstantSetCallBack类的指针 回调函数,传递参数,不传,设0
*base material 基础材质
*
*如果将shader写在string里,可以调用addShaderMaterial()
*
*这里创建两个不同材质:
*固态的:EMT_SOLID,透明的:EMT_TRANSPARENT_ADD_COLOR
*/
video::IGPUProgrammingServices* gpu = driver->getGPUProgrammingServices();
s32 newMaterialType1 = 0;
s32 newMaterialType2 = 0;
if (gpu)
{
//回调函数
MyShaderCallBack* mc = new MyShaderCallBack();
//根据shaders的版本不同,设置
//不同之处在于,高级语言写的shader需要设置入口和编译版本
//高级版本还有几何着色器
if (UseHighLevelShaders)
{
//有多个重载函数
newMaterialType1 = gpu->addHighLevelShaderMaterialFromFiles(
vsFileName,/*vertexShaderProgram*/
"vertexMain",/*vertexShaderEntryPointName,入口,默认main*/
video::EVST_VS_1_1,/*vsCompileTarget*/
psFileName,/*pixelShaderProgram*/
"pixelMain",/*入口*/
video::EPST_PS_1_1,/*psCompileTarget*/
mc,
video::EMT_SOLID,
0/*userData*/
);
newMaterialType2 = gpu->addHighLevelShaderMaterialFromFiles(
vsFileName,/*vertexShaderProgram*/
"vertexMain",/*vertexShaderEntryPointName,入口,默认main*/
video::EVST_VS_1_1,/*vsCompileTarget*/
psFileName,/*pixelShaderProgram*/
"pixelMain",/*入口*/
video::EPST_PS_1_1,/*psCompileTarget*/
mc,
video::EMT_TRANSPARENT_ADD_COLOR,
0/*userData*/);
}
else
{
//低级的shader,asm,arb_asm写的
newMaterialType1 = gpu->addShaderMaterialFromFiles(
vsFileName, psFileName, mc, video::EMT_SOLID);
newMaterialType2 = gpu->addShaderMaterialFromFiles(
vsFileName, psFileName, mc, video::EMT_TRANSPARENT_ADD_COLOR);
}
mc->drop();
}
/**
*4.测试新材质
*添加了一个立方体,设置不同的材质,
*设置一个文本节点显示此立方体所用的材质
*为立方体添加一个场景节点和动画
*/
//4.1节点1,使用新材质1
scene::ISceneNode* node = smgr->addCubeSceneNode(50);
node->setPosition(core::vector3df(0, 0, 0));
node->setMaterialTexture(0, driver->getTexture("../media/wall.bmp"));
node->setMaterialFlag(video::EMF_LIGHTING, false);
node->setMaterialType((video::E_MATERIAL_TYPE)newMaterialType1);
//添加文本节点
smgr->addTextSceneNode(guiev->getBuiltInFont(),
L"PS&VS&EMT_SOLID",
video::SColor(255, 255, 255, 255), node);
//添加动画
scene::ISceneNodeAnimator* anim = smgr->createRotationAnimator(
core::vector3df(0, 0.3f, 0));
node->addAnimator(anim);
anim->drop();
//4.2节点2,使用新材质2
node = smgr->addCubeSceneNode(50);
node->setPosition(core::vector3df(0, -10, 50));
node->setMaterialTexture(0, driver->getTexture("../media/wall.bmp"));
node->setMaterialFlag(video::EMF_LIGHTING, false);
node->setMaterialType((video::E_MATERIAL_TYPE)newMaterialType2);
//添加文本
smgr->addTextSceneNode(guiev->getBuiltInFont(), L"PS&VS&EMT_TRANSPARENT",
video::SColor(255, 255, 255, 255), node);
anim = smgr->createRotationAnimator(
core::vector3df(0, 0.3f, 0));
node->addAnimator(anim);
anim->drop();
//4.3不使用shader
node = smgr->addCubeSceneNode(50);
node->setPosition(core::vector3df(0, 50, 25));
node->setMaterialTexture(0, driver->getTexture("../media/wall.bmp"));
node->setMaterialFlag(video::EMF_LIGHTING, false);
smgr->addTextSceneNode(guiev->getBuiltInFont(), L"NOSHADER",
video::SColor(255, 255, 255, 255), node);
/**
*5.添加天空盒子
*先关闭mipmap
*/
driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
smgr->addSkyBoxSceneNode(
driver->getTexture("../media//irrlicht2_up.jpg"),
driver->getTexture("../media//irrlicht2_dn.jpg"),
driver->getTexture("../media//irrlicht2_lf.jpg"),
driver->getTexture("../media//irrlicht2_rt.jpg"),
driver->getTexture("../media//irrlicht2_ft.jpg"),
driver->getTexture("../media//irrlicht2_bk.jpg")
);
//开启mipmap
driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);
// smgr->addCameraSceneNode(0, core::vector3df(0, 30, -40), core::vector3df(0, 5, 0));
smgr->addCameraSceneNodeFPS();
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"Campfire FX example [";
str += driver->getName();
str += "]FPS.",
str += fps;
device->setWindowCaption(str.c_str());
lastFPS = fps;
}
}
device->drop();
return 0;
}