Irrlicht学习笔记(9)--Shader

说明:

这个例程介绍如何使用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;
}



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值